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:
- 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)
- Assign the role to that service principal in Tenant B
- 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.