Part 8 of 1625 min read · 2 diagramsAI-assisted

Storage: Blob, Files & Disks

Managed disks (Part 3) are Azure's block-storage-for-VMs answer; this chapter covers the rest of Azure's storage surface — Blob, Files, and the storage account itself.

Table of Contents#

  1. The Storage Account — One Umbrella Resource, Many Services
  2. Storage Redundancy Options — LRS, ZRS, GRS, GZRS
  3. Blob Storage — Containers and Blob Types
  4. Azure Data Lake Storage Gen2 — Hierarchical Namespace
  5. Blob Index Tags
  6. Blob Access Tiers — Hot, Cool, Cold, and Archive
  7. Blob Lifecycle Management
  8. Rehydrating From Archive
  9. Blob Versioning and Soft Delete
  10. Blob Immutability (WORM) Policies
  11. Object Replication
  12. Shared Access Signatures
  13. Stored Access Policies
  14. Storage Account Keys and Rotation
  15. Azure Storage Explorer and AzCopy
  16. Azure Files — SMB vs. NFS
  17. Azure Files Identity-Based Access
  18. Premium File Shares and the Provisioned Model
  19. File Share Snapshots and Soft Delete
  20. Storage Account Encryption
  21. Static Website Hosting on Blob Storage
  22. Storage Firewalls and Network Rules
  23. Storage Account Failover
  24. A Full Worked Storage Bootstrap for Meridian Freight
  25. Part 8 CLI Cheat Sheet
  26. Common Mistakes and Interview Traps
  27. Worked Practice Problems
  28. Summary and What's Next

The Storage Account — One Umbrella Resource, Many Services#

A storage account is the top-level resource containing Blob, File, Queue (Part 11), and Table storage — one account, several distinct services, each with its own namespace and access model.

az storage account create --name stmeridianfreight --resource-group rg-shipment-api-prod \
  --sku Standard_ZRS --kind StorageV2 --access-tier Hot --location eastus

Meridian Freight's docs-processor pipeline stores scanned bills of lading as blobs, driver-portal shares configuration files via Azure Files, and Part 11's messaging chapter uses the same account's Queue service — all under one account, with per-service access control layered on top.


Storage Redundancy Options — LRS, ZRS, GRS, GZRS#

Diagram
OptionProtects againstDurability
LRSDrive/rack failure within one datacenterLowest, cheapest
ZRSDatacenter-level failure within one regionHigher — survives a full zone loss
GRSFull regional outage (secondary region copy, not readable by default)High, but secondary not zone-redundant
RA-GRSSame as GRS, plus the secondary region copy is READABLESame durability, added read availability
GZRSZone failure in primary AND full regional outageHighest — "16 nines" durability

Why ZRS is worth treating as the sensible default for most production workloads rather than the cheaper LRS, worth stating explicitly: LRS's "3 copies in one datacenter" durability doesn't survive the exact same datacenter-level failure this series' compute chapters (Part 3) already spend real design effort protecting against with Availability Zones — pairing zone-redundant compute with only locally-redundant storage leaves a real, mismatched gap in the design's actual resilience.


Blob Storage — Containers and Blob Types#

az storage container create --name docs --account-name stmeridianfreight --auth-mode login

az storage blob upload --account-name stmeridianfreight --container-name docs \
  --name "bol-2026-001.pdf" --file ./bol-2026-001.pdf --auth-mode login
Blob typeBest fit
Block blobMost common — discrete files uploaded as blocks (documents, images, backups)
Page blobRandom read/write access — the underlying type behind managed disks (Part 3)
Append blobOptimized for append-only writes — log files being continuously written to

Azure Data Lake Storage Gen2 — Hierarchical Namespace#

Data Lake Storage Gen2 (ADLS Gen2) is not a separate service — it's a capability layered on top of a regular Blob Storage account, enabling a genuine hierarchical namespace (real, atomic directories and subdirectories, like a filesystem) instead of Blob Storage's default flat namespace, where "folders" are purely a naming convention (a / in the blob name) with no real directory object behind them.

