Theoretical Foundation: Create and Use Shared Access Signature (SAS) Tokens
1. Initial Intuitionβ
Imagine you have a confidential document in a safe and you need a specific client to be able to read that document tomorrow between 2 PM and 4 PM, without having to give them the safe combination. You create a temporary key that opens only that document, only during that time, and only for reading.
This is exactly what a SAS Token (Shared Access Signature) is: a cryptographically signed URL that grants delegated and controlled access to Azure Storage resources, without exposing the account's main credentials.
Instead of giving an external partner the Account Key (which would give full access to everything), you generate a SAS Token that says: "you can read only this specific file, until tomorrow at 4 PM, and only for reading". When the deadline expires, the token automatically stops working.
In practice, SAS Tokens are the mechanism used to:
- Temporarily share files with external users
- Allow applications to upload files directly to storage without going through a backend server
- Integrate third-party systems that need limited access to specific data
2. Contextβ
Where SAS fits in Azure Storage authentication mechanismsβ
Why SAS Tokens existβ
Account Keys are like the master key of a building: they give access to everything, don't expire automatically, and are difficult to revoke quickly. Sharing Account Keys for temporary or limited access is a bad security practice.
Azure AD RBAC is powerful, but requires the recipient to have an identity in Azure AD. External partners, user mobile applications, or legacy systems often don't have this identity.
SAS Tokens solve this problem: precise, temporary access without the need for an Azure AD identity, cryptographically signed to ensure integrity.
3. Building Conceptsβ
3.1 The three types of SASβ
Before understanding how to create a SAS, it's fundamental to understand the three types and when each applies:
Account SASβ
Grants access to resources from one or more services of the storage account (Blob, File, Queue, Table). The scope can be broad (multiple services, multiple resource types).
Signed with: Account Key (512-bit key of the storage account) Scope: Entire account, with filters by service, resource and operations Risk: if the Account Key is compromised, all Account SAS signed with it must be considered compromised
Service SASβ
Grants access to resources from a single service (only Blob, or only File, or only Queue, or only Table). More restrictive than Account SAS.
Signed with: Account Key Scope: A specific service, can be a container, a specific blob, a queue, a table Differential: can be associated with a Stored Access Policy, which allows revocation without needing to regenerate the Account Key
User Delegation SASβ
Grants access to Blob resources (and Data Lake Gen2). Works only for the Blob service.
Signed with: Azure AD credentials of the user who generated the token (not with Account Key) Scope: Blob service only Critical advantage: can be revoked without impacting other SAS or the Account Key. It's the safest and most recommended type.
### 3.2 Anatomy of a SAS Token
A SAS Token is a string of query parameters that, when appended to a storage resource URL, defines the access conditions. See a real example of SAS Token for a blob:
https://stgprod001.blob.core.windows.net/container1/arquivo.pdf ?sv=2022-11-02 (signedVersion - API version) &ss=b (signedServices - b=blob) &srt=o (signedResourceTypes - o=object) &sp=r (signedPermissions - r=read) &se=2026-03-25T18%3A00%3A00Z (signedExpiry - expiration date/time) &st=2026-03-24T10%3A00%3A00Z (signedStart - start date/time) &spr=https (signedProtocol - HTTPS only) &sig=abc123... (signature - HMAC-SHA256 signature)
Each parameter has a specific purpose:
| Parameter | Name | Possible values |
|---|---|---|
| `sv` | Signed Version | Storage API version (ex: 2022-11-02) |
| `ss` | Signed Services | `b`=Blob, `f`=File, `q`=Queue, `t`=Table |
| `srt` | Signed Resource Types | `s`=Service, `c`=Container, `o`=Object |
| `sp` | Signed Permissions | `r`=Read, `w`=Write, `d`=Delete, `l`=List, `a`=Add, `c`=Create, `u`=Update, `p`=Process, `t`=Tag, `f`=Filter, `i`=Set Immutability, `x`=Delete version |
| `se` | Signed Expiry | UTC ISO 8601 date/time |
| `st` | Signed Start | UTC ISO 8601 date/time (optional) |
| `sip` | Signed IP | Allowed IP or IP range |
| `spr` | Signed Protocol | `https` or `https,http` |
| `sig` | Signature | HMAC-SHA256 of all parameters |
The **signature** (`sig`) is what ensures integrity: it's the cryptographic hash of all parameters combined with the Account Key or Azure AD credential. If any parameter is modified, the signature doesn't match and the request is rejected.
### 3.3 Permissions by service
Available permissions vary by service:
| Permission | Blob | File | Queue | Table |
|---|---|---|---|---|
| Read (r) | Read blob | Read file | Read message (peek) | Read entities |
| Write (w) | Create/write blob | Create/write file | N/A | Insert/update |
| Delete (d) | Delete blob | Delete file | Delete message | Delete entities |
| List (l) | List containers/blobs | List directories/files | N/A | N/A |
| Add (a) | Add block | N/A | Add message | Insert entity |
| Create (c) | Create blob | Create file | N/A | N/A |
| Update (u) | Update blob pages | N/A | Update message | Merge entity |
| Process (p) | N/A | N/A | Process message | N/A |
### 3.4 Stored Access Policies
A **Stored Access Policy** (SAP) is an access policy stored in the container, queue, table or file share itself, which can be referenced by a Service SAS.
The critical advantage of a SAP is **centralized revocation**: instead of issuing a SAS with permissions and expiration hardcoded in the token, you reference a SAP. If you need to revoke access, just modify or delete the SAP, without needing to revoke/regenerate Account Keys.
```mermaid
```mermaid
sequenceDiagram
participant App as Application
participant Storage as Azure Storage
participant Admin as Administrator
Admin->>Storage: Creates SAP "policy-read-only"\nexpiration: 2026-12-31
App->>Storage: Generates Service SAS referencing\n"policy-read-only"
Storage-->>App: SAS Token generated
App->>Storage: Uses SAS Token to access blob
Storage-->>App: Access granted
Admin->>Storage: Modifies SAP "policy-read-only"\nto expire NOW
App->>Storage: Attempts to use the same SAS Token
Storage-->>App: 403 Forbidden\n(SAP expired, SAS revoked)
A SAS without SAP is like a physical key that can't be invalidated before expiring. A SAS with SAP is like an electronic badge that can be deactivated in the system immediately.
---
## 4. Structural View
### SAS Token validation flow by Azure Storage
```mermaid
flowchart TD
A["Request with SAS Token"] --> B{"Correct protocol?\n(https if spr=https)"}
B -->|No| C["403: Forbidden"]
B -->|Yes| D{"Source IP is in\nsignedIP?"}
D -->|No| C
D -->|Yes| E{"Token still valid?\n(se < se and > st)"}
E -->|No| C
E -->|Yes| F{"Is Service SAS with SAP?"}
F -->|Yes| G{"SAP exists and\nis valid?"}
G -->|No| C
G -->|Yes| H{"Requested operation\nis in sp (permissions)?"}
F -->|No| H
H -->|No| C
H -->|Yes| I{"Signature sig\nis valid?"}
I -->|No| C
I -->|Yes| J["Access GRANTED"]
style C fill:#d32f2f,stroke:#b71c1c,color:#fff
style J fill:#2e7d32,stroke:#1b5e20,color:#fff
SAS Token lifecycleβ
5. Practical Operationβ
How an Account SAS is generated (technical process)β
The generation process of an Account SAS or Service SAS involves:
- Build the string-to-sign: concatenate parameters in specific order defined by the API version
- Sign with HMAC-SHA256 using the Account Key
- Base64-encode the signature
- Build the final URL with all parameters
String-to-sign for Account SAS (sv=2020-12-06 onwards):
accountName\n
signedPermissions\n
signedServices\n
signedResourceTypes\n
signedStart\n
signedExpiry\n
signedIP\n
signedProtocol\n
signedVersion\n
signedEncryptionScope\n
Each empty field still contributes a \n. The order is exact and cannot be changed. Any modification to the token parameters in the URL invalidates the signature.
How a User Delegation SAS is generatedβ
The User Delegation SAS has an additional step: first it's necessary to obtain a User Delegation Key using Azure AD credentials, and then use that key (not the Account Key) to sign the SAS.
The User Delegation Key has a maximum duration of **7 days**, which limits the maximum validity of any User Delegation SAS.
### Non-obvious behaviors
**SAS cannot have expiration greater than the User Delegation Key.**
If you generate a User Delegation Key valid for 2 days and try to create a SAS with expiration in 5 days, storage rejects on creation or the SAS stops working when the User Delegation Key expires.
**Rotating Account Key invalidates all SAS signed with it.**
Account SAS and Service SAS are signed with the Account Key. If you rotate the Account Key (key1 or key2), all SAS signed with that specific key stop working immediately. This is the mass revocation mechanism for SAS without SAP.
**SAS without signedStart (`st`) is valid immediately.**
If you omit the `st` parameter, the SAS is valid from the moment of generation until `se`. Recommendation: always define `st` a few minutes in the past (to compensate for clock differences between servers) or omit it consciously.
**SAS without `spr` (signedProtocol) accepts HTTP and HTTPS.**
For security reasons, always specify `spr=https` to force HTTPS. Without this parameter, the SAS Token can be transmitted and reused via unencrypted HTTP.
**SAS without `sip` (signedIP) accepts any IP.**
Without IP restriction, anyone who obtains the token can use it from anywhere until it expires.
---
## 6. Implementation Methods
### Azure Portal
**When to use:** manual generation for immediate sharing, testing, demonstrations
**Generate Account SAS (Storage Account level):**
1. Portal > Storage Account > **Shared access signature** (side menu)
2. Select: Allowed services (Blob, File, Queue, Table)
3. Select: Allowed resource types (Service, Container, Object)
4. Select: Allowed permissions
5. Define: Start and expiry date/time (UTC)
6. Optionally: Allowed IP addresses, Allowed protocols
7. Select: Signing key (key1 or key2)
8. **Generate SAS and connection string**
**Generate Service SAS (container or blob level):**
1. Portal > Storage Account > Containers > select container or blob
2. Click **...** (menu) > **Generate SAS**
3. Configure permissions, expiration, IP
4. **Generate SAS token and URL**
**Limitation:** manual, not reproducible, doesn't scale for real-time generation in applications.
---
### Azure CLI
```bash
# Generate Account SAS
az storage account generate-sas \
--account-name "stgprod001" \
--account-key "<account-key>" \
--services b \
--resource-types co \
--permissions rl \
--expiry "2026-03-25T18:00:00Z" \
--start "2026-03-24T10:00:00Z" \
--https-only \
--ip "200.200.200.0/24" \
--output tsv
# Generate Service SAS for a specific container
az storage container generate-sas \
--account-name "stgprod001" \
--account-key "<account-key>" \
--name "container1" \
--permissions rl \
--expiry "2026-03-25T18:00:00Z" \
--https-only \
--output tsv
# Generate Service SAS for a specific blob
az storage blob generate-sas \
--account-name "stgprod001" \
--account-key "<account-key>" \
--container-name "container1" \
--name "relatorio-financeiro.pdf" \
--permissions r \
--expiry "2026-03-24T20:00:00Z" \
--https-only \
--output tsv
# Generate complete URL (SAS + blob URL)
SAS_TOKEN=$(az storage blob generate-sas \
--account-name "stgprod001" \
--account-key "<account-key>" \
--container-name "container1" \
--name "relatorio.pdf" \
--permissions r \
--expiry "2026-03-25T12:00:00Z" \
--https-only \
--output tsv)
BLOB_URL="https://stgprod001.blob.core.windows.net/container1/relatorio.pdf?$SAS_TOKEN"
echo "$BLOB_URL"
# Generate User Delegation SAS (requires Azure AD login, not Account Key)
az login # or az login --service-principal
az storage blob generate-sas \
--account-name "stgprod001" \
--container-name "container1" \
--name "relatorio.pdf" \
--permissions r \
--expiry "2026-03-25T12:00:00Z" \
--https-only \
--auth-mode login \
--as-user \
--output tsv
# Create Stored Access Policy in a container
az storage container policy create \
--account-name "stgprod001" \
--account-key "<account-key>" \
--container-name "container1" \
--name "policy-read-only" \
--permissions r \
--expiry "2026-12-31T23:59:59Z" \
--start "2026-01-01T00:00:00Z"
# Generate Service SAS referencing a SAP
az storage container generate-sas \
--account-name "stgprod001" \
--account-key "<account-key>" \
--name "container1" \
--policy-name "policy-read-only" \
--output tsv
# List Stored Access Policies of a container
az storage container policy list \
--account-name "stgprod001" \
--account-key "<account-key>" \
--container-name "container1" \
--output table
# Revoke SAS: delete the SAP
az storage container policy delete \
--account-name "stgprod001" \
--account-key "<account-key>" \
--container-name "container1" \
--name "policy-read-only"
# Revoke SAS without SAP: rotate the Account Key
az storage account keys renew \
--account-name "stgprod001" \
--resource-group "rg-storage" \
--key key1
Azure PowerShellβ
# Configure storage context
$ctx = New-AzStorageContext `
-StorageAccountName "stgprod001" `
-StorageAccountKey "<account-key>"
# Generate Account SAS
$accountSAS = New-AzStorageAccountSASToken `
-Context $ctx `
-Service Blob `
-ResourceType Container,Object `
-Permission "rl" `
-ExpiryTime (Get-Date).AddDays(1) `
-StartTime (Get-Date).AddMinutes(-5) `
-Protocol HttpsOnly `
-IPAddressOrRange "200.200.200.0/24"
# Generate Service SAS for container
$containerSAS = New-AzStorageContainerSASToken `
-Context $ctx `
-Name "container1" `
-Permission "rl" `
-ExpiryTime (Get-Date).AddHours(24) `
-Protocol HttpsOnly
# Generate Service SAS for specific blob
$blobSAS = New-AzStorageBlobSASToken `
-Context $ctx `
-Container "container1" `
-Blob "relatorio.pdf" `
-Permission "r" `
-ExpiryTime (Get-Date).AddHours(8) `
-Protocol HttpsOnly `
-FullUri # Returns full URL with SAS
# Generate User Delegation SAS
$ctx_aad = New-AzStorageContext `
-StorageAccountName "stgprod001" `
-UseConnectedAccount # Uses current Azure AD credentials
$udSAS = New-AzStorageBlobSASToken `
-Context $ctx_aad `
-Container "container1" `
-Blob "relatorio.pdf" `
-Permission "r" `
-ExpiryTime (Get-Date).AddDays(1) `
-Protocol HttpsOnly
# Create Stored Access Policy
New-AzStorageContainerStoredAccessPolicy `
-Context $ctx `
-Container "container1" `
-Policy "policy-read-only" `
-Permission "r" `
-ExpiryTime (Get-Date).AddMonths(6)
# Generate SAS with SAP reference
$sapSAS = New-AzStorageContainerSASToken `
-Context $ctx `
-Name "container1" `
-Policy "policy-read-only"
# List container SAPs
Get-AzStorageContainerStoredAccessPolicy `
-Context $ctx `
-Container "container1"
# Revoke SAP
Remove-AzStorageContainerStoredAccessPolicy `
-Context $ctx `
-Container "container1" `
-Policy "policy-read-only"
Programmatic generation with SDK (Python)β
from datetime import datetime, timedelta, timezone
from azure.storage.blob import (
BlobServiceClient,
BlobSasPermissions,
ContainerSasPermissions,
generate_blob_sas,
generate_container_sas,
UserDelegationKey
)
from azure.identity import DefaultAzureCredential
# Account SAS / Service SAS with Account Key
account_name = "stgprod001"
account_key = "<account-key>"
# SAS for specific blob (read, 8 hours)
sas_token = generate_blob_sas(
account_name=account_name,
container_name="container1",
blob_name="relatorio.pdf",
account_key=account_key,
permission=BlobSasPermissions(read=True),
expiry=datetime.now(timezone.utc) + timedelta(hours=8),
start=datetime.now(timezone.utc) - timedelta(minutes=5),
protocol="https",
ip="200.200.200.0/24"
)
blob_url = f"https://{account_name}.blob.core.windows.net/container1/relatorio.pdf?{sas_token}"
print(blob_url)
# User Delegation SAS with Azure AD
credential = DefaultAzureCredential()
blob_service_client = BlobServiceClient(
account_url=f"https://{account_name}.blob.core.windows.net",
credential=credential
)
# Get User Delegation Key (valid for up to 7 days)
delegation_key_start = datetime.now(timezone.utc)
delegation_key_expiry = datetime.now(timezone.utc) + timedelta(days=2)
user_delegation_key = blob_service_client.get_user_delegation_key(
key_start_time=delegation_key_start,
key_expiry_time=delegation_key_expiry
)
# Generate User Delegation SAS
ud_sas = generate_blob_sas(
account_name=account_name,
container_name="container1",
blob_name="relatorio.pdf",
user_delegation_key=user_delegation_key, # Does not use account_key
permission=BlobSasPermissions(read=True),
expiry=datetime.now(timezone.utc) + timedelta(hours=4),
protocol="https"
)
7. Control and Securityβ
Revocation mechanisms by SAS typeβ
This is the most important table for security decisions:
| SAS Type | How to revoke | Revocation impact |
|---|---|---|
| Account SAS without SAP | Rotate Account Key used to sign | Revokes ALL SAS signed with that key (key1 or key2) |
| Service SAS with SAP | Delete or modify the SAP | Revokes only SAS that reference that SAP |
| Service SAS without SAP | Rotate Account Key used | Revokes ALL SAS signed with that key |
| User Delegation SAS | Revoke User Delegation Key via API | Revokes all SAS generated with that delegation key |
The storage account has two Account Keys (key1 and key2) exactly to allow rotation without downtime: you can rotate key1, update applications to use key2, and then rotate key2. But during the rotation window, SAS signed with the rotated key are revoked.
Principle of least privilege in SASβ
Configure only the strictly necessary permissions:
Protection against SAS Token leakageβ
SAS Tokens are like passwords: once obtained, anyone can use them until they expire. Protection strategies:
IP restriction (sip): limits token usage to the expected origin IP or range. If the token leaks, it only works from the authorized IP.
HTTPS protocol (spr=https): prevents the token from being intercepted on unencrypted connections.
Short expiration: tokens lasting only hours limit the usage window in case of leakage. Generate tokens on-demand when needed, not long-duration tokens.
Don't log complete SAS Tokens: in application logs, truncate or hash SAS Tokens. A log with the complete token is an attack vector if logs are compromised.
8. Decision Makingβ
When to use each SAS typeβ
| Situation | Recommended type | Reason |
|---|---|---|
| Temporarily share file with external user | Service SAS on specific blob | Minimal scope, short expiration |
| Mobile app needs direct blob upload | Service SAS on container, write permission | Backend generates token on-demand for each upload |
| External partner needs container access for months | Service SAS with SAP | Can revoke SAP without affecting other accesses |
| Internal system (Azure Functions, AKS) needs blob access | Azure AD + RBAC (not SAS) | Managed Identity is more secure than SAS |
| Audit: know who generated each SAS | User Delegation SAS | Traceable by Azure AD identity |
| Read access to public files | Anonymous Access (not SAS) | SAS is unnecessary if data is public |
| External backup needs to read all containers | Account SAS with minimal permission + SAP | Covers multiple containers with revocable policy |
SAS vs. Account Keys vs. RBACβ
| Method | Scope | Expiration | Revocation | Auditing | Ideal for |
|---|---|---|---|---|---|
| Account Key | Total | Never | Rotate key | Limited | Don't use for sharing |
| SAS Token | Configurable | Mandatory | Complex (without SAP) | Limited | Temporary external access |
| RBAC/Azure AD | By role | Depends on role | Immediate | Complete via Azure AD | Internal services, Azure resources |
| User Delegation SAS | Blob | Max 7 days | Via delegation key | Traceable by identity | Best SAS for Blob |
9. Best Practicesβ
Prefer User Delegation SAS for Blob when possible. It's the most secure type because it doesn't use Account Keys and can be traced to an Azure AD identity. For Blob service, there's never a reason to use Account SAS or Service SAS if the generator can authenticate via Azure AD.
Use Stored Access Policies for any long-duration SAS. SAS lasting more than 24 hours should reference a SAP. This allows revocation without systemic impact. For hour-long SAS, automatic revocation by expiration is sufficient.
Always define spr=https.
SAS without this restriction can be transmitted via HTTP and intercepted. There's no legitimate production scenario where HTTP should be accepted.
Generate SAS on-demand, don't store pre-generated tokens. The correct practice is for the backend to generate the SAS when the client needs it. Pre-generated and stored SAS tokens (in databases, configuration files) are security risks.
Use sip (signedIP) whenever the receiver's IP is predictable.
For B2B integrations with partners that have fixed IPs, IP restriction adds a critical protection layer.
Monitor Storage Access Logs to detect SAS misuse. Configure Diagnostic Settings to send logs to Log Analytics. Periodic queries for SAS with many requests or from unexpected IPs can indicate leakage.
Document each SAS issued for long-duration access. Create a record (spreadsheet, ticket, database) with: who received it, for which resource, with what permissions, until when it expires. Long-duration SAS tokens without tracking become shadow access that's difficult to audit.
10. Common Errorsβ
| Error | Why it happens | How to avoid |
|---|---|---|
| SAS with years of expiration | "Avoid regenerating later" | Use SAP + Service SAS with short period and long-duration SAP |
SAS without spr=https | Portal default sometimes includes http | Always explicitly specify https-only |
| Rotate Account Key without updating all consumers | Forget that SAS signed with the key stop working | Audit all SAS and connections before rotating |
| User Delegation SAS with expiration longer than 7 days | Not knowing the limit | User Delegation Key has maximum of 7 days |
| Log complete URL with SAS in application logs | Logs are treated as non-sensitive | Mask or truncate SAS Token in logs |
| SAS generated but container/blob doesn't exist | Created before resource exists | Verify resource exists before generating SAS |
| Confuse SAS expiration with SAP expiration | Both have independent expiration | The more restrictive prevails: SAS expires by the shorter of the two |
| Generate SAS with all permissions "just in case" | "Better to have extra" mentality | Define minimal permissions for specific operation |
Most critical errorβ
Storing Account SAS Tokens (without SAP) in source code or long-duration environment variables. If source code leaks (accidentally public repository, ex-employee), all data covered by the SAS becomes exposed until the Account Key is rotated, which frequently causes downtime in other applications using the same key.
11. Operation and Maintenanceβ
Audit SAS Token usageβ
# Configure storage logs (blob service) for Log Analytics
az monitor diagnostic-settings create \
--name "blob-access-logs" \
--resource "/subscriptions/<sub-id>/resourceGroups/rg-storage/providers/Microsoft.Storage/storageAccounts/stgprod001/blobServices/default" \
--workspace "<log-analytics-workspace-id>" \
--logs '[
{"category": "StorageRead", "enabled": true},
{"category": "StorageWrite", "enabled": true},
{"category": "StorageDelete", "enabled": true}
]'
Useful queries in Log Analytics:
// Access via SAS Token (identified by AuthenticationType)
StorageBlobLogs
| where AuthenticationType == "SAS"
| where TimeGenerated > ago(24h)
| project TimeGenerated, CallerIpAddress, OperationName, Uri, StatusCode
| order by TimeGenerated desc
// SAS with many requests (possible abuse)
StorageBlobLogs
| where AuthenticationType == "SAS"
| where TimeGenerated > ago(1h)
| summarize RequestCount = count() by CallerIpAddress, bin(TimeGenerated, 5m)
| where RequestCount > 100
| order by RequestCount desc
// Denied access attempts with SAS
StorageBlobLogs
| where AuthenticationType == "SAS" and StatusCode == 403
| project TimeGenerated, CallerIpAddress, OperationName, Uri, StatusCode, StatusText
Check existing Stored Access Policiesβ
# List container SAPs
az storage container policy list \
--account-name "stgprod001" \
--account-key "<account-key>" \
--container-name "container1" \
--output table
# Check if expired SAP still exists
az storage container policy list \
--account-name "stgprod001" \
--account-key "<account-key>" \
--container-name "container1" \
--query "[?expiry < '$(date -u +%Y-%m-%dT%H:%M:%SZ)'].name" \
--output tsv
12. Integration and Automationβ
Pattern: Backend generates SAS on-demand for direct uploadβ
This is the most common pattern for mobile or web applications that need to upload files:
This pattern eliminates the need for the file to pass through the backend server, reducing latency, processing cost, and scaling complexity.
Account Key rotation automation with SAP updatesβ
# Script: Rotate Account Key and recreate SAPs
param(
[string]$accountName,
[string]$resourceGroup,
[string]$keyToRotate # "key1" or "key2"
)
# 1. Save existing SAPs before rotating
$ctx = New-AzStorageContext -StorageAccountName $accountName -StorageAccountKey (
(Get-AzStorageAccountKey -ResourceGroupName $resourceGroup -Name $accountName) |
Where-Object { $_.KeyName -eq $keyToRotate }
).Value
$containers = Get-AzStorageContainer -Context $ctx
$existingPolicies = @{}
foreach ($container in $containers) {
$policies = Get-AzStorageContainerStoredAccessPolicy -Context $ctx -Container $container.Name
if ($policies) {
$existingPolicies[$container.Name] = $policies
}
}
# 2. Rotate the key
New-AzStorageAccountKey `
-ResourceGroupName $resourceGroup `
-Name $accountName `
-KeyName $keyToRotate
Write-Output "Account Key $keyToRotate rotated. All SAS signed with it have been revoked."
# 3. Create new context with new key
$newKey = (Get-AzStorageAccountKey -ResourceGroupName $resourceGroup -Name $accountName |
Where-Object { $_.KeyName -eq $keyToRotate }).Value
$newCtx = New-AzStorageContext -StorageAccountName $accountName -StorageAccountKey $newKey
# 4. Recreate SAPs with new key
foreach ($containerName in $existingPolicies.Keys) {
foreach ($policy in $existingPolicies[$containerName]) {
New-AzStorageContainerStoredAccessPolicy `
-Context $newCtx `
-Container $containerName `
-Policy $policy.Policy `
-Permission $policy.Permissions `
-ExpiryTime $policy.SharedAccessExpiryTime `
-StartTime $policy.SharedAccessStartTime
Write-Output "SAP '$($policy.Policy)' recreated in container '$containerName'"
}
}
13. Final Summaryβ
Essential points:
- SAS Token is a cryptographically signed URL that delegates controlled access to Azure Storage without exposing main credentials
- Three types exist: Account SAS (multiple services, signed with Account Key), Service SAS (one service, signed with Account Key, supports SAP) and User Delegation SAS (Blob only, signed with Azure AD credential, most secure)
- The SAS Token encodes permissions, services, expiration, IP and protocol in query parameters; the signature (
sig) ensures integrity and any modification invalidates the token - Stored Access Policy (SAP) allows Service SAS revocation without rotating Account Keys
- User Delegation SAS has maximum validity of 7 days (limited by User Delegation Key)
- SAS Tokens are irrevocable after issuance, except: rotate Account Key (Account/Service SAS), delete SAP (Service SAS with SAP), revoke User Delegation Key (User Delegation SAS)
Critical differences:
- Account SAS vs. Service SAS: Account SAS covers multiple services; Service SAS covers one service and supports SAP for granular revocation
- Service SAS with SAP vs. without SAP: with SAP allows immediate revocation by deleting the SAP; without SAP can only be revoked by rotating the Account Key
- User Delegation SAS vs. Account/Service SAS: User Delegation uses Azure AD (more secure, traceable, revocable via delegation key); Account and Service use Account Key (systemic risk if compromised)
- SAS Token vs. RBAC: SAS is for external temporary access without Azure AD identity; RBAC is for Azure services and users with managed identity
What needs to be remembered for AZ-104:
- User Delegation SAS requires the generating user to have the
Microsoft.Storage/storageAccounts/blobServices/generateUserDelegationKeypermission - SAP can be created on: containers, individual blobs, queues and file shares (not at account level)
- A SAS without
spr=httpsaccepts HTTP; always specifyhttps-only - The storage account has two Account Keys (key1 and key2) for rotation without downtime
- SAS Tokens are generated on the client; Azure Storage only validates the signature when the token is used
signedStart(st) is optional; without it the SAS is valid immediately after generation