Skip to main content

Azure Storage

Volume 4 — Azure Foundations

An Azure Storage account is a single resource that can host several different storage services underneath it. This chapter focuses on Blob Storage, since that's what backs document storage across the Traxs suite — with enough of the surrounding services covered to know when they're the better fit instead.

1. The Storage Services Under One Account

ServiceWhat it storesTypical use
BlobUnstructured binary data — files, documents, imagesDocument storage, uploaded attachments, backups
TableNoSQL key-value dataSimple, high-scale structured data without a relational database
QueueMessages, for asynchronous processing between componentsLightweight task queuing
FileFully managed file shares (SMB/NFS)Legacy apps expecting a traditional network file share

Traxs's document storage use case is Blob Storage specifically — the others exist as options worth knowing about, not currently in active use.

2. Containers and Blobs

Storage Account: traxsstorage
└── Container: documents
├── invoices/2026/07/inv-4821.pdf
├── leave-attachments/req-42/doctor-note.pdf
└── receipts/exp-118.jpg
  • A container is roughly analogous to a top-level folder — it's the access-control boundary and the unit az commands operate against.
  • A blob is an individual file. Blob names can contain / characters, which makes them look like a nested folder structure in tools like Storage Explorer or the Portal — but blob storage is fundamentally flat; there's no real directory object, just naming convention creating the visual illusion of folders.
az storage container create --account-name traxsstorage --name documents
az storage blob upload --account-name traxsstorage --container-name documents --file invoice.pdf --name "invoices/2026/07/inv-4821.pdf"
az storage blob list --account-name traxsstorage --container-name documents --output table

3. Access Tiers

TierCost to storeCost to accessBest for
HotHigherLowerFrequently accessed data — active documents, current-period invoices
CoolLowerHigherInfrequently accessed, retained for at least 30 days — older records still occasionally needed
ArchiveLowestHighest, plus a rehydration delay (hours) before the blob is even readable againLong-term retention, rarely if ever accessed — compliance archives

Tiers can be set per-blob, not just per-account, and changed after the fact — meaning a lifecycle policy (Section 6) can automatically age documents from Hot to Cool to Archive as they get older, without any application code change.

4. Authentication Methods

MethodHow it worksBest for
Connection stringA single string embedding the account name and a full access keySimple, but the key grants broad access to the entire account — must be treated as a secret and rotated if ever exposed
SAS (Shared Access Signature) tokenA time-limited, scope-limited token granting specific permissions (read-only, a specific container, an expiration time) without exposing the account's actual keySharing controlled, temporary access — e.g. a signed URL letting a user download one specific document without broader account access
Azure AD / Managed IdentityThe calling application authenticates as itself (its managed identity), granted a specific RBAC role like Storage Blob Data ContributorThe strongest option for application-to-storage access — no secret to leak, no manual rotation, access governed entirely by Azure RBAC (see the Azure RBAC chapter)

Managed identity access is the pattern to prefer for the API's own access to its storage account — it removes an entire category of "the connection string leaked" or "the key needs rotating" concerns, since there's no static credential involved at all.

# Grant the App Service's managed identity blob access, instead of using a connection string
az role assignment create \
--assignee "<app-service-managed-identity-object-id>" \
--role "Storage Blob Data Contributor" \
--scope "/subscriptions/<sub-id>/resourceGroups/traxs-prod/providers/Microsoft.Storage/storageAccounts/traxsstorage"

5. Generating a SAS Token

az storage blob generate-sas \
--account-name traxsstorage \
--container-name documents \
--name "invoices/2026/07/inv-4821.pdf" \
--permissions r \
--expiry 2026-07-24T00:00:00Z \
--https-only

The resulting token, appended to the blob's URL, grants exactly the permissions and time window specified — nothing more. Always set the shortest expiry that's actually practical for the use case — a SAS token embedded in a link (e.g. emailed to a customer) is only as safe as that link is; anyone who obtains the full URL before it expires has exactly the access the token grants.

6. Lifecycle Management

A lifecycle policy automates moving or deleting blobs based on age, without any application code:

{
"rules": [
{
"name": "ArchiveOldInvoices",
"type": "Lifecycle",
"definition": {
"filters": { "blobTypes": ["blockBlob"], "prefixMatch": ["documents/invoices/"] },
"actions": {
"tierToCool": { "daysAfterModificationGreaterThan": 90 },
"tierToArchive": { "daysAfterModificationGreaterThan": 365 }
}
}
}
]
}

This moves invoices older than 90 days to Cool, and older than a year to Archive, automatically and continuously — the kind of ongoing cost optimization that's easy to forget as a manual process but trivial to set up once as policy.

7. CORS on Storage

If a frontend application needs to upload or fetch blobs directly from the browser (rather than proxying through the API), the storage account itself needs CORS rules configured — separate from any CORS configuration on the API:

az storage cors add \
--account-name traxsstorage \
--services b \
--methods GET PUT \
--origins "https://roundtrips.app" \
--allowed-headers "*" \
--exposed-headers "*" \
--max-age 3600

A CORS error in the browser console when uploading directly to blob storage is specifically about this configuration — it has nothing to do with the API's own CORS settings, since the browser is talking directly to the storage account, not through the API at all.

8. Troubleshooting Playbook

SymptomLikely causeWhat to check
403 This request is not authorizedWrong/expired SAS token, missing RBAC role for managed identity access, or account key rotated without updating a stored connection stringConfirm which auth method is in use and check its specific validity — expired SAS, missing role assignment, or stale key
Blob "not found" but it should existWrong container name, or the blob name (including its full path-like prefix) doesn't match exactlyaz storage blob list to confirm the exact name/path as stored
Upload succeeds from Postman/API but fails from the browser directlyCORS not configured on the storage accountaz storage cors list --account-name to check current rules against the actual origin making the request
Archive-tier blob download fails or hangsArchive blobs require rehydration before they're readable — not instantly accessibleTrigger rehydration first (az storage blob set-tier back to Hot/Cool), then wait for it to complete before attempting to read
SAS token stopped working before expected expiryAccount key was regenerated, invalidating account-key-based SAS tokens signed with the old keyRegenerate and redistribute affected SAS tokens after any key rotation
Connection string based access broke after a "routine" key rotationRotating a storage account key invalidates every connection string signed with that specific keyUpdate the stored connection string (e.g. in Key Vault/App Service settings) immediately after any key rotation, or migrate to managed identity to avoid this entirely

9. Quick Reference

CategoryCommand / ConceptPurpose
Containersaz storage container createCreate a container
Blobsaz storage blob uploadUpload a file
Blobsaz storage blob listList blobs in a container
AuthManaged identity + RBACPreferred for application-to-storage access, no static secret
AuthSAS tokenTime-limited, scope-limited access for sharing
TiersHot / Cool / ArchiveBalance storage cost vs. access cost vs. retrieval delay
AutomationLifecycle policyAuto-tier or delete blobs by age, no code required
CORSaz storage cors addRequired separately from API CORS, for direct browser access

Part of the Traxs Engineering Handbook — Volume 4: Azure Foundations. Companion chapters in this volume: Azure CLI, Azure RBAC, Azure Networking, Azure Key Vault, Microsoft Entra ID.