Azure · Entra ID · March 2025

Cross-tenant RBAC in Azure: a problem I didn't see coming

Hit an unexpected AuthorizationFailed error with cross-tenant role assignments and MSI identity in a production environment. Here's how I diagnosed and resolved it, and why it happens.

I was working with a partner on a multi-tenant Azure setup — a common pattern in CSP environments where a service provider manages resources across multiple customer tenants. The architecture involved a managed identity (MSI) on a resource in Tenant A that needed to call an API and write to storage in Tenant B. On the surface, this seems straightforward. In practice, it required understanding some non-obvious constraints in how Entra ID handles cross-tenant identity and RBAC.

The error

The managed identity was configured, the role assignment was in place, and the application was still returning AuthorizationFailed (403) when attempting to write to the Blob Storage in Tenant B. The error message was not particularly helpful — it identified the principal but didn't explain why the assignment wasn't being honoured.

What was actually happening

Managed identities are fundamentally scoped to their home tenant. A system-assigned managed identity created in Tenant A is an object in Tenant A's Entra ID directory. When it tries to access resources in Tenant B, Tenant B needs to trust a representation of that identity — and by default, it doesn't know about it.

The role assignment I'd created in Tenant B was pointing to the managed identity's object ID from Tenant A. But from Tenant B's perspective, that object ID doesn't correspond to any known principal — it's not in their directory. So the assignment existed, but Azure's authorisation engine in Tenant B couldn't resolve the principal and the request was denied.

The resolution

The correct approach for true cross-tenant access with managed identity involves using workload identity federation or establishing a B2B service principal in the target tenant. For this specific scenario, we used the B2B approach:

  1. Create a service principal representation of the managed identity in Tenant B (via an app registration or by inviting the MSI as an external identity where the tenant's external collaboration settings permit)
  2. Assign the role to that service principal in Tenant B
  3. Update the application to acquire tokens for Tenant B's resource endpoint, not its home tenant

The token acquisition piece is the part that's easy to miss. The managed identity's DefaultAzureCredential will by default request a token scoped to the home tenant. For cross-tenant access, you need to explicitly specify the target tenant in the credential configuration.

# Example: Specifying the target tenant when acquiring a token
# (Azure SDK for Python)
from azure.identity import ManagedIdentityCredential
from azure.storage.blob import BlobServiceClient

credential = ManagedIdentityCredential()
# The resource scope must point to the target tenant's resource
blob_client = BlobServiceClient(
    account_url="https://<storage-account>.blob.core.windows.net",
    credential=credential
)

What I learned

The RBAC layer in Azure is only half the picture. Identity resolution — whether the target resource's tenant can actually find and validate the principal — is equally important and far less visible in the portal. If you're building multi-tenant automation or cross-tenant resource access, test the full auth chain early, not after you've built everything else.

Also: the Azure Activity Log in the target tenant was crucial for diagnosis. The failed authorisation events showed the principal ID being evaluated, which helped me confirm the mismatch between what was being requested and what the tenant recognised.

← Back to blog