Storage SAS — user delegation, service and account SAS, expiration policy and pitfalls
Which shared access signature to use and why it fails — user delegation SAS signed with an Entra-issued key (7-day limit, Delegator roles, user-bound SAS from service version 2025-07-05, cross-tenant delegation off by default), service and account SAS signed with the account key, stored access policies for revocation, the account's SAS expiration policy with Log or Block, clock skew, and which SAS still works when Shared Key is off.
markdownreferencev1CET4-R3F1stage: buildtrust: officialservice: storageinterface: sdkinterface: cliuser delegation SASuser delegation keygetUserDelegationKeygenerateUserDelegationSasUrlgenerateUserDelegationKey actionStorage Blob Delegatorservice SASaccount SASstored access policyrevoke SASsasPolicysasExpirationPeriodexpirationAction BlockSasExpiryStatusPolicy violatedrequireUserBoundUserDelegationSasdelegatedUserObjectIduser-bound user delegation SASallowCrossTenantDelegationSasdelegatedUserTenantIdSAS clock skew 15 minutesSAS start timeaz storage blob generate-sas --as-userSAS seven days
Storage SAS — user delegation, service and account SAS
Use a user delegation SAS whenever a SAS is needed. It's signed with a key that Entra ID issues to the caller, so it works with Shared Key off, it carries at most the caller's own permissions, and it never touches the account key. Service and account SAS are signed with the account key: whoever holds the key can mint them, and the only revocation is rotating the key (or deleting a stored access policy).
The three kinds
| Kind | Signed with | Services | Revoke by |
|---|---|---|---|
| User delegation SAS | User delegation key from Get User Delegation Key, called with an Entra token | Blob and Data Lake; the SAS overview and the Queue and File SDKs add Queue, Table and Files | Key expiry (≤ 7 days), or az storage account revoke-delegation-keys -n <acct> -g <rg> (revokes every key) |
| Service SAS | Account key | One service | Rotate the key, or delete its stored access policy |
| Account SAS | Account key | One or more services (ss), resource types (srt) | Rotate the key |
- To get the key, the caller needs a role with
…/generateUserDelegationKey/action: Storage Blob Delegator (or Queue, File, Table Delegator), or Storage Blob Data Contributor/Owner which include it. The SAS then grants only the permissions both in the SAS and in the caller's data roles. - The key's start and expiry must both be within 7 days of now, and a user delegation SAS can't outlive 7 days whatever the account's expiration policy says.
- Stored access policies work only with a service SAS. User delegation and account SAS must be ad hoc.
With Shared Key off (allowSharedKeyAccess: false), service and account SAS fail with KeyBasedAuthenticationNotPermitted; a user delegation SAS keeps working. Microsoft's shared-key page labels user delegation SAS "Blob Storage only" in the same place, which contradicts its SAS overview: verify a Queue or Files user delegation SAS against your account before depending on it.
User-bound user delegation SAS (2025-07-05 and later)
A SAS can be bound to one user: set delegatedUserObjectId when signing, and the SAS works only alongside an Entra token issued to that object ID. The account can demand it: sasPolicy.requireUserBoundUserDelegationSas: true, with requireUserBoundUserDelegationSasAction None, Log or Block (2026-06-01 API). A user delegation key for another tenant (delegatedUserTenantId) works only if allowCrossTenantDelegationSas is true; its default is false.
SAS expiration policy
sasPolicy.sasExpirationPeriod (format DD.HH:MM:SS) sets a recommended maximum validity for service and account SAS:
expirationAction: Log(the default) allows out-of-policy SAS and writesSasExpiryStatusin resource logs (StorageBlobLogs | where SasExpiryStatus startswith "Policy violated").Blockrefuses them.- With a policy in effect, every SAS must include a signed start (
st); validity isse − st. - It doesn't apply to user delegation SAS through the HDFS endpoint, or to service SAS that use a stored access policy.
- CLI:
az storage account update -n <acct> -g <rg> --sas-expiration-period 1.00:00:00 --sas-expiration-action Block. Built-in policy: Storage accounts should have shared access signature (SAS) policies configured.
Pitfalls
- Clock skew. Up to 15 minutes either way. Don't set
stto now: omit it (valid immediately) or set it 15 minutes in the past; leave margin onse. With an expiration policy,stis required, so back-date it. - Permissions per operation. Overwriting a blob needs write and delete; listing needs
l; an account-level call (list containers) needs an account SAS, not a service SAS. spr.httpsonly is recommended; HTTP-only isn't a valid value.siptakes IPv4 only, and a SAS IP restriction never grants access the firewall denies.- A SAS is a bearer secret. Don't log the URL; hand it out with the shortest life that works.
- Generate it with the SDK or CLI. A hand-built token fails with
SAS field 'sv' is not well formed.and similar.
CLI, user delegation SAS valid for one hour (--as-user requires --auth-mode login and --expiry):
az storage blob generate-sas --account-name <acct> --container-name <c> --name <blob> \
--permissions r --expiry <yyyy-mm-ddThh:mmZ> --https-only --as-user --auth-mode login --full-uri
TypeScript: guides/storage/access-blobs-from-typescript.md §4. Errors: reference/storage/sas-and-key-errors.md.