Australian cloud for business software — built and run in Sydney since 2003

NetLeverage
NetLeverage

Azure cost exports: finding what drives the bill, and reservations against savings plans

Technical article
Cloud

About this article: AI-assisted writing, reviewed against the cited sources. This is source research, not a lab validation of the procedures. Check the stated versions and your own recovery requirements before making changes.

Export both Actual Cost and Amortized Cost at the billing scope. Use Actual Cost to reconcile the invoice and Amortized Cost to explain the run rate, then split each meter's change into quantity and price before buying any commitment. Choose a reservation for a stable workload with fixed attributes, and a savings plan for compute or database use that moves between services or regions. A savings plan cannot be cancelled, and the exit rules for reservations tighten on 1 February 2027.

The SQL below was written for this article from Microsoft's documented field names. It was not run in a lab or against a real export.

Why it matters

A bill view can mislead in both directions. A reservation paid up front appears as one large purchase in Actual Cost, and the virtual machines it covers show zero cost. A month can therefore look expensive because of a purchase, and later months can look cheap while usage grows.

Commitments also hide waste. Discounts reduce rates, not waste, so a commitment bought over an oversized estate locks in the oversizing for one or three years.

How it works

Two datasets answer two questions

Actual Cost Amortized Cost
Purpose Reconcile with the monthly bill Show the monetary value of commitment use by resource
Commitment purchase Row with ChargeType = Purchase Not shown as a purchase
EffectivePrice on covered usage Zero Prorated hourly commitment price
Unused commitment Not shown UnusedReservation or UnusedSavingsPlan rows
Matches the invoice Yes No

Microsoft's pricing behaviour notes define the three price columns:

  • PayGPrice is the market price, even on rows where a benefit applied.
  • UnitPrice is the negotiated price under the contract, also regardless of any benefit.
  • EffectivePrice is what was actually paid per unit. It is the price to multiply by Quantity.

Do not treat the ChargeType field description as complete. It lists Usage, Purchase and Refund, but the same page documents RoundingAdjustment, and the commitment pages document the unused charge types.

One savings plan page spells the value both UnusedSavingPlan and UnusedSavingsPlan. The query below matches on Unused% so that it does not depend on the spelling.

What to export

Create daily exports of Cost and usage details for both actual and amortized data, plus monthly exports of closed months. Details that change how the files are used:

  • Scope. Use the EA billing account or the MCA billing profile where possible. Purchases are not available at EA department or enrollment account scope. The rounding adjustment record exists only at EA enrollment or MCA billing profile scope.
  • Management groups. These exports work for EA only, and carry usage charges only. They exclude purchases and amortized reports, so they cannot answer a commitment question.
  • Partitions. Partitioning is always on, and each uncompressed file stays under 1 GB. Read file names and row counts from manifest.json, because Microsoft warns that naming conventions may change.
  • Overwrite. Overwrite is on by default and replaces the previous day's file. A daily export holds month-to-date data, so the inference is that adding each day's file to a table without replacing the earlier one counts the month repeatedly.
  • Late charges. For the first five days of each month, a daily export also runs a second time to write the prior month's full costs. That run is not shown in the run history, so check the prior month's folder rather than the history.
  • History. The portal retrieves up to 13 months, one month at a time. The REST API goes back up to seven years for cost and usage data.
  • Format. CSV or Parquet, with Gzip for CSV or Snappy for Parquet.

The FOCUS export combines actual and amortized costs in one file, with BilledCost and EffectiveCost columns. Microsoft says it reduces processing time and storage cost, but check it against this task first. In version 1.2-preview, CommitmentDiscountStatus, the column meant to separate used from unused commitment, is documented as currently null or empty. FOCUS is also unsupported for management group scope and for MOSP accounts.

Field casing differs between account types. The EA schema uses CostInBillingCurrency, and the MCA schema uses costInBillingCurrency. The manifest records dataVersion. Check it against the schema page before trusting a column.

The order discounts apply

Azure applies benefits from most specific to least specific: reservation, pre-purchase plan, savings plan, then pay-as-you-go. Reservations are evaluated from resource group scope outward to shared scope.

Within savings plans, Azure applies the plan first to the usage with the greatest discount, then deducts the discounted cost from the hourly commitment until it is gone. Three-year plans apply before one-year plans, and narrower scopes before broader ones.

Unused value is lost in both models. An hour's savings plan benefit cannot roll over, and unused reserved hours cannot be carried forward.

The billing system re-evaluates a savings plan hour as usage arrives up to 48 hours later. Inside that window charges can change, and utilisation can briefly read above 100%.

Finding what drives the bill

1. Reconcile a closed month

