Blob Storage Access Denied Errors in Azure
Azure Blob Storage access denied errors come with two distinct HTTP 403 error codes that each point to a different root cause. This page covers the five most common causes of Blob Storage authorization failures, the Azure CLI commands to diagnose each one, and how to fix them without granting excessive permissions.
The error messages and what they mean
Two error responses cover most Blob Storage 403 failures:
AuthorizationFailure
This request is not authorized to perform this operation.
RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Time: 2026-03-19T10:00:00.0000000ZThis appears when the identity (user, managed identity, or service principal) has no RBAC role on the storage account or container, or when shared key access is disabled and the request used a key-based method.
AuthorizationPermissionMismatch
This request is not authorized to perform this operation using this permission.This appears when an RBAC role exists but it is the wrong role — for example, Storage Blob Data Reader attempting to upload a blob (which requires Storage Blob Data Contributor).
A third error is specific to anonymous access attempts:
PublicAccessNotPermitted
Public access is not permitted on this storage account.This means the blob container was previously public but the storage account now has public access disabled.
Cause 1: RBAC role assigned at wrong scope
The most common cause of Blob Storage access denied is an RBAC role assigned at the wrong scope. The role must be assigned at the storage account level, resource group level, or subscription level — not at a parent management group that does not inherit to the storage account properly, and not confused with a role assigned at a container level via the portal.
Check all effective role assignments for an identity on a storage account:
STORAGE_ID=$(az storage account show \
--name mystorageaccount \
--resource-group myResourceGroup \
--query id -o tsv)
PRINCIPAL_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # Object ID
az role assignment list \
--assignee "$PRINCIPAL_ID" \
--scope "$STORAGE_ID" \
--include-inherited \
--include-groups \
--output tableThe --include-inherited flag is essential — it shows roles assigned at the subscription and resource group level that are inherited by this storage account. Omitting it will show an empty list even when valid inherited assignments exist.
The correct roles for Blob Storage operations:
| Operation | Required Role |
|---|---|
| Read blobs | Storage Blob Data Reader |
| Read and write blobs | Storage Blob Data Contributor |
| Full blob control including ACLs | Storage Blob Data Owner |
| List containers and read properties | Reader (ARM role, not data role) |
The ARM-level Reader or Contributor roles do NOT grant data access to Blob Storage. Contributor allows you to manage the storage account resource (change settings, regenerate keys) but does not allow reading or writing blob data. Data access requires one of the Storage Blob Data roles.
Assign the correct role:
az role assignment create \
--assignee "$PRINCIPAL_ID" \
--role "Storage Blob Data Contributor" \
--scope "$STORAGE_ID"Cause 2: Shared key access disabled
Azure Storage accounts can have shared key access disabled, which means connection strings with AccountKey= and SAS tokens generated from the account key stop working immediately. This is an organizational policy often applied via Azure Policy.
Check if shared key access is disabled:
az storage account show \
--name mystorageaccount \
--resource-group myResourceGroup \
--query "allowSharedKeyAccess" \
--output tsvIf this returns false, all key-based authentication fails. Applications must switch to either:
- RBAC authentication using a managed identity or service principal
- User-delegation SAS tokens (generated from an Entra ID identity, not an account key)
Re-enable shared key access if needed (confirm this is allowed by your security policy):
az storage account update \
--name mystorageaccount \
--resource-group myResourceGroup \
--allow-shared-key-access trueTo generate a user-delegation SAS token that works even with shared key disabled:
# Must be logged in with az login first
EXPIRY=$(date -u -d '+1 day' +%Y-%m-%dT%H:%MZ)
az storage blob generate-sas \
--account-name mystorageaccount \
--container-name mycontainer \
--name myblob.txt \
--permissions r \
--expiry "$EXPIRY" \
--auth-mode login \
--as-user \
--output tsvThe --auth-mode login --as-user flags use the current Entra ID identity instead of the account key to sign the SAS token.
Cause 3: SAS token expired or wrong permissions
SAS token errors manifest as 403 without a detailed error code in the response body. The error appears as:
AuthorizationFailure: Server failed to authenticate the request.Common SAS token problems:
- Token expired: the
se(signed expiry) parameter is in the past - Wrong permissions: the
spparameter does not include the required permission (rfor read,wfor write,dfor delete,lfor list) - IP restriction: the
sipparameter restricts the token to specific IPs that the current client does not match - Wrong protocol: the
sprparameter is set tohttpsbut the client is usinghttp - Signed resource mismatch: a container-level SAS (
sr=c) cannot access a specific blob path with a service SAS (sr=b)
Decode a SAS URL to inspect its parameters:
SAS_URL="https://mystorageaccount.blob.core.windows.net/mycontainer/myblob?sv=2020-08-04&se=2026-01-01T00%3A00%3A00Z&sr=b&sp=r&sig=..."
# Extract the query string and decode URL encoding
echo "$SAS_URL" | sed 's/.*?//' | tr '&' '\n' | python3 -c "
import sys, urllib.parse
for line in sys.stdin:
line = line.strip()
if '=' in line:
key, val = line.split('=', 1)
print(f'{key}: {urllib.parse.unquote(val)}')
"The se value is the expiry in ISO 8601 format. Compare to the current UTC time to check if it has passed.
Cause 4: Public access disabled blocking anonymous reads
Storage accounts that previously allowed anonymous blob access may have public access disabled by Azure Policy or manual configuration. Applications or users trying to access blobs without authentication will fail with PublicAccessNotPermitted.
Check the public access setting:
az storage account show \
--name mystorageaccount \
--resource-group myResourceGroup \
--query "allowBlobPublicAccess" \
--output tsvCheck individual container access level:
az storage container show-permission \
--account-name mystorageaccount \
--name mycontainer \
--auth-mode loginIf public access was previously used intentionally (for a public website or public assets), re-enable it at the account level and set the container to blob (blob-level public read) or container (full container listing and blob read):
# Enable public access at account level
az storage account update \
--name mystorageaccount \
--resource-group myResourceGroup \
--allow-blob-public-access true
# Set container to public blob access
az storage container set-permission \
--account-name mystorageaccount \
--name mycontainer \
--public-access blob \
--auth-mode loginIf public access should remain disabled, applications must authenticate using RBAC or SAS tokens — there is no third option.
Cause 5: Network firewall blocking public endpoint access
When a storage account has network rules configured to deny public access (or allows only specific VNets), requests from outside those allowed networks receive an HTTP 403:
AuthorizationFailure: This request is not authorized to perform this operation.
(The storage account is configured to reject requests from outside specific virtual networks or IP ranges)Check network rules on the storage account:
az storage account show \
--name mystorageaccount \
--resource-group myResourceGroup \
--query "networkRuleSet" \
--output jsonKey fields in the network rule set:
defaultAction: Deny— all traffic is blocked except explicitly allowed sourcesipRules— list of allowed public IP rangesvirtualNetworkRules— list of allowed VNet subnetsbypass— services that bypass the rules (Logging, Metrics, AzureServices)
Add the current client IP to the allowed list:
MY_IP=$(curl -s https://api.ipify.org)
az storage account network-rule add \
--resource-group myResourceGroup \
--account-name mystorageaccount \
--ip-address "$MY_IP"Add a VNet subnet:
SUBNET_ID=$(az network vnet subnet show \
--resource-group myResourceGroup \
--vnet-name myVNet \
--name mySubnet \
--query id -o tsv)
az storage account network-rule add \
--resource-group myResourceGroup \
--account-name mystorageaccount \
--subnet "$SUBNET_ID"If the storage account uses a private endpoint and the client is inside the VNet, verify DNS resolution returns the private IP (see the DNS troubleshooting page) — accessing via the public endpoint from inside the VNet when a private endpoint exists will be blocked by the network rules.
Common mistakes
- Assigning the ARM Contributor role and expecting data access. Contributor allows management of the storage account resource but does not grant any data plane permissions. You need Storage Blob Data Contributor (or Reader/Owner) for data access. Many engineers assign Contributor and are confused when they still cannot read blobs.
- Not using —include-inherited when checking role assignments. A managed identity or service principal may have been assigned the storage role at the subscription or resource group level rather than directly on the storage account. Running az role assignment list without —include-inherited shows zero assignments even though access should work.
- Generating SAS tokens from an account key after allowSharedKeyAccess is disabled. The Azure portal and Azure CLI will generate key-based SAS tokens without warning you that they will not work. Test SAS tokens immediately after generation by making a test request, not by inspecting the parameters.
- Forgetting that private endpoint network rules do not apply the same as public network rules. When a storage account has a private endpoint, clients inside the VNet should always route to the private IP — but if DNS is misconfigured and they hit the public endpoint, the network rules block them with a 403. The fix is DNS, not network rules.
Summary
- AuthorizationFailure means no valid RBAC role exists — assign Storage Blob Data Reader, Contributor, or Owner using
az role assignment createwith—include-inheritedto verify existing assignments. - If shared key access is disabled, connection strings and key-derived SAS tokens fail — switch to managed identity RBAC or user-delegation SAS tokens.
- SAS token failures are often expired tokens or missing permission flags — decode the SAS parameters to check the expiry and
spvalue. - Network rule 403 errors from inside a VNet usually indicate DNS resolution is returning the public endpoint IP instead of the private endpoint IP.
Frequently asked questions
What is the difference between AuthorizationFailure and AuthorizationPermissionMismatch in Blob Storage errors?
AuthorizationFailure means the identity has no permission to perform the operation at all. AuthorizationPermissionMismatch means the identity has some RBAC role but not the specific one needed — for example, Storage Blob Data Reader trying to write a blob. Both require checking RBAC assignments, but the latter means the role exists at the wrong level or is the wrong role type.
Why does my RBAC role assignment on a storage account not grant access to a specific container?
RBAC role assignments are inherited from parent scopes. Assigning Storage Blob Data Reader at the storage account level grants access to all containers. However, if you assigned the role at the subscription or resource group level, it may not appear when checking the storage account directly. Use az role assignment list --include-inherited to see all effective assignments including inherited ones.
Can I use both storage access keys and RBAC on the same storage account?
Yes, by default both are enabled. Storage access keys bypass RBAC entirely — a valid key gives full access regardless of RBAC assignments. If you disable shared key access (allowSharedKeyAccess = false), access keys and SAS tokens derived from keys stop working immediately. Only RBAC and user-delegation SAS tokens remain valid.