# Azure Storage SAS Tokens: User Delegation SAS vs Account Keys

> Share Azure blobs through expiring links without handing out account keys. Compare the three SAS types, create a user delegation SAS with the Azure CLI and revoke it.

Source: https://www.epifive.com/blog/azure-storage-sas-tokens-user-delegation · Published 2026-10-29 · EpiFive

## Key takeaways

- A shared access signature (SAS) is a signed token appended to a storage URL that limits what a client can access, with which permissions and for how long.
- Azure Storage has three SAS types, and Microsoft recommends the user delegation SAS because it is secured with Microsoft Entra credentials instead of the account key.
- A user delegation SAS is capped at 7 days because the user delegation key that signs it is valid for at most 7 days.
- You can revoke every user delegation SAS on an account at once with az storage account revoke-delegation-keys, while service SAS tokens need a stored access policy or key rotation.
- Setting allow-shared-key-access to false blocks service and account SAS tokens but still permits a user delegation SAS for Blob Storage.

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](https://www.epifive.com/blog/azure-storage-account-redundancy-lrs-zrs-grs). For a similar keyless pattern on another service, see [Azure OpenAI keyless access with managed identity](https://www.epifive.com/blog/azure-openai-keyless-managed-identity-private-endpoint).

## 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:

```bash
az role assignment create \
    --role "Storage Blob Data Contributor" \
    --assignee admin@contoso.com \
    --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:

```bash
az login
```

## Create 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.

```bash
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-uri
```

What each part does:

- `--permissions r` grants read only. For a blob, the supported user delegation permissions are Add, Create, Delete, Read and Write, which you can combine. The documentation example uses `acdrw`.
- `--expiry` is 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-uri` returns 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.

```bash
az storage container generate-sas \
    --account-name <storage-account> \
    --name reports \
    --permissions lr \
    --expiry 2026-11-05T00:00:00Z \
    --auth-mode login \
    --as-user
```

Prefer 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:

```bash
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:

```bash
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:

```kusto
StorageBlobLogs
| where AuthenticationType in ("AccountKey", "SAS") and TimeGenerated > ago(7d)
| summarize count() by CallerIpAddress, UserAgentHeader, AccountName
| top 10 by count_ desc
```

Note 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:

```bash
az storage account update \
    --name <storage-account> \
    --resource-group <resource-group> \
    --allow-shared-key-access false
```

This 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:

```bash
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-keys` for 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 false` once nothing depends on keys.
- If a container's contents are meant to be public, public read access may be simpler than distributing SAS tokens.

## References

- [Grant limited access to data with shared access signatures (SAS) - Microsoft Learn](https://learn.microsoft.com/en-us/azure/storage/common/storage-sas-overview)
- [Use Azure CLI to create a user delegation SAS for a container or blob - Microsoft Learn](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blob-user-delegation-sas-create-cli)
- [Prevent authorization with Shared Key - Microsoft Learn](https://learn.microsoft.com/en-us/azure/storage/common/shared-key-authorization-prevent)
- [Create a user delegation SAS (REST API) - Microsoft Learn](https://learn.microsoft.com/en-us/rest/api/storageservices/create-user-delegation-sas)

## Frequently asked questions

### What is the difference between a user delegation SAS and a service SAS?

A user delegation SAS is signed with a key obtained through Microsoft Entra credentials, so no account key is involved. A service SAS is signed with the storage account key. Microsoft recommends the user delegation SAS wherever you need a SAS.

### How long can a user delegation SAS be valid?

The user delegation key that signs it is valid for at most 7 days from its start time. A SAS with a later expiry is still only valid until the key expires, so plan on an expiry within 7 days of the start time.

### How do I revoke a user delegation SAS?

Run az storage account revoke-delegation-keys for the storage account. This revokes all user delegation keys for the account, which invalidates every SAS signed with them. Azure Storage caches keys and role assignments, so the SAS may keep working briefly.

### Does a user delegation SAS still work if I disable Shared Key access?

Yes for Blob Storage. A user delegation SAS is authorized with Microsoft Entra ID, so requests are permitted when AllowSharedKeyAccess is false. Service SAS and account SAS requests are denied.

