Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8948b1a
feat(queries): add FinOps KPI query catalog
Jun 3, 2026
87443aa
feat(sre-agent): add FinOps recipe content
Jun 3, 2026
6a01421
feat(sre-agent): add deployable Azure template
Jun 3, 2026
a1f7008
fix(queries): remove markdown trailing whitespace
Jun 3, 2026
7e609aa
merge: update KPI query catalog base
Jun 3, 2026
a019530
fix(sre-agent): remove recipe whitespace
Jun 3, 2026
42cc7f4
merge: update SRE Agent recipe base
Jun 3, 2026
c044e75
test(sre-agent): keep deployment tests with deploy slice
Jun 3, 2026
258ee3c
merge: update SRE Agent recipe base
Jun 3, 2026
05601e6
fix(sre-agent): keep deploy checks in deploy slice
Jun 3, 2026
b332089
fix(sre-agent): address deploy CI failures
Jun 3, 2026
3e83081
test(sre-agent): normalize bash stub path on Windows
Jun 3, 2026
60d7857
test(sre-agent): harden bash stub permissions
Jun 3, 2026
e46aa39
test(sre-agent): stub extras builder in deploy tests
Jun 3, 2026
de97720
test(sre-agent): fix Windows Python extras stub
Jun 3, 2026
4ed9fb9
test(sre-agent): preserve Azure resource IDs on Windows
Jun 3, 2026
a6d68e6
fix(sre-agent): address recipe review feedback
Jun 4, 2026
fe4aa54
chore: update mslearn dates
Jun 4, 2026
d07b722
Merge remote-tracking branch 'origin/dev' into features/sre-agent-recipe
Jun 17, 2026
4b64484
fix(sre-agent): vendor capacity skill, default read-only access, cust…
Jun 17, 2026
f7cca43
fix(sre-agent): unify template with secure deploy defaults and clean …
Jun 17, 2026
a1133af
fix(sre-agent): refresh ms.date, default actionMode to review, drop g…
MSBrett Jun 17, 2026
4f3eee1
test(sre-agent): expect review-mode default for portal actionMode
MSBrett Jun 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "src/templates/sre-agent/submodules/azcapman"]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Submodule makes the recipe build non-hermetic and is currently broken. azcapman is pinned only by gitlink SHA (no branch), is uninitialized, and three files under recipes/finops-hub/config/skills/azure-capacity-management/ (SKILL.md, references/docs, references/scripts) are symlinks into it. build-extras.py raises Expected skill directory missing SKILL.md or has a broken symlink: azure-capacity-management, so Build-SreAgentTemplate.ps1 fails unless git submodule update --init was run first — and no build doc/README states that prerequisite. Adding a submodule also burdens every toolkit clone and any git archive/source zip (empty submodule → broken symlinks). Recommend vendoring the azcapman skill/docs/scripts directly into the template and dropping the submodule + symlinks; if the submodule must stay, pin a branch and add an enforced, documented submodule update --init step in the build.

path = src/templates/sre-agent/submodules/azcapman
url = https://github.com/microsoft/azcapman.git
34 changes: 28 additions & 6 deletions src/queries/INDEX.md

Large diffs are not rendered by default.

83 changes: 83 additions & 0 deletions src/queries/KPI.md

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions src/queries/catalog/ai-cost-by-application.kql
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// ============================================================================
// Query: Azure OpenAI Cost by Application
// Description:
// Breaks down Azure OpenAI costs by application, cost center, team, and environment tags.
// Useful for AI workload showback, chargeback, and unit economics analysis.
// Author: FinOps Toolkit Team
// Parameters:
// startDate: Start date for the reporting period (e.g., startofmonth(ago(30d)))
// endDate: End date for the reporting period (e.g., startofmonth(now()))
// Output:
// Each row represents a tagged Azure OpenAI cost grouping with token count and cost metrics.
// Usage:
// Use this query to allocate Azure OpenAI usage and cost to applications, teams, and environments.
// Last Updated: 2026-05-26
// ============================================================================

