Multi-Tenant SaaS on Azure: Tenant-Scoped Auth
Tenant isolation fails silently when background jobs use shared identities. Here's how to scope service auth per tenant—workload identity, per-tenant service principals, audit trails, and what to do when a tenant admin leaves.

If your multi‑tenant SaaS architecture on Azure treats service authentication the same as user login, you’re already leaking between tenants. The real isolation wall isn’t built at the sign‑in page—it’s built for the background jobs, the pipeline runs, the silent machine‑to‑machine calls that nobody watches. Miss that, and a single compromised service principal becomes a skeleton key to every tenant’s data.
We proved this the hard way while shipping Taqaddum, a bilingual school assessment platform where each school is a tenant. Schools run Arabic, Islamic Education, and Social Studies assessments, and every cohort’s baseline‑to‑end‑of‑year comparison must stay incorruptible. A shared service account that generated progress reports would have been a data‑mixing disaster. So we built tenant‑scoped service identities from day one.
This guide walks through the architecture and the step‑by‑step implementation—service principals per tenant, workload identity, per‑tenant Key Vault secrets, Azure DevOps integrations—and shows you what to do when a tenant admin leaves without notice.
Why tenant‑scoped service auth is the real hard part of multi‑tenant SaaS architecture on Azure
Answer: Isolating human identity with Azure AD B2C or B2B is a solved problem. The cracks appear when a daemon, a CI/CD agent, or a webhook handler needs to act on one tenant’s behalf. In a shared compute model—one Function App, one AKS cluster, or one DevOps pipeline pool—the service principal becomes the blast radius.
Traditional multi‑tenant architecture on Azure often leans on a single enterprise application per service tier. That works until you need to:
- Invoke Azure DevOps to deploy infrastructure changes for a single tenant
- Pull secrets that belong to only one school’s SQL database
- Audit which tenant triggered a data export job
Without tenant‑scoped identities, the operational reality is that you either over‑permission one principal or you build fragile tags-on-tags-on-tags access logic that auditors hate.
Microsoft’s identity considerations for multitenant solutions reinforce the need to treat non‑human identities with the same isolation principle applied to users. Yet the docs focus on delegation between tenants; they don’t spell out the operational playbook when a tenant admin leaves and the automation breaks.
Isolation models and why one service principal per tenant scales
Model | Description | Service Auth Isolation | Operational Complexity |
|---|---|---|---|
Shared service principal + tenant tags | One Azure AD app, conditional access policies tagged by tenant metadata | None—a single credential hits all tenants | Low upfront, breaks down under audit |
Per‑tenant service principal (our approach) | One dedicated app registration and service principal created at tenant provisioning | Hard wall—each tenant gets its own identity | Higher automation effort, clean blast radius |
Workload identity federation (per‑tenant) | No secrets; trust relationship between an external platform (e.g., Azure DevOps) and the service principal | Cryptographic proof per tenant, no static credential | Moderate, requires OIDC configuration per pipeline |
For a platform that manages sensitive school data like Taqaddum, the per‑tenant model is non‑negotiable. The rest of this guide implements that model end‑to‑end.
Step 1: Provision a service principal per tenant during sign‑up
Takeaway: Automate app registration and service principal creation inside your tenant provisioning flow. Do it once—never expose a human to the client secret.
# Azure CLI: create a dedicated application + service principal for a new tenant
$tenantId = "school-a-tenant-id"
$appName = "taqaddum-svc-${tenantId}"
$app = az ad app create --display-name $appName --sign-in-audience AzureADMyOrg --output json | ConvertFrom-Json
$sp = az ad sp create --id $app.appId --output json | ConvertFrom-Json
# Generate a secret valid for 24 months (rotate proactively)
$secret = az ad app credential reset --id $app.appId --display-name "default" --years 2 --output json | ConvertFrom-JsonYou now have a service principal that holds zero permissions. Next, scope what it can touch.
Step 2: Store secrets per tenant in Azure Key Vault with RBAC
Takeaway: Every tenant gets a Key Vault. The service principal from Step 1 receives a data‑plane role assignment scoped to only its own vault.
# Create per-tenant Key Vault
az keyvault create --name kv-saastant-${tenantId} --resource-group rg-saas-${tenantId} --location eastus
# Store client secret
az keyvault secret set --vault-name kv-saastant-${tenantId} --name "TaqaddumSvcSecret" --value $secret.secretText
# Assign Key Vault Secrets User role to the service principal on this vault
az role assignment create --assignee $sp.id --role "Key Vault Secrets User" --scope "/subscriptions/<subscription-id>/resourceGroups/rg-saas-${tenantId}/providers/Microsoft.KeyVault/vaults/kv-saastant-${tenantId}"Now the backend service that needs to act for tenant “school‑a” can fetch the secret using managed identity or workload federation—and it will fail if it tries any other vault.
Step 3: Use workload identity federation for Azure DevOps pipelines
Takeaway: Don’t embed secrets in pipeline variables. Use OpenID Connect (OIDC) to establish trust between an Azure DevOps service connection and the per‑tenant service principal.
# Azure DevOps service connection snippet (azure-pipelines.yml)
- task: AzureCLI@2
inputs:
azureSubscription: 'taqaddum-school-a-oidc' # service connection built on federation
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
# The pipeline runs as the school‑a service principal
az storage blob upload --account-name stschoola --container-name reports --file ./progress.xlsxUnder the hood, you configure the app registration with a federated credential that trusts the Azure DevOps organization and service connection issuer. No rotated secrets. If a tenant admin leaves, revoking the federation trust is instant—zero impact on other tenants.
Step 4: What happens when a tenant admin leaves: rotation and burst credential invalidation
Takeaway: The classic “admin left and took the only secret with them” panic disappears when you own the identity stack. You have two levers: rotate the secret and, if necessary, delete the whole service principal to kill access across all integrations.
# Immediate rotation: generate a new secret, delete the old one
$newSecret = az ad app credential reset --id $app.appId --display-name "rotation-$(Get-Date -Format 'yyyyMMddHHmmss')" --years 1 --output json | ConvertFrom-Json
az ad app credential delete --id $app.appId --key-id $oldKeyId
# Update Key Vault
az keyvault secret set --vault-name kv-saastant-${tenantId} --name "TaqaddumSvcSecret" --value $newSecret.secretTextBecause the secret lives in the tenant’s own vault and the application code fetches it at invocation time, the next data‑plane operation picks up the new value. No deployment slot swaps, no cross‑tenant coupling.
If the admin leave is hostile or you suspect compromise, delete the service principal outright:
az ad sp delete --id $sp.idThe tenant’s automation stops cold—and only that tenant’s. Every other school keeps running.
Auditing service actions by tenant context
Takeaway: The service principal’s appId becomes the tenant discriminator in every log. Tie it back with Azure Policy diagnostics and Log Analytics.
Send all activity logs to a central workspace and query by identity:
AzureActivity
| where Caller contains "taqaddum-svc-"
| extend TenantId = tostring(split(Caller, "svc-")[1])
| summarize count() by TenantId, bin(TimeGenerated, 1h)Combine with Key Vault audit logs to prove “Secret for school‑a was read 47 times by identity X between 08:00 and 09:00”. That’s the level of granularity that passes a school board’s data protection review.
Real‑world proof: how Taqaddum’s assessment platform kept schools isolated
When we built Taqaddum, we embedded this pattern into the core architecture. Each school’s identity is provisioned automatically via a function triggered by subscription confirmation. The per‑tenant service principal owns the right to:
- Pull exam templates from the school’s dedicated Blob Storage container (using Azure Blob Storage tenant‑scoped access)
- Generate Excel progress reports and push them back to the same container
- Feed attainment data into the shared reporting engine, but only after the engine verifies the caller’s tenant claim
No human ever touches a secret. When a school’s admin changes, the rotation function runs during a maintenance window and no other tenant notices. The baseline‑mid‑year‑end‑of‑year comparisons remain locked to the correct school because the identity that assembled them could only see that school’s data.
If you’re designing a multi‑tenant SaaS architecture on Azure that must hold up under compliance scrutiny, the identity layer is where the hard work hides. We’ve taken lab‑proven SaaS architecture approaches that weigh build‑versus‑rent decisions, but the per‑tenant service principal pattern is a non‑negotiable we build into every client platform we design at techpotions.
FAQ
How do I create a service principal per tenant automatically during onboarding?
Use Microsoft Graph to create an Azure AD application and service principal for each tenant during sign‑up. Automate it with the Graph API or Azure CLI, and store the one‑time client secret in a tenant‑dedicated Key Vault. A provisioning function that fires on subscription confirmation can raise the whole identity without any manual steps.
What’s the best way to rotate per‑tenant service principal secrets without downtime?
Rotate secrets with Key Vault versioning and a cron‑driven function that regenerates the client secret, stores the new version, and updates the downstream application configuration. For production pipelines that can use OIDC, switch to workload identity federation—keys are rotated transparently by Azure AD itself, and there’s no secret to expire.
Can I use a single service principal with tenant‑scoped permissions instead of one per tenant?
You can, if you add tenant‑aware access policies that restrict the principal’s scope by tenant metadata. But that pattern creates a single point of compromise: if that principal is leaked, one attacker can iterate across all tenants. Per‑tenant service principals give you hard walls where a stolen credential for one tenant can’t reach another. When audit and blast‑radius isolation matter, per‑tenant identities are the clear choice.