az storage account create --name stmeridiananalytics --resource-group rg-shipment-api-prod \
  --sku Standard_ZRS --kind StorageV2 --hierarchical-namespace true
Diagram

Why this distinction matters concretely for a genuinely large dataset, worth stating the underlying reasoning: a flat namespace's "folder rename" is actually a full copy-and-delete of every blob under that prefix — for a large analytics dataset, this can be a slow, expensive operation touching millions of objects. A hierarchical namespace makes directory-level operations atomic and metadata-only, which is exactly why ADLS Gen2 is the standard choice for big-data analytics workloads (feeding tools like Azure Databricks or Synapse) rather than a flat-namespace Blob Storage account, even though both ultimately store the same underlying blob data.


Blob Index Tags#

Blob index tags attach searchable key-value metadata directly to a blob — genuinely different from a container's own resource tags (Part 1), since these are queryable across an entire account without needing to know a blob's path in advance.

az storage blob tag set --account-name stmeridianfreight --container-name docs \
  --name "bol-2026-001.pdf" --tags carrier=acme-logistics status=processed --auth-mode login

# Query blobs by tag, across the WHOLE account, without knowing paths
az storage blob list --account-name stmeridianfreight --container-name docs \
  --query-tag "status='processed' AND carrier='acme-logistics'" --auth-mode login

Why this is worth treating as a real, distinct capability from simply naming files with a convention (like acme-logistics-bol-001.pdf), worth stating explicitly: index tags are natively queryable via the Blob Storage API itself, without needing a separate index/database tracking which blob has which attributesdocs-processor can tag each processed document with its carrier and status directly, then query "every unprocessed document for carrier X" as a single API call, rather than maintaining a separate lookup table purely to answer that question.

# Update just the status tag as a document moves through processing,
# without touching the blob's actual content or other tags
az storage blob tag set --account-name stmeridianfreight --container-name docs \
  --name "bol-2026-001.pdf" --tags carrier=acme-logistics status=awaiting-review --auth-mode login

Blob Access Tiers — Hot, Cool, Cold, and Archive#

A genuinely current fact worth stating explicitly: Azure now offers four access tiers, not the three (Hot/Cool/Archive) many existing tutorials and older training material still describe — Cold was added as a distinct tier between Cool and Archive.

az storage blob set-tier --account-name stmeridianfreight --container-name docs \
  --name "bol-2024-001.pdf" --tier Cold --auth-mode login
TierAccess patternMinimum storage durationRetrieval
HotFrequent accessNoneImmediate
CoolInfrequent access (roughly monthly)30 daysImmediate
ColdRare access (roughly quarterly or less)90 daysImmediate
ArchiveEffectively offline180 daysUp to 15 hours (rehydration)

Why the minimum storage duration matters concretely as a real cost trap, worth stating explicitly: moving, overwriting, or deleting a blob before its tier's minimum duration elapses triggers an EARLY DELETION FEE — equal to the storage cost for the remaining days in that minimum period. A lifecycle policy (next section) that moves data too aggressively into Cold or Archive, only for it to be accessed and moved back out early, can end up costing more in early-deletion fees than it saved in tiered storage pricing — the policy needs to reflect a genuinely realistic access pattern, not an aggressive cost-cutting guess.


Blob Lifecycle Management#

{
  "rules": [{
    "name": "meridian-docs-lifecycle",
    "type": "Lifecycle",
    "definition": {
      "filters": {"blobTypes": ["blockBlob"], "prefixMatch": ["docs/"]},
      "actions": {
        "baseBlob": {
          "tierToCool": {"daysAfterModificationGreaterThan": 30},
          "tierToCold": {"daysAfterModificationGreaterThan": 180},
          "tierToArchive": {"daysAfterModificationGreaterThan": 365},
          "delete": {"daysAfterModificationGreaterThan": 2555}
        }
      }
    }
  }]
}
az storage account management-policy create --account-name stmeridianfreight \
  --resource-group rg-shipment-api-prod --policy @lifecycle-policy.json

