Skip to main content
emnode
Cost

Investigate an Azure cost anomaly

An Azure cost anomaly surfaced in Cost analysis isn't always a problem, but it always means the bill changed. Triage, attribute, decide.

15 min·10 sections·AZURE

Last reviewed

Azure cost anomalies: the basics

What does it mean for Azure spend to be "anomalous"?

A cost anomaly is a day of spend that lands far enough outside the expected range for a subscription that a model trained on recent daily usage flags it as unlikely to be normal variation. Daily Azure spend has a natural rhythm: batch jobs run overnight, traffic dips at the weekend, a monthly reservation purchase lands on the same date. Microsoft Cost Management learns that rhythm and surfaces the days that break it.

Azure Cost Management runs anomaly detection automatically for every subscription. It applies a model to your daily usage, and when a day's spend sits materially above or below the prediction it surfaces the result as an insight inside Cost analysis. You do not configure or pay for the detection itself: it is a free Cost Management feature, and the insight names the day, the magnitude of the change and the direction. The detector's output is not "something is wrong", it is "this looks unusual, here is what changed".

A real example: total spend on a subscription jumped from an expected 188 of currency per day to an actual 1,366 per day, a roughly 7x change, driven by a single resource group. The detector had been watching that subscription for weeks and was confident the previous days fit the pattern. That is an anomaly worth a human looking at: not necessarily an emergency, but the bill changed and someone needs to know why.

In this lesson you will learn how Azure cost anomaly detection actually works, the triage decision tree for any spike (usage change vs. rate change), how to attribute a 7x anomaly back to a specific resource group and a specific team in under thirty minutes, and how to decide whether the new cost should be accepted (budget up) or fixed (root-cause down). You will see the Cost analysis attribution workflow, how scheduled anomaly alerts and Budgets alerts complement each other, and the one-line post-mortem that turns a one-off spike into team-wide muscle memory.

Fun fact

The most expensive bug is the one nobody owns

A well-cited industry estimate is that 30%+ of cloud spend is wasted, and a meaningful chunk of that waste lives in anomalies that nobody investigated because they fell into the cracks between teams. The detection is the cheap part: Azure Cost Management runs anomaly detection on every subscription automatically and for free. A 7x spike on a shared subscription is everyone's problem and therefore nobody's. The first team to claim an anomaly tends to fix it; the ones that bounce between Teams channels for two weeks become the line items finance asks about in the next quarterly review.

Triaging an Azure cost anomaly in action

Marco runs FinOps at a healthcare SaaS. On a Friday morning he opens Cost analysis for the production subscription and the Insights surface a detected anomaly: daily spend jumped from an expected 188 to an actual 1,366 on 2026-02-26, a roughly 7x change. Cost Management flags it automatically; Marco had also set up a scheduled anomaly alert, so the same insight arrived by email overnight. He did not have to build the detector, it ships with the subscription.

Before pinging anyone, Marco runs the triage decision tree. Cost = usage x rate. So the spike is either a usage change (a new resource, more requests, more data) or a rate change (a reservation that lapsed, a region with different pricing, a SKU change). He starts with usage because that is the more common cause and the easier one to verify.

In Cost analysis he changes the Group by from None to Resource group for that subscription and date range, then narrows to the anomalous day. The 80/20 of any anomaly is usually one resource group doing 80% of the damage. He then groups by Service name and by Tag to pin the exact resource and the owning team, the whole attribution workflow living inside the portal because the detection itself is portal-driven.

Anomaly detection is surfaced in the Cost analysis portal, so the attribution here is portal-driven: group by resource group, then service, then tag. The optional commands below set up the email alerting that complements it. First, create a scheduled anomaly alert so the next anomaly emails you the day it is detected.

$ # Scheduled anomaly alerts are created in Cost Management > Cost alerts > Anomaly alerts (portal). # They run the same automatic detection on the subscription's daily usage and email # the named recipients when an anomaly is found. # # Portal flow: # Cost Management > Cost alerts > + Add > Anomaly alert # Scope: the subscription | Recipients: [email protected] # Frequency: Daily (the detection model runs on daily usage)
Anomaly alert 'finops-daily-anomalies' created.
Scope: /subscriptions/00000000-0000-0000-0000-000000000000
Recipients: [email protected] | Frequency: Daily
# Detection itself is free and always on; the alert just emails the result.

