The Scenario
You’re deploying a new microservice on a Friday afternoon that needs to read blobs from a storage account. The CI/CD pipeline completes successfully, but the application immediately starts throwing 403 errors when trying to access the storage account from your AKS cluster.
Symptoms
This request is not authorized to perform this operationin application logs403 (This operation is not permitted)in Azure Storage SDK responsesAuthenticationFailederror code in Azure Storage REST API responses
Root Cause
Azure Storage accounts have multiple security layers that must all be satisfied simultaneously. The most common cause is a mismatch between the identity being used (managed identity, service principal, or SAS token) and the RBAC permissions on the storage account, combined with network restrictions that block the request before authentication is even checked.
Resolution (Step-by-Step)
-
Check if the storage account has public network access disabled
az storage account show --name <storage-account> --resource-group <rg> --query "networkRuleSet.defaultAction"If this returns
Deny, you need to either enable a service endpoint or private endpoint for your client. -
Add a service endpoint for your subnet
az network vnet subnet update \ --resource-group <rg> \ --vnet-name <vnet> \ --name <subnet> \ --service-endpoints Microsoft.Storage -
Add the subnet to the storage account firewall
az storage account network-rule add \ --resource-group <rg> \ --account-name <storage-account> \ --vnet <vnet> \ --subnet <subnet> -
Verify RBAC permissions for the managed identity
az role assignment list --assignee <managed-identity-principal-id> --scope /subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Storage/storageAccounts/<storage-account>Ensure the role is
Storage Blob Data ContributororStorage Blob Data Reader, not justContributor. -
If using SAS tokens, regenerate and validate
az storage account keys list --account-name <storage-account> --resource-group <rg> az storage container generate-sas --account-name <storage-account> --name <container> --permissions r --expiry 2026-12-31 --account-key <key>
Why This Sometimes Doesn’t Work
The biggest gotcha is that RBAC roles are evaluated after network rules. If your network rule blocks the request, you’ll get a 403 even with the correct RBAC permissions. Also, managed identities in AKS require az aks update --enable-managed-identity to be explicitly enabled on the cluster — just assigning the role isn’t enough if the cluster itself isn’t configured for managed identity.
Verification
az storage blob exists --account-name <storage-account> --container-name <container> --name test.txt --auth-mode login
Expected output: True
Common Follow-up Questions
Why does the Azure portal work but my app doesn’t? The portal uses your user identity which may have different RBAC permissions than your application’s managed identity or service principal.
Do I need both RBAC and network rules? No, network rules are separate from RBAC. You need either public access with RBAC, or private/service endpoint access with RBAC. Both layers must pass.
Can I just allow all networks temporarily?
Yes, but only for debugging. Set defaultAction to Allow with az storage account update --default-action Allow. Revert immediately after testing.
Prevent This in the Future
The most common cause of this error is a missing network rule or RBAC assignment that isn’t caught until deployment. Set up monitoring with Better Stack to alert on 403 errors from your storage account before they become production incidents.