Cost Forecast Workflow
Use this workflow when the user wants to project future costs.
⚠️ Warning: If the user wants historical cost data, use the Cost Query Workflow. If they want to reduce costs, use the Cost Optimization Workflow.
Key Differences from Query API
| Aspect | Query API | Forecast API |
|---|---|---|
| Purpose | Historical cost data | Projected future costs |
| Time period | Past dates only | Must include future dates |
| Grouping | Up to 2 dimensions | Not supported |
includeActualCost | N/A | Include historical alongside forecast |
| Response columns | Cost, Date, Currency | Cost, Date, CostStatus, Currency |
| Max response rows | 5,000/page | 40 rows recommended |
| Timeframe | Multiple presets + Custom | Typically Custom only |
Step 1: Determine Scope
Use the same scope patterns from the Scope Reference table in the main SKILL.md.
Step 2: Choose Report Type
ActualCost is most common for forecasting. AmortizedCost for reservation/savings plan projections.
Step 3: Set Time Period
⚠️ Warning: The
todate MUST be in the future.
- Set
timeframetoCustomand providetimePeriodwithfromandtodates fromcan be in the past — shows actual costs up to today, then forecast toto- Minimum 28 days of historical cost data required
- Maximum forecast period: 10 years
Full rules: Forecast Guardrails
Step 4: Configure Dataset
- Granularity:
DailyorMonthlyrecommended - Aggregation: Typically
SumofCost - See Forecast Request Body Schema for full schema
⚠️ Warning: Grouping is NOT supported for forecast. Suggest using the Cost Query Workflow for grouped historical data instead.
Step 5: Set Forecast-Specific Options
| Field | Default | Description |
|---|---|---|
includeActualCost | true | Include historical actual costs alongside forecast |
includeFreshPartialCost | true | Include partial cost data for recent days. Requires includeActualCost: true |
Step 6: Construct and Execute
Create temp/cost-forecast.json:
{
"type": "ActualCost",
"timeframe": "Custom",
"timePeriod": {
"from": "<first-of-month>",
"to": "<last-of-month>"
},
"dataset": {
"granularity": "Daily",
"aggregation": {
"totalCost": { "name": "Cost", "function": "Sum" }
},
"sorting": [{ "direction": "Ascending", "name": "UsageDate" }]
},
"includeActualCost": true,
"includeFreshPartialCost": true
}Execute:
New-Item -ItemType Directory -Path "temp" -Force
az rest --method post `
--url "/subscriptions/<subscription-id>/providers/Microsoft.CostManagement/forecast?api-version=2023-11-01" `
--headers "ClientType=GitHubCopilotForAzure" `
--body '@temp/cost-forecast.json'Step 7: Interpret Response
| CostStatus | Meaning |
|---|---|
Actual | Historical actual cost (when includeActualCost: true) |
Forecast | Projected future cost |
💡 Tip: "Forecast is unavailable for the specified time period" is not an error — it means the scope has insufficient historical data. Suggest using the Cost Query Workflow for available data.
Key Guardrails
| Rule | Constraint |
|---|---|
to date | Must be in the future |
| Grouping | Not supported |
| Min training data | 28 days of historical cost data |
| Max forecast period | 10 years |
| Response row limit | 40 rows recommended |
includeFreshPartialCost | Requires includeActualCost: true |
| Monthly + includeActualCost | Requires explicit timePeriod |
Full details: Forecast Guardrails
Error Handling
| Status | Error | Remediation |
|---|---|---|
| 400 | Can't forecast on the past | Ensure to date is in the future. |
| 400 | Missing dataset | Add required dataset field. |
| 400 | Invalid dependency | Set includeActualCost: true when using includeFreshPartialCost. |
| 403 | Forbidden | Needs Cost Management Reader role on scope. |
| 424 | Bad training data | Insufficient history; falls back to actual costs if available. |
| 429 | Rate limited | Check all x-ms-ratelimit-microsoft.costmanagement-*-retry-after headers (qpu, entity, tenant). Wait for the longest value. Max 3 retries. |
| 503 | Service unavailable | Check Azure Status. |
Full details: Forecast Error Handling
For more forecast examples, see forecast examples.