Worth stating precisely how lifecycle management actually runs, since it's a common source of "why hasn't this happened yet" confusion: policies run once per day and process blobs asynchronously — a tier transition can take 24-48 hours after a blob first meets the age condition, not instantly the moment the threshold is crossed.


Rehydrating From Archive#

az storage blob set-tier --account-name stmeridianfreight --container-name docs \
  --name "bol-2020-001.pdf" --tier Hot --rehydrate-priority High --auth-mode login
Rehydrate priorityTypical completion time
StandardUp to 15 hours
HighOften under 1 hour, for smaller blobs

Why archive retrieval time is worth surfacing explicitly to any application design touching archived data, not just a storage-layer implementation detail: an application that assumes ANY blob is instantly readable will break in a genuinely confusing way against an archived one — a design retrieving potentially-archived documents needs an explicit "this may take up to 15 hours" UX path, not a synchronous read assumption that silently fails or hangs.


Blob Versioning and Soft Delete#

az storage account blob-service-properties update --account-name stmeridianfreight \
  --resource-group rg-shipment-api-prod --enable-versioning true

az storage account blob-service-properties update --account-name stmeridianfreight \
  --resource-group rg-shipment-api-prod --enable-delete-retention true --delete-retention-days 30

Versioning keeps every prior version of a blob automatically whenever it's overwritten; soft delete keeps a deleted blob recoverable for a configured retention window. Together they cover both accidental overwrite and accidental deletion — two genuinely distinct failure modes worth protecting against with both mechanisms, not just one.


Blob Immutability (WORM) Policies#

For regulatory requirements needing write-once-read-many guarantees (financial records, certain compliance-regulated documents), an immutability policy makes a blob genuinely unmodifiable and undeletable for a defined retention period — not even a Storage Account Owner can bypass it once locked.

az storage container immutability-policy create --account-name stmeridianfreight \
  --container-name docs --resource-group rg-shipment-api-prod --period 2555

az storage container immutability-policy lock --account-name stmeridianfreight \
  --container-name docs --resource-group rg-shipment-api-prod --if-match "<etag>"

Why the LOCK step is worth stating as genuinely irreversible, not a formality: an unlocked immutability policy can still be modified or removed — locking it makes the retention period itself immutable, which is precisely the guarantee a regulator or auditor requiring WORM compliance actually needs. This should be treated as a deliberate, reviewed action, not a default step applied casually.


Object Replication#

az storage account or-policy create --account-name stmeridianfreight --resource-group rg-shipment-api-prod \
  --destination-account stmeridianfreighteurope \
  --source-container docs --destination-container docs

Object replication copies block blobs asynchronously between a source and destination storage account — distinct from GRS/GZRS's account-level redundancy, worth stating the difference precisely: object replication is CONTAINER-scoped and lets the destination account live in a different region with independent read/write access, useful for a scenario like feeding docs-processor's European regional deployment from the primary account without a full account-level GZRS failover relationship.


Shared Access Signatures#

A Shared Access Signature (SAS) grants time-limited, scoped access to storage resources without sharing the account's own keys.

az storage container generate-sas --account-name stmeridianfreight --name docs \
  --permissions r --expiry 2026-09-01T00:00:00Z --auth-mode login --as-user

A genuinely important current recommendation worth stating explicitly: --as-user generates a SAS backed by Entra ID (Part 2) credentials rather than the account key — a User Delegation SAS, which can be revoked by revoking the underlying Entra identity's access, unlike an account-key-based SAS, which remains valid until it expires or the account key itself is rotated. User delegation SAS is Microsoft's current recommended default over key-based SAS for exactly this reason — genuine, immediate revocability.


Stored Access Policies#

az storage container policy create --account-name stmeridianfreight --container-name docs \
  --name policy-read-only --permissions r --expiry 2026-12-31T00:00:00Z --auth-mode login

