Skip to content
techpotions
Azure · SaaS Architecture · multi-tenant · workload identity · service principals · Key Vault · tenant isolation · Azure DevOps · assessment-platformNovember 11, 20268 min read

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.

Cover illustration for “Multi-Tenant SaaS on Azure: Tenant-Scoped Auth”

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.

Text
# 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-Json

You 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.

Shell
# 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.

YAML
# 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.xlsx

Under 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.

Text
# 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.secretText

Because 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:

Text
az ad sp delete --id $sp.id

The 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:

Text
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.

Written by
techpotions
All entries
Migrate WordPress to New Domain Without SEO Loss
The weekly

One email a week, from the workshop.

What we published, what we shipped, and the free packs as they land. No drip sequence, no webinar, unsubscribe in one click.