Work from a closed month. During an open month, costs are estimates based on the highest pricing tier, and charges can be rerated until the invoice closes. Azure typically finalises a billing period up to 72 hours after it ends. Rounding adjustments appear only when the month closes.

-- Written for this article. cost_actual holds one closed month of Actual Cost.
SELECT ChargeType, PricingModel, SUM(CostInBillingCurrency) AS cost
FROM cost_actual
GROUP BY ChargeType, PricingModel
ORDER BY cost DESC;

The cost column is before credits and taxes, so the total should tie to the invoice before those two items. If it does not, stop and fix the load. Common suspects are missing partitions, or a scope that excludes purchases.

Microsoft's reconciliation method for EA is EffectivePrice * Quantity. For MCA, that result is multiplied by ExchangeRatePricingToBilling. Cost files are not rounded, so small differences against the portal are expected.

2. Split each meter into quantity and price

Load two closed months of Amortized Cost, adding a period column with the value prior or current during the load. Then compare at meter level.

-- Written for this article. Standard SQL; adjust for your engine.
WITH m AS (
  SELECT MeterId, MeterCategory, MeterSubCategory, MeterName, UnitOfMeasure,
    SUM(CASE WHEN period = 'prior'   THEN Quantity ELSE 0 END) AS q1,
    SUM(CASE WHEN period = 'current' THEN Quantity ELSE 0 END) AS q2,
    SUM(CASE WHEN period = 'prior'   THEN CostInBillingCurrency ELSE 0 END) AS c1,
    SUM(CASE WHEN period = 'current' THEN CostInBillingCurrency ELSE 0 END) AS c2
  FROM cost_amortized
  WHERE ChargeType = 'Usage'
  GROUP BY MeterId, MeterCategory, MeterSubCategory, MeterName, UnitOfMeasure
)
SELECT m.*, c2 - c1 AS change,
  CASE WHEN q1 > 0 AND q2 > 0 THEN (q2 - q1) * (c1 / q1) END AS quantity_effect,
  CASE WHEN q1 > 0 AND q2 > 0 THEN (c2 / q2 - c1 / q1) * q2 END AS price_effect
FROM m
ORDER BY ABS(c2 - c1) DESC;

The two effects add up exactly to the change. Here is a hypothetical example, with invented figures:

Prior month Current month
Quantity (hours) 20,000 23,000
Average effective price 0.40 0.36
Cost 8,000 8,280

The meter rose only 280. The quantity effect is (23,000 − 20,000) × 0.40 = 1,200. The price effect is (0.36 − 0.40) × 23,000 = −920. Usage grew 15%, and better commitment coverage hid most of it.

Read the output with these limits:

3. Follow the largest row down

Microsoft's drill-down for an unexpected increase runs from resource group to resource to meter, then to the people with write access who can explain the change. The same order works on the export.

Anomaly detection compares each day with a forecast from the previous 60 days and runs 36 hours after the end of the day in UTC. It works at subscription scope and catches spikes. It is not a substitute for this month-over-month comparison.

Allocation has its own gaps:

  • The Tags column excludes resource group tags.
  • Tag inheritance adds subscription and resource group tags to usage records, and is available on EA, MCA and MPA accounts. On an MCA billing profile it also adds billing profile and invoice section tags. It covers the current month within 24 hours.
  • Tags are not applied retroactively to earlier usage, and are not supported on reservation purchases.
  • In the new export experience, unused reservation, unused savings plan and rounding adjustment rows have a null or empty subscription ID instead of the empty GUID. Our inference is that data from older exports can still carry the GUID, so filter for all three forms. Report these rows as a central pool rather than forcing them onto a team.

4. Measure wasted commitment

-- Written for this article. Run against Amortized Cost.
SELECT ProductOrderId, ProductOrderName, ChargeType, ReservationName, benefitName,
       SUM(Quantity) AS unused_quantity,
       SUM(CostInBillingCurrency) AS unused_cost
FROM cost_amortized
WHERE ChargeType LIKE 'Unused%'
GROUP BY ProductOrderId, ProductOrderName, ChargeType, ReservationName, benefitName
ORDER BY unused_cost DESC;

A fully used reservation has an unused record with quantity 0. Group by ProductOrderId for the long view: split, merge, partial refund and exchange can create new reservations, and ReservationId can differ within the same order.

For reservations applied across sizes, multiply Quantity by RINormalizationRatio from AdditionalInfo to get the reserved hours used on that row.

Then check the common causes against the documents:

5. Find what is still on demand

-- Written for this article. Multiply at_list by ExchangeRatePricingToBilling on MCA.
SELECT MeterCategory, PricingModel,
       SUM(CostInBillingCurrency) AS amortized_cost,
       SUM(Quantity * PayGPrice) AS at_list
FROM cost_amortized
WHERE ChargeType = 'Usage'
GROUP BY MeterCategory, PricingModel
ORDER BY amortized_cost DESC;