let startDate = startofmonth(ago(30d));
let endDate = startofmonth(now());
Costs()
| where ChargePeriodStart >= startDate and ChargePeriodStart < endDate
| where x_SkuMeterSubcategory has "OpenAI"
| extend parsedTags = parse_json(Tags)
| extend Application = tostring(parsedTags["application"])
| extend CostCenter = tostring(parsedTags["CostCenter"])
| extend Environment = tostring(parsedTags["environment"])
| extend Team = tostring(parsedTags["team"])
| summarize
TokenCount = sum(ConsumedQuantity),
EffectiveCost = sum(EffectiveCost),
BilledCost = sum(BilledCost)
by Application, CostCenter, Team, Environment, ResourceName, x_SkuMeterSubcategory
| extend CostPer1KTokens = iff(TokenCount == 0, 0.0, EffectiveCost / TokenCount * 1000)
| order by EffectiveCost desc
27 changes: 27 additions & 0 deletions src/queries/catalog/ai-daily-trend.kql
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// ============================================================================
// Query: Azure OpenAI Daily Cost and Token Trend
// Description:
// Returns daily Azure OpenAI token consumption and effective cost.
// Useful for AI workload anomaly detection, forecasting, and trend reporting.
// Author: FinOps Toolkit Team
// Parameters:
// startDate: Start date for the reporting period (e.g., ago(30d))
// endDate: End date for the reporting period (e.g., now())
// Output:
// Each row represents one day with token count, cost, and cost per 1K tokens.
// Usage:
// Use this query to monitor AI workload consumption trends and detect daily cost spikes.
// Last Updated: 2026-05-26
// ============================================================================

let startDate = ago(30d);
let endDate = now();
Costs()
| where ChargePeriodStart >= startDate and ChargePeriodStart < endDate
| where x_SkuMeterSubcategory has "OpenAI"
| summarize
DailyTokens = sum(ConsumedQuantity),
DailyCost = sum(EffectiveCost)
by bin(ChargePeriodStart, 1d)
| extend CostPer1KTokens = iff(DailyTokens == 0, 0.0, DailyCost / DailyTokens * 1000)
| order by ChargePeriodStart asc
31 changes: 31 additions & 0 deletions src/queries/catalog/ai-model-cost-comparison.kql
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// ============================================================================
// Query: Azure OpenAI Model Cost Comparison
// Description:
// Compares token volume, effective cost, list cost, and discount percentage by model.
// Useful for AI model cost efficiency analysis and rate optimization.
// Author: FinOps Toolkit Team
// Parameters:
// startDate: Start date for the reporting period (e.g., startofmonth(ago(30d)))
// endDate: End date for the reporting period (e.g., startofmonth(now()))
// Output:
// Each row represents one Azure OpenAI model or SKU description with cost per 1K tokens.
// Usage:
// Use this query to compare model economics and identify where model or commitment changes may reduce AI spend.
// Last Updated: 2026-05-26
// ============================================================================

let startDate = startofmonth(ago(30d));
let endDate = startofmonth(now());
Costs()
| where ChargePeriodStart >= startDate and ChargePeriodStart < endDate
| where x_SkuMeterSubcategory has "OpenAI"
| extend Model = x_SkuDescription
| summarize
TokenCount = sum(ConsumedQuantity),
EffectiveCost = sum(EffectiveCost),
ListCost = sum(ListCost)
by Model
| extend CostPer1KTokens = iff(TokenCount == 0, 0.0, EffectiveCost / TokenCount * 1000)
| extend ListPer1KTokens = iff(TokenCount == 0, 0.0, ListCost / TokenCount * 1000)
| extend DiscountPercent = iff(ListCost == 0, 0.0, (ListCost - EffectiveCost) / ListCost * 100)
| order by EffectiveCost desc
35 changes: 35 additions & 0 deletions src/queries/catalog/ai-token-usage-breakdown.kql
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// ============================================================================
// Query: Azure OpenAI Token Usage Breakdown
// Description:
// Breaks Azure OpenAI token consumption down by model version and input/output direction.
// Calculates effective unit cost per token and cost per 1K tokens.
// Author: FinOps Toolkit Team
// Parameters:
// startDate: Start date for the reporting period (e.g., startofmonth(ago(30d)))
// endDate: End date for the reporting period (e.g., startofmonth(now()))
// Output:
// Each row represents one model and direction with token count and cost metrics.
// Usage:
// Use this query to analyze AI workload unit economics, token direction mix, and model cost efficiency.
// Last Updated: 2026-05-26
// ============================================================================

let startDate = startofmonth(ago(30d));
let endDate = startofmonth(now());
Costs()
| where ChargePeriodStart >= startDate and ChargePeriodStart < endDate
| where x_SkuMeterSubcategory has "OpenAI"
| extend Model = x_SkuDescription
| extend Direction = case(
Model contains "Input", "Input",
Model contains "Output", "Output",
"Other")
| summarize
TokenCount = sum(ConsumedQuantity),
EffectiveCost = sum(EffectiveCost),
BilledCost = sum(BilledCost),
ListCost = sum(ListCost)
by Model, Direction, x_SkuDescription
| extend UnitCostPerToken = iff(TokenCount == 0, 0.0, EffectiveCost / TokenCount)
| extend CostPer1KTokens = UnitCostPerToken * 1000
| order by EffectiveCost desc
35 changes: 35 additions & 0 deletions src/queries/catalog/allocation-accuracy-index.kql
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// ============================================================================
// Query: Allocation Accuracy Index
// Description:
// Calculates the percentage of total effective cost that is directly attributed using a
// three-signal heuristic: allocation rule, cost center, or ownership-tag evidence.
// KPI: Allocation Accuracy Index (AAI)
// Formula: Allocation Accuracy Index (AAI) = (Directly Attributed Costs / Total Infrastructure Costs) × 100
// Author: FinOps toolkit
// Parameters:
// startDate: datetime; start date for the reporting period (default: startofmonth(ago(30d)))
// endDate: datetime; end date for the reporting period (default: startofmonth(now()))
// allocationEvidenceTagKeys: dynamic; tag keys treated as ownership evidence (default: dynamic(['cost-center','team','owner','application','product']))
// Output:
// Each row represents one BillingCurrency and returns DirectlyAttributedCost, TotalEffectiveCost,
// and AAI for Hub-visible CSP costs in the reporting window.
// Usage:
// Use this query to measure how much effective cost is directly attributable within FinOps Hub.
// Scope Notes: Hub-only AAI. Does not include on-prem, SaaS, or other infrastructure outside Hub schema.
// Last Tested: 2026-05-28 against msbwftktreyhub.westus.kusto.windows.net/Hub (1,366,763 cost rows in 2026-04 window). UAT result: PASS — 1 row returned
// =========================================================================
let startDate = startofmonth(ago(30d));
let endDate = startofmonth(now());
let allocationEvidenceTagKeys = dynamic(['cost-center','team','owner','application','product']);
Costs()
| where ChargePeriodStart >= startDate and ChargePeriodStart < endDate
| where not(ChargeCategory == 'Purchase' and isnotempty(CommitmentDiscountCategory))
| extend tmp_IsAttributed = isnotempty(x_CostAllocationRuleName)
or isnotempty(x_CostCenter)
or array_length(set_intersect(bag_keys(Tags), allocationEvidenceTagKeys)) > 0
| summarize
DirectlyAttributedCost = todouble(sumif(EffectiveCost, tmp_IsAttributed)),
TotalEffectiveCost = todouble(sum(EffectiveCost))
by BillingCurrency
| extend AAI = iff(TotalEffectiveCost == 0, 0.0, DirectlyAttributedCost / TotalEffectiveCost * 100.0)
| project BillingCurrency, DirectlyAttributedCost, TotalEffectiveCost, AAI
53 changes: 53 additions & 0 deletions src/queries/catalog/anomaly-detection-rate.kql
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// ============================================================================
// Query: Anomaly Detection Rate
// Description:
// Calculates the percentage of effective spend attributable to anomaly-flagged days.
// Builds daily cost series per service category and billing currency, then applies time-series anomaly detection.
// KPI: Anomaly Detection Rate
// Formula: Total Cost of Anomaly Spikes / Total Spend = Anomaly Cost %
// Author: FinOps toolkit
// Parameters:
// startDate: datetime; Start date for the reporting period (default: startofmonth(ago(30d)))
// endDate: datetime; End date for the reporting period (default: startofmonth(now()))
// sensitivity: real; Anomaly detection sensitivity for series_decompose_anomalies() (default: 1.5)
// Output:
// Each row returns a BillingCurrency and ServiceCategory pair (plus an Overall rollup row) with AnomalyCost, TotalCost, and AnomalyRatePercent as double values.
// Usage:
// Use this query to quantify how much effective spend falls on anomaly-flagged days by service category and by billing currency.
// Scope Notes:
// Treats both positive spikes and negative drops as anomalies (AnomalyFlags != 0); the Overall row is per BillingCurrency only.
// Missing days are zero-filled via make-series default=0.0, and commitment purchase rows are excluded to avoid amortization double-counting.
// Last Tested: 2026-05-28 against msbwftktreyhub.westus.kusto.windows.net/Hub (1,366,763 cost rows in 2026-04 window). UAT result: PASS — 13 rows returned (per service category)
// =========================================================================
let startDate = startofmonth(ago(30d));
let endDate = startofmonth(now());
let sensitivity = 1.5;
let expanded = materialize(
Costs()
| where ChargePeriodStart >= startDate and ChargePeriodStart < endDate
| where not(ChargeCategory == 'Purchase' and isnotempty(CommitmentDiscountCategory))
| summarize DailyCost = sum(todouble(EffectiveCost))
by bin(ChargePeriodStart, 1d), ServiceCategory, BillingCurrency
| make-series CostSeries = sum(DailyCost) default=0.0
on ChargePeriodStart from startDate to endDate step 1d
by ServiceCategory, BillingCurrency
| extend AnomalyFlags = series_decompose_anomalies(CostSeries, sensitivity)
| mv-expand CostSeries to typeof(double), AnomalyFlags to typeof(int)
);
let perCategory =
expanded
| summarize
AnomalyCost = todouble(sumif(CostSeries, AnomalyFlags != 0)),
TotalCost = todouble(sum(CostSeries))
by BillingCurrency, ServiceCategory
| extend AnomalyRatePercent = todouble(iff(TotalCost == 0.0, 0.0, AnomalyCost / TotalCost * 100.0));
let overall =
expanded
| summarize
AnomalyCost = todouble(sumif(CostSeries, AnomalyFlags != 0)),
TotalCost = todouble(sum(CostSeries))
by BillingCurrency
| extend ServiceCategory = 'Overall'
| extend AnomalyRatePercent = todouble(iff(TotalCost == 0.0, 0.0, AnomalyCost / TotalCost * 100.0));
union perCategory, overall
| project BillingCurrency, ServiceCategory, AnomalyCost, TotalCost, AnomalyRatePercent
65 changes: 65 additions & 0 deletions src/queries/catalog/anomaly-variance-total.kql
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// ============================================================================
// Query: Total Unpredicted Variance of Spend
// Description:
// Calculates the net unpredicted variance between actual effective cost and the anomaly baseline
// for anomaly-flagged daily buckets by service category and billing currency.
// KPI: Total Unpredicted Variance of Spend
// Formula: Total effective cost associated with all anomaly events detected less the predicted spend of the services related to the identified anomalies.
// Author: FinOps toolkit
// Parameters:
// startDate: datetime; start of the reporting period (default: startofmonth(ago(12 * 30d)))
// endDate: datetime; end of the reporting period (default: now())
// anomalyThreshold: real; anomaly detection sensitivity threshold (default: 1.5)
// Output:
// Each row returns BillingCurrency, ServiceCategory, AnomalyEventCount, UnpredictedVarianceSigned,
// and UnpredictedVarianceAbs for anomaly-detected daily buckets; "(All Services)" rows provide
// per-currency totals across all service categories.
// Usage:
// Use this query to quantify net anomalous overspend or underspend by service category and currency.
// Scope Notes:
// Per-group anomaly detection runs independently per (ServiceCategory, BillingCurrency), and groups
// with sparse history are filtered when no anomaly baseline is produced. Each BillingCurrency is
// reported separately and must not be summed across currencies without FX conversion. The default
// 12-month window supports STL seasonality decomposition, and UnpredictedVarianceAbs is defined as
// abs(sum(actual - baseline)) rather than sum(abs(actual - baseline)).
// Last Tested: 2026-05-28 against msbwftktreyhub.westus.kusto.windows.net/Hub (1,366,763 cost rows in 2026-04 window). UAT result: PASS — 13 rows returned (per service category)
// =========================================================================
let startDate = startofmonth(ago(12 * 30d));
let endDate = now();
let anomalyThreshold = 1.5;
let anomalyBuckets =
Costs()
| where ChargePeriodStart >= startDate and ChargePeriodStart < endDate
| where not(ChargeCategory == 'Purchase' and isnotempty(CommitmentDiscountCategory))
| summarize DailyCost = sum(EffectiveCost) by ServiceCategory, BillingCurrency, bin(ChargePeriodStart, 1d)
| make-series CostSeries = sum(DailyCost) default=0.0 on ChargePeriodStart from startDate to endDate step 1d by ServiceCategory, BillingCurrency
| extend (ad_flag, ad_score, ad_baseline) = series_decompose_anomalies(CostSeries, anomalyThreshold)
| mv-expand ChargePeriodStart to typeof(datetime), CostSeries to typeof(real), ad_flag to typeof(real), ad_score to typeof(real), ad_baseline to typeof(real)
| extend ad_flag = toint(ad_flag), CostSeries = toreal(CostSeries), ad_score = toreal(ad_score), ad_baseline = toreal(ad_baseline)
| where isnotnull(ad_baseline)
| where ad_flag != 0
| extend Variance = todouble(CostSeries) - todouble(ad_baseline);
union
(
anomalyBuckets
| summarize
AnomalyEventCount = count(),
UnpredictedVarianceSigned = todouble(sum(Variance)),
UnpredictedVarianceAbs = todouble(abs(sum(Variance)))
by BillingCurrency, ServiceCategory
),
(
anomalyBuckets
| summarize
AnomalyEventCount = count(),
UnpredictedVarianceSigned = todouble(sum(Variance)),
UnpredictedVarianceAbs = todouble(abs(sum(Variance)))
by BillingCurrency
| extend ServiceCategory = "(All Services)"
)
| project
BillingCurrency,
ServiceCategory,
AnomalyEventCount,
UnpredictedVarianceSigned = todouble(UnpredictedVarianceSigned),
UnpredictedVarianceAbs = todouble(UnpredictedVarianceAbs)
88 changes: 88 additions & 0 deletions src/queries/catalog/commitment-discount-waste.kql
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// ============================================================================
// Query: Percentage of Commitment Discount Waste
// Description:
// Computes the percentage of commitment discount waste for each commitment by
// comparing unused amortized EffectiveCost to total commitment EffectiveCost.
// Includes a grand-total row per BillingCurrency across all commitments in the
// reporting window.
// KPI: Percentage of Commitment Discount Waste
// Formula: (Cost of Commitment Discount unused / total cost Commitment Discount) x 100
// Author: FinOps toolkit
// Parameters:
// startDate: datetime = startofmonth(ago(30d))
// endDate: datetime = startofmonth(now())
// Output:
// BillingCurrency: string. Billing currency for the commitment or grand-total row.
// CommitmentDiscountId: string. Commitment identifier; empty string on the grand-total row.
// CommitmentDiscountName: string. Commitment name; '(All Commitments)' on the grand-total row.
// CommitmentDiscountType: string. Commitment type; empty string on the grand-total row.
// UnusedCost: double. Sum of EffectiveCost for rows where CommitmentDiscountStatus == 'Unused'.
// TotalCost: double. Sum of EffectiveCost across all commitment rows after filters.
// WastePercent: double. Percentage of TotalCost represented by UnusedCost.
// Usage:
// Use this query to identify underutilized commitment discounts and quantify waste by commitment and billing currency.
// Scope Notes:
// - Double-counting prevention: This query operates on amortized-style rows only
// (purchase rows excluded).
// - Unused row semantics: CommitmentDiscountStatus == 'Unused' rows represent
// wasted amortized commitment cost for the charge period.
// - CommitmentDiscountStatus values: Only 'Used' and 'Unused' are expected.
// - Currency mixing: Grand-total output is scoped per BillingCurrency; cross-
// currency aggregation is out of scope.
// - Period window: Default startDate/endDate cover one calendar month; adjust
// the let bindings for rolling windows.
// - KPI limitation vs canonical definition: This implementation uses
// EffectiveCost as the cost basis for commitment-discount waste analytics.
// Last Tested: 2026-05-28 against msbwftktreyhub.westus.kusto.windows.net/Hub (1,366,763 cost rows in 2026-04 window). UAT result: PASS_SCHEMA_WITH_DATA_GAP — query executed and returned expected columns; the hub has 0 commitment-discount rows in the test window, so value semantics require re-UAT on a commitment-active hub.
// =========================================================================
let startDate = startofmonth(ago(30d));
let endDate = startofmonth(now());
let filteredCosts =
Costs()
| where ChargePeriodStart >= startDate and ChargePeriodStart < endDate
| where isnotempty(CommitmentDiscountId)
| where not(ChargeCategory == 'Purchase' and isnotempty(CommitmentDiscountCategory));
let commitmentWaste =
filteredCosts
| summarize
UnusedCost = sum(iff(CommitmentDiscountStatus == 'Unused', EffectiveCost, real(0))),
TotalCost = sum(EffectiveCost)
by BillingCurrency, CommitmentDiscountId, CommitmentDiscountName, CommitmentDiscountType
| extend WastePercent = iff(TotalCost == 0.0, 0.0, todouble(UnusedCost) / todouble(TotalCost) * 100.0)
| project
BillingCurrency,
CommitmentDiscountId,
CommitmentDiscountName,
CommitmentDiscountType,
UnusedCost = todouble(UnusedCost),
TotalCost = todouble(TotalCost),
WastePercent = todouble(WastePercent);
let grandTotals =
filteredCosts
| summarize
UnusedCost = sum(iff(CommitmentDiscountStatus == 'Unused', EffectiveCost, real(0))),
TotalCost = sum(EffectiveCost)
by BillingCurrency
| extend
CommitmentDiscountId = '',
CommitmentDiscountName = '(All Commitments)',
CommitmentDiscountType = ''
| extend WastePercent = iff(TotalCost == 0.0, 0.0, todouble(UnusedCost) / todouble(TotalCost) * 100.0)
| project
BillingCurrency,
CommitmentDiscountId,
CommitmentDiscountName,
CommitmentDiscountType,
UnusedCost = todouble(UnusedCost),
TotalCost = todouble(TotalCost),
WastePercent = todouble(WastePercent);
union commitmentWaste, grandTotals
| order by BillingCurrency asc, WastePercent desc, CommitmentDiscountName asc
| project
BillingCurrency,
CommitmentDiscountId,
CommitmentDiscountName,
CommitmentDiscountType,
UnusedCost,
TotalCost,
WastePercent
Loading