Skip to content
Ops Log
Operator RunbookSecurity & Infrastructure28 July 202611 min read

Eight Azure cost levers, and how to size each one on your own bill

Eight Azure cost levers with the formula for each, so the figure comes from your own consumption data rather than someone else's case study: an inventory script, the Advisor thresholds that actually apply, the retention and rehydration penalties on blob tiering, what a Basic log plan costs in detection capability, and a rollback order ranked by reversibility.

MJ
Michal Jatczak
Founder, ITSailor

This runbook lists eight Azure cost levers and, for each one, the arithmetic that turns your own consumption data into a figure. It quotes no savings percentage: a percentage measured on somebody else's estate carries no information about yours.

Everything here is modelled. The method is fixed, the number is yours, and every documented Microsoft constraint that decides whether a lever is safe is named where it is used.

Prerequisites

  • Reader on every subscription in scope, plus Cost Management Reader at the billing scope you want to measure. Rate work also needs the price sheet, which lives at billing account or billing profile scope.
  • Azure CLI 2.22.0 or later, the minimum Microsoft states for the resource-graph extension.
  • A daily Cost Management export in FOCUS format to a storage account you own; it combines actual and amortised cost. Portal reruns reach 13 months back, the Exports REST API seven years.
  • Reader on the Log Analytics workspace for lever seven.
  • A change window: two of the eight levers restart virtual machines.

Step 1: build the inventory

Every formula below needs a denominator. These four queries produce them.

bash
az extension add --name resource-graph --only-show-errors
SUB=$(az account show --query id -o tsv)

# 1. Running VMs by size and region. Levers 1, 2 and 3 use this.
az graph query --subscriptions "$SUB" -o table -q "
Resources
| where type =~ 'microsoft.compute/virtualmachines'
| extend size = tostring(properties.hardwareProfile.vmSize)
| summarize vms = count() by size, location
| order by vms desc"

# 2. Managed disks by SKU, size and attachment state. Lever 5.
az graph query --subscriptions "$SUB" -o table -q "
Resources
| where type =~ 'microsoft.compute/disks'
| extend sku = tostring(sku.name),
         gib = toint(properties.diskSizeGB),
         state = tostring(properties.diskState)
| summarize disks = count(), tib = round(sum(gib) / 1024.0, 2) by sku, state
| order by tib desc"

# 3. Resources missing any of the four tags that make showback possible.
#    bag_keys(null) is null, so untagged resources need their own branch.
az graph query --subscriptions "$SUB" -o table -q "
Resources
| extend tagKeys = iff(isnull(tags), dynamic([]), bag_keys(tags))
| extend missing = set_difference(
      dynamic(['Owner','CostCentre','Environment','Workload']), tagKeys)
| where array_length(missing) > 0
| summarize resources = count() by type, tostring(missing)
| order by resources desc"

# 4. Storage accounts and the access tier new blobs inherit. Lever 6.
az graph query --subscriptions "$SUB" -o table -q "
Resources
| where type =~ 'microsoft.storage/storageaccounts'
| project name, resourceGroup, tier = tostring(properties.accessTier)
| order by name asc"

Expected output

Four tables, read in this order.

  1. A VM histogram. One size dominating for months is the commitment candidate; a long thin list suits a savings plan.
  2. A disk table. Check the Premium_LRS row against the Attached and Unattached split. Unattached disks bill at full rate and belong to a delete decision.
  3. A tag-gap table. Resources with no tags at all appear with all four listed as missing; if that row is absent from a partly untagged estate, the null branch has been edited out. This is the ceiling on how much of the bill has an owner.
  4. A storage account list with the inherited access tier. A blank or Hot column on an archive-shaped workload is where lever 6 starts.

The eight levers