Detection runs automatically and free on every subscription; the scheduled anomaly alert is the optional email layer on top, configured in the Cost Management portal.

Budget alerts are a separate, complementary mechanism: threshold-based rather than model-based. Create the budget with the az CLI, then add its alert thresholds and recipients on the budget in the portal, so finance is warned when month-to-date spend crosses a fixed percentage of plan, regardless of whether the model calls it anomalous.

$ az consumption budget create \ --budget-name finops-prod-monthly \ --amount 25000 \ --category cost \ --time-grain monthly \ --start-date 2026-02-01 \ --end-date 2027-02-01
{
"name": "finops-prod-monthly",
"amount": 25000.0,
"timeGrain": "Monthly",
"notifications": null
}
# Budget created. Add its alert thresholds and recipients in the portal: Cost Management > Budgets.
# A budget alert is a tripwire on a fixed number; the anomaly model catches the shape of the change.

Budgets live under Cost Management > Budgets: create the budget with the CLI, then add its alert thresholds and recipients there. They complement, rather than replace, the model-based anomaly detection, covering both a sudden anomalous shape and a slow creep towards the plan limit.

How Azure cost anomaly detection actually worksdeep dive

Under the hood, Azure Cost Management applies a model to the subscription's daily usage. It learns the recent pattern of spend (weeknight batch peaks, weekend dips, end-of-month jobs), computes an expected value for each new day, and flags the day when the actual lands materially outside that expectation. The result is surfaced as an insight in Cost analysis, naming the day, the magnitude and the direction of the change. Detection runs at subscription scope, automatically, and is a free Cost Management feature: there is no model to train or service to pay for.

Persistence is the strongest signal. A single day at 7x could be a one-off (a one-shot batch job, a botched test). Several consecutive days at 7x means the cost base has moved and the change is real. When you investigate, sort your attention by how many days the elevated spend has persisted, not by the headline magnitude of any single day. A one-day spike that self-corrects is noise; a sustained shift is a forecast event.

It is worth contrasting this with AWS. AWS Cost Anomaly Detection asks you to create monitors across dimensions (services, linked accounts, cost categories, tags), each with its own alert subscription and threshold. Azure's model is more turnkey: detection is on by default at subscription scope with no monitors to configure, surfaced in Cost analysis Insights, with optional scheduled anomaly alerts for email. The detection concept is the same on both clouds (learn a daily baseline, flag the days that break it); the difference is that Azure does the wiring for you, so the work shifts almost entirely to attribution and response.

# Azure cost anomaly detection is surfaced in the Cost analysis portal Insights,
# not via a dedicated anomaly CLI, so attribution is done in the portal:
#
#   Cost Management > Cost analysis
#   Scope: the subscription
#   Group by: Resource group   -> find the 80/20 resource group
#   Group by: Service name     -> find the dominant service
#   Group by: Tag              -> find the owning team
#   Date: narrow to the anomalous day
#
# To export the underlying daily usage for offline attribution, pull cost details
# with the az CLI (the resource group is embedded in each instanceId / resource ID):
az consumption usage list \
  --start-date 2026-02-26 --end-date 2026-02-26 \
  --query "[?contains(instanceName, 'gpu')].[instanceName, pretaxCost, instanceId]" \
  --output table

What is the impact of unattributed Azure cost anomalies?

The direct impact is the bill. A persistent 7x spike on a subscription that was running 188 per day costs an extra ~35k a month if nobody catches it, and "nobody catches it" is the default outcome when an anomaly bounces between Teams channels for a week. The detector did its job, and it did it for free; the cost is the gap between detection and ownership.

The second-order impact is forecast pollution. Budgets, reservation recommendations, and capacity plans all train on recent spend. An unflagged anomaly that persists becomes the new baseline; six weeks later you are sizing your reservation commitment on numbers that include 35k a month of accidental GPU spend. The model cannot tell the difference between intentional growth and a forgotten test rig.