Why a stored access policy is worth using for any SAS meant to be revocable before its natural expiry, worth stating the underlying mechanism: a SAS generated WITHOUT a stored access policy cannot be individually revoked before it expires — revoking the policy it's associated WITH is the only way to invalidate an already-issued SAS early, since the SAS token itself is just a signed string, not a server-side record Azure can look up and delete directly.


Storage Account Keys and Rotation#

# Storage account keys grant FULL access — treat rotation as a
# routine, scheduled operation, not a reactive one
az storage account keys renew --account-name stmeridianfreight --resource-group rg-shipment-api-prod --key primary

Worth stating as a hard recommendation, directly reinforcing Part 2's identity-first philosophy: prefer Entra ID-based authentication (managed identities, RBAC) over account keys entirely wherever a client supports it — an account key is a long-lived, all-or-nothing credential with none of the scoping, auditability, or revocability Entra ID-backed access provides.


Azure Storage Explorer and AzCopy#

# AzCopy — the high-throughput CLI tool for bulk data movement,
# genuinely faster than the Azure CLI's own blob commands for large transfers
azcopy copy "./local-folder" "https://stmeridianfreight.blob.core.windows.net/docs?<sas-token>" --recursive

Storage Explorer (a GUI application) and AzCopy (the CLI tool) serve complementary purposes: Explorer for interactive browsing/small operations, AzCopy for scripted, high-throughput bulk transfers — AzCopy is what Meridian Freight's initial bulk migration of historical documents into docs should actually use, not a script built around individual az storage blob upload calls one file at a time.

# AzCopy sync — transfers only NEW or CHANGED files, not a full
# re-copy every time, genuinely important for a recurring migration job
azcopy sync "./local-folder" "https://stmeridianfreight.blob.core.windows.net/docs?<sas-token>" \
  --recursive --delete-destination=false

Why sync (rather than repeatedly running copy) is worth using for any RECURRING transfer job, worth stating the underlying reasoning: copy re-transfers everything every run, while sync compares source and destination first and transfers only the delta — for Meridian Freight's nightly batch upload of newly scanned documents, sync avoids re-uploading the entire historical archive every single night, a real, meaningful difference in both transfer time and egress cost at scale.


Azure Files — SMB vs. NFS#

az storage share-rm create --storage-account stmeridianfreight --name config-share \
  --resource-group rg-shipment-api-prod --enabled-protocols SMB --quota 100
SMBNFS
Client OSWindows, Linux, macOSLinux only
Available onStandard and Premium tiersPremium (SSD-backed) tier only
Mixing with the other protocol on the SAME shareNot supportedNot supported

Worth stating precisely, since it's an easy assumption to get wrong: a single file share is EITHER SMB or NFS, never both — an application needing to serve both Windows and Linux clients over their respective native protocols needs two separate shares (possibly within the same storage account), not one share configured for "both."


Azure Files Identity-Based Access#

az storage account update --name stmeridianfreight --resource-group rg-shipment-api-prod \
  --enable-files-aadkerb true

Identity-based access lets Azure Files authenticate and authorize using Entra ID (via Kerberos) or on-premises AD DS, applying real per-user NTFS-style permissions on a share — rather than every client sharing one storage account key with no per-user distinction at all. For Meridian Freight's driver-portal config share, this means individual engineers' access can be revoked exactly the way Part 2 already governs every other Entra-backed resource, rather than requiring a full storage-key rotation to cut off one person's access.


Premium File Shares and the Provisioned Model#

az storage account create --name stmeridianpremium --resource-group rg-shipment-api-prod \
  --sku Premium_LRS --kind FileStorage

az storage share-rm create --storage-account stmeridianpremium --name high-perf-share \
  --resource-group rg-shipment-api-prod --quota 1024

A genuinely important structural fact worth stating precisely: Premium file shares live in a DEDICATED FileStorage kind account, entirely separate from the general-purpose v2 accounts Standard shares use — an existing GPv2 storage account cannot simply be "upgraded" to host a Premium share; a new, purpose-built account is required. Premium is also provisioned, not consumption-based — the account is billed for the provisioned quota regardless of actual data stored, the same billing model Part 3's Premium SSD v2 disks use.


