Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
155 commits
Select commit Hold shift + click to select a range
3c89c43
feat: add Start-FinOpsMultitool cmdlet for interactive FinOps scanner…
May 19, 2026
1a63159
feat(multitool): cost access warnings, tag query fixes, MG hierarchy …
May 27, 2026
8db8eaa
perf(multitool): optimize tag cost query pacing and filter low-covera…
May 27, 2026
981e4c9
perf: parallel per-sub cost queries in Get-CostByTag
May 27, 2026
d3d7c1f
fix: filter Azure system tags in Get-CostByTag
May 27, 2026
a5fd3e3
fix: dynamic tag cap, remove Contact from system filter
May 27, 2026
54129e2
Add TUI, Hub data routing, and display fixes for FinOps Multitool
May 28, 2026
35790a6
Add TUI README
May 28, 2026
9455685
Add contextual FinOps guidance after each scan result
May 28, 2026
e65b62d
Add tenant picker to TUI for multi-tenant support
May 28, 2026
f09870d
Auto-invoke when script is run directly
May 28, 2026
c735364
Severity-colored guidance with FinOps education context
May 28, 2026
a5e4190
Enrich Hub cost data with live forecast from Cost Management API
May 28, 2026
c976654
Colorize dollar amounts green, budget rows by risk severity
May 28, 2026
1fbc9c0
Fix variable collision: forecast total was overwriting scan count
May 28, 2026
92d17d9
Fix forecast: show full-month projection (actual + remaining forecast)
May 28, 2026
caf4cb2
Fix tag coverage: query ARG for true total/untagged counts when using…
May 28, 2026
62e27b8
Remove Hub coverage caveat from tag guidance, lower resource cost thr…
May 28, 2026
e730754
Fix Cost by Tag guidance: use max untagged cost per tag instead of su…
May 28, 2026
74127a7
Add permission context and diagnostics for missing scan data
May 28, 2026
af92edf
Update TUI README with multi-tenant, guidance, permissions, and Hub e…
May 28, 2026
5215c38
Add styled HTML report export, pre-populated export path, enhanced su…
May 28, 2026
a8654bf
Add MCP server exposing all scan modules as AI-callable tools
May 28, 2026
0d2c2d5
feat(agent-skills): add FinOps multitool skill ecosystem
Jun 1, 2026
0be59a2
Add cost data source routing to FinOps multitool
Jun 1, 2026
3febeda
Fix: throttle-resilient MG cost-scope resolution in FinOps Multitool
Jun 2, 2026
53f9b86
perf(cost-trend): single grouped MG query + single-sub fast path
Jun 2, 2026
f57b4a9
perf(savings): MG query grouped by SubscriptionId + single-sub fast path
Jun 2, 2026
10fc88b
feat(finops-multitool): friendly rotating throttle messages, Budget H…
Jun 2, 2026
cb593b3
feat(finops-multitool): add carbon, unit economics, and legacy resour…
Jun 3, 2026
4f4fee9
fix(finops-multitool): use KQL verbatim string for legacy VM regex
Jun 3, 2026
26d8c1e
feat(finops-multitool): derive AI workload KPIs from FinOps Hub expor…
Jun 3, 2026
9b4e761
Show a single status message while waiting out rate limits
Jun 3, 2026
c779195
Port ResourceId-based cost-by-tag engine to FinOps Multitool
Jun 3, 2026
285239d
Quietly handle management group access denial in hierarchy load
Jun 3, 2026
4802b27
fix(finops-multitool): correct MACC consumption reading (consumed = o…
Jun 4, 2026
e8b1f12
feat(finops-multitool): permission-aware commitment & reservation sca…
Jun 4, 2026
9ee96a4
Update FinOps Multitool
Jun 4, 2026
ad4d9b6
Update FinOps Multitool
Jun 5, 2026
94d31ce
Update FinOps Multitool
Jun 5, 2026
a409555
Update FinOps Multitool
Jun 5, 2026
3c8eaa1
Update FinOps Multitool
Jun 5, 2026
7d6b5e3
Update FinOps Multitool
Jun 6, 2026
78c90ac
Update FinOps Multitool
Jun 6, 2026
423735c
Update FinOps Multitool
Jun 6, 2026
d4ee137
Update FinOps Multitool
Jun 7, 2026
83af32c
Update FinOps Multitool
Jun 7, 2026
6263227
Update FinOps Multitool
Jun 7, 2026
b7f7867
Update FinOps Multitool
Jun 7, 2026
151a223
Update FinOps Multitool
Jun 7, 2026
687bfe9
Update FinOps Multitool
Jun 8, 2026
b550f15
Update FinOps Multitool
Jun 8, 2026
44c1118
Update FinOps Multitool
Jun 11, 2026
8290178
Update FinOps Multitool
Jun 11, 2026
3e6df7c
Update FinOps Multitool
Jun 11, 2026
ead35b3
Update FinOps Multitool
Jun 15, 2026
20474c0
Update FinOps Multitool
Jun 15, 2026
e58931b
Update FinOps Multitool
Jun 15, 2026
532b176
Update FinOps Multitool
Jun 15, 2026
1ef543e
Update FinOps Multitool
Jun 15, 2026
3ba8107
Update FinOps Multitool
Jun 18, 2026
ca47281
Update FinOps Multitool
Jun 19, 2026
ae181d7
Update FinOps Multitool
Jun 19, 2026
0540b94
Update FinOps Multitool
Jul 2, 2026
a13b7a0
Update FinOps Multitool
Jul 2, 2026
8c0dab8
Documentation for multitool
Aug 17, 2026
d551f10
Update FinOps multitool
Aug 17, 2026
959ac15
Update FinOps multitool
Aug 18, 2026
a3ad502
Update FinOps multitool
Aug 18, 2026
59cf0f1
Update FinOps multitool
Aug 18, 2026
caf7d5d
Update FinOps multitool
Aug 20, 2026
19e0d85
Update FinOps multitool
Aug 20, 2026
39eaf0e
Update FinOps multitool
Aug 20, 2026
76910ec
Update FinOps multitool
Aug 20, 2026
5f5deaa
Update FinOps multitool
Aug 20, 2026
a8da2f4
fix(multitool): address review findings 1, 2, 3, 4, 6, 9
Aug 21, 2026
69add06
fix(multitool): correct realized savings math (review finding 7)
Aug 21, 2026
4974f36
fix(multitool): resolve explicit subscription scope before the picker…
Aug 21, 2026
08e945a
fix(multitool): project scan results to flat rows before CSV export (…
Aug 21, 2026
5e3a1b2
fix(multitool): support consoles without arrow-key input and add a no…
Aug 22, 2026
ddc72db
fix(multitool): remove the unreachable write path and correct the doc…
Aug 22, 2026
fa442bf
chore(multitool): remove a scratch test script committed by mistake
Aug 22, 2026
4e4615e
Merge origin/dev into feature/finops-multitool
Aug 22, 2026
a9f1690
docs(multitool): drop the production safety claim from the read-only …
Aug 22, 2026
6311634
fix(multitool): render scan guidance in the HTML report and scope the…
Aug 22, 2026
9a8fcf4
chore(multitool): remove three unreachable modules
Aug 22, 2026
b336ab0
fix(multitool): address the three issues from the 08/23 review
Aug 24, 2026
cdb9680
Merge remote-tracking branch 'origin/dev' into feature/finops-multitool
Aug 24, 2026
801c6ac
fix(multitool): correct tag allocation KPIs and measure coverage per …
Aug 24, 2026
6744f0a
chore(multitool): ignore the scan output folder
Aug 24, 2026
05c00ea
fix(multitool): default export path outside the working folder
Aug 24, 2026
6478445
Update FinOps Multitool
Aug 25, 2026
b92ac4f
Update FinOps Multitool
Aug 25, 2026
5588974
Update FinOps Multitool
Aug 25, 2026
e6ab672
Update FinOps Multitool
Aug 25, 2026
bd868d1
Update FinOps Multitool
Aug 25, 2026
3d14bbb
Update FinOps Multitool
Aug 25, 2026
fa57b3d
Update FinOps Multitool
Aug 25, 2026
5478627
Update FinOps Multitool
Aug 25, 2026
c10ee78
Update FinOps Multitool
Aug 25, 2026
3c3b3b7
Update FinOps Multitool
Aug 25, 2026
3d14393
Update FinOps Multitool
Aug 25, 2026
82558ef
Update FinOps Multitool
Aug 25, 2026
e5ab880
Update FinOps Multitool
Aug 25, 2026
736bb27
Update FinOps Multitool
Aug 25, 2026
346c405
Update FinOps Multitool
Aug 26, 2026
20c38f3
Update FinOps Multitool
Aug 26, 2026
f5c1d60
Update FinOps Multitool
Aug 26, 2026
fcb24ab
Update FinOps Multitool
Aug 26, 2026
4037641
Merge remote-tracking branch 'origin/dev' into feature/finops-multitool
Aug 26, 2026
bf2d27b
Update FinOps Multitool
Aug 27, 2026
859178d
Merge remote-tracking branch 'origin/dev' into feature/finops-multitool
Sep 7, 2026
215001f
Update FinOps Multitool
Sep 7, 2026
4c5ec11
FinOps Multitool Update - Sep 07-08 Review findings
Sep 9, 2026
ab1c349
FinOps Multitool Update - Sep 07-08 Review findings
Sep 9, 2026
cc458b4
FinOps Multitool Update - Sep 07-08 Review findings
Sep 9, 2026
595ca0a
FinOps Multitool Update - Sep 07-08 Review findings
Sep 9, 2026
940c027
FinOps Multitool Update - Sep 07-08 Review findings
Sep 9, 2026
97a7159
Update FinOps Multitool
Sep 9, 2026
45f6a4e
Update FinOps Multitool
Sep 9, 2026
4c99844
FinOps Multitool Update - Sep 09-14 Review findings
Sep 14, 2026
435e895
FinOps Multitool Update - Sep 14 Review findings
Sep 15, 2026
89fb634
FinOps Multitool Update - Sep 14 Review findings
Sep 15, 2026
582ff2f
FinOps Multitool Update - Sep 14 Review findings
Sep 15, 2026
93b0339
FinOps Multitool Update - Sep 14 Review findings
Sep 15, 2026
478ab89
Documentation update
Sep 15, 2026
8ce188f
Update documentation
Sep 15, 2026
1d90c50
FinOps Multitool Update - Sep 15 Review findings
Sep 15, 2026
582aca3
FinOps Multitool Update - Sep 15 Review findings
Sep 15, 2026
250652f
Merge remote-tracking branch 'origin/dev' into feature/finops-multitool
Sep 15, 2026
018dba5
FinOps Multitool Update - Sep 15 Review findings
Sep 16, 2026
b37d3e9
FinOps Multitool Update - Sep 15 Review findings
Sep 16, 2026
8b1d68c
FinOps Multitool Update - Sep 16 Review findings
Sep 16, 2026
d3c99d1
FinOps Multitool Update - Sep 16 Review findings
Sep 16, 2026
1fe0cda
Documentation update
Sep 16, 2026
9fd8a4c
FinOps Multitool Update - Sep 16 Review findings
Sep 17, 2026
e11f860
chore: merge dev into finops-multitool
Sep 17, 2026
1af4f06
fix(multitool): FinOps Multitool Update - Sep 17 Review findings
Sep 17, 2026
787504a
fix(multitool): FinOps Multitool Update - Sep 17 Review findings
Sep 17, 2026
791de7d
fix(multitool): FinOps Multitool Update - Sep 17-18 Review findings
Sep 18, 2026
839276d
fix(multitool): auto-save reports and correct Hub output
Sep 19, 2026
dc5be03
fix(multitool): improve reports and policy coverage
Sep 20, 2026
7de42fc
fix(multitool): harden data handling
Sep 20, 2026
ed43aab
fix(multitool): honor budget history filters
Sep 20, 2026
87e03fa
fix(multitool): handle incomplete scan results
Sep 21, 2026
ae36741
fix(multitool): FinOps Multitool Update - Sep 23-24 Review findings
Sep 24, 2026
f11f861
fix(ci): scope multitool temporary paths to test steps
Sep 24, 2026
a1b3537
feat(multitool): explain KPI values and scan thresholds
Sep 24, 2026
fe9ddf1
fix(multitool): FinOps Multitool Update - Sep 30-Oct 01 Review findings
Oct 1, 2026
0962de3
feat(multitool): add Cost Management export source and apply Oct 1-2 …
Oct 2, 2026
2dc0e1b
Update FinOps Multitool
Oct 3, 2026
fae5ce0
Update FinOps Multitool
Oct 4, 2026
fba4d01
Update FinOps Multitool
Oct 4, 2026
0989e6d
Update FinOps Multitool
Oct 7, 2026
825d895
FinOps Multitool Update - Oct 07 Review findings
Oct 8, 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
4 changes: 2 additions & 2 deletions .build/BuildHelper/Build-PsModule.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ function Build-PsModule
$moduleName = 'FinOpsToolkit'
$moduleFullName = "$moduleName.psm1"
$modulePath = Join-Path -Path $rootPath -ChildPath "src/powershell/$moduleFullName"
$privatePath = Join-Path -Path $rootPath -ChildPath "src/powershell/private"
$publicPath = Join-Path -Path $rootPath -ChildPath "src/powershell/public"
$privatePath = Join-Path -Path $rootPath -ChildPath "src/powershell/Private"
$publicPath = Join-Path -Path $rootPath -ChildPath "src/powershell/Public"
$stringsPath = Join-Path -Path $rootPath -ChildPath 'src/powershell/en-US'
$releasePath = Join-Path -Path $rootPath -ChildPath "release/$moduleName/$baseVersion"
$manifestPath = Join-Path -Path $releasePath -ChildPath "$moduleName.psd1"
Expand Down
130 changes: 130 additions & 0 deletions .github/workflows/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ name: 'PowerShell Tests'
on:
pull_request:
paths:
- '.github/workflows/dev.yml'
- '.build/BuildHelper/**'
- '.build/BuildHelper.psm1'
- 'package.json'
- 'src/scripts/Get-Version.ps1'
- 'src/powershell/**'
# KQL sources are covered by unit tests (HubsKqlOperators.Tests.ps1)
- 'src/templates/finops-hub/**/*.kql'
Expand All @@ -11,6 +16,10 @@ on:
- 'src/templates/finops-hub/**/timeZones.bicep'
- 'docs/deploy/finops-hub-latest.json'
- 'docs/deploy/finops-hub-preview.json'

permissions:
contents: read

jobs:
run_pester_tests:
name: Pester
Expand All @@ -25,3 +34,124 @@ jobs:
- name: Run Tests
shell: pwsh
run: .build/start.ps1 -Task Test.PowerShell.All

multitool_native:
name: Multitool (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
env:
DOTNET_CLI_TELEMETRY_OPTOUT: '1'
DOTNET_NOLOGO: '1'
steps:
- name: Install and cache test dependencies
uses: potatoqualitee/psmodulecache@9e4b63833c22c1768d648e115a9c849afdda22a5 # v6.3
with:
modules-to-cache: Pester:6.0.0, Az.Accounts, Az.Resources, Az.ResourceGraph, Az.Storage
shell: pwsh
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Install SDK for native Parquet restore
if: runner.os == 'Linux'
uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5
with:
dotnet-version: '8.0.x'
- name: Run native multitool tests
shell: pwsh
env:
TMPDIR: ${{ runner.temp }}
run: |
$ErrorActionPreference = 'Stop'
Import-Module Pester -RequiredVersion 6.0.0 -Force
$names = @(
'AzGraphPagination', 'BudgetCoverage', 'CommitmentUtilizationDedupe'
'CostQueryPagination', 'CurrencyLabel', 'FOHubProvider', 'HubSizeProbe'
'MultitoolResultIntegrity', 'MultitoolSafety', 'ParquetPackageClient', 'PolicyEffect'
'RetryJitter', 'Start-FinOpsMultitool'
)
$paths = @($names | ForEach-Object {
Join-Path 'src/powershell/Tests/Unit' "$_.Tests.ps1"
})
foreach ($path in $paths) {
if (-not (Test-Path -LiteralPath $path -PathType Leaf)) { throw "Missing test: $path" }
}
$configuration = New-PesterConfiguration
$configuration.Run.Path = $paths
$configuration.Run.PassThru = $true
$configuration.Run.Exit = $false
$configuration.Output.Verbosity = 'Detailed'
$configuration.TestResult.Enabled = $true
$configuration.TestResult.OutputFormat = 'NUnitXml'
$configuration.TestResult.OutputPath = Join-Path $env:RUNNER_TEMP 'multitool-unit.xml'
$result = Invoke-Pester -Configuration $configuration
$result | Select-Object Result, TotalCount, PassedCount, FailedCount, SkippedCount, FailedContainersCount
@(
'## Multitool validation'
"Tested merge commit: $env:GITHUB_SHA"
"Host: $([Runtime.InteropServices.RuntimeInformation]::OSDescription); PowerShell: $($PSVersionTable.PSVersion)"
"Unit tests: $($result.Result); passed $($result.PassedCount), failed $($result.FailedCount), skipped $($result.SkippedCount)."
) | Add-Content -LiteralPath $env:GITHUB_STEP_SUMMARY
if ($null -eq $result -or $result.Result -ne 'Passed' -or
$result.TotalCount -le 0 -or $result.PassedCount -le 0 -or
$result.FailedCount -ne 0 -or $result.FailedContainersCount -ne 0 -or
$result.FailedBlocksCount -ne 0 -or $result.Containers.Count -ne $paths.Count) {
throw 'Native multitool tests failed or discovery was incomplete.'
}
exit 0
- name: Run package and real Parquet integration tests
if: ${{ !cancelled() }}
shell: pwsh
env:
TMPDIR: ${{ runner.temp }}
run: |
$ErrorActionPreference = 'Stop'
Import-Module Pester -RequiredVersion 6.0.0 -Force
$paths = @('src/powershell/Tests/Integration/MultitoolPackage.Tests.ps1')
$minimumPassed = 3
$parquetCoverage = 'Not run on macOS: NuGet signed-package verification is not supported. https://learn.microsoft.com/dotnet/core/tools/nuget-signed-package-verification#macos'
if (-not $IsMacOS) {
$paths += 'src/powershell/Tests/Integration/MultitoolParquet.Tests.ps1'
$minimumPassed += 2
$parquetCoverage = 'Real signed Parquet restore and cold/cached reads in separate processes.'
}
foreach ($path in $paths) {
if (-not (Test-Path -LiteralPath $path -PathType Leaf)) { throw "Missing test: $path" }
}
$configuration = New-PesterConfiguration
$configuration.Run.Path = $paths
$configuration.Run.PassThru = $true
$configuration.Run.Exit = $false
$configuration.Output.Verbosity = 'Detailed'
$configuration.TestResult.Enabled = $true
$configuration.TestResult.OutputFormat = 'NUnitXml'
$configuration.TestResult.OutputPath = Join-Path $env:RUNNER_TEMP 'multitool-integration.xml'
$result = Invoke-Pester -Configuration $configuration
$result | Select-Object Result, TotalCount, PassedCount, FailedCount, SkippedCount, FailedContainersCount
@(
"Integration tests: $($result.Result); passed $($result.PassedCount), failed $($result.FailedCount), skipped $($result.SkippedCount)."
'Checks: isolated module build and public launch; CSV, HTML, and text reports.'
"Parquet coverage: $parquetCoverage"
'Azure access is replaced with synthetic responses. Package downloads and signature verification require network access.'
) | Add-Content -LiteralPath $env:GITHUB_STEP_SUMMARY
if ($null -eq $result -or $result.Result -ne 'Passed' -or
$result.PassedCount -lt $minimumPassed -or $result.FailedCount -ne 0 -or
$result.SkippedCount -ne 0 -or $result.NotRunCount -ne 0 -or
$result.FailedContainersCount -ne 0 -or $result.FailedBlocksCount -ne 0 -or
$result.Containers.Count -ne $paths.Count) {
throw 'Multitool integration tests failed, were skipped, or discovery was incomplete.'
}
exit 0
- name: Upload multitool test evidence
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: multitool-tests-${{ matrix.os }}
path: |
${{ runner.temp }}/multitool-unit.xml
${{ runner.temp }}/multitool-integration.xml
if-no-files-found: error
retention-days: 14
12 changes: 12 additions & 0 deletions docs-mslearn/TOC.yml
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,12 @@
href: toolkit/alerts/finops-alerts-overview.md
- name: Configure alerts
href: toolkit/alerts/configure-finops-alerts.md
- name: FinOps multitool
items:
- name: Overview
href: toolkit/multitool/finops-multitool-overview.md
- name: Commands
href: toolkit/powershell/multitool/finops-multitool-commands.md
- name: Optimization engine
items:
- name: Overview
Expand Down Expand Up @@ -242,6 +248,12 @@
href: toolkit/powershell/cost/remove-finopscostexport.md
- name: Start-FinOpsCostExport
href: toolkit/powershell/cost/start-finopscostexport.md
- name: FinOps multitool
items:
- name: FinOps multitool commands
href: toolkit/powershell/multitool/finops-multitool-commands.md
- name: Start-FinOpsMultitool
href: toolkit/powershell/multitool/start-finopsmultitool.md
- name: FinOps hubs
items:
- name: FinOps hubs commands
Expand Down
56 changes: 55 additions & 1 deletion docs-mslearn/toolkit/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: FinOps toolkit changelog
description: Review the latest features and enhancements in the FinOps toolkit, including updates to FinOps hubs, Power BI reports, and more.
author: MSBrett
ms.author: brettwil
ms.date: 09/11/2026
ms.date: 10/07/2026
ms.topic: reference
ms.service: finops
ms.subservice: finops-toolkit
Expand Down Expand Up @@ -54,6 +54,60 @@ The following section lists features and enhancements that are currently in deve
- **Fixed**
- Made the idle application gateway and idle public IP query join kinds explicit so they no longer rely on the `innerunique` default ([#2225](https://github.com/microsoft/finops-toolkit/pull/2225)).

### [FinOps multitool](multitool/finops-multitool-overview.md)

- **Added**
- Added the FinOps multitool, which scans an Azure environment for cost optimization, governance, and FinOps insights through a PowerShell 7 terminal UI ([#2155](https://github.com/microsoft/finops-toolkit/pull/2155)).
- Included 30 read-only scan modules, with 26 available in the menu, covering orphaned resources, idle VMs, storage tier advice, Azure Hybrid Benefit, tag and policy inventory and recommendations, cost data, cost trend, cost by tag, resource costs, reservation advice, commitment utilization, estimated savings, budget status and history, anomaly alerts, Advisor recommendations, billing structure, and contract info.
- Added a companion set of agent skills that carry the investigation routing, the queries, and the interpretation rules so AI agents can run the same analysis through Azure CLI or an Azure MCP server.
- Added engine-side aggregation through the FinOps hub's Azure Data Explorer or Microsoft Fabric Kusto database for large environments, with a storage reader as a small-dataset fallback.
- Added a non-interactive mode for an already-authenticated pipeline or scheduled job, and automatic private CSV, HTML, and text reports. Consoles that can't render the arrow-key menus, such as PowerShell remoting sessions, fall back to numbered prompts.
- Added `-Accessible` to select numbered prompts without clearing the screen or repainting menus in cursor-capable terminals.
- Added a searchable KPI reference and in-report calculation details for cost shares, unit rates, VM and storage screening, and budget coverage and forecast availability.
- Added aggregate and per-subscription cost trend views with captured UTC periods, partial-month labels, and separate empty and unverified coverage states.
- Added sticky table headers, row numbers, sorting, resizable columns, and expanded views to the local HTML report without changing CSV data.
- Added an explicit ordinary Cost Management CSV export source without requiring a FinOps hub, with selected-scope coverage, reads limited to the chosen export folder, manifest-verified partitions, and no silent live-cost fallback. Discovery reports progress per scope and per storage account. If you choose exports from the menu and none can be verified, you're asked whether to use the Cost Management API instead. Interactive runs ask before scanning more than 100 storage accounts for exports.
- **Fixed**
- Fixed literal `\u000D` text appearing at the ends of terminal table rows on Windows ([#2155](https://github.com/microsoft/finops-toolkit/pull/2155)).
- Fixed policy recommendations failing when a complete inventory contains no policy assignments ([#2155](https://github.com/microsoft/finops-toolkit/pull/2155)).
- Fixed incomplete billing-scope discovery appearing as complete commitment utilization.
- Kept unreadable Hub tags unverified instead of counting them as missing tags.
- Rejected explicit empty scan lists instead of running the default scans.
- Fixed malformed budget records counting toward confirmed budget coverage.
- Preserved budget inventory failures and partial coverage in budget history reports.
- Rejected unsafe export blob paths and invalid fallback container names, and escaped control characters in storage-discovery diagnostics.
- Made failed and unreadable Kusto discovery visible without changing the selected tenant or subscriptions.
- Rejected malformed Kusto responses and partial query failures instead of returning incomplete or empty success results.
- Rejected empty authentication tokens before sending Kusto queries and retained detailed Kusto HTTP errors on PowerShell 7.
- Reported the macOS Parquet signature-verification limitation before invoking a package client or downloading packages.
- Fixed AI token totals using inconsistent account and deployment measurements, and kept same-named deployments in different accounts separate.
- Fixed commitment SKU and kind metadata, incomplete fallback pagination, and unavailable utilization appearing as measured zero.
- Clarified budget sampling and CPU units, distinguished storage lookup failures from missing permissions, and added private launcher help and read-only regression checks.
- Hardened CSV exports against formula prefixes after whitespace or invisible characters and unsafe column names, while preserving numeric credits.
- Fixed storage-backed Parquet imports returning empty values or misaligning costs when an export contains nested metadata.
- Fixed measured zero unit-cost KPIs appearing unavailable.
- Fixed automatic Hub discovery failures aborting scans, including when every probe fails, while preserving explicit source choices and tenant boundaries ([#2155](https://github.com/microsoft/finops-toolkit/pull/2155)).
- Fixed provider-discovery exceptions aborting detected-Hub scans and preserved the selected storage fallback through scan execution ([#2155](https://github.com/microsoft/finops-toolkit/pull/2155)).
- Required a Y or N answer before switching to the Cost Management API when a detected FinOps hub's storage can't be reached.
- Limited estimated savings and unit costs to the selected subscriptions, querying subscriptions individually when a management group doesn't contain every selected subscription.
- Kept orphaned-resource costs and resource forecasts in their billing currencies instead of combining or relabeling them. Resource costs from the Cost Management API now include `ForecastSource`, which shows whether each forecast comes from a Cost Management forecast or a month-to-date projection. Forecasts stay empty when they're unavailable or can't be split across resources, and a failed forecast request keeps actual resource costs and reports the gap as limited data.
- Rejected partitioned export runs without a valid manifest, and read only the newest snapshot of a legacy unpartitioned export instead of adding snapshots together.
- Kept unattributed FinOps hub charges with their own subscription, and kept tag values that differ only by case separate.
- Counted the recommended `ApplicationName` and `OpsTeam` tags toward cost allocation.
- Reported incomplete Advisor recommendations and unreadable storage capacity instead of presenting partial results as complete.
- Stopped Advisor cost and reservation recommendations from being counted twice when the Resource Graph read fails partway through and the per-subscription fallback runs.
- Kept every resource cost row from the per-subscription Cost Management fallback, including unattributed charges and resource IDs that differ only by case, instead of keeping only the last row for each ID.
- Reported the count and amount in each currency of export rows that have no subscription, such as purchases or refunds billed outside a subscription. These rows aren't included in subscription totals.
- Confirmed billing account ownership before reporting contract details, and counted only enabled anomaly alert rules as detection coverage.
- Fixed FinOps hub CSV fallback reads to match hub ingestion: each export run counts toward the month in its manifest, and only the latest FOCUS cost run with rows is kept for each export scope and month, instead of adding runs together or dropping other scopes. Exports for unselected subscriptions are skipped, a manifest that can't be verified stops the read only when it could affect the months being read, and CSV files that no readable manifest lists are reported. The fallback checks at most 2,000 export manifests, doesn't download manifests over 1 MB, and stops instead of counting a file that two runs list. Fixed agreement detection from subscription metadata when billing account details can't be read.
- Corrected agent skill guidance on billed and effective cost and on when tags appear in cost data, and replaced references to unavailable tools with the terminal UI data sources.
- Stopped unresolved explicit subscription lookups from searching other tenants or widening the scan scope ([#2155](https://github.com/microsoft/finops-toolkit/pull/2155)).
- Fixed policy-definition read failures appearing as complete scan results; warnings and reports now identify unread definitions while retaining assignments and valid compliance data ([#2335](https://github.com/microsoft/finops-toolkit/issues/2335)).
- Limited management-group cost discovery to 25 candidates, including the tenant root, while preserving pagination, selected-subscription fallback, and tenant-specific caching ([#2335](https://github.com/microsoft/finops-toolkit/issues/2335)).
- Fixed crowded HTML reports for large subscription selections with bounded columns, compact scope lists, expandable tag and policy details, and local table filtering and pagination without reducing CSV detail.
- Fixed missing resource-cost periods and unclear resource identities by retaining the requested UTC window, displaying resource and reservation-charge labels, and preserving full IDs in HTML and CSV.
- Added verified subscription and management-group display names to policy locations while retaining scope IDs when name lookup fails.

### [Power BI reports](power-bi/reports.md)

- **Changed**
Expand Down
Loading