LeverMeasure from your own dataMonthly figureConstraint that decides whether it is safe
1. Commitment purchaseSteady-state hours per SKU, family and region over 12 monthsSteady hours x (pay-as-you-go rate minus commitment rate), from your price sheetSavings plan purchases are final; unused hourly commitment expires
2. VM right-sizingAdvisor resize recommendations against your own CPU and memory seriesSum of (current SKU rate minus target SKU rate) x running hoursAdvisor prices at retail and ignores reservations you hold
3. Non-production schedulesRunning hours per week per VMCompute rate x (168 minus scheduled hours) x 4.35 weeksDisks, public IPs and reserved capacity keep billing while the VM is deallocated
4. Azure SQL compute modelActive seconds per database, and idle windows longer than the auto-pause delayProvisioned vCore-hours billed today minus modelled serverless vCore-secondsReservations and Azure Hybrid Benefit do not apply to the serverless tier
5. Managed disk tierProvisioned size and observed IOPS per disk(Premium rate minus Standard SSD rate at the same size) x disk countStandard SSD meets its targets 99% of the time, Premium SSD 99.9%
6. Blob lifecycleGB per container and last-access dateGB x (hot rate minus target tier rate), less the transition transactionsMinimum retention 30 days Cool, 90 Cold, 180 Archive, with a prorated early deletion charge
7. Log table plansBillable GB per DataType over 30 daysGB per day x 30 x (Analytics rate minus Basic rate), less the new per-query chargesBasic tables lose resource-scope queries and Insights; one switch per table per week
8. Traffic and vault placementEgress GB by source and destination regionGB x the band rate on the Azure bandwidth pricing pageMoving a backup vault to its workload's region is a re-provisioning job with a cutover

Commitment purchase is the biggest lever and the least reversible

Both mechanisms support monthly payment at no extra cost, and Microsoft states the up-front and monthly totals of a reservation are identical, so there is no payback month to calculate. What differs is the exit. Any hour where eligible usage falls below a savings plan commitment is lost. Reservations can be exchanged or refunded, but cancelled future payments count towards the USD 50,000 refund limit. Commit only to what you would still run if every in-flight project were cancelled tomorrow.

Right-sizing: read what Advisor actually measures

Azure Advisor analyses the last seven days by default, configurable to 7, 14, 21, 30, 60 or 90 days, with up to 48 hours before a changed setting shows. A shutdown recommendation needs three conditions together: P95 of maximum CPU summed across all cores below 3%, P100 of average CPU over the last three days at or below 2%, and outbound network below 2%. A resize recommendation targets P95 CPU and outbound network at or below 40% on the new SKU for user-facing workloads and 80% for the rest, with P99 memory at 60% and 80%. The estimate is priced at retail and excludes reservations you hold, so a cross-family move can raise the bill.

Disk tier: the ceiling is set per size

Standard SSD has no single IOPS ceiling: base IOPS run up to 500 for every size from E1 through E50, then 2,000 on E60, 4,000 on E70 and 6,000 on E80. Premium SSD provisions its IOPS and throughput and is designed to deliver them 99.9% of the time; Standard SSD delivers up to its limits 99% of the time, and latency variance under burst is what an application notices. Conversion costs a VM restart, capped at two disk type changes per day.

Blob lifecycle: the penalty is on the way out

The storage rate drops as the tier cools, and lifecycle management automates the transition. The charge sits on the other side: delete, overwrite or re-tier before the minimum retention expires and Azure bills the remainder as though the blob had stayed. A blob in Archive cannot be read until rehydration finishes, up to 15 hours at standard priority, and lifecycle policies cannot rehydrate.

Lever seven costs detection capability as well as euros

Measure the volume before pricing it. This query gives the 30-day billable picture per table in a Log Analytics workspace. Quantity in the Usage table is in MB and Azure Monitor bills a GB as 1,000 MB, so the divisor is 1000, matching Microsoft's usage analysis page. The StartTime and EndTime bounds are the day buckets Microsoft specifies for that table; without them the window carries two partial days and skews the figure.

kusto
Usage
| where TimeGenerated > ago(32d)
| where StartTime >= startofday(ago(31d)) and EndTime < startofday(now())
| where IsBillable == true
| summarize BillableGB = round(sum(Quantity) / 1000, 1) by DataType, Solution
| extend GBPerDay = round(BillableGB / 30, 2)
| order by BillableGB desc

