App to Blob Storage with a managed identity
The handoff from an App Service, Functions, Container Apps or VM workload to Blob or Data Lake storage without keys — user-assigned identity, Storage Blob Data Contributor at the container, Shared Key off, a network path that works for same-region callers (VNet rule or private endpoint, not an IP rule), BlobServiceClient with ManagedIdentityCredential — with known-bad configurations, their exact error codes, and which side is wrong.
markdownpatternv12GNJ-V717stage: buildtrust: officialtrust: fieldservice: storageservice: managed-identityservice: rbacinterface: iacinterface: sdkinterface: cliApp Service Blob Storage managed identityFunctions storage managed identityContainer Apps storage managed identityStorage Blob Data Contributor managed identityAuthorizationPermissionMismatch managed identityKeyBasedAuthenticationNotPermitted connection stringAuthorizationFailure App Service same regionstorage IP rule App Service outbound IPstorage private endpoint App ServiceManagedIdentityCredential BlobServiceClientwhich side is wrong storage
App side: the app runs as a managed identity and asks for a token for https://storage.azure.com. Storage side: the account's network lets the app in, then a data role on the account or container grants the operation. Every failure below is one of those, and the error code tells you which.
Known-good
- A user-assigned identity
id-orders, attached to the app; its client ID in the app settingAPP_IDENTITY_CLIENT_ID. - Storage Blob Data Contributor for the identity's
principalIdon the container (or account),principalType: ServicePrincipal,guid()name. Reader if it only reads. - Account:
allowSharedKeyAccess: false,minimumTlsVersion: 'TLS1_2',allowBlobPublicAccess: false(guides/storage/create-secure-storage-account.md). - Network, for an app in the same region: VNet integration into a subnet that has the
Microsoft.Storageservice endpoint and a VNet rule on the account; orpublicNetworkAccess: Disabledwith a private endpoint forblob(anddfsfor Data Lake) and theprivatelink.blob.core.windows.netzone linked to the app's VNet. - Code:
new BlobServiceClient("https://<acct>.blob.core.windows.net", new ManagedIdentityCredential({ clientId: process.env.APP_IDENTITY_CLIENT_ID })), one client per process (guides/storage/access-blobs-from-typescript.md).
Setting up the identity: guides/managed-identity/use-managed-identity.md. Assigning the role: guides/rbac/assign-roles.md.
Known-bad
| Configuration | Symptom |
|---|---|
| Identity holds Owner or Contributor on the account, no data role | 403 AuthorizationPermissionMismatch This request is not authorized to perform this operation using this permission. |
| Role assigned a minute ago | Same 403 for 10–30 minutes, then it works |
| Role on a group containing the identity | Same 403 for hours: managed identity tokens are cached (about 24 h) with the group memberships they were issued with |
| App still uses a connection string after Shared Key was turned off | 403 KeyBasedAuthenticationNotPermitted Key based authentication is not permitted on this storage account. |
Firewall defaultAction: Deny with the app's outbound IPs in ipRules, app in the same region | 403 AuthorizationFailure This request is not authorized to perform this operation.: same-region traffic arrives on private addresses, so IP rules never match |
Relying on bypass: AzureServices for App Service or Functions | Same AuthorizationFailure: they aren't trusted services |
| Private endpoint created, DNS zone not linked to the app's VNet | The name resolves to the public IP: AuthorizationFailure if public access is denied, or a timeout |
Data Lake account with only a blob private endpoint | Directory and ACL operations through dfs fail |
Two user-assigned identities, DefaultAzureCredential with no client ID | Token for the wrong identity or none: 403, or ManagedIdentityCredential errors |
Custom HTTP code requests a token for another audience (for example https://management.azure.com) | 401 Server failed to authenticate the request. Please refer to the information in the www-authenticate header. |
Which side is wrong
- No token (
ManagedIdentityCredential…, IMDS orIDENTITY_ENDPOINTerrors) → the app: identity attached, right client ID (reference/managed-identity/errors.md). - 401 → the token's audience. Use the SDK; the scope is
https://storage.azure.com/.default. - 403
AuthorizationFailure→ the storage account's network: firewall, public network access, private endpoint DNS (reference/storage/network-and-firewall.md). - 403
AuthorizationPermissionMismatch→ the role: missing, on Owner/Contributor only, on a group, not propagated, or (Data Lake, no role) a missing ACL Execute on a parent. - 403
KeyBasedAuthenticationNotPermitted→ the app's configuration still uses a key or a non-user-delegation SAS.
az storage account show -n <acct> --query "{public:publicNetworkAccess, firewall:networkRuleSet, sharedKey:allowSharedKeyAccess}" answers the storage-side questions in one call; az role assignment list --assignee <principal-id> --all -o table answers the role question.