The third-order impact is trust. Finance loses confidence in cloud spend forecasts when anomalies do not get explained; engineering loses confidence in the alert channel when too many anomalies fire without follow-up. Both problems compound: finance starts demanding manual approval for any spend increase, engineering starts muting the channel and the scheduled anomaly alerts that came for free.

The good news: anomalies that are attributed within 24 hours rarely cost more than a few thousand in waste. Speed of triage is the dominant variable. Because the detection is already free and automatic in Cost analysis, the only thing standing between a 35k surprise and a 2k one-off is whether a documented owner per subscription claims the anomaly fast.

How do you triage and act on an Azure cost anomaly?

Anomaly response is a four-step loop. The detection is free and automatic, so the work is all in the response. Skipping any step turns a one-day spike into a six-week mystery.

1. Attribute: find the resource and the owner

In Cost analysis, group the anomalous day by Resource group to find the 80/20, then by Service name to find the dominant service, then by Tag to find the owning team. If your resources carry an owner or cost-centre tag, the whole exercise is 15 minutes. The detection already pointed you at the day; this step turns the day into a name.

2. Diagnose: usage change or rate change?

Cost = usage x rate. If usage rose, check the activity log for new resource deployments and Azure Monitor for traffic, queue or processing increases on existing resources. If usage is flat, check rate: a reservation or savings plan that lapsed, a workload that moved to a more expensive region, a switch from spot to standard virtual machines. Data-transfer spikes (especially cross-region and cross-zone bandwidth) are the most-missed category, so always look at the networking services in the Service name grouping.

3. Decide: intentional or accidental?

Once you know what changed and who owns it, ask the owner one question: was this intentional? If yes (feature launch, planned capacity, marketing event) update the budget and forecast: that is now the new normal and the model will relearn it over the coming days. If no (forgotten test rig, autoscale bug, runaway loop) treat it as a real incident: fix the root cause and verify the next day's spend returns to the expected band in Cost analysis.

4. Layer the alerts and write the one-liner

Make sure a scheduled anomaly alert emails the model's detections, and add a Budgets alert at a sensible percentage of plan as a separate threshold-based backstop, the two cover different failure modes. Then every anomaly should produce a single-sentence note: "X caused Y, fixed by Z, prevented by W." Build the team library so the second occurrence of a pattern (GPU virtual machines left running over a weekend, a reservation that lapsed, log retention misconfigured) is a 30-second triage, not a re-investigation.

# The model-based detection is on by default and free. Add the two alerting
# layers that complement it.
#
# 1) Scheduled anomaly alert (model-based, emailed) is created in the portal:
#    Cost Management > Cost alerts > + Add > Anomaly alert
#    Scope the subscription, set Daily frequency, add recipients.
#
# 2) Budget alert (threshold-based), a separate complementary tripwire. Create the
#    budget with the CLI, then add its alert rules in the portal (Cost Management > Budgets):
az consumption budget create \
  --budget-name finops-prod-monthly \
  --amount 25000 \
  --category cost \
  --time-grain monthly \
  --start-date 2026-02-01 \
  --end-date 2027-02-01
#
# Then in the portal (Cost Management > Budgets) open finops-prod-monthly and add alert
# conditions: actual >= 80% and forecast >= 100%, with finance as the recipients.

Quick quiz

Question 1 of 5

Cost analysis Insights surfaces an anomaly: daily spend on a subscription jumped from an expected 188 to an actual 1,366, a roughly 7x change, detected on a single day. What is the right next step?

You have completed Investigate an Azure cost anomaly. You now know that Azure Cost Management detects anomalies automatically and for free at subscription scope, surfacing them in Cost analysis Insights, with scheduled anomaly alerts for email and Budgets as a separate threshold-based backstop. You know the usage-vs-rate triage decision tree, how to attribute a 7x spike to a resource group and an owner in under thirty minutes by grouping in Cost analysis, and how to close every anomaly with a one-line post-mortem. The next time the Insights flag 1,177 of variance, you will have a four-step loop (attribute, diagnose, decide, alert-and-document) ready to run.

Back to the library