Budget Health Check
Quick answer to the three questions users actually ask about a budget: Am I over? Will I overshoot? Is anything firing?
Steps
List Budgets — Call list_budgets with the user's scope.
- If no budgets exist on the scope, skip to the No budgets found handoff below.
- If the call returns an access-denied error, the user lacks Cost Management read access on this scope — surface that distinctly rather than reporting it as "no budgets found."
End-of-Month Forecast — Call forecast_costs with the same scope. It returns one row per day, each tagged CostStatus Actual or Forecast, and the same date can appear as both. For the month-end projection, take one value per date — the Actual if present, otherwise the Forecast — and sum PreTaxCost. Compare that to the budget amount.
- No Forecast rows? If the response has only
Actual rows, a projection isn't available for this scope — report month-to-date actual and say a forecast couldn't be produced; don't present partial actuals as a month-end projection. This happens on multi-currency scopes (below) and where there's too little history to project.
- Multi-currency caveat: the Cost Management Forecast API doesn't return a forecast for scopes that span multiple currencies — a management group or billing account can mix, e.g., USD and GBP, in which case the response comes back as
Actual-only rows per currency. Re-scope to a single-currency subscription to get a forecast, and never sum across currencies.
Current Month Spend — Call query_costs with timeframe=MonthToDate, groupBy=ServiceName, granularity=None for the spend breakdown by service. If it returns no rows (early in the billing cycle, before current-month data is available), say so rather than implying zero spend — optionally repeat with timeframe=TheLastMonth for recent context.
- Multi-currency caveat: at management-group or billing-account scope, the result may contain multiple
Currency values. Group by Currency before summing or comparing — don't combine USD and GBP into one total.
- Source-of-truth note: for the budget % use
list_budgets' currentSpend.amount (this is what Azure evaluates Actual-cost budget alerts against; Forecasted alerts evaluate against forecastSpend). Use the query_costs MTD result only for the service-level drill-down.
Active Alerts — Call list_alerts to surface triggered budget or anomaly alerts. Each alert carries a periodStartDate and stays in Active status until dismissed, so the list can include alerts from earlier months. Check periodStartDate: present only current-period alerts as firing now, and label older ones as prior-period history.
Presentation
For each budget, show:
- Budget — Name, amount, time grain, current spend vs limit (amount and %, with currency code — e.g.
1,200 / 2,000 USD (60%))
- Status — 🟢 On Track (<80%) | 🟡 Warning (80–95%) | 🔴 Over/Critical (>95%)
- Forecast — Projected end-of-month spend; will it exceed the limit? By how much?
- Active Alerts — Current-period triggered budget or anomaly alerts (older periods noted separately as history)
Then, briefly: top 3–5 services by MTD spend (from Step 3) so the user can see what's driving the number.
Handoffs
If forecast > budget limit, or if any anomaly alert is firing:
⚠️ Projected to exceed budget by $X (Y%). Want me to investigate the drivers?
I can use the cost-investigation skill to find what's pushing spend up.
If the user says yes, hand off to cost-investigation with the same scope.
No budgets found on the scope:
No budgets are configured on this scope. Without budgets, cost overruns go unnoticed until the invoice arrives.
Want help setting them up? I can use the budget-setup skill to recommend amounts based on your spend baseline and create tiered alerts.
If the user says yes, hand off to budget-setup with the same scope.
1---2name: budget-health3description: Current spend vs limits, end-of-month forecast, and active alerts. WHEN: "budget status", "am I over budget", "budget health", "will I exceed budget", "budget forecast". INVOKES: list_budgets, forecast_costs, query_costs, list_alerts4license: MIT5---67# Budget Health Check89Quick answer to the three questions users actually ask about a budget: *Am I over? Will I overshoot? Is anything firing?*1011## Steps12131. **List Budgets** — Call `list_budgets` with the user's scope.14 - If no budgets exist on the scope, skip to the *No budgets found* handoff below.15 - If the call returns an access-denied error, the user lacks Cost Management read access on this scope — surface that distinctly rather than reporting it as "no budgets found."16172. **End-of-Month Forecast** — Call `forecast_costs` with the same scope. It returns one row per day, each tagged `CostStatus` `Actual` or `Forecast`, and the same date can appear as both. For the month-end projection, take one value per date — the `Actual` if present, otherwise the `Forecast` — and sum `PreTaxCost`. Compare that to the budget amount.18 - **No Forecast rows?** If the response has only `Actual` rows, a projection isn't available for this scope — report month-to-date actual and say a forecast couldn't be produced; don't present partial actuals as a month-end projection. This happens on multi-currency scopes (below) and where there's too little history to project.19 - **Multi-currency caveat:** the Cost Management [Forecast API](https://learn.microsoft.com/en-us/rest/api/cost-management/forecast/usage) doesn't return a forecast for scopes that span multiple currencies — a management group or billing account can mix, e.g., USD and GBP, in which case the response comes back as `Actual`-only rows per currency. Re-scope to a single-currency subscription to get a forecast, and never sum across currencies.20213. **Current Month Spend** — Call `query_costs` with timeframe=MonthToDate, groupBy=ServiceName, granularity=None for the spend breakdown by service. If it returns no rows (early in the billing cycle, before current-month data is available), say so rather than implying zero spend — optionally repeat with timeframe=TheLastMonth for recent context.22 - **Multi-currency caveat:** at management-group or billing-account scope, the result may contain multiple `Currency` values. Group by Currency before summing or comparing — don't combine USD and GBP into one total.23 - **Source-of-truth note:** for the budget % use `list_budgets`' `currentSpend.amount` (this is what Azure evaluates Actual-cost budget alerts against; Forecasted alerts evaluate against `forecastSpend`). Use the `query_costs` MTD result only for the service-level drill-down.24254. **Active Alerts** — Call `list_alerts` to surface triggered budget or anomaly alerts. Each alert carries a `periodStartDate` and stays in `Active` status until dismissed, so the list can include alerts from earlier months. Check `periodStartDate`: present only current-period alerts as firing now, and label older ones as prior-period history.2627## Presentation2829For each budget, show:3031- **Budget** — Name, amount, time grain, current spend vs limit (amount and %, with currency code — e.g. `1,200 / 2,000 USD (60%)`)32- **Status** — 🟢 On Track (<80%) | 🟡 Warning (80–95%) | 🔴 Over/Critical (>95%)33- **Forecast** — Projected end-of-month spend; will it exceed the limit? By how much?34- **Active Alerts** — Current-period triggered budget or anomaly alerts (older periods noted separately as history)3536Then, briefly: top 3–5 services by MTD spend (from Step 3) so the user can see what's driving the number.3738## Handoffs3940**If forecast > budget limit**, or if any anomaly alert is firing:4142> ⚠️ Projected to exceed budget by $X (Y%). Want me to investigate the drivers?43>44> I can use the **cost-investigation** skill to find what's pushing spend up.4546If the user says yes, hand off to **cost-investigation** with the same scope.4748**No budgets found on the scope:**4950> No budgets are configured on this scope. Without budgets, cost overruns go unnoticed until the invoice arrives.51>52> Want help setting them up? I can use the **budget-setup** skill to recommend amounts based on your spend baseline and create tiered alerts.5354If the user says yes, hand off to **budget-setup** with the same scope.