# Azure Cloud Architecture — Part 8: Storage: Blob, Files & Disks

> **Series:** Azure Cloud Architecture (8 of 16)
> **Part 1:** `01-fundamentals-and-governance.md` — Fundamentals & Governance
> **Part 2:** `02-identity-and-access.md` — Identity & Access
> **Part 3:** `03-compute-vms-and-scale-sets.md` — Compute: Virtual Machines & Scale Sets
> **Part 4:** `04-networking-foundations-vnets-ip-and-dns.md` — Networking Foundations: VNets, IP & DNS
> **Part 5:** `05-networking-hybrid-connectivity.md` — Networking: Hybrid Connectivity
> **Part 6:** `06-networking-application-delivery.md` — Networking: Application Delivery
> **Part 7:** `07-networking-private-access-and-security.md` — Networking: Private Access & Security
> **Part 8:** This file — Storage: Blob, Files & Disks
> **Part 9:** `09-databases-and-data-services.md` — Databases & Data Services
> **Part 10:** `10-containers-and-serverless.md` — Containers & Serverless
> **Part 11:** `11-application-architecture-and-messaging.md` — Application Architecture & Messaging
> **Part 12:** `12-security-and-compliance.md` — Security & Compliance
> **Part 13:** `13-monitoring-logging-and-observability.md` — Monitoring, Logging & Observability
> **Part 14:** `14-business-continuity-backup-dr-and-migration.md` — Business Continuity: Backup, DR & Migration
> **Part 15:** `15-cicd-and-iac.md` — CI/CD & Infrastructure as Code
> **Part 16:** `16-multi-region-cost-optimization-and-cheatsheet.md` — Multi-Region, Cost Optimization & Cheat Sheet
> **Questions:** `questions.md`

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](#the-storage-account--one-umbrella-resource-many-services)
2. [Storage Redundancy Options — LRS, ZRS, GRS, GZRS](#storage-redundancy-options--lrs-zrs-grs-gzrs)
3. [Blob Storage — Containers and Blob Types](#blob-storage--containers-and-blob-types)
4. [Azure Data Lake Storage Gen2 — Hierarchical Namespace](#azure-data-lake-storage-gen2--hierarchical-namespace)
5. [Blob Index Tags](#blob-index-tags)
6. [Blob Access Tiers — Hot, Cool, Cold, and Archive](#blob-access-tiers--hot-cool-cold-and-archive)
7. [Blob Lifecycle Management](#blob-lifecycle-management)
8. [Rehydrating From Archive](#rehydrating-from-archive)
9. [Blob Versioning and Soft Delete](#blob-versioning-and-soft-delete)
10. [Blob Immutability (WORM) Policies](#blob-immutability-worm-policies)
11. [Object Replication](#object-replication)
12. [Shared Access Signatures](#shared-access-signatures)
13. [Stored Access Policies](#stored-access-policies)
14. [Storage Account Keys and Rotation](#storage-account-keys-and-rotation)
15. [Azure Storage Explorer and AzCopy](#azure-storage-explorer-and-azcopy)
16. [Azure Files — SMB vs. NFS](#azure-files--smb-vs-nfs)
17. [Azure Files Identity-Based Access](#azure-files-identity-based-access)
18. [Premium File Shares and the Provisioned Model](#premium-file-shares-and-the-provisioned-model)
19. [File Share Snapshots and Soft Delete](#file-share-snapshots-and-soft-delete)
20. [Storage Account Encryption](#storage-account-encryption)
21. [Static Website Hosting on Blob Storage](#static-website-hosting-on-blob-storage)
22. [Storage Firewalls and Network Rules](#storage-firewalls-and-network-rules)
23. [Storage Account Failover](#storage-account-failover)
24. [A Full Worked Storage Bootstrap for Meridian Freight](#a-full-worked-storage-bootstrap-for-meridian-freight)
25. [Part 8 CLI Cheat Sheet](#part-8-cli-cheat-sheet)
26. [Common Mistakes and Interview Traps](#common-mistakes-and-interview-traps)
27. [Worked Practice Problems](#worked-practice-problems)
28. [Summary and What's Next](#summary-and-whats-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.

```bash
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

```mermaid
graph TD
    LRS["LRS — 3 copies,\nONE datacenter"] --> ZRS["ZRS — 3 copies,\nacross Availability Zones\nin ONE region"]
    ZRS --> GRS["GRS — LRS in primary +\nLRS in a SECONDARY region\n(not zone-redundant in either)"]
    GRS --> GZRS["GZRS — ZRS in primary +\nLRS in a secondary region —\nthe strongest combined option"]
```

| Option | Protects against | Durability |
|---|---|---|
| LRS | Drive/rack failure within one datacenter | Lowest, cheapest |
| ZRS | Datacenter-level failure within one region | Higher — survives a full zone loss |
| GRS | Full regional outage (secondary region copy, not readable by default) | High, but secondary not zone-redundant |
| RA-GRS | Same as GRS, plus the secondary region copy is READABLE | Same durability, added read availability |
| GZRS | Zone failure in primary AND full regional outage | Highest — "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

```bash
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 type | Best fit |
|---|---|
| Block blob | Most common — discrete files uploaded as blocks (documents, images, backups) |
| Page blob | Random read/write access — the underlying type behind managed disks (Part 3) |
| Append blob | Optimized 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.

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

```mermaid
graph TD
    Flat["Flat namespace\n(regular Blob Storage) —\n'docs/2026/january/file.pdf'\nis just a NAME, no real\ndirectory object"] --> Rename1["Renaming 'docs/2026' requires\nrewriting EVERY blob\nunder that prefix"]
    Hierarchical["Hierarchical namespace\n(ADLS Gen2) — 'docs/2026/january/'\nis a REAL directory object"] --> Rename2["Renaming 'docs/2026' is a\nsingle, atomic METADATA\noperation — no data rewrite"]
```

**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.

```bash
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 attributes** — `docs-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.

```bash
# 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.

```bash
az storage blob set-tier --account-name stmeridianfreight --container-name docs \
  --name "bol-2024-001.pdf" --tier Cold --auth-mode login
```

| Tier | Access pattern | Minimum storage duration | Retrieval |
|---|---|---|---|
| Hot | Frequent access | None | Immediate |
| Cool | Infrequent access (roughly monthly) | 30 days | Immediate |
| Cold | Rare access (roughly quarterly or less) | 90 days | Immediate |
| Archive | Effectively offline | 180 days | Up 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

```json
{
  "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}
        }
      }
    }
  }]
}
```

```bash
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

```bash
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 priority | Typical completion time |
|---|---|
| Standard | Up to 15 hours |
| High | Often 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

```bash
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.

```bash
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

```bash
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.

```bash
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

```bash
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

```bash
# 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

```bash
# 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.

```bash
# 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

```bash
az storage share-rm create --storage-account stmeridianfreight --name config-share \
  --resource-group rg-shipment-api-prod --enabled-protocols SMB --quota 100
```

| | SMB | NFS |
|---|---|---|
| Client OS | Windows, Linux, macOS | Linux only |
| Available on | Standard and Premium tiers | Premium (SSD-backed) tier only |
| Mixing with the other protocol on the SAME share | Not supported | Not 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

```bash
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

```bash
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

```bash
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.

```bash
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

```bash
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

```bash
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

```bash
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

```bash
# 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

| Area | Command | Purpose |
|---|---|---|
| Account | `az storage account create` | Create a storage account |
| Blob | `az storage container create` / `blob upload` | Create a container and upload a blob |
| Tiers | `az storage blob set-tier` | Move a blob between access tiers |
| Lifecycle | `az storage account management-policy create` | Apply automated tiering/deletion rules |
| Versioning | `az storage account blob-service-properties update --enable-versioning` | Enable blob versioning |
| Immutability | `az storage container immutability-policy create/lock` | Apply and lock a WORM policy |
| Replication | `az storage account or-policy create` | Configure object replication |
| SAS | `az storage container generate-sas --as-user` | Generate a revocable User Delegation SAS |
| Files | `az storage share-rm create` | Create a file share |
| Files identity | `az storage account update --enable-files-aadkerb` | Enable Entra Kerberos identity-based access |
| Firewall | `az storage account network-rule add` | Restrict access to specific VNets/subnets |
| Failover | `az storage account failover` | Fail over a GRS/GZRS account to its secondary region |
| ADLS Gen2 | `az storage account create --hierarchical-namespace true` | Enable a real hierarchical namespace for analytics workloads |
| Index tags | `az storage blob tag set` / `blob list --query-tag` | Attach and query searchable blob metadata |

---

## Common Mistakes and Interview Traps

| Mistake | Why It's Wrong | Fix |
|---|---|---|
| Using LRS for production data protected by zone-redundant compute | Creates a mismatched resilience gap — compute survives a zone loss, storage doesn't | Use ZRS (or GZRS for cross-region) to match the compute layer's redundancy |
| Aggressively tiering data to Cold/Archive without confirming real access patterns | Early access triggers an early-deletion fee that can exceed the tiering savings | Base lifecycle rules on a realistic, observed access pattern, not an aggressive cost-cutting guess |
| Assuming a lifecycle policy transition happens instantly | Policies run daily and process asynchronously — up to 24-48 hours of lag | Account for this lag in any design depending on tier transitions |
| Assuming any blob is instantly readable regardless of tier | An archived blob needs rehydration, up to 15 hours | Design an explicit "may take time" UX path for potentially-archived data |
| Using account-key-based SAS tokens by default | Cannot be individually revoked before expiry without a stored access policy | Prefer User Delegation SAS (Entra-backed) for genuine revocability |
| Leaving a storage account's default network action as Allow | Relies entirely on identity/key-layer controls with no network-layer defense | Set `--default-action Deny` and use Private Link/VNet rules |
| Assuming a Premium file share can be added to an existing GPv2 storage account | Premium shares require a dedicated `FileStorage` kind account | Create a new, purpose-built `FileStorage` account for Premium shares |
| Configuring GRS/GZRS reactively during an actual regional outage | Redundancy must already be configured before a failover is needed | Set 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 reorganization | Folder renames become full copy-and-delete operations across every affected blob | Enable hierarchical namespace (ADLS Gen2) for atomic, metadata-only directory operations |
| Building a separate lookup database purely to track blob metadata like processing status | Duplicates what blob index tags already provide natively, queryable via the Storage API | Use 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.