Rows with PricingModel = OnDemand show remaining exposure. That exposure may not be eligible for a discount:

For EA, Microsoft's savings calculation sums (UnitPrice - EffectivePrice) * Quantity on reservation usage rows, then subtracts EffectivePrice * Quantity on unused rows. A negative result points to heavy waste. The MCA amortized file does not contain UnitPrice for reservations, and cost allocation rules can leave UnitPrice showing as zero. PayGPrice is an alternative, but it measures against the market price rather than the negotiated one.

What to choose and why

These are editorial recommendations, resting on the Microsoft terms linked in the table.

Reservation Savings plan
Commitment Specific product, attributes and region Fixed hourly spend across eligible services and regions
Maximum published discount Up to 72% Up to 65% compute, up to 35% databases
Software Not covered for VMs and SQL capacity Compute no; database plans cover software IP
Early exit Refund, with cancelled commitments capped at USD 50,000 per rolling 12 months across the billing scope (enrollment or billing profile). Red Hat plans, SUSE plans and pre-purchase plans are not refundable None: all purchases are final
Change of mind Exchange, restricted from 1 February 2027; trade-in to a savings plan remains Cannot be exchanged
End of term Check the renewal setting Not renewed by default

Use Microsoft's sequence: right-size first, exchange underused reservations, trade in the reservations that no longer fit, buy new reservations for stable workloads, and add a savings plan last on the cleaned-up baseline.

Three further points apply when sizing the purchase:

  • Do not size from the daily file alone. Cost details are presented at daily granularity, while commitments apply by the hour. A daily total can hide an overnight trough where the commitment is wasted. Azure's recommendation engines work from hourly usage, so use them for quantity and the export for checking the assumptions behind them.
  • Know what the recommendation assumes. For regularly shut-down resources the reservation simulation finds no savings and offers no recommendation. Savings plan recommendations present the lower of a 3-day and a 30-day result, to guard against recent decommissioning. Advisor shows subscription scope only for savings plans. The portal covers shared, management group, subscription and resource group scopes.
  • Buy one at a time. Microsoft says to avoid buying both together. Its savings plan pages say to wait at least seven days, and its reservation page says at least three. Use the longer figure.

Dates that change the decision

From 1 February 2027, reservations bought after that date cannot be exchanged if the service is supported by savings plans. That covers Azure Virtual Machines, App Service, SQL Database and similar services. A reservation bought before that date can be exchanged as many times as you like before it. After the date it has one final exchange, and the new reservation isn't exchangeable. A partial exchange leaves each remaining quantity with one more exchange. A reservation that renews after the date becomes a new purchase and cannot be exchanged. Exclusions include services that savings plans do not cover, such as Azure VMware Solution, and products approaching end of life.

Since 1 July 2026, new purchases and renewals have stopped for one-year reservations on Av2, Amv2, Bv1, D, Ds, Dv2, Dsv2, F, Fs, Fsv2, G, Gs, Ls and Lsv2, and for one-year and three-year reservations on Dv3, Dsv3, Ev3 and Esv3. Existing reservations run to their end dates, after which the VMs fall back to pay-as-you-go if nothing replaces them. Microsoft advises starting to plan 6 to 12 months before expiry, so inventory those expiry dates now.

Trade-ins apply to VM, Dedicated Host, App Service and database reservations, with up to 100 reservations at a time. The new plan's total commitment must be at least the returned reservation's remaining commitment. The minimum hourly commitment is that outstanding amount divided by the number of hours in the new term. In Microsoft's example, USD 250 outstanding on a one-year term gives about USD 0.029 an hour. Microsoft warns that the default hourly commitment might not cover the VMs the reservation covered, so calculate the commitment before accepting it.

Risks and limits

Warning: A savings plan purchase cannot be cancelled or exchanged. An oversized plan is paid for every hour of its term. Check the hourly basis before buying.

  • Refund rules can change. Microsoft does not currently charge early termination fees on refunds, but says it might in future. Do not treat today's position as a term of the purchase.
  • Savings plan rates can change. Price changes take effect on the first of a month, and current savings plan pricing applies regardless of when the plan was bought. A saving modelled at purchase is not fixed.
  • Amortized totals will not match the invoice. Keep the two datasets in separate tables, and name reports for the dataset they use.
  • A pay-as-you-go subscription can buy a reservation, but Cost Analysis does not show amortized reservation costs for it.
  • A new subscription can take up to 48 hours before all Cost Management features work.
  • Field definitions move. Microsoft's own pages differ on charge type names and on dataset versions, so validate each load against the schema page for the dataVersion in its manifest.

Sources

Opened 2026-11-10. Sources behind the corrections reopened 10 November 2026.