Lab 01 — WAF Tradeoff Modeler

Phase: 00 — The Azure Principal: Control Plane, Well-Architected & Tradeoffs Difficulty: ⭐⭐☆☆☆ (the arithmetic is easy; the judgment is ⭐⭐⭐⭐⭐) Time: 3–4 hours

Round-1 of an Azure architecture interview — and every design review you will ever run — is back-of-envelope arithmetic worn as architecture. This lab builds the calculator: parse an ARM resource ID and classify its scope, compose SLAs (serial multiply, redundant combine), turn availability into downtime minutes/month, model a monthly bill, and run a weighted Well-Architected tradeoff resolver whose entire point is that the same two architectures flip winners when the workload's pillar weights change — which is why "best architecture" is a meaningless phrase.

What you build

  • parse_resource_id / ResourceId — turn the canonical ARM URI /subscriptions/{s}/resourceGroups/{rg}/providers/{ns}/{type}/{name} into a frozen dataclass; support subscription-, resource-group-, and resource-level IDs and nested types (virtualNetworks/vnet/subnets/sub); reject malformed input with ValueError.
  • resource_scope_level — classify any ID as subscription / resourceGroup / resource — the unit of RBAC, Policy, tagging, and locks, and the first move in every "who can touch this?" question.
  • serial_sla / redundant_sla / downtime_minutes_per_month — the SLA arithmetic a principal does before the design review: serial dependencies multiply (∏ Aᵢ), redundant copies combine (1 − ∏(1 − Aᵢ)), and (1 − A) × 43200 is the minutes/month that make a nines-target real.
  • monthly_cost_usd — a first-order bill (compute hours + storage GB-month + egress GB) so you can see which term dominates before you optimise the wrong one.
  • Architecture (five WAF pillars) + resolve_tradeoff — score two designs under the workload's weights and return the winner and the deciding pillar (the one sentence for your ADR).

Key concepts

ConceptWhat to understand
ARM is the one control planeevery write is an idempotent PUT to a resource ID; the ID is the address, the RBAC scope, the Policy scope
Scope inheritancea role/policy at MG → Sub → RG → resource flows down; the scope of an ID decides who already has access
Control plane ≠ data planeOwner (manage the storage account) ≠ Blob Data Reader (read a blob) — different Actions vs DataActions
Serial multipliesmore hops in the request path → strictly lower composite SLA; adding a microservice is never free
Redundancy combines1 − ∏(1 − Aᵢ) — zones/regions raise the SLA, and the jump from three to four nines is an architecture, not a flag
Downtime makes it real99.9 % = 43.2 min/month; 99.99 % = 4.32 — the number that ends the argument
Weights decide the winner"best architecture" is a category error; best for these requirements is engineering

Files

FilePurpose
lab.pyskeleton with # TODO markers — your implementation
solution.pycomplete reference; python solution.py runs a worked example
test_lab.pythe proof — run it red, make it green
requirements.txtpytest only (pure stdlib otherwise)

Run

pip install -r requirements.txt
pytest test_lab.py -v                       # against your lab.py
LAB_MODULE=solution pytest test_lab.py -v   # against the reference
python solution.py                          # the worked example

Success criteria

  • All tests pass against your implementation (and LAB_MODULE=solution).
  • You can explain why parse_resource_id rejects an odd number of path segments (ARM IDs are key/value pairs — a dangling key has no value).
  • You can explain why a nested ID's resource_type is virtualNetworks/subnets while its name is the leaf (snet-app), not the parent vnet.
  • You can explain why test_serial_sla_exact asserts three 99.9 % services compose to 0.997002999 (≈ 3× the downtime of one), and why two 99 % redundant copies give 0.9999.
  • You can explain why test_weights_flip_the_winner is the whole point of the lab.
  • Given any "design X on Azure" prompt you can, in two minutes, name the dominant pillar, write a serial+redundant SLA estimate, and a monthly bill on paper.

How this maps to real Azure

The miniatureThe production mechanismWhere to verify it
parse_resource_idThe ARM resource ID is the literal addressing scheme for every resource; az resource show --ids <id> and every REST URI use it. Nested types (subnets, containers, secrets) are real.az resource list --query "[].id" -o tsv
resource_scope_levelThe scope string is exactly what you pass to az role assignment create --scope <id> and az policy assignment create --scope <id>; assignments inherit down the hierarchy.az role assignment list --scope <id>
serial_sla / redundant_slaAzure publishes per-service SLAs (e.g. App Service 99.95 %, zone-redundant SQL 99.99 %); you compose them by hand because Azure does not publish a composite for your topology.Azure SLAs
downtime_minutes_per_monthThe "nines → minutes" table every reliability review uses; 43,200 = 30-day-month minutes.WAF Reliability pillar
monthly_cost_usdThe shape of a real bill: compute (vCPU-hours), storage (GB-month, e.g. $0.018 LRS hot), and egress ($0.087/GB) — the term that ambushes multi-region designs.Azure Pricing Calculator
resolve_tradeoffA scored Well-Architected review: each pillar weighted by the workload, with the deciding pillar written into an ADR.Azure Well-Architected Framework

Limits of the miniature (be honest in the interview): availabilities are not independent (a region outage takes correlated copies down together, so redundant_sla over-estimates real multi-region availability); real bills add reserved-instance and savings-plan discounts, request/transaction costs, and tiered egress; and a WAF score is a structured conversation, not a number you can sum mechanically across every workload.

Extensions (build these on your own subscription)

  • Add availability_zone_sla(component_sla, zones, correlation) that introduces a correlation factor so redundant copies aren't treated as fully independent — the honest version of the multi-region math.
  • Add egress_cost_tiered(gb) with Azure's banded egress pricing (first 100 GB free, then banded) and show how a chatty cross-region call pattern dominates the bill.
  • Extend resolve_tradeoff to N architectures returning a ranked list with the pairwise deciding pillar — then defend the ranking against a friend who changes the weights.
  • Write the matching ARM/Bicep: a Microsoft.Network/virtualNetworks/subnets resource and confirm az resource show --ids returns the same nested ID your parser produced.
  • Emit an ADR from a TradeoffResult (context = the weights, decision = the winner, consequence = the pillar you traded away) using the template in WARMUP.md Ch. 7.

Interview / resume

  • Talking points: "Walk me through what happens between az ... create and the resource existing." "Why is Owner not enough to read a blob?" "Compose the SLA of a web app calling SQL calling Key Vault — show me the number." "Defend single-region over multi-region with numbers."
  • Resume bullet: Built a Well-Architected decision-support model that parses ARM resource-ID scopes, composes serial/redundant SLAs into downtime budgets, estimates monthly cost, and produces explainable, weight-driven architecture tradeoffs for design reviews and ADRs.