Skip to main content

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​

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

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.

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

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

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

5. Practical Operation​

How an Account SAS is generated (technical process)​

The generation process of an Account SAS or Service SAS involves:

  1. Build the string-to-sign: concatenate parameters in specific order defined by the API version
  2. Sign with HMAC-SHA256 using the Account Key
  3. Base64-encode the signature
  4. 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.

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

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 TypeHow to revokeRevocation impact
Account SAS without SAPRotate Account Key used to signRevokes ALL SAS signed with that key (key1 or key2)
Service SAS with SAPDelete or modify the SAPRevokes only SAS that reference that SAP
Service SAS without SAPRotate Account Key usedRevokes ALL SAS signed with that key
User Delegation SASRevoke User Delegation Key via APIRevokes 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:

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

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​

SituationRecommended typeReason
Temporarily share file with external userService SAS on specific blobMinimal scope, short expiration
Mobile app needs direct blob uploadService SAS on container, write permissionBackend generates token on-demand for each upload
External partner needs container access for monthsService SAS with SAPCan revoke SAP without affecting other accesses
Internal system (Azure Functions, AKS) needs blob accessAzure AD + RBAC (not SAS)Managed Identity is more secure than SAS
Audit: know who generated each SASUser Delegation SASTraceable by Azure AD identity
Read access to public filesAnonymous Access (not SAS)SAS is unnecessary if data is public
External backup needs to read all containersAccount SAS with minimal permission + SAPCovers multiple containers with revocable policy

SAS vs. Account Keys vs. RBAC​

MethodScopeExpirationRevocationAuditingIdeal for
Account KeyTotalNeverRotate keyLimitedDon't use for sharing
SAS TokenConfigurableMandatoryComplex (without SAP)LimitedTemporary external access
RBAC/Azure ADBy roleDepends on roleImmediateComplete via Azure ADInternal services, Azure resources
User Delegation SASBlobMax 7 daysVia delegation keyTraceable by identityBest 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​

ErrorWhy it happensHow to avoid
SAS with years of expiration"Avoid regenerating later"Use SAP + Service SAS with short period and long-duration SAP
SAS without spr=httpsPortal default sometimes includes httpAlways explicitly specify https-only
Rotate Account Key without updating all consumersForget that SAS signed with the key stop workingAudit all SAS and connections before rotating
User Delegation SAS with expiration longer than 7 daysNot knowing the limitUser Delegation Key has maximum of 7 days
Log complete URL with SAS in application logsLogs are treated as non-sensitiveMask or truncate SAS Token in logs
SAS generated but container/blob doesn't existCreated before resource existsVerify resource exists before generating SAS
Confuse SAS expiration with SAP expirationBoth have independent expirationThe more restrictive prevails: SAS expires by the shorter of the two
Generate SAS with all permissions "just in case""Better to have extra" mentalityDefine 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:

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

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/generateUserDelegationKey permission
  • SAP can be created on: containers, individual blobs, queues and file shares (not at account level)
  • A SAS without spr=https accepts HTTP; always specify https-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