A shared access signature (SAS) is a token you append to a storage URL so a client can read or write specific data for a limited time. For blob links that should expire, create a user delegation SAS: it is signed with Microsoft Entra credentials rather than the storage account key, which is why Microsoft recommends it over the other two SAS types. The sections below show how each type works, how to create and revoke a user delegation SAS with the Azure CLI, and how to shut off account keys entirely.
Who this is for and what you will have at the end
This guide is for developers and administrators who need to hand out time-limited access to Azure Blob Storage, for example a download link for a customer, an upload target for a web app, or a one-off file transfer to a partner. You do not need to be an Azure expert, but you should be comfortable with a terminal and know what a storage account and container are.
By the end you will be able to:
- Explain the difference between user delegation, service and account SAS tokens.
- Create a read-only, expiring link to a single blob with the Azure CLI.
- Revoke user delegation SAS tokens when one leaks.
- Disallow account key authorization so that only Microsoft Entra-based access works.
If you are still choosing how a storage account replicates data, see Azure storage account redundancy: LRS, ZRS and GRS. For a similar keyless pattern on another service, see Azure OpenAI keyless access with managed identity.
How a SAS works
A SAS is a string of query parameters appended to the URI of a storage resource. One of those parameters, the signature, is computed from the other parameters and signed with a key. When a request arrives, Azure Storage checks the parameters and the signature. If the signature is valid, the request is authorized; otherwise it is declined with error code 403 (Forbidden).
The token is generated on the client side, for example by the Azure CLI or an SDK. Azure Storage does not track tokens in any way, you can create an unlimited number of them, and there is no API that lists the SAS tokens issued for an account.
Two practical consequences follow:
- Anyone who holds the token can use it, within the permissions and time window it encodes.
- Microsoft notes that generating a SAS cannot be audited. Anyone with the account key, or with an Azure role that lets them generate a SAS, can do so without the storage account owner knowing. Restrict who has those permissions.
The ? that separates a URL from its query string is not part of the SAS token. When you build a URL by hand, add the ? yourself.
The three SAS types
Azure Storage supports three kinds of SAS. The difference that matters most is what signs the token.
| Type | Signed with | Scope | Stored access policy |
|---|---|---|---|
| User delegation SAS | User delegation key (Microsoft Entra credentials) | Blob Storage (including Data Lake Storage), Queue, Table or Azure Files | Not supported |
| Service SAS | Storage account key | One service: Blob, Queue, Table or Azure Files | Supported |
| Account SAS | Storage account key | One or more storage services, including service-level operations | Not supported |
A user delegation SAS and an account SAS must be ad hoc, meaning the start time, expiry time and permissions are all written into the token. A service SAS can instead reference a stored access policy defined on a container, queue, table or file share, and inherit its constraints from the policy.
Why Microsoft recommends the user delegation SAS
Microsoft's documentation states that when you need a SAS, a user delegation SAS provides superior security because it is secured with Microsoft Entra credentials. You never need to keep the account key in your code or configuration. The key that signs the SAS is derived from an identity you can control with Azure RBAC, and you can revoke it without rotating the account keys that other workloads may depend on.
The trade-offs to know about:
- Lifetime is capped at 7 days (explained below).
- The identity that creates it needs a role that includes the generateUserDelegationKey action.
- No stored access policies, so constraints are fixed inside the token.
Prerequisites
You need:
- A storage account with a container and at least one blob.
- Azure CLI version 2.0.78 or later. Check with
az --version. - A Microsoft Entra account that can sign in to the CLI.
- An Azure role for that account that includes
Microsoft.Storage/storageAccounts/blobServices/generateUserDelegationKey, assigned at the storage account, resource group or subscription scope.
Microsoft's example uses the built-in Storage Blob Data Contributor role, which includes that action. Assign it at the storage account level:
az role assignment create \
--role "Storage Blob Data Contributor" \
--assignee [email protected] \
--scope "/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.Storage/storageAccounts/<storage-account>"If you cannot assign roles yourself, ask the account owner or an administrator. Sign in before you continue:
az loginCreate a user delegation SAS for a blob
Use az storage blob generate-sas. Two parameters are required for a user delegation SAS: --auth-mode login so that requests are authorized with your Microsoft Entra credentials, and --as-user so that the token returned is a user delegation SAS.
az storage blob generate-sas \
--account-name <storage-account> \
--container-name reports \
--name q3-summary.pdf \
--permissions r \
--expiry 2026-11-05T00:00:00Z \
--auth-mode login \
--as-user \
--full-uriWhat each part does:
--permissions rgrants read only. For a blob, the supported user delegation permissions are Add, Create, Delete, Read and Write, which you can combine. The documentation example usesacdrw.--expiryis when the token stops working. The start and expiry you give are also used as the start and expiry of the user delegation key.--full-urireturns the blob URL with the token already appended, so you can share the output directly.
The result is a URL of the form https://<storage-account>.blob.core.windows.net/reports/q3-summary.pdf?se=...&sp=r&sv=...&sr=b&skoid=...&sktid=...&sig=.... Without --full-uri you get only the token, and you must add ? before it.
Keep expiry within 7 days
The maximum interval over which a user delegation key is valid is 7 days from the start date. The SAS is invalid once the key expires, so a SAS with an expiry later than 7 days is still only valid for 7 days. Choose an expiry inside that window, and shorter if the task allows it. Short expiry also limits what a leaked link can cost you, since the account is billed for any usage, including usage through a SAS.
Create a container-level SAS
To share a whole container, use az storage container generate-sas. Supported user delegation permissions on a container are Add, Create, Delete, List, Read and Write.
az storage container generate-sas \
--account-name <storage-account> \
--name reports \
--permissions lr \
--expiry 2026-11-05T00:00:00Z \
--auth-mode login \
--as-userPrefer a blob-level token with read-only permission when the recipient needs a single file. Least privilege limits the damage if a token leaks.
Clock skew and start time
If you set the SAS start time to the current time, requests can fail intermittently for the first few minutes because machines disagree slightly about the time. Microsoft's guidance is to set the start time at least 15 minutes in the past or leave it out, which makes the token valid immediately. Allow for up to 15 minutes of skew at the expiry end too. If you use AzCopy, date and time values must include seconds, in the format +%Y-%m-%dT%H:%M:%SZ.
Test the link
Open the full URI in a browser or fetch it from a terminal:
curl -I "https://<storage-account>.blob.core.windows.net/reports/q3-summary.pdf?<sas-token>"A 200 OK response means the token works. If you try a permission the token does not grant, such as a write with a read-only token, Azure Storage returns a 403.
Revoke a user delegation SAS
You cannot list or delete an individual SAS, because Azure Storage does not track them. For user delegation tokens, the lever is the user delegation key. This command revokes all user delegation keys for the storage account and invalidates every SAS signed with them:
az storage account revoke-delegation-keys \
--name <storage-account> \
--resource-group <resource-group>Two cautions from the documentation. First, the revocation applies to all user delegation keys on the account, so every outstanding user delegation SAS stops working, not only the leaked one. Second, Azure Storage caches both the user delegation key and Azure role assignments, so there can be a delay between running the command and the token becoming invalid.
Service SAS and account SAS: when keys are involved
If you sign a SAS with the account key you create a service SAS or an account SAS. Anyone who can obtain the key, or who holds the Microsoft.Storage/storageAccounts/listkeys/action permission, can mint tokens. Revoking a leaked key-signed token without a stored access policy means rotating the account key, which affects every other client using that key.
If you must use a service SAS:
- Attach it to a stored access policy on the container so that you can revoke it by changing or deleting the policy instead of rotating keys. There is a limit of five stored access policies per container.
- Use near-term expiry on any ad hoc service or account SAS.
- Configure a SAS expiration policy on the storage account. It sets a recommended maximum validity for service and account SAS tokens. Users who exceed it see a warning, and an entry is written to the Azure Storage logs if logging with Azure Monitor is enabled. It does not block creation.
Disallow account keys
The most effective way to keep key-signed tokens out of your environment is to disable Shared Key authorization on the account. Microsoft documents the effect on each SAS type:
| SAS type | Behavior when AllowSharedKeyAccess is false |
|---|---|
| User delegation SAS (Blob Storage only) | Request is permitted |
| Service SAS | Request is denied for all Azure Storage services |
| Account SAS | Request is denied for all Azure Storage services |
Before you flip the setting, check what depends on keys. Some Azure services and tools authenticate to storage with Shared Key, and they stop working when it is disallowed. Azure Files is the notable case: by default the Azure portal uses Shared Key to access file shares, so file share access in the portal fails unless you have the proper RBAC assignments. Azure Cloud Shell stores its files in an Azure file share, which becomes inaccessible if the account disallows Shared Key. Also note that Microsoft Entra Conditional Access policies can protect a storage account only if Shared Key authorization is disallowed.
To find out who is still using keys or SAS, create a diagnostic setting that sends storage logs to a Log Analytics workspace and run this query, which lists the ten callers sending the most requests authorized with an account key or a SAS in the last seven days:
StorageBlobLogs
| where AuthenticationType in ("AccountKey", "SAS") and TimeGenerated > ago(7d)
| summarize count() by CallerIpAddress, UserAgentHeader, AccountName
| top 10 by count_ descNote that logs report all SAS types together under SAS, so a user delegation SAS also shows up here.
When clients are migrated, disallow Shared Key access:
az storage account update \
--name <storage-account> \
--resource-group <resource-group> \
--allow-shared-key-access falseThis needs Azure CLI 2.20.0 or later. In the portal, open the storage account, go to Configuration under Settings, and set Allow storage account key access to Disabled. Verify the change:
az storage account show \
--name <storage-account> \
--resource-group <resource-group> \
--query "allowSharedKeyAccess"The command returns false when Shared Key authorization is disallowed. Afterward, a request that uses Shared Key fails with a 403 (Forbidden) and an error saying key-based authorization is not permitted.
To keep it that way, assign the built-in Azure Policy Storage accounts should prevent shared key access, first with the Audit effect to find non-compliant accounts, then with Deny. Policy changes can take up to 30 minutes to apply.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| 403 Forbidden on a fresh token | Start time set to now and clock skew | Remove the start time or set it at least 15 minutes in the past |
| 403 after a few days | User delegation key expired after 7 days | Generate a new token with a shorter, in-window expiry |
| Cannot create the SAS | Signing identity lacks the generateUserDelegationKey action, or you omitted --auth-mode login or --as-user | Assign Storage Blob Data Contributor or another role with that action, and include both parameters |
| Link works for the container but not nested blobs | Token created for the wrong resource type | Check the sr parameter: c for container, b for blob |
| Service SAS suddenly denied | Shared Key access disallowed on the account | Move to a user delegation SAS or Microsoft Entra authorization |
| Token still works after revocation | Cached keys and role assignments | Wait and retest; the documentation warns of a delay |
| Portal cannot browse file shares after disabling keys | Portal uses Shared Key for Azure Files by default | Authorize with Microsoft Entra in the portal and assign the needed RBAC roles |
Checklist
- Use a user delegation SAS wherever the data is in Blob Storage.
- Grant the fewest permissions on the narrowest resource, usually read on one blob.
- Keep expiry short and always inside 7 days.
- Always create and share SAS links over HTTPS.
- Validate anything that clients write using a SAS before your application trusts it.
- Know how you would revoke:
az storage account revoke-delegation-keysfor user delegation tokens, a stored access policy or key rotation for service tokens. - Restrict who holds roles that can generate keys or SAS tokens, since generation cannot be audited.
- Monitor authorization failures with Azure Monitor and storage logs.
- Move to
--allow-shared-key-access falseonce nothing depends on keys. - If a container's contents are meant to be public, public read access may be simpler than distributing SAS tokens.
Comments
Questions and tips are welcome. Comments are checked by the EpiFive team before they appear; please do not share passwords, phone numbers or other private details.