| name | aks-cost-analysis |
| description | Break down Azure Kubernetes Service spend by cluster, namespace, and utilization bucket. Identifies the most expensive clusters and namespaces, and how much spend sits in idle vs active workloads. WHEN: "AKS cost", "Kubernetes cost", "cluster cost", "namespace cost", "container cost", "break down AKS", "AKS utilization", "idle pods", "chargeback AKS". INVOKES: query_aks_costs, generate_query, validate_query, execute_query |
| license | MIT |
| metadata | {"version":"1.0.0"} |
AKS Cost Analysis
Break down AKS spend by cluster, namespace, and utilization bucket. Surfaces the highest-spend clusters and namespaces, and how much spend sits in idle vs active workloads.
Prerequisite: The AKS Cost Analysis add-on must be enabled on the cluster(s). When the add-on isn't enabled, query_aks_costs returns no cost data. See Step 1 for the enablement check, and Enable AKS Cost Analysis for setup instructions.
Run the ARG query through the generate → validate → execute workflow. The Step 1 query is ready to run, but pass it to validate_query first — it confirms the syntax and property paths before you spend a call on execute_query. If you need to adapt it to a different resource type or field, draft it with generate_query (a natural-language prompt), then validate and execute.
Step 1: Verify Clusters Exist and Cost Analysis is Enabled
First, confirm AKS clusters exist on the scope. Call execute_query with this Azure Resource Graph query (replace the <sub-id-…> placeholders with the target subscription IDs):
Resources
| where type =~ 'Microsoft.ContainerService/managedClusters'
| where subscriptionId in ('<sub-id-1>', '<sub-id-2>')
| extend costAnalysisEnabled = tobool(properties.metricsProfile.costAnalysis.enabled)
| project name, resourceGroup, location, subscriptionId, costAnalysisEnabled
| order by subscriptionId, name
If the response includes a skipToken, re-call execute_query with that token to fetch the next page and merge results — large tenants may return clusters across multiple pages.
Clusters with costAnalysisEnabled = false won't produce cost rows in Steps 2–3 — flag those clusters in your output so missing rows aren't mistaken for zero spend.
Then call query_aks_costs with groupBy=Cluster as a probe. If it returns no cost rows but clusters exist, the add-on isn't enabled — point users to the enablement steps linked above and re-run after enablement.
If no clusters exist, surface that and stop.
Step 2: Cluster-Level Breakdown
Call query_aks_costs with timeframe=MonthToDate, groupBy=Cluster. If the result is empty (common early in the billing cycle, before the current month has cost data), retry with timeframe=TheLastMonth and label the window in your output so the user knows which period the numbers cover. Report:
- Cost per cluster, sorted descending.
- The top 1–3 clusters by cost — these are where investigation pays off in the next steps.
Use the same timeframe choice (MTD or TheLastMonth) for Step 3.
Step 3: Namespace Breakdown for Top Clusters
For each of the top 1–3 clusters from Step 2, call query_aks_costs with groupBy=Cluster,Namespace, filterDimension=Cluster + filterValues=<cluster resource ID> (see note below), and the same timeframe you settled on in Step 2. Surface:
- Top 5 namespaces by cost per cluster.
- Separate platform / system namespaces (
kube-system, gatekeeper-system, etc.) from application namespaces — platform overhead vs team workloads tells you different things.
Filter values: for the Cluster dimension, pass the full ARM resource ID exactly as it appears in Step 2's response (typically lowercased: /subscriptions/<id>/resourcegroups/<rg>/providers/microsoft.containerservice/managedclusters/<name>).
Cost categories in the Namespace dimension. Rows like #service charges# (service-level charges such as Uptime SLA and Defender for Containers), #idle charges# (provisioned capacity not used by any workload), #system charges# (node capacity AKS reserves for system processes like the kubelet and container runtime), and #unallocated charges# (cost that couldn't be attributed to a namespace) are cluster-level cost categories, not real namespaces — present them separately and don't recommend workload deletes against them.
Step 4: Idle and Overhead Split
Using the Step 3 result (no new query needed), split each top cluster's spend into three buckets:
- Idle (
#idle charges#) — provisioned capacity no workload consumed. This is the primary optimization signal.
- Overhead (
#service charges# + #system charges# + #unallocated charges#) — service, system-reservation, and unattributed cost.
- Allocated — the sum of the real namespace rows (everything that isn't a
#…# category).
A cluster where Idle is a large share of total spend is a strong candidate for node-pool resizing, cluster-autoscaler tuning, or workload consolidation.
For pod-level CPU/memory metrics behind the spend, use Azure Monitor / Container Insights separately.
Step 5: Recommendation & Handoff
Based on what Steps 2–4 surfaced, route the user to the right next step:
- Idle / low-utilization dominates a cluster → hand off to cost-optimization: review node-pool SKUs, enable cluster autoscaler, and tune HPA / VPA (or KEDA for event-driven workloads) on the top namespaces.
- One namespace dominates a cluster's cost → suggest a chargeback conversation with the owning team. Look for ownership / cost-center tags on the cluster resource via ARG (common names:
Owner, CostCenter, BusinessUnit) — exact keys vary by org. If the cluster is multi-tenant, confirm namespace ownership before attributing.
- Compute pattern is stable and well-utilized → hand off to commitment-analysis for a Reservation scoped to the node-pool SKU family, or a Compute Savings Plan covering the cluster's broader compute spend (SP is flexible across SKUs, regions, and OS). Note: Spot node pools don't receive RI/SP discounts, so subtract Spot spend before sizing the commitment.
Presentation
- AKS portfolio — Total AKS spend for the reporting window, cluster count, subscription count.
- Top clusters — Cluster, RG, region, spend for the reporting window.
- Top namespaces per cluster — Per top cluster, top 5 namespaces by spend, with system vs app namespaces called out.
- Idle & overhead — Per top cluster, $ in Idle / Service / System / Unallocated vs allocated workload spend; flag clusters with a large Idle share.
- Next step — One concrete recommendation: which cluster to investigate first and which skill to use next.