Only the top rows are worth touching. Then price the capability you give up. Moving an Analytics table to Basic removes Insights, removes resource-scope queries so the table is only queryable at workspace scope, and starts charging per query, including those that refresh a dashboard. Reduced alerting, search jobs, restore and data export survive. Summary rules break if the rule used resource query scope, and data older than 30 days becomes long-term retention. The Auxiliary plan stops alerts on that table entirely.

Tags decide whether any of this survives the quarter

Untagged spend has no owner, and unowned spend drifts back. Azure Policy handles both halves: a deny effect blocks resource group creation without the required tag, and a modify effect adds or replaces a tag, inheriting the value from the parent resource group. Group them into one initiative at management group scope for a single compliance number. Existing non-compliant resources need a remediation task.

The tag set that carries cost work is small: an owner identity, a cost centre code, an environment, and a workload name finance recognises. Four tags applied everywhere beat twelve applied to sixty per cent of the estate. Running that as a standing practice is the shape of the FinOps and cost management engagement.

Side effects

  • Levers 2 and 5 restart virtual machines. Right-sizing needs a deallocation to change SKU; a disk tier change needs a restart, capped at two per day.
  • Lever 3 stops compute charges only. Managed disks, static public IPs and reserved capacity keep billing on a deallocated VM. The portal's Auto-shutdown schedule stops a machine but does not start it; that needs separate automation such as Start/Stop VMs v2.
  • Lever 4 introduces resume latency. A paused serverless database returns error 40613 on the first connection attempt while it resumes, so client retry logic has to exist first. Geo-replication, long-term backup retention, a DNS alias on the logical server and use as an Elastic Jobs job database each block auto-pause.
  • Lever 6 changes read behaviour. Cool and Cold raise access and transaction charges; Archive removes read access until rehydration completes.
  • Lever 7 changes what your detections can see and adds a per-query charge on the tables you moved.

Rollback

Rank the eight by how cleanly they undo; pull the reversible ones first.

  • Fully reversible in minutes: schedules, tags, and the SQL compute model (back to provisioned with one ALTER DATABASE).
  • Reversible with a restart: VM size and disk tier. Keep the original SKU and disk SKU in the change record; the twice-per-day disk limit means a failed change plus its rollback consumes the day's budget.
  • Reversible with a delay and a bill: log table plans, which switch once per table per week, and blob tiering, where returning to Hot means a rehydration wait plus the prorated early deletion charge.
  • Not reversible: savings plan purchases. Reservations can be exchanged or refunded within the published limits. Treat commitment as the last lever pulled, after the estate has stopped moving, not the first.

Limitations

This runbook assumes an Azure-primary estate on an Enterprise Agreement, Microsoft Customer Agreement or CSP subscription. Sponsorship and MOSP subscriptions differ: FOCUS datasets are unavailable on Azure MOSP billing scopes, and sponsorship credits cannot buy reservations, though reservation discounts do apply to sponsorship-backed resources.

It stops applying where the estate is not steady. With a migration, a re-platform or a region move in flight, levers 1 and 6 are actively harmful: both charge you for guessing wrong about the next 12 to 36 months. It also stops applying to AKS node pools, Cosmos DB throughput, Fabric capacity and OpenAI provisioned throughput, which carry their own commitment models.

The arithmetic is only as good as the rate you feed it. Every rate must come from your own price sheet export or the current regional pricing page, in your currency, on the day you run the model. Last quarter's rate card is a guess dressed as a calculation. For the same inventory produced continuously against a live subscription, see SEAWALL, the Azure and AWS cost engine.

Last verified 2026-07-28. Advisor thresholds, reservation and savings plan refund terms, blob retention and rehydration, disk performance targets, serverless auto-pause, Log Analytics table plan rules, the Usage table unit convention and the resource-graph CLI minimum were read on Microsoft Learn on this date; DORA Articles 28 and 30 on EUR-Lex. No figure came from any estate.

Sources and further reading

Was this field note useful?
Apply the runbook

Turn the procedure into a tenant decision.

The Architecture Workshop maps the checks, side effects, and rollback path to your own Microsoft 365 environment.

Review the workshop