Commitment discounts are the single largest lever in an Azure bill, and the one teams most often pull wrong. The failure mode is not “we forgot to buy reservations” — it is buying the wrong instrument, at the wrong scope, for a baseline that drifts, and then discovering at renewal that half the commitment went unused while pay-as-you-go (PAYG) charges piled up next to it. Every dollar of unused commitment is a negative discount: you paid up front and got nothing.
This guide is the decision logic, not a feature tour. We pick the right instrument per workload, size it from real utilization data, choose scope deliberately, stack Azure Hybrid Benefit (AHB) on top, and build the exchange-and-review cadence that keeps the portfolio matched to a moving estate. Numbers here are illustrative; pull live rates from the Azure pricing calculator and your EA/MCA price sheet before committing.
Mental model. Reservations and savings plans discount compute and a few other resource types. AHB discounts licensing (Windows Server, SQL Server) and is independent — it stacks. PAYG is your liquidity reserve for the part of the baseline you are not yet confident is permanent. Most mature estates run all three at once.
1. PAYG vs reservations vs savings plans: where each wins
The three instruments trade discount depth against flexibility. That is the entire decision.
| Instrument | Typical discount | Locks you to | Best for |
|---|---|---|---|
| Pay-as-you-go | 0% (baseline) | nothing | spiky, short-lived, or uncertain workloads |
| Compute savings plan | moderate | an hourly $ spend, 1 or 3 yr | compute that moves across SKU/region/OS |
| Reservation | deepest | a specific SKU + region (or instance-size-flexible group) | stable, long-lived, predictable footprint |
The hierarchy is simple: reservations are the deepest discount but the least flexible; savings plans are shallower but flex across SKU family, region, and OS; PAYG is full price but free to walk away. Azure’s billing engine applies the deepest applicable discount first each hour. The official guidance — and the right default — is that a reservation, when it fits, beats a savings plan on price for the same usage. Savings plans win when the shape of usage is uncertain even though the spend is stable.
Decision flow for any compute baseline:
- Is this footprint stable in SKU and region for 1+ year? -> Reservation (instance-size-flexible where the family supports it).
- Stable in spend but likely to change SKU/region/OS (autoscaling fleets, migrations, AKS node pool churn)? -> Compute savings plan.
- Neither stable nor predictable? -> Leave on PAYG and revisit next cycle.
A nuance that trips people: the compute savings plan covers a broad set of compute services (VMs, dedicated hosts, App Service, Container Instances, Azure Functions Premium, and more) under one hourly-dollar commitment, automatically applying to the highest-PAYG-rate eligible usage first. Reservations are per-resource-type — a VM reservation does not pay down an App Service plan. That breadth is exactly why savings plans absorb migration churn that would strand a reservation.
What savings plans do not cover is just as important: storage, networking/egress, databases priced by DTU/vCore reservation (those have their own reservation products), Spot, and anything already on PAYG-only meters. Do not assume a savings plan blankets the bill.
2. Reading utilization and coverage to size commitments correctly
Never size a commitment off a single month or a portal eyeball. You need two numbers per workload, tracked over time:
- Coverage — what fraction of eligible usage is already under a commitment. Low coverage on a stable workload = money on the table.
- Utilization — what fraction of a purchased commitment you actually consumed. Anything under ~95-100% on a reservation means you over-bought.
Size to the stable floor of the last 30-60 days, not the average and never the peak. The classic mistake is committing to the mean, which guarantees you under-utilize during the troughs.
Pull the raw signal from amortized cost data via Cost Management. The amortized view spreads an upfront reservation purchase across its term so a day’s true run-rate is visible instead of a lump on purchase day.
# Export amortized usage so reservation/savings-plan cost is spread across the term,
# not booked as a lump on the purchase date.
az costmanagement export create \
--name amortized-daily \
--type AmortizedCost \
--scope "subscriptions/<sub-id>" \
--storage-account-id "<storage-resource-id>" \
--storage-container "cost-exports" \
--storage-directory "amortized" \
--timeframe MonthToDate \
--recurrence Daily \
--recurrence-period from="2026-06-01T00:00:00Z" to="2026-12-31T00:00:00Z"
Then query the exported data (or the same fields in a Log Analytics / cost workspace) to find the stable hourly floor per SKU. The shape that matters is the minimum sustained concurrency, which is your safe reservation quantity:
// Stable VM concurrency floor per SKU over 60 days.
// The 5th-percentile hourly running count is a defensible reservation quantity:
// you run at least this many essentially all the time.
Usage
| where TimeGenerated > ago(60d)
| where MeterCategory == "Virtual Machines"
| summarize RunningVMs = dcount(ResourceId) by SkuName, bin(TimeGenerated, 1h)
| summarize FloorQty = percentile(RunningVMs, 5),
MedianQty = percentile(RunningVMs, 50),
PeakQty = max(RunningVMs)
by SkuName
| order by FloorQty desc
Commit the FloorQty to reservations, cover the band between floor and median with a savings plan, and let the peak ride on PAYG. That layering is the whole strategy in one query.
For savings-plan sizing specifically, Azure’s own recommendation engine is worth consulting — Cost Management surfaces a recommended hourly commitment at 1-yr and 3-yr terms based on your last 7/30/60 days of eligible usage, with a projected utilization. Treat it as a starting point, then haircut it 5-10% toward the conservative side because the recommendation assumes your recent past repeats exactly.
3. Reservation scope: shared, single-subscription, and management-group
Scope decides which usage a reservation is allowed to pay down. Get it wrong and a perfectly-sized reservation sits idle next to PAYG usage it was never permitted to touch.
| Scope | A reservation here applies to | Use when |
|---|---|---|
| Single subscription | matching usage in one subscription only | one team owns and funds the workload; strict isolation |
| Single resource group | matching usage in one RG | even tighter chargeback boundary |
| Shared | matching usage across all subscriptions in the billing context | maximize utilization; central platform team owns commitments |
| Management group | matching usage across subscriptions under that MG | shared, but bounded to an org unit |
The default and usually correct choice for a platform team is shared (or management-group scope when you want a boundary): it lets one reservation float to wherever the matching SKU is running, which maximizes utilization — the number that actually determines ROI. Pin to single-subscription only when a business unit funds its own commitment and must not subsidize another, or when chargeback rules forbid cross-charging.
The operationally valuable fact: scope is mutable after purchase, at no cost. If a shared reservation is under-utilizing because the workload it was bought for shrank, narrow or re-point its scope to where the matching usage actually lives — no exchange, no refund, just a property change.
# List reservations and their current utilization so you can spot misallocation.
az reservations reservation-order list -o table
# Re-point a reservation to a management group (or flip to Shared) to chase usage.
az reservations reservation update \
--reservation-order-id "<order-id>" \
--reservation-id "<reservation-id>" \
--applied-scope-type ManagementGroup \
--applied-scope-groups "/providers/Microsoft.Management/managementGroups/landing-zones"
Governance gotcha. A shared reservation can be consumed by any subscription in the billing scope, which can quietly cross-subsidize teams and muddy showback. If your chargeback model is strict, prefer management-group scope so the discount stays inside an org boundary, and reconcile with amortized cost so each team sees its effective (post-discount) rate, not the list price.
4. Compute savings plans for flexible, cross-SKU and cross-region coverage
A compute savings plan is a commitment to spend a fixed dollars-per-hour on eligible compute for 1 or 3 years, in exchange for savings-plan rates on usage up to that hourly amount. Usage above the commitment bills at PAYG; usage below means you paid for hours you did not consume — the same under-utilization trap as a reservation, just denominated in dollars.
Why platform teams reach for it over reservations:
- SKU-agnostic. Rebalance a fleet from
D-seriestoE-series, or to a newer generation, and the commitment keeps applying. A reservation would strand. - Region- and OS-agnostic. Move workloads between regions or from Windows to Linux without losing the discount.
- Multi-service. One commitment spans VMs, App Service, Container Instances, Functions Premium, dedicated hosts, and more — applied automatically to the highest-rate eligible usage first.
That last point is the key behavioral difference from reservations: you do not assign a savings plan to a resource. The billing engine sweeps each hour and discounts the most expensive eligible usage until your hourly commitment is exhausted. You buy flexibility, and you pay for it with a slightly shallower discount than the equivalent reservation.
# Inspect savings-plan recommendations (hourly commitment + projected utilization)
# before you buy. Look-back of 30 days, 3-year term, shared scope.
az billing-benefits savings-plan-order list -o table
Practical rule: layer savings plans under reservations, not instead of them. Reservations cover the immovable floor at the deepest discount; the savings plan covers the flexible band above it at a still-meaningful discount; PAYG handles the spiky top. Buying only savings plans because they are “easier” leaves the deepest reservation discount on the table for the part of the estate that genuinely never moves.
5. Azure Hybrid Benefit for Windows Server and SQL licensing
AHB is a licensing discount, orthogonal to compute commitments. If you own Windows Server or SQL Server licenses with active Software Assurance (or qualifying subscription licenses), AHB lets you bring them to Azure and stop paying the license portion of the meter — you pay the base compute rate only. For Windows Server VMs the savings are substantial; for SQL the savings can be larger still because SQL licensing is expensive.
Core entitlements to know:
- Windows Server: each qualifying license with SA covers up to a defined number of vCPUs (commonly 16, with a 8-core minimum per license), and you can apply it to Azure VMs.
- SQL Server: AHB applies to SQL on Azure VMs, Azure SQL Database (vCore model), SQL Managed Instance, and others, converting license-included pricing to base rate.
- Dual-use rights during migration: for a defined window you may run the workload both on-prem and in Azure under the same license, so cutovers do not double-charge you.
Enabling AHB is a property, not a purchase — flip it on existing resources:
# Windows Server VM: switch the license type to bring your own Windows license.
az vm update -g rg-prod -n vm-app-01 --license-type Windows_Server
# Convert back to pay-as-you-go licensing (e.g., if you run out of entitlements).
az vm update -g rg-prod -n vm-app-01 --license-type None
For SQL on Azure VMs you set the license type on the SQL virtual machine resource (the IaaS extension), not the VM:
# SQL Server IaaS: AHB / bring-your-own-license at the SQL VM resource level.
az sql vm update -g rg-prod -n vm-sql-01 --license-type AHUB
Azure SQL Database and Managed Instance expose AHB as a property of the database/instance:
# Azure SQL Database: enable Azure Hybrid Benefit (base-rate licensing).
az sql db update -g rg-prod -s sql-logical-01 -n salesdb --license-type BasePrice
In Bicep, make AHB the default for managed compute so nobody forgets it at deploy time:
resource sqlMi 'Microsoft.Sql/managedInstances@2023-08-01-preview' = {
name: 'mi-prod-01'
location: location
sku: { name: 'GP_Gen8', tier: 'GeneralPurpose' }
properties: {
// BasePrice = Azure Hybrid Benefit; LicenseIncluded = pay for the license.
licenseType: 'BasePrice'
vCores: 8
storageSizeInGB: 256
subnetId: subnetId
}
}
Compliance is on you. Azure does not verify you actually hold the licenses you claim. AHB is an entitlement you assert; a license audit can claw it back. Track AHB usage against owned SA seats in a spreadsheet or your SAM tool, and gate the property behind policy so it is a deliberate, attributable choice — not a default someone flipped to cut their bill.
6. Stacking AHB with reservations and dev/test pricing
The discounts compose, and the savings multiply because they hit different parts of the meter:
- A reservation (or savings plan) discounts the compute portion.
- AHB removes the license portion.
- Reserved VM Instance pricing for Windows is published assuming AHB — i.e., the deepest published Windows reservation rate already excludes the license, which you then satisfy with your owned license. Stack them and you pay reserved-compute rate + zero license.
For non-production there is a fourth lever: an Enterprise Dev/Test or Pay-As-You-Go Dev/Test subscription removes the Windows/SQL license charge entirely for Visual Studio subscribers’ dev/test workloads, with no SA required. You generally would not apply AHB there — the dev/test subscription already zeroes the license. The discount layering therefore differs by environment:
| Environment | Compute discount | License treatment |
|---|---|---|
| Production | reservation / savings plan | AHB (bring SA licenses) |
| Dev/Test | reservation / savings plan (dev/test rates) | covered by Dev/Test subscription; AHB not needed |
The trap is applying AHB to a dev/test subscription and assuming it stacks — it does not double-discount the license, and you have now consumed an SA entitlement that a production VM could have used. Reserve AHB entitlements for production, where they are the only way to drop the license charge.
7. Exchanges, refunds, and avoiding underutilized commitments
Estates drift. The instrument that fit at purchase will not fit forever, so you must know the exit mechanics before you buy.
Reservations.
- Refunds (cancellations): you can return reservations for a prorated refund, subject to an early-termination fee and an annual cap on total refunds per billing scope (currently $50,000 in a 12-month window). Refunds are the escape hatch, not a planning tool.
- Exchanges: historically you could exchange one reservation for another of equal-or-greater value with no fee. This is the big policy change to internalize: Microsoft ended like-for-like compute (VM, dedicated host, etc.) reservation exchanges for purchases made after a cutoff date — the intended migration path for flexibility is now the compute savings plan. Non-compute reservations (e.g., storage, some databases) still support exchange. Do not architect a strategy around freely exchanging VM reservations; it no longer exists for new purchases. If you need flexibility, that is the savings plan’s job by design.
Savings plans.
- Effectively non-cancelable and non-refundable for the term — no exchanges, no early termination. This is the price of their flexibility: the commitment is to a dollar amount, and Azure auto-applies it broadly, so there is nothing to “exchange” for. Size them conservatively.
The practical implication ties the whole strategy together:
Because VM reservation exchanges are gone and savings plans cannot be canceled, conservatism at purchase is the entire game. Buy reservations only for the part of the floor you are certain is permanent. Cover everything merely “probable” with a savings plan sized below your true floor. Let the rest ride PAYG. Under-committing costs you a little discount; over-committing costs you cash you cannot get back.
Catch under-utilization early with a recurring report. Reservation utilization below 100% over a trailing window is the first signal to re-scope (cheap) before considering a refund (expensive, capped):
# Trailing reservation utilization. Anything under ~95% is a re-scope candidate.
az consumption reservation summary list \
--grain daily \
--start-date 2026-05-01 \
--end-date 2026-05-31 \
-o table
Enterprise scenario
A platform team running a regulated insurance workload had a stable production fleet of roughly 120 D8s_v5 VMs in westeurope and bought 3-year VM reservations against it at shared scope. Six months in, the application team kicked off a migration to E-series memory-optimized VMs (heavier in-memory rating engine) and, for a new data-residency requirement, stood up a second region in northeurope. Reservation utilization on the D8s_v5 order cratered to ~55% almost overnight: the E-series and northeurope usage billed at full PAYG while the paid-for D8s_v5 reservation sat idle. The instinct was to exchange the reservations into E-series — but those reservations were purchased after the compute-exchange cutoff, so like-for-like VM exchange was unavailable, and a refund was both capped and wasteful.
The constraint, then: a deep but now-mismatched 3-year reservation that could not be exchanged, against a fleet that had become flexible by nature (active migration, multi-region, changing SKU family).
The fix was to stop fighting the reservation model and let it expire gracefully on the residual D8s_v5 footprint while moving the flexible portion of the estate onto a compute savings plan, which is SKU- and region-agnostic by design and therefore immune to exactly this churn. They sized the savings plan from the 5th-percentile hourly spend across both regions and both SKU families combined — not per-SKU — so the commitment floated to wherever usage landed.
// Cross-SKU, cross-region hourly spend floor that a savings plan can safely commit to.
// Summing eligible compute spend (not per-SKU) is what makes the commitment
// resilient to the D->E migration and the new region.
Usage
| where TimeGenerated > ago(60d)
| where MeterCategory in ("Virtual Machines")
| where ResourceLocation in ("westeurope", "northeurope")
| summarize HourlySpend = sum(PreTaxCost) by bin(TimeGenerated, 1h)
| summarize CommitFloorPerHour = percentile(HourlySpend, 5)
They committed ~80% of that floor as a 1-year savings plan (short term, because the migration meant the future shape was still moving), kept AHB enabled across both SKU families and regions to hold the Windows license savings constant through the migration, and let the old D8s_v5 reservations amortize out. Blended effective discount recovered to within a couple of points of the original target within one billing cycle, with zero stranded commitment going forward. The lesson the team wrote into their standards: reserve only the immovable floor; everything that can migrate goes on a savings plan.
Verify
Confirm the portfolio is actually working, not just purchased:
# 1. Reservation utilization should sit at ~100% across the trailing month.
az consumption reservation summary list --grain monthly \
--start-date 2026-05-01 --end-date 2026-05-31 -o table
# 2. Savings-plan utilization (via cost exports / Cost Management) should be high;
# persistent unused commitment means you over-sized the hourly dollar amount.
az billing-benefits savings-plan-order list -o table
# 3. AHB is actually applied where you think. Windows VMs should report a license type.
az vm list --query "[?licenseType=='Windows_Server'].{name:name, rg:resourceGroup}" -o table
# 4. SQL VMs carry AHUB where licensed.
az sql vm list --query "[].{name:name, license:sqlServerLicenseType}" -o table
Then reconcile against amortized cost in Cost Management: effective per-team rates should be visibly below list price, and your coverage percentage on stable workloads should be high. If utilization is high and coverage is high and amortized rates beat PAYG, the strategy is landing.
Commitment-review cadence checklist
Run this on a fixed monthly cadence with finance and the platform team in the room. Commitments are not fire-and-forget — they decay against a moving estate.