File Share Snapshots and Soft Delete#

az storage share snapshot --account-name stmeridianfreight --name config-share --auth-mode login

az storage account file-service-properties update --account-name stmeridianfreight \
  --resource-group rg-shipment-api-prod --enable-soft-delete true --delete-retention-days 14

Share-level snapshots and soft delete mirror the blob-level protections covered earlier in this chapter — worth configuring both for any share holding data that would be genuinely painful to lose, the same reasoning already applied to Blob Storage.


Storage Account Encryption#

Every storage account is encrypted at rest by default using Microsoft-managed keys — worth knowing the stronger alternative exists for compliance-sensitive data.

az storage account update --name stmeridianfreight --resource-group rg-shipment-api-prod \
  --encryption-key-source Microsoft.Keyvault \
  --encryation-key-vault "<key-vault-uri>" --encryption-key-name storage-cmk

Customer-managed keys (CMK), stored in Key Vault (Part 12), let an organization control the encryption key's own lifecycle — including revoking it to make ALL data in the account cryptographically inaccessible instantly, a real, powerful control unavailable with Microsoft-managed keys, at the cost of the organization now owning key rotation and availability as its own operational responsibility.


Static Website Hosting on Blob Storage#

az storage blob service-properties update --account-name stmeridianfreight \
  --static-website --index-document index.html --404-document 404.html

A genuinely useful, low-cost pattern worth knowing exists: Blob Storage can serve a static website directly, with no VM, App Service, or container required at all — a real fit for Meridian Freight's own marketing/documentation site, or a static status page, at a small fraction of the cost of any compute-backed hosting option, typically paired with Front Door (Part 6) in front for a custom domain and caching.


Storage Firewalls and Network Rules#

az storage account update --name stmeridianfreight --resource-group rg-shipment-api-prod \
  --default-action Deny

az storage account network-rule add --account-name stmeridianfreight --resource-group rg-shipment-api-prod \
  --vnet-name vnet-shipment-api --subnet snet-app

Setting --default-action Deny is worth treating as the correct default for any storage account holding real data, worth stating explicitly: the default configuration allows access from any network by default, relying entirely on SAS tokens/keys/RBAC for protection — explicitly denying all network access except from approved VNets/subnets (or, per Part 7, requiring Private Link entirely) adds a genuine second layer, directly embodying this series' repeated Zero Trust recommendation rather than relying on identity-layer controls alone.


Storage Account Failover#

az storage account failover --name stmeridianfreight --resource-group rg-shipment-api-prod --failover-type Planned

For a GRS/GZRS account, a failover promotes the secondary region to primary — Part 14 covers the full disaster recovery decision framework (when to failover, RTO/RPO implications) in depth; this chapter's scope is knowing the mechanism exists and requires GRS/GZRS redundancy already configured before it's ever needed, not something that can be added reactively during an actual regional outage.


A Full Worked Storage Bootstrap for Meridian Freight#

# 1. Create the primary storage account with ZRS and a default-deny firewall
az storage account create --name stmeridianfreight --resource-group rg-shipment-api-prod \
  --sku Standard_ZRS --kind StorageV2 --default-action Deny

# 2. Enable versioning and soft delete for both Blob and File services
az storage account blob-service-properties update --account-name stmeridianfreight \
  --resource-group rg-shipment-api-prod --enable-versioning true --enable-delete-retention true --delete-retention-days 30

# 3. Add a Private Endpoint (Part 7) rather than relying on firewall rules alone
az network private-endpoint create --name pe-storage-shipment-api --resource-group rg-shipment-api-prod \
  --vnet-name vnet-shipment-api --subnet snet-private-endpoints \
  --private-connection-resource-id "<storage-account-resource-id>" --group-id blob

# 4. Apply a lifecycle policy tiering docs to Cool/Cold/Archive over time
az storage account management-policy create --account-name stmeridianfreight \
  --resource-group rg-shipment-api-prod --policy @lifecycle-policy.json

