The goal
A vendor or a script needs to read or upload files in one container for a few hours. You don't want to hand over the storage account key, because that key opens the whole account and has no expiry. A shared access signature (SAS) is a signed URL that limits what can be done, where and until when.
The three kinds of SAS
| SAS type | Signed with | Use it when |
|---|---|---|
| User delegation SAS | Your Entra ID identity | Blob and Data Lake access. Prefer this one |
| Service SAS | Account key | A service other than blobs needs it, or Entra ID isn't possible |
| Account SAS | Account key | Rarely; it can span several services |
A user delegation SAS isn't tied to the account key and can be revoked without rotating that key. It can last at most 7 days.
Step 1: check that you have the right role
You need a data role on the container or account. Storage Blob Data Reader, Storage Blob Data Contributor and Storage Blob Delegator all include the permission that creates the delegation key. Owner or Contributor on the account isn't enough for data access.
az login
SCOPE=$(az storage account show --name stops01 --resource-group rg-ops --query id -o tsv)
az role assignment list --assignee "you@contoso.com" --scope "$SCOPE" --include-inherited -o table
A new role assignment can take a few minutes to apply.
Step 2: create the SAS
This one allows read and list on the reports container until the given UTC time:
SAS=$(az storage container generate-sas \
--account-name stops01 \
--name reports \
--permissions rl \
--expiry 2026-11-07T18:00Z \
--https-only \
--auth-mode login \
--as-user \
--output tsv)
echo "$SAS"
--as-user with --auth-mode login is what makes it a user delegation SAS. Leave the start time out, so small clock differences don't reject the token right after you issue it.
Common permission letters:
| Letter | Allows |
|---|---|
r | Read blobs |
l | List blobs |
w | Write blobs |
c | Create blobs |
a | Append |
d | Delete |
Give only what's needed. An upload-only partner usually needs cw, not rwdl.
Step 3: test it
Always put the URL in quotes. The token contains &, which the shell would otherwise treat as a command separator.
azcopy list "https://stops01.blob.core.windows.net/reports?$SAS"
azcopy copy "https://stops01.blob.core.windows.net/reports/june.csv?$SAS" ./june.csv
PowerShell needs the same quotes:
azcopy list "https://stops01.blob.core.windows.net/reports?$sas"
When it returns 403
| Error text | Likely cause | Fix |
|---|---|---|
Signature not valid in the specified time window | The token expired, or the start time is in the future | Issue a new token; drop the start time |
Signature did not match | The URL was changed or badly encoded, or the token belongs to another container | Copy the whole token again and quote the URL |
AuthorizationPermissionMismatch | The token lacks that action, such as writing with rl | Issue one with the right letters |
AuthorizationFailure | The storage firewall blocked the caller's IP | Allow the IP or network; a SAS doesn't bypass the firewall |
KeyBasedAuthenticationNotPermitted | Shared key access is off, and this SAS uses the account key | Use a user delegation SAS |
To see the status code and the storage error code, use curl:
curl -s -i "https://stops01.blob.core.windows.net/reports/june.csv?$SAS" | head -20
The response has an x-ms-error-code header with the exact reason.
Warning
A SAS is a bearer token: anyone who has the URL has the access. Don't paste it into tickets or chat, and remember that it can end up in proxy and browser logs. Keep the expiry as short as the job allows.
Revoke a token early
A SAS can't be deleted individually. To stop user delegation SAS tokens, revoke the delegation keys:
az storage account revoke-delegation-keys --name stops01 --resource-group rg-ops
Caution
This invalidates every user delegation SAS on the account, not just the leaked one. Warn other SAS users and reissue the tokens they still need.
Prevent it next time
- Set a short expiry and record who received each token.
- Use managed identities for Azure workloads. Keep SAS for callers that can't use Entra ID. See managed identity basics.
- Once nothing depends on account keys, turn them off:
az storage account update --name stops01 --resource-group rg-ops --allow-shared-key-access false. Test this first, because every service SAS and key-based client stops working. - Start from a secure storage account baseline. For wider access failures, see Azure Storage 403 troubleshooting.