From 306a9628f185fe88f7369be0d31a52a1921990b9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Oct 2025 20:29:14 +0000 Subject: [PATCH 1/3] Initial plan From 950b803b214c1ea758cd76a0343fe9f326199506 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Oct 2025 20:41:18 +0000 Subject: [PATCH 2/3] Add troubleshooting guide for reservation savings calculations Co-authored-by: MSBrett <24294904+MSBrett@users.noreply.github.com> --- docs-mslearn/TOC.yml | 4 +- docs-mslearn/toolkit/changelog.md | 4 + docs-mslearn/toolkit/help/errors.md | 12 +- .../toolkit/hubs/savings-calculations.md | 2 + .../troubleshooting-reservation-savings.md | 244 ++++++++++++++++++ 5 files changed, 263 insertions(+), 3 deletions(-) create mode 100644 docs-mslearn/toolkit/hubs/troubleshooting-reservation-savings.md diff --git a/docs-mslearn/TOC.yml b/docs-mslearn/TOC.yml index 8126a2bcf..1718b9f93 100644 --- a/docs-mslearn/TOC.yml +++ b/docs-mslearn/TOC.yml @@ -139,7 +139,9 @@ - name: Configure AI agents href: toolkit/hubs/configure-ai.md - name: Savings calculations - href: toolkit/hubs/savings-calculations.md + href: toolkit/hubs/savings-calculations.md + - name: Troubleshoot reservation savings + href: toolkit/hubs/troubleshooting-reservation-savings.md - name: Deployment template href: toolkit/hubs/template.md - name: Data model diff --git a/docs-mslearn/toolkit/changelog.md b/docs-mslearn/toolkit/changelog.md index 7c5d94e4b..483073be5 100644 --- a/docs-mslearn/toolkit/changelog.md +++ b/docs-mslearn/toolkit/changelog.md @@ -40,6 +40,10 @@ The following section lists features and enhancements that are currently in deve ### Documentation improvements +- **Added** + - New troubleshooting guide for [reservation savings calculations](hubs/troubleshooting-reservation-savings.md) to help diagnose and resolve zero savings issues. + - Enhanced error documentation for MissingContractedCost and MissingListCost with references to the troubleshooting guide. + ### [Power BI reports](power-bi/reports.md) v13 diff --git a/docs-mslearn/toolkit/help/errors.md b/docs-mslearn/toolkit/help/errors.md index e774b2da8..315346cfa 100644 --- a/docs-mslearn/toolkit/help/errors.md +++ b/docs-mslearn/toolkit/help/errors.md @@ -417,9 +417,13 @@ TODO: Consider the following ways to streamline this in the future: This error code is shown in the `x_SourceChanges` column when `ContractedCost` is either null or 0 and `EffectiveCost` is greater than 0. The error indicates Microsoft Cost Management didn't include `ContractedCost` for the specified rows, which means savings can't be calculated. +This is a known limitation in the FOCUS export for all MCA reservation usage, EA reservation usage when cost allocation is enabled, and Marketplace charges. See [FOCUS conformance summary](https://learn.microsoft.com/cloud-computing/finops/focus/conformance-summary#missing-data) for details. + **Mitigation**: As a workaround to the missing data, FinOps toolkit reports copy the `EffectiveCost` into the `ContractedCost` column for rows flagged with this error code. Savings aren't available for these records. -To calculate complete savings, you can join cost and usage data with prices. For more information, see [issue #873](https://github.com/microsoft/finops-toolkit/issues/873). +To calculate complete savings for reservation usage, export and ingest price sheet data. FinOps hubs will automatically join price data with cost data to populate missing ContractedCost values. For step-by-step guidance, see [Troubleshooting reservation savings](../hubs/troubleshooting-reservation-savings.md). + +For more information about the underlying issue, see [issue #873](https://github.com/microsoft/finops-toolkit/issues/873).
@@ -443,9 +447,13 @@ For details on how missing prices affect savings calculations, see [Understandin This error code is shown in the `x_SourceChanges` column when `ListCost` is either null or 0 and `ContractedCost` is greater than 0. The error indicates Microsoft Cost Management didn't include `ListCost` for the specified rows, which means savings can't be calculated. +This is a known limitation in the FOCUS export for all MCA reservation usage, EA reservation usage, and Marketplace charges. See [FOCUS conformance summary](https://learn.microsoft.com/cloud-computing/finops/focus/conformance-summary#missing-data) for details. + **Mitigation**: As a workaround to the missing data, FinOps toolkit reports copy the `ContractedCost` into the `ListCost` column for rows flagged with this error code. Savings aren't available for these records. -To calculate complete savings, you can join cost and usage data with prices. For more information, see [issue #873](https://github.com/microsoft/finops-toolkit/issues/873). +To calculate complete savings for reservation usage, export and ingest price sheet data. FinOps hubs will automatically join price data with cost data to populate missing ListCost values. For step-by-step guidance, see [Troubleshooting reservation savings](../hubs/troubleshooting-reservation-savings.md). + +For more information about the underlying issue, see [issue #873](https://github.com/microsoft/finops-toolkit/issues/873).
diff --git a/docs-mslearn/toolkit/hubs/savings-calculations.md b/docs-mslearn/toolkit/hubs/savings-calculations.md index 89ee6ef77..e7c4dc754 100644 --- a/docs-mslearn/toolkit/hubs/savings-calculations.md +++ b/docs-mslearn/toolkit/hubs/savings-calculations.md @@ -144,6 +144,8 @@ If you see many zero savings values: 2. **Enable price population** - In storage reports, enable "Experimental: Populate Missing Prices" parameter. 3. **Use FinOps hubs** - FinOps hubs with Data Explorer automatically populate missing prices when available. +For detailed steps to resolve missing reservation savings, see [Troubleshooting reservation savings](./troubleshooting-reservation-savings.md). +
## Give feedback diff --git a/docs-mslearn/toolkit/hubs/troubleshooting-reservation-savings.md b/docs-mslearn/toolkit/hubs/troubleshooting-reservation-savings.md new file mode 100644 index 000000000..df8af24cf --- /dev/null +++ b/docs-mslearn/toolkit/hubs/troubleshooting-reservation-savings.md @@ -0,0 +1,244 @@ +--- +title: Troubleshooting reservation savings calculations +description: Learn how to diagnose and fix issues with reservation savings showing as zero in FinOps toolkit reports. +author: flanakin +ms.author: micflan +ms.date: 10/10/2025 +ms.topic: troubleshooting-article +ms.service: finops +ms.subservice: finops-toolkit +ms.reviewer: micflan +#customer intent: As a FinOps practitioner, I want to understand why reservation savings are showing as zero so I can fix the issue and get accurate savings calculations. +--- + + +# Troubleshooting reservation savings calculations + +If your FinOps toolkit reports show zero savings for reservations even though you have active reservations in use, this article helps you diagnose and resolve the issue. + +This is typically caused by missing price data in the Cost Management FOCUS export, which is a known limitation documented in the [FOCUS conformance summary](https://learn.microsoft.com/cloud-computing/finops/focus/conformance-summary#missing-data). + +
+ +## Symptoms + +You may experience one or more of the following symptoms: + +- Rate optimization reports show 0.00% estimated savings rate (ESR) for reservations +- ContractedCost equals EffectiveCost for reservation usage charges +- ListCost equals ContractedCost equals EffectiveCost for reservation usage charges +- x_SourceChanges column contains "MissingContractedCost" or "MissingListCost" for reservation rows + +
+ +## Root cause + +Microsoft Cost Management FOCUS exports set ContractedCost and ListCost to zero for: + +- All Microsoft Customer Agreement (MCA) reservation usage +- Enterprise Agreement (EA) reservation usage when cost allocation is enabled +- EA and MCA Marketplace charges + +This is documented behavior, not a bug. The workaround is to join cost data with price sheet data, which FinOps toolkit does automatically when price data is available. + +
+ +## Verify price data is available + +### Step 1: Confirm price sheet exports exist + +For Microsoft Customer Agreement (MCA) accounts: + +1. Navigate to **Cost Management + Billing** in the Azure portal +2. Select your **billing profile** (not billing account) +3. Go to **Exports** and verify you have a price sheet export configured +4. Check the export run history to confirm it has run successfully + +For Enterprise Agreement (EA) accounts: + +1. Navigate to **Cost Management + Billing** in the Azure portal +2. Select your **billing account** +3. Go to **Exports** and verify you have a price sheet export configured +4. Check the export run history to confirm it has run successfully + +> [!IMPORTANT] +> For MCA accounts, price sheet exports must use the billing profile scope, not the billing account scope. The scope should look like: `/providers/Microsoft.Billing/billingAccounts/{billingAccountId}/billingProfiles/{billingProfileId}` + +### Step 2: Verify export order + +Price sheet data must be exported and ingested **before** cost data for each billing period. This ensures the price join can populate missing ContractedCost and ListCost values. + +Recommended export order: +1. Price sheet (first) +2. Cost and usage (FOCUS) +3. Reservation details +4. Reservation recommendations +5. Reservation transactions + +### Step 3: Check Data Explorer tables + +If using FinOps hubs with Data Explorer, run this query to verify price data exists: + +```kusto +// Check if prices exist for your billing profile and time period +Prices_final_v1_2 +| where x_BillingProfileId == "YOUR_BILLING_PROFILE_ID" // Replace with your actual billing profile ID +| where x_EffectivePeriodStart >= datetime(2025-08-01) and x_EffectivePeriodStart < datetime(2025-09-01) +| where x_SkuPriceType == 'Consumption' +| summarize PriceCount = count(), SampleMeters = make_set(x_SkuMeterId, 10) by x_BillingProfileId, Month = substring(x_EffectivePeriodStart, 0, 7) +``` + +Expected results: +- Should return rows for your billing profile ID +- Should show prices for the months you're analyzing +- Should show multiple meters with Consumption price type + +If this query returns no results, your price sheet data was not exported or ingested correctly. + +
+ +## Diagnose price join failures + +Even when price data exists, the join may fail due to mismatched keys. Run this diagnostic query to identify the issue: + +```kusto +// Find reservation costs that are missing prices +let reservationCosts = Costs_final_v1_2 +| where BillingPeriodStart >= datetime(2025-08-01) and BillingPeriodStart < datetime(2025-09-01) +| where CommitmentDiscountType == "Reservation" +| where ChargeCategory == "Usage" +| extend CostLookupKey = tolower(strcat(x_BillingProfileId, substring(ChargePeriodStart, 0, 7), x_SkuMeterId, x_SkuOfferId)) +| summarize + CostRows = count(), + SampleCostLookupKey = any(CostLookupKey), + MissingPrices = countif(x_SourceChanges contains "MissingContractedCost" or x_SourceChanges contains "MissingListCost") + by x_BillingProfileId, x_SkuMeterId, x_SkuOfferId, Month = substring(ChargePeriodStart, 0, 7); + +// Check if matching prices exist +let prices = Prices_final_v1_2 +| where x_SkuPriceType == 'Consumption' +| extend PriceLookupKey = tolower(strcat(x_BillingProfileId, substring(x_EffectivePeriodStart, 0, 7), x_SkuMeterId, x_SkuOfferId)) +| summarize + PriceRows = count(), + SamplePriceLookupKey = any(PriceLookupKey), + SampleListPrice = any(ListUnitPrice), + SampleContractedPrice = any(ContractedUnitPrice) + by x_BillingProfileId, x_SkuMeterId, x_SkuOfferId, Month = substring(x_EffectivePeriodStart, 0, 7); + +// Compare +reservationCosts +| join kind=leftouter (prices) on x_BillingProfileId, x_SkuMeterId, x_SkuOfferId, Month +| project + x_BillingProfileId, + x_SkuMeterId, + x_SkuOfferId, + Month, + CostRows, + MissingPrices, + PriceRows, + HasMatchingPrice = isnotnull(PriceRows), + SampleCostLookupKey, + SamplePriceLookupKey, + SampleListPrice, + SampleContractedPrice +| order by HasMatchingPrice asc, MissingPrices desc +``` + +This query shows: +- Which reservation meters have cost data +- Whether matching prices exist for those meters +- How many rows are missing prices +- Sample lookup keys for debugging + +Common issues identified by this query: +- **HasMatchingPrice = false**: Price sheet is missing prices for these meters +- **PriceRows = 0**: No prices exist for this meter/month combination +- **SampleListPrice or SampleContractedPrice = 0**: Prices exist but are zero + +
+ +## Resolution steps + +### Issue: No price sheet exports + +**Solution**: Create and run price sheet exports + +1. Navigate to the appropriate scope (billing profile for MCA, billing account for EA) +2. Create a new export with: + - Dataset: Price sheet + - Frequency: Monthly export of last month's costs + - Format: CSV or Parquet +3. Manually run the export for historical months using "Export selected dates" +4. Wait for Data Explorer ingestion pipeline to complete + +### Issue: Price exports exist but use wrong scope + +**Solution**: Create exports at the correct scope + +For MCA accounts, price sheet exports must use the billing profile scope: +- ✅ Correct: `/providers/Microsoft.Billing/billingAccounts/{id}/billingProfiles/{id}` +- ❌ Incorrect: `/providers/Microsoft.Billing/billingAccounts/{id}` + +Delete the incorrect export and create a new one at the billing profile level. + +### Issue: Prices exported after costs + +**Solution**: Re-export data in the correct order + +1. Export price sheet for the affected months +2. Wait for price ingestion to complete +3. Re-export cost data for the same months +4. Wait for cost ingestion to complete + +The cost ingestion will now join with the available price data. + +### Issue: Prices missing for specific meters + +**Solution**: Verify meter eligibility and pricing + +Some meters may not have Consumption-type prices in the price sheet: +- Marketplace services (priced by third-party vendors) +- Services with custom pricing agreements +- Preview or private offer services + +For these scenarios, ContractedCost and ListCost will remain zero, which is expected. + +
+ +## Verify the fix + +After implementing the resolution steps, verify that savings are now calculated correctly: + +```kusto +// Verify reservation savings are calculated +Costs_final_v1_2 +| where BillingPeriodStart >= datetime(2025-08-01) and BillingPeriodStart < datetime(2025-09-01) +| where CommitmentDiscountType == "Reservation" +| where ChargeCategory == "Usage" +| summarize + TotalRows = count(), + RowsWithSavings = countif(x_CommitmentDiscountSavings > 0), + RowsMissingPrices = countif(x_SourceChanges contains "MissingContractedCost"), + AvgEffectiveCost = avg(EffectiveCost), + AvgContractedCost = avg(ContractedCost), + AvgListCost = avg(ListCost), + TotalSavings = sum(x_CommitmentDiscountSavings) +``` + +Expected results after fix: +- RowsWithSavings > 0 (showing actual savings) +- RowsMissingPrices = 0 (no missing price errors) +- AvgContractedCost > AvgEffectiveCost (showing the discount) +- TotalSavings > 0 (positive savings amount) + +
+ +## Related content + +- [Understanding savings calculations](./savings-calculations.md) +- [FOCUS conformance summary](https://learn.microsoft.com/cloud-computing/finops/focus/conformance-summary) +- [Configure scopes for FinOps hubs](./configure-scopes.md) +- [Deploy FinOps hubs](./deploy.md) +- [Troubleshoot common FinOps toolkit errors](../help/errors.md) + +
From ee819aeaf60722d1ad927b0af67c432f2038374b Mon Sep 17 00:00:00 2001 From: msbrett Date: Sat, 11 Oct 2025 13:16:38 -0700 Subject: [PATCH 3/3] fix: correct documentation author fields to msbrett/brettwil --- .../toolkit/hubs/troubleshooting-reservation-savings.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs-mslearn/toolkit/hubs/troubleshooting-reservation-savings.md b/docs-mslearn/toolkit/hubs/troubleshooting-reservation-savings.md index df8af24cf..4e0283cf6 100644 --- a/docs-mslearn/toolkit/hubs/troubleshooting-reservation-savings.md +++ b/docs-mslearn/toolkit/hubs/troubleshooting-reservation-savings.md @@ -1,8 +1,8 @@ --- title: Troubleshooting reservation savings calculations description: Learn how to diagnose and fix issues with reservation savings showing as zero in FinOps toolkit reports. -author: flanakin -ms.author: micflan +author: msbrett +ms.author: brettwil ms.date: 10/10/2025 ms.topic: troubleshooting-article ms.service: finops