# 5. Create the driver-portal config share with identity-based access
az storage share-rm create --storage-account stmeridianfreight --name config-share \
  --resource-group rg-shipment-api-prod --enabled-protocols SMB
az storage account update --name stmeridianfreight --resource-group rg-shipment-api-prod \
  --enable-files-aadkerb true

Part 8 CLI Cheat Sheet#

AreaCommandPurpose
Accountaz storage account createCreate a storage account
Blobaz storage container create / blob uploadCreate a container and upload a blob
Tiersaz storage blob set-tierMove a blob between access tiers
Lifecycleaz storage account management-policy createApply automated tiering/deletion rules
Versioningaz storage account blob-service-properties update --enable-versioningEnable blob versioning
Immutabilityaz storage container immutability-policy create/lockApply and lock a WORM policy
Replicationaz storage account or-policy createConfigure object replication
SASaz storage container generate-sas --as-userGenerate a revocable User Delegation SAS
Filesaz storage share-rm createCreate a file share
Files identityaz storage account update --enable-files-aadkerbEnable Entra Kerberos identity-based access
Firewallaz storage account network-rule addRestrict access to specific VNets/subnets
Failoveraz storage account failoverFail over a GRS/GZRS account to its secondary region
ADLS Gen2az storage account create --hierarchical-namespace trueEnable a real hierarchical namespace for analytics workloads
Index tagsaz storage blob tag set / blob list --query-tagAttach and query searchable blob metadata

Common Mistakes and Interview Traps#

MistakeWhy It's WrongFix
Using LRS for production data protected by zone-redundant computeCreates a mismatched resilience gap — compute survives a zone loss, storage doesn'tUse ZRS (or GZRS for cross-region) to match the compute layer's redundancy
Aggressively tiering data to Cold/Archive without confirming real access patternsEarly access triggers an early-deletion fee that can exceed the tiering savingsBase lifecycle rules on a realistic, observed access pattern, not an aggressive cost-cutting guess
Assuming a lifecycle policy transition happens instantlyPolicies run daily and process asynchronously — up to 24-48 hours of lagAccount for this lag in any design depending on tier transitions
Assuming any blob is instantly readable regardless of tierAn archived blob needs rehydration, up to 15 hoursDesign an explicit "may take time" UX path for potentially-archived data
Using account-key-based SAS tokens by defaultCannot be individually revoked before expiry without a stored access policyPrefer User Delegation SAS (Entra-backed) for genuine revocability
Leaving a storage account's default network action as AllowRelies entirely on identity/key-layer controls with no network-layer defenseSet --default-action Deny and use Private Link/VNet rules
Assuming a Premium file share can be added to an existing GPv2 storage accountPremium shares require a dedicated FileStorage kind accountCreate a new, purpose-built FileStorage account for Premium shares
Configuring GRS/GZRS reactively during an actual regional outageRedundancy must already be configured before a failover is neededSet the correct redundancy tier at account creation, not as an incident response
Using a flat-namespace Blob Storage account for a large analytics dataset needing frequent directory reorganizationFolder renames become full copy-and-delete operations across every affected blobEnable hierarchical namespace (ADLS Gen2) for atomic, metadata-only directory operations
Building a separate lookup database purely to track blob metadata like processing statusDuplicates what blob index tags already provide natively, queryable via the Storage APIUse blob index tags for metadata that needs to be searchable across an account

Worked Practice Problems#

Problem 1: Meridian Freight applies a lifecycle policy moving docs-processor's scanned documents to Archive tier after 90 days, based on an assumption that documents are rarely accessed after processing completes. Three months later, a compliance audit requires retrieving documents from 100 days prior, and the team discovers both a multi-hour retrieval delay AND an unexpected early-deletion fee on the account's bill. What two things went wrong?

