Why write a profile before picking any tool
Level 1 laid out the whole toolkit on one page. You can't choose from that page yet, because every later choice depends on facts about your estate. Do you need Managed Grafana? That depends on who reads the dashboards. Are commitment tiers worth it? That depends on how many GB of logs you ingest each day. Should you use deployment stacks? That depends on how you control change today.
An operations profile is the brief you write before the build. An architect doesn't pick materials until someone has written down how many people will live in the house and what the budget is. Your profile does the same job for an Azure estate. It has nine parts, and you will reuse it in every lesson from here to the final scorecard.
Three rules make it useful:
- Numbers over adjectives. "Lots of logs" can't be priced. "38 GB/day, 60% from firewall logs" can.
- Label every line
MUSTorPREFER. A hard requirement knocks a service out of the running. A preference only adds or takes away points. - Write
GAPwhen you don't know. A visible unknown beats a confident guess.
The nine-part template, filled in
Here is a profile for a sample estate: a mid-size retailer called Contoso Retail. Keep it as YAML in the same repo as your infrastructure code, so it gets reviewed and versioned like code.
# ops-profile.yaml - owner: platform team, reviewed quarterly
estate: # 1. Estate size and environments
subscriptions: 6
environments: [dev, test, prod] # MUST: prod isolated in its own subscription
vms: 140 # 95 Windows, 45 Linux (from Resource Graph)
aks_clusters: 2
paas_apps: 22
telemetry: # 2. Telemetry volume and retention
logs_gb_per_day: GAP # no one has measured it -> action A1
interactive_retention_days: 31 # PREFER
total_retention_days: 365 # MUST: audit policy says 1 year
slos_alerting: # 3. SLOs and alerting
checkout_availability: 99.9% # MUST: agreed with the business
on_call: PagerDuty, 1 rota # PREFER: keep the existing tool
iac_change: # 4. IaC and change control
tool: Bicep 70%, portal 30% # PREFER: move to 100% IaC
approvals: PR review + CAB for prod # MUST
patching: # 5. Patching
window: GAP # nobody owns the Linux window -> action A2
critical_patch_sla_days: 14 # MUST: from security policy
cost_allocation: # 6. Cost allocation
required_tags: [costCentre, env, owner] # MUST
tag_coverage: GAP # -> action A3
guardrails: # 7. Guardrails (covered in the security course)
allowed_regions: [uksouth, ukwest] # MUST
skills: # 8. Team skills
kql: 2 people bicep: 3 terraform: 1 grafana: 0
cost_model: # 9. Cost model
billing: pay-as-you-go, EA renewal in 2027
monthly_ops_tooling_budget: GAP # -> action A4
Gotcha: don't write "retention: 7 days to save money" for log data. In the Analytics plan, 31 days of interactive retention is already included in the ingestion price, so going below 31 days saves nothing. Longer total retention, up to 12 years, is a separate setting with its own charge.
Collecting the numbers: two quick commands
You can fill most of part 1 with one Resource Graph query:
# Count VMs per environment tag across every subscription you can read
az graph query -q "Resources
| where type =~ 'Microsoft.Compute/virtualMachines'
| summarize vms = count() by env = tostring(tags.env)" -o table
# Example output
# Env Vms
# ------ ---
# prod 61
# test 38
# dev 29
# 12 <- blank env tag: that is your tag-coverage gap, made visible
If a workspace already exists, its Usage table tells you how much log data you ingest. Quantity is in MB, so divide by 1,000 to get GB:
az monitor log-analytics query -w <workspace-guid> --analytics-query "
Usage
| where TimeGenerated > ago(30d) and IsBillable == true
| summarize GB = sum(Quantity) / 1000 by DataType
| sort by GB desc" -o table
Divide the 30-day total by 30 to get GB/day. That one number feeds your cost model. For example, Azure Monitor's commitment tiers for Analytics logs start at 100 GB/day. If you ingest 38 GB/day, they aren't worth considering yet. If you ingest 140 GB/day, they are. The Analytics plan isn't the only one either: Basic and Auxiliary table plans exist for verbose logs you rarely query. Later levels cover them, so for now just record which logs you query often and which you only keep.
Hard requirements vs preferences
To tell them apart, ask: "If a service can't do this, would we reject it outright?"
| Statement | Label | Why |
|---|---|---|
| Keep audit logs for 365 days | MUST | Written policy; failing it is a compliance finding |
| Data stays in UK regions | MUST | Contract or regulation |
| Keep PagerDuty for paging | PREFER | Switching hurts, but it's possible |
| Team would like Grafana dashboards | PREFER | A taste, and today nobody has the skill |
Trade-off: label too many things MUST and every service fails. Label too few and the scorecard can't rule anything out. A useful test: every MUST should point to a document, contract or named sign-off.
Turning gaps into actions
A GAP is only useful if it becomes an action with an owner and a date. Work through each one the same way: name the gap → say why it matters (the cost or risk it hides) → choose how to measure it → assign an owner and a due date.
actions:
A1: {gap: log GB/day, risk: "can't price ingestion or judge tiers",
measure: "Usage table query, 30 days", owner: Priya, due: 2026-11-13}
A2: {gap: Linux patch window, risk: "14-day SLA breached silently",
measure: "agree window with app owners", owner: Tom, due: 2026-11-20}
A3: {gap: tag coverage, risk: "untagged spend lands in no cost centre",
measure: "Resource Graph: count resources missing costCentre", owner: Priya, due: 2026-11-13}
A4: {gap: tooling budget, risk: "no ceiling to test options against",
measure: "ask finance", owner: Sam, due: 2026-11-27}
The gaps that do the most damage are unknown log volume, which hides the biggest variable cost, and unowned patch windows, which hide the biggest operational risk. Close those two first.
Key takeaways
- The profile has nine parts: estate and environments, telemetry volume and retention, SLOs and alerting, IaC and change control, patching, cost allocation, guardrails, team skills, and cost model.
- Record numbers, and collect them with tools: Resource Graph for inventory, the
Usagetable for GB/day. - Label each line MUST or PREFER. A MUST should point to a policy, contract or sign-off.
- Write
GAPinstead of guessing, then turn each gap into an action with a measurement method, an owner and a date. - Know the cost facts behind the numbers: 31 days of Analytics retention is already included, and commitment tiers start at 100 GB/day.
- Store the profile as versioned YAML. Every later lesson and the final scorecard read from it.