Skip to content

Setting a Cost Alert on Azure OpenAI Spend

10 min read · updated August 11, 2026

A Cost Management budget will email you when Azure OpenAI spend crosses a threshold. It will not stop the spend, and it will not tell you today. Both of those are documented properties, and designing around them is most of the work.

What a budget is

Microsoft is explicit: notifications are triggered when the budget thresholds are exceeded, resources are not affected, and your consumption is not stopped. A budget is an accounting alarm, not a control.

The timing matters as much as the semantics. Cost and usage data is typically available within 8 to 24 hours, budgets are evaluated against those costs every 24 hours, and when a threshold is met the email is normally sent within an hour of the evaluation. Chain those and the worst case between the spend happening and you hearing about it is well over a day. Microsoft, Create and manage budgets.

Budgets reset automatically at the end of each period when the expiration date is in the future, and a budget that expires is automatically deleted. A quarterly budget divides its amount evenly across the three months; an annual one divides evenly across twelve.

Three prerequisites bite before any of this works. Creating and managing budgets needs contributor permission — Owner can create, modify or delete budgets for a subscription; Contributor and Cost Management contributor can create, modify or delete their own and can modify the amount on budgets created by others; Reader and Cost Management reader can only view. On a brand-new subscription you cannot create a budget at all immediately: Microsoft documents that it might take up to 48 hours before Cost Management features are usable. And budget evaluation requires that every subscription within the scope operates under a single currency — multi-currency evaluation is not supported, and the documented consequence is that you may simply miss your alerts rather than get an error.

Scoping it to the OpenAI resource

Budgets attach to a scope — management group, subscription or resource group under Azure RBAC, plus various billing scopes under EA and MCA agreements. There is no “budget on one resource” scope.

Two ways to get resource-level behaviour. The clean one is structural: put the Azure OpenAI resource in its own resource group and budget the resource group. The other is a filter on a subscription-scoped budget, which Cost Management supports on the same properties as cost analysis.

One filter pair is worth adding deliberately. Microsoft documents that budget cost evaluations now include reserved instance and purchase data. If you buy provisioned throughput reservations, a month with a purchase in it looks like a spend explosion. To evaluate against first-party consumption only, add the filters Publisher Type: Azure and Charge Type: Usage. Budget evaluations are based on actual cost and do not include amortization, so a reservation lands as one lump rather than spread.

Actual and forecasted thresholds

Two kinds, selected by the Type field on each alert. An actual alert fires on cost already accrued. A forecasted alert fires when the forecast projection crosses the threshold, which is the one that gives you warning rather than news.

Documented limits: at least one threshold and one email address are required; you can have up to five thresholds and five email addresses per budget; and thresholds support a range of 0.01% to 1000% of the budget amount. That upper bound is not a typo and it is occasionally useful — a threshold above 100% catches the case where the budget number itself was set too low to be meaningful.

A sensible ladder for a model workload is a forecasted alert at 80%, an actual at 100%, and an actual at 150% routed somewhere louder. Add [email protected] to your approved senders or the whole exercise is a junk folder.

Assigning roles rather than addresses is possible through the Budgets API but not through the portal. Action groups — which is how you reach a webhook, a Logic App or a Function — are supported only at subscription and resource group scopes, which is another reason to give the OpenAI resource its own resource group.

Creating it without the portal

  1. Create an action group so the alert can do something other than email.
    email1=$(az monitor action-group receiver email create \
      --email-address [email protected] --name EmailReceiver1 \
      --resource-group rg-model --query id -o tsv)
    
    ActionGroupId=$(az monitor action-group create \
      --resource-group rg-model --name ai-budget-ag --short-name aibudget \
      --receiver $email1 --query id -o tsv)
  2. Create the budget against the resource group holding the OpenAI resource. The start date must be the first day of the current month for a monthly budget.
    az consumption budget create-with-rg \
      --resource-group rg-model \
      --budget-name aoai-monthly \
      --amount 2000 \
      --category Cost \
      --time-grain Monthly \
      --time-period '{"start-date":"2026-08-01","end-date":"2027-12-31"}' \
      --notifications "{\"Key1\":{\"enabled\":\"true\", \"operator\":\"GreaterThanOrEqualTo\", \"contact-emails\":[], \"threshold\":80.0, \"contact-groups\":[\"$ActionGroupId\"]}}"
  3. Verify it appears, and check it in cost analysis where the budget line is drawn against the spend trend.
    az consumption budget list --resource-group rg-model -o table

Microsoft notes that CLI and PowerShell commands here may lag the API, and recommends the Budgets REST API for Microsoft Customer Agreement customers and for anything programmatic. If the CLI rejects a field you expect, that is why.

Why this is not a circuit breaker

Put the two documented cadences together. A retry loop that hammers a model deployment can run for the better part of a day before the first email, because the cost data itself has not landed. A budget is the right tool for “is this quarter tracking” and the wrong tool for “something is on fire right now”.

The control that is enforced in real time is the deployment’s tokens-per-minute quota. TPM is checked as each request arrives and converts overspend into a 429 immediately, which is a far blunter and far faster limit than any budget. Deliberately under-allocating TPM on a non-production deployment is a legitimate spend control, and Microsoft acknowledges customers doing exactly this — while noting it is not the Azure best practice, and that automatic quota tier upgrades can raise the ceiling you were relying on unless you opt out.

If you do want the budget to act, the supported path is the action group rather than the email. A budget at subscription or resource group scope can call an action group, and an action group can invoke a webhook, a Logic App or an Azure Function. That gives you somewhere to put a real response — page the on-call rota, drop the TPM allocation on a non-production deployment, disable a feature flag. It still runs on the 24-hour evaluation cadence, so treat it as a daily reconciliation rather than a kill switch, and put the fast controls elsewhere.

A workable layering, then, is three controls at three timescales: the deployment’s TPM quota enforcing per-minute, your own per-tenant or per-feature counters enforcing per-hour, and the budget catching the slow drift nobody notices — a batch job that doubled, a prompt that grew a retrieval step, a model swapped for a dearer one. Only the last of those is what Cost Management is for, and it is genuinely good at it.

Two smaller notes on the mechanics. Budgets appear in cost analysis once created, drawn against the spend trend, which is the fastest way to sanity-check that the scope and filters caught what you meant. And a budget that expires is deleted automatically — so an end date chosen casually two years ago is a control that silently stops existing.

Evaluation cadences, threshold counts and the 0.01–1000% range are Microsoft’s documented values at the time of writing.