Answer: First, the team didn't account for archive tier's retrieval characteristic — an archived blob requires rehydration (up to 15 hours, or under an hour with High priority for smaller blobs) before it's readable, which the audit's presumably tighter timeline didn't anticipate; any workflow that might need archived data should plan for this delay explicitly rather than assuming instant access. Second, and likely the source of the unexpected fee: if any of the 100-day-old documents were moved out of Archive (rehydrated) before reaching Archive's 180-day minimum storage duration, an early-deletion fee applies — equal to the storage cost for the remaining days in that minimum period. The underlying lesson is that the lifecycle policy's 90-day Archive threshold didn't match the ACTUAL access pattern (compliance audits requiring retrieval well before the 180-day minimum), which is exactly the mismatch this chapter's own guidance warns against — tiering rules need to reflect a realistic access pattern, not just a cost-minimizing guess.

Problem 2: An engineer generates a SAS token for a partner integration granting read access to a container, valid for one year, without creating a stored access policy first. Two months later, the partnership ends abruptly and the security team needs to revoke that partner's access immediately. What's the problem, and what should have been done differently?

Answer: A SAS token generated without an associated stored access policy cannot be individually revoked before its natural expiry — the token itself is a self-contained, signed string that Azure validates cryptographically rather than looking up in a revocable server-side record, so there's no direct way to invalidate just this one token early. The only recourse now is rotating the storage account's keys entirely (if it was a key-based SAS), which would also invalidate every OTHER SAS token generated from the same key — a far more disruptive fix than necessary. The stored access policy should have been created first, with the SAS generated against it — revoking or modifying the stored access policy would then immediately invalidate the SAS without affecting any other tokens tied to different policies, precisely the targeted revocation this incident needed.

Problem 3: Meridian Freight configures a storage account with GZRS redundancy specifically to protect against a full regional outage, but during an actual eastus regional incident, the platform team discovers they cannot access the data in the secondary region at all — access attempts simply fail. What configuration step was likely missed?

Answer: The account was very likely configured as GZRS rather than RA-GZRS (read-access GZRS) — plain GZRS (like plain GRS) replicates data to the secondary region but does NOT make that secondary copy readable by default; only the read-access variant exposes a separate read-only endpoint for the secondary region before a failover is performed. Without RA-GZRS, the secondary copy exists but is genuinely inaccessible until an actual failover operation is performed, promoting it to primary — during an active regional outage, this is exactly the moment such an operation carries the most risk and time pressure. The team should reconfigure to RA-GZRS, allowing read access to the secondary copy immediately during a future incident without necessarily needing a full failover first.

Problem 4: A team building driver-portal's configuration file share is deciding between SMB and NFS protocols, needing the share accessible from both Windows-based corporate laptops AND Linux-based VMSS instances. A proposal suggests configuring the share to support "both protocols" for simplicity. Why isn't this possible, and what's the correct design?

Answer: A single Azure file share supports exactly one protocol — SMB or NFS — never both simultaneously; this isn't a configuration option that can be toggled on, it's a hard architectural constraint. The correct design creates two separate shares — an SMB share for the Windows laptops and an NFS share for the Linux VMSS instances — either within the same storage account (since a storage account can host classic SMB and NFS shares side by side, just not on the same individual share) or across two accounts if other requirements (like NFS's Premium-tier-only restriction) drive that split. If both audiences need access to the exact same underlying data rather than independent copies, a different pattern (rsync-style synchronization, or restructuring to a single-protocol design with a gateway/proxy for the other client type) would be needed — but "one share, both protocols" is simply not an available configuration.

Problem 5: Meridian Freight's security team requires that if a specific encryption key is ever compromised, all data in a particular storage account must be made cryptographically inaccessible IMMEDIATELY, without needing to delete or move any actual data. Which encryption configuration satisfies this, and why doesn't the default configuration?

Answer: Customer-managed keys (CMK), stored in Key Vault, satisfy this requirement — because the organization controls the key's own lifecycle independently of the storage account, revoking or disabling that key in Key Vault immediately makes every blob encrypted under it cryptographically inaccessible, without touching the underlying data at all. The default configuration (Microsoft-managed keys) doesn't satisfy this because Microsoft controls the key lifecycle entirely — there's no customer-facing action that revokes a Microsoft-managed key on demand. The tradeoff worth surfacing to the team: adopting CMK shifts real operational responsibility (key rotation, key availability — a Key Vault outage or accidentally deleted key can itself make data inaccessible) onto the organization, which is a genuine cost of the added control, not a purely additive upgrade.

Problem 6: Meridian Freight's analytics team stores several terabytes of historical shipment data in a regular flat-namespace Blob Storage account, organized by a year/month/day/ prefix convention. A data reorganization project needs to rename the top-level 2025/ prefix to archive-2025/ across millions of blobs, and the operation is taking hours and generating significant transaction costs. What structural choice caused this, and what would have avoided it?

Answer: The flat namespace is the structural cause — in a regular Blob Storage account, "renaming a folder" is not a real operation at all, since directories are just a naming convention; Azure has to copy every single blob under that prefix to a new name and delete the original, which is exactly why it's slow and costly at millions-of-blobs scale. Enabling hierarchical namespace (Data Lake Storage Gen2) from the start would have made 2025/ a real directory object, and renaming it to archive-2025/ would be a single, atomic, metadata-only operation regardless of how many blobs live underneath it. This is a genuinely important design decision to make BEFORE data accumulates at scale — hierarchical namespace can be enabled on some existing accounts, but planning for it from account creation avoids ever hitting this exact costly reorganization scenario.

Problem 7: docs-processor needs to answer "show me every document still awaiting review for carrier X" efficiently, across potentially millions of documents, without maintaining a separate database purely to track this. What Blob Storage capability fits, and why is it a better fit than embedding this information in the blob's file name?

Answer: Blob index tags fit precisely — tagging each blob with carrier=X and status=awaiting-review (or similar) makes it natively queryable via az storage blob list --query-tag, without any separate index or database to build and keep in sync. Embedding the same information in the file name (e.g., carrier-X-status-awaiting-review-doc123.pdf) would technically work for a human browsing blobs, but it isn't efficiently queryable at scale — finding "every carrier-X, awaiting-review blob" would require listing and pattern-matching against every blob name in the container, which doesn't scale the way a native tag query does, and renaming would be needed every time a document's status changes (itself an operation with real cost in a flat namespace, per the previous problem). Index tags are purpose-built for exactly this kind of searchable-metadata need.


Summary and What's Next#

  • The storage account is one umbrella resource spanning Blob, File, Queue, and Table services — Meridian Freight's docs-processor, driver-portal, and messaging (Part 11) needs can share one account with per-service access control.
  • ZRS (or GZRS) matches the resilience level this series' compute chapters already build for — LRS alone leaves a mismatched gap against zone-redundant compute.
  • Azure now has FOUR blob access tiers (Hot, Cool, Cold, Archive), not the three many older tutorials describe — each with a minimum storage duration whose early-deletion fee needs to inform lifecycle policy design, not just the storage-cost savings.
  • User Delegation SAS (Entra-backed, --as-user) is the current recommended default over key-based SAS — genuinely revocable by revoking the underlying identity, unlike a key-based token.
  • Azure Files identity-based access extends Part 2's Entra-first access model to file shares — per-user permissions and revocation, rather than one shared account key for every client.
  • Premium file shares require a dedicated FileStorage account and use a provisioned billing model — structurally distinct from Standard shares in a general-purpose v2 account.
  • A storage account's default network configuration allows access from anywhere — explicitly denying by default and layering Private Link (Part 7) or VNet rules on top is the Zero-Trust-aligned correction.
  • Data Lake Storage Gen2's hierarchical namespace makes directory operations atomic and metadata-only — the standard choice for large analytics datasets over a flat-namespace account's costly copy-and-delete folder renames.
  • Blob index tags provide natively queryable metadata across an entire account — a purpose-built alternative to embedding searchable attributes in file names or maintaining a separate lookup database.

Continue to Part 9 (09-databases-and-data-services.md) for Azure's managed database services — Azure SQL, Cosmos DB, and managed PostgreSQL/MySQL — the structured-data counterpart to this chapter's blob/file storage.