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 withValueError.resource_scope_level— classify any ID assubscription/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) × 43200is 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
| Concept | What to understand |
|---|---|
| ARM is the one control plane | every write is an idempotent PUT to a resource ID; the ID is the address, the RBAC scope, the Policy scope |
| Scope inheritance | a role/policy at MG → Sub → RG → resource flows down; the scope of an ID decides who already has access |
| Control plane ≠ data plane | Owner (manage the storage account) ≠ Blob Data Reader (read a blob) — different Actions vs DataActions |
| Serial multiplies | more hops in the request path → strictly lower composite SLA; adding a microservice is never free |
| Redundancy combines | 1 − ∏(1 − Aᵢ) — zones/regions raise the SLA, and the jump from three to four nines is an architecture, not a flag |
| Downtime makes it real | 99.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
| File | Purpose |
|---|---|
lab.py | skeleton with # TODO markers — your implementation |
solution.py | complete reference; python solution.py runs a worked example |
test_lab.py | the proof — run it red, make it green |
requirements.txt | pytest 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_idrejects 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_typeisvirtualNetworks/subnetswhile itsnameis the leaf (snet-app), not the parent vnet. - You can explain why
test_serial_sla_exactasserts 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_winneris 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 miniature | The production mechanism | Where to verify it |
|---|---|---|
parse_resource_id | The 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_level | The 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_sla | Azure 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_month | The "nines → minutes" table every reliability review uses; 43,200 = 30-day-month minutes. | WAF Reliability pillar |
monthly_cost_usd | The shape of a real bill: compute (vCPU-hours), storage (GB-month, e.g. | Azure Pricing Calculator |
resolve_tradeoff | A 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_tradeoffto 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/subnetsresource and confirmaz resource show --idsreturns 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 inWARMUP.mdCh. 7.
Interview / resume
- Talking points: "Walk me through what happens between
az ... createand the resource existing." "Why isOwnernot 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.