# Commitment Analysis

> RI/Savings Plan utilization, coverage of compute spend, new purchase recommendations, and an optional investigation when utilization drops. WHEN: "commitment benefits", "RI utilization", "savings plan utilization", "reservation coverage", "utilization dropped", "how much am I saving", "new RI recommendation". INVOKES: list_benefit_utilization, get_benefit_recommendations, query_costs, list_reservation_transactions, generate_query, validate_query, execute_query

- Skill: `azure-samples/commitment-analysis` (Agent Skill)
- Install (CLI): `npx skillmds@latest add azure-samples/commitment-analysis`
- Raw SKILL.md: https://api.skillmd.com/api/skills/azure-samples/commitment-analysis/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: Azure-Samples (https://skillmd.com/u/azure-samples)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/azure-samples/commitment-analysis

---


# Commitment Benefits & Utilization

Reports current RI / Savings Plan utilization, how much of eligible spend is covered, and what new commitments are recommended. If utilization is low, runs an optional ARG-based investigation to explain why.

> **Scope note:** `list_benefit_utilization` requires a billing scope (`/providers/Microsoft.Billing/billingAccounts/{id}`). If the user only provides a subscription, ask for the billing account ID before running.

> The ARG queries in Step 4 take subscription-id placeholders (`<sub-id-1>, <sub-id-2>`) — replace with the subscriptions where the reservation is scoped (Single/RG) or applied (Shared). To target a management group, first resolve member subs via `ResourceContainers | where type == 'microsoft.resources/subscriptions' | where properties.managementGroupAncestorsChain has '<mg-name>' | project subscriptionId`.

> **Run every ARG query through the generate → validate → execute workflow.** The queries in Step 4 are ready to run, but pass each one to `validate_query` first — it confirms the syntax and property paths before you spend a call on `execute_query`. If you need to adapt a query to a different resource type or field, draft it with `generate_query` (a natural-language prompt), then validate and execute.

## Step 1: Portfolio Utilization

Call `list_benefit_utilization` for the billing scope. The summary returns a utilization percentage per benefit, each identified by its `benefitId` / `benefitOrderId` and `kind` (Reservation or SavingsPlan). For each commitment, report:

- Utilization % with status: 🟢 ≥90% | 🟡 70–89% | 🔴 <70%
- Which benefit it is — `kind` (RI vs Savings Plan) and the benefit/order identifier.

The commitment's term, applied scope, and committed SKU/region describe the underlying reservation or savings-plan order, not the utilization summary — read them from the order itself if the user needs them.

If any commitment is below 90%, hold onto it for the optional investigation in Step 4.

## Step 2: Coverage of Eligible Spend

Call `query_costs` with metric=AmortizedCost, groupBy=PricingModel for a recent window. There's no built-in 30-day timeframe, so use timeframe=Custom with a trailing 30-day from/to (or timeframe=TheLastMonth for the prior calendar month). Amortized cost spreads reservation and savings-plan purchases across their term, so committed usage shows up under the `Reservation` / `SavingsPlan` pricing models rather than as a lump-sum purchase.

Report the breakdown by pricing model — how much recent spend ran on `Reservation` / `SavingsPlan` versus `OnDemand`. A large `OnDemand` share relative to the committed buckets points to headroom for new commitments; size any new purchase from Azure's recommendation in Step 3, not from this split.

Do **not** compute a "real savings vs PAYG retail" number here. An accurate comparison requires per-SKU retail-price lookup against actual usage hours and is out of scope for this skill — defer to `get_benefit_recommendations` (Step 3) for vetted savings estimates from Azure.

## Step 3: New Purchase Recommendations

Call `get_benefit_recommendations` for the user's scope. Show, as returned by the API:

- **Recommended commitment** — `kind` (Reservation or Savings Plan) and `armSkuName` (for a Savings Plan this is `Compute_Savings_Plan`, which is SKU- and region-flexible), with the hourly `commitmentAmount`.
- **Estimated savings** — `savingsAmount` and `savingsPercentage`. These are computed **over the look-back period** (e.g., the last 60 days), not per month — state the period rather than implying a monthly figure.
- **Projected effect if purchased** — `coveragePercentage` and `averageUtilizationPercentage`.

Frame these as *additions* to the current portfolio (Step 1), not replacements.

## Step 4 *(Optional)*: Utilization Drop Investigation

Run only if Step 1 shows utilization <90% or the user explicitly asks why utilization dropped.

**Start by checking the commitment ledger with `list_reservation_transactions`** (billing scope; pass a `from` date — the API looks back at most 92 days, so use a window inside the last ~3 months). Each transaction carries an `eventType` (Purchase, Refund, Exchange, Cancel) and `eventDate`:

- A recent **Refund**, **Exchange**, or **Cancel** directly removes covered capacity and explains a utilization drop.
- A **Purchase** date plus the commitment's term tells you whether a benefit has reached end of term (expiry isn't itself a transaction, so reason about it from the purchase date and term).
- The API only covers the last ~3 months; for older events it points to Cost Management Exports — say so rather than implying nothing changed.

**Then rule out the other simple causes:**

- **Scope mismatch?** Is the reservation scoped Single/RG when resources moved subscriptions or RGs?
- **Region mismatch?** Are new resources deploying in a region the reservation doesn't cover?

**If those don't explain it, call `execute_query` for two Azure Resource Graph queries (one inventory, one change history):**

1. **Inventory of resources matching the committed type.** Adapt the resource type to the reservation (e.g., `Microsoft.Compute/virtualMachines` for VM RIs):

   ```kusto
   Resources
   | where type =~ '<resource-type-from-reservation>'
   | where subscriptionId in ('<sub-id-1>', '<sub-id-2>')
   | summarize count() by tostring(properties.hardwareProfile.vmSize), location
   | order by count_ desc
   ```

   Drop in the SKU/tier projection that matches the resource type. A lower count than the reservation expects explains a utilization drop.

2. **Recent deletions and SKU changes** via `resourcechanges`:

   ```kusto
   resourcechanges
   | where subscriptionId in ('<sub-id-1>', '<sub-id-2>')
   | where properties.changeType in ('Delete', 'Update')
   | where properties.targetResourceType =~ '<resource-type-from-reservation>'
   | project targetResource=tostring(properties.targetResourceId),
             changeType=tostring(properties.changeType),
             timestamp=todatetime(properties.changeAttributes.timestamp),
             changedBy=tostring(properties.changeAttributes.changedBy)
   | order by timestamp desc
   | limit 30
   ```

   For SKU changes, inspect the row's `properties.propertyChanges` array (each entry has `propertyName`, `previousValue`, `newValue`) to confirm the SKU/size shifted.

   > ⚠️ **Caveats on `resourcechanges`**: results depend on tenant permissions and on whether change tracking captured the event. An empty result does **not** prove nothing changed — it may mean the change is outside the retention window or not visible at the current scope.

   ## Presentation

- **Utilization summary** — Per-commitment % with status indicators.
- **Commitment coverage** — recent spend split across `Reservation` / `SavingsPlan` versus `OnDemand`.
- **New recommendations** — the commitment(s) returned by `get_benefit_recommendations`, with API-reported savings over the look-back period.
- **(If Step 4 ran) Root-cause summary** — What changed and when, e.g., "3 VMs deleted on April 15; SKU resized on 2 VMs on April 18." Include a one-line remediation hint:
  - Exchange the reservation for a different SKU/region (within exchange window)
  - Redeploy workloads to match the committed capacity
  - Consider Savings Plans (SKU/region flexible) for the next purchase
  - Refund if the commitment is no longer needed (subject to policy)

