A PowerShell WPF application that scans an Azure tenant and provides a single-pane-of-glass view of costs, tagging health, optimization opportunities, and FinOps maturity — organized around the three FinOps pillars: Understand, Quantify, and Optimize.
A lightweight, read‑only scanner intended for use alongside native Azure Cost Management capabilities and community FinOps tooling. It does not replace Cost Management, FinOps Hubs, or Power BI starter kits. Instead, it helps practitioners quickly identify gaps, validate assumptions, and accelerate conversations during FinOps workshops and scoping engagements.
Many Azure customers know they have a FinOps problem. They don't know where to start.
The standard path — deploy FinOps Hubs, configure FOCUS exports, build Power BI reports — is powerful, but it assumes time, Power BI expertise, and organizational buy-in that most teams don't have on day one. The result: customers stall before they get their first win.
The Azure FinOps Multitool was built to solve the cold start problem. Run one script, get a complete picture of where your tenant stands: what things cost, what's untagged, what's orphaned, what policies are missing, and what your next three moves should be. No infrastructure to deploy. No dashboards to build. Just answers.
It's designed as the on-ramp and is the tool that surfaces the first wins, and gives customers the foundation they need to grow into the full FinOps Toolkit and Cost Management capabilities Microsoft provides.
| Area | Data Source | What You See |
|---|---|---|
| Hierarchy | Management Groups API | Full MG tree with subscriptions, costs inline |
| Costs | Cost Management API (MG scope) | Actual month-to-date + forecast per subscription |
| Cost Trend | Cost Management API (6 months) | Bar chart showing monthly spend over the last 6 months |
| Cost Anomalies | Cost Trend + per-sub cost data | Subscriptions with 25%+ month-over-month cost changes |
| Resource Costs | Cost Management API (per sub) | Per-resource spend with type, RG, forecast, % of total — filtered by dynamic spend threshold (0.1% of total) |
| Contract | Billing Accounts API + ARM quotaId | EA, MCA, PAYGO, or CSP detection (quotaId fallback) |
| Tags | Azure Resource Graph | Every tag name/value in use, untagged resource count |
| Cost by Tag | Cost Management API | Spend broken down by CAF allocation tags (CostCenter, BusinessUnit, ApplicationName, etc.) plus auto-backfill of non-priority tags (up to 5 total); auto-fallback to last month |
| Tag Deploy | ARM Tags API (PATCH merge/delete) | Inline Add/Remove buttons per tag in the recommendations grid; deploy or remove tags from subscriptions or RGs |
| AHB | Azure Resource Graph | Windows VMs, SQL VMs, and SQL DBs missing Hybrid Benefit |
| Commitments | Reservation Summaries + Benefit Utilization API | RI and Savings Plan utilization %, underutilized commitments |
| Orphaned Resources | Azure Resource Graph (6 KQL queries) | Orphaned disks, unattached IPs/NICs, deallocated VMs, empty ASPs, old snapshots — with per-resource Cost (MTD) and Est. Annual waste |
| RI / SP | Advisor + Reservation Recs API | RI and SP recs with Actual (MTD), Forecast, and savings |
| Advisor | Azure Advisor (Cost category) | Rightsize, shutdown, delete, modernize recs with cost data |
| Budget Status | Consumption Budgets API | Budget vs actual per subscription, % used, risk level; deploy budgets with up to 4 custom thresholds (Actual/Forecasted) |
| Savings Realized | Cost Management (ActualCost + AmortizedCost) | Monthly savings from existing RIs, Savings Plans, and AHB |
| Scorecard | All of the above | Per-subscription health: cost, tags, optimizations, orphan savings, budget, trend |
| Tag Recs | Cloud Adoption Framework baseline | Gap analysis against 7 CAF allocation tags (CostCenter, BusinessUnit, ApplicationName, WorkloadName, OpsTeam, Criticality, DataClassification) with deployment location |
| Policy Inventory | ARM Policy Assignment API + Resource Graph | All effective policy and initiative assignments including MG-inherited, with compliance state |
| Policy Recs | CAF-aligned built-in policies & initiatives | Missing cost, tagging, security, and monitoring policies with deploy-from-GUI capability |
| Policy Deploy | ARM Policy Assignment API (PUT/DELETE) | Inline Deploy/Unassign buttons per policy in the recommendations grid |
| Policy Remediation | Policy Insights API (2021-10-01) | Trigger remediation tasks for DeployIfNotExists/Modify policy assignments |
| Budget Policy | ARM Policy Assignment API (PUT) | Deploy budget enforcement policies (AuditIfNotExists / DeployIfNotExists) at subscription or MG scope |
| Billing | Billing Accounts/Profiles API | Billing accounts, profiles, invoice sections, EA depts |
| Cost Allocation | Cost Management Allocation API | Existing cost allocation rules with source/target counts |
| Idle VMs | Azure Monitor Metrics API | Running VMs with <5% CPU and minimal network over 14 days — candidates Advisor missed |
| Storage Tiers | Azure Monitor Metrics API | Hot-tier storage accounts with low transaction activity — candidates for Cool/Archive |
| FinOps Guidance | All of the above | FinOps Maturity Score (0-100) with weighted category breakdown and actionable advice |
| Resources | Static (curated links) | Links to FinOps Foundation, Cost Management docs, orphaned resources workbook, toolkit |
-
Windows with PowerShell 5.1+ (WPF requires Windows)
-
Az PowerShell modules — install if missing:
Install-Module Az.Accounts, Az.Resources, Az.ResourceGraph, Az.CostManagement, Az.Advisor, Az.Billing -Scope CurrentUser
-
Azure RBAC — the signed-in account needs:
Scanning (read-only):
Role Scope Why Reader Tenant root or MG Read management groups + resources Cost Management Reader Tenant root or MG Query cost and forecast data Billing Reader Billing account Detect contract type (optional) Deploying tags and policies (optional write actions):
Role Scope Why Tag Contributor Subscription or RG Deploy tags via ARM Tags API Resource Policy Contributor Subscription or MG Deploy policy assignments (Audit/Deny) Owner (or Resource Policy Contributor + User Access Administrator) Subscription or MG Deploy policies with Modify or DeployIfNotExists effects (requires managed identity role assignment) If some roles are missing, the tool still works — it just skips the data it can't access and shows warnings. Write permissions are only needed if you click the deploy buttons on the Tags or Policy tabs.
-
Azure Government — fully supported. Use the Gov Tenant button to authenticate against
AzureUSGovernment; use the Commercial Tenant button for standardAzureCloudtenants.
# If downloaded from GitHub, unblock the files first:
Get-ChildItem -Path .\AzureFinOpsMultitool -Recurse | Unblock-File
# Set execution policy if needed (current user only):
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
cd AzureFinOpsMultitool
.\Start-FinOpsMultitool.ps1Alternative — run with bypass (no policy change required):
powershell -ExecutionPolicy Bypass -File .\Start-FinOpsMultitool.ps1"Not digitally signed" error? Windows marks downloaded files as blocked. Run
Unblock-Fileon the extracted folder, or use the-ExecutionPolicy Bypasscommand above, or right-click the.ps1file → Properties → check Unblock.
- The WPF window opens (no authentication yet)
- Click Commercial Tenant (or Gov Tenant for Azure Government) — a browser login opens; after sign-in, a tenant picker dialog lists all accessible tenants
- Select a tenant and click Select
- Click Scan — the tool runs through 23 data-collection stages with a progress bar
- When done, browse the tabs:
- Overview — cost summary cards, savings realized, budget status, subscription cost table (with orphan savings), top resources by spend, subscription scorecard
- Cost Analysis -- 6-month cost trend bar chart, cost anomaly flags (25%+ MoM change), pick a tag from the dropdown to see spend by tag value
- Tags -- tag inventory with unique values, coverage %, CAF compliance check, inline Add/Remove buttons per tag to deploy or remove tags directly on subscriptions/RGs
- Policy -- policy and initiative assignment inventory, compliance %, CAF-recommended policies and initiatives, clickable buttons to deploy policies with desired effect, remediation tasks for DINE/Modify policies
- Optimization -- commitment utilization (RI/SP %), orphaned/idle resources with cost data and estimated annual waste, idle VM detection (14-day metrics), storage tier advice, AHB gaps, RI recs, SP recs, Advisor recs
- Billing -- billing accounts, billing profiles (MCA), invoice sections, EA departments, cost allocation rules
- FinOps Guidance — pillar-by-pillar assessment with selectable/copyable references
- Resources — curated links to FinOps Framework, Cost Management, Azure Workbooks, orphaned resources workbook, and more
The Commercial Tenant / Gov Tenant buttons show a lock icon: unlocked while choosing, locked once connected.
- Click Export Scan Results to save as HTML, CSV, or Power BI template (.pbit)
- Click Commercial Tenant or Gov Tenant again any time to switch tenants without restarting
AzureFinOpsMultitool/
├── Start-FinOpsMultitool.ps1 # Entry point — loads modules, launches GUI
├── modules/
│ ├── Initialize-Scanner.ps1 # Auth, tenant picker, environment detection (Commercial/Gov)
│ ├── Get-TenantHierarchy.ps1 # Management group tree
│ ├── Get-ContractInfo.ps1 # Billing account / contract type
│ ├── Get-CostData.ps1 # Actual + forecast costs (MG scope → per-sub fallback)
│ ├── Get-CostTrend.ps1 # 6-month monthly cost trend data
│ ├── Get-ResourceCosts.ps1 # Per-resource cost breakdown with pagination
│ ├── Get-TagInventory.ps1 # Tag names, values, coverage
│ ├── Get-CostByTag.ps1 # Cost breakdown by tag (MG → per-sub fallback)
│ ├── Deploy-ResourceTag.ps1 # Deploy tags to subscriptions/RGs via ARM API
│ ├── Get-AHBOpportunities.ps1 # Azure Hybrid Benefit gaps
│ ├── Get-CommitmentUtilization.ps1 # RI & Savings Plan utilization data
│ ├── Get-OrphanedResources.ps1 # Orphaned disks, IPs, NICs, VMs, ASPs, snapshots
│ ├── Get-IdleVMs.ps1 # Idle & underutilized VM detection (14-day metrics)
│ ├── Get-StorageTierAdvice.ps1 # Storage tier optimization (Hot → Cool/Archive)
│ ├── Get-ReservationAdvice.ps1 # RI / SP recs (Resource Graph → REST fallback)
│ ├── Get-OptimizationAdvice.ps1 # Advisor cost optimizations (Resource Graph → REST fallback)
│ ├── Get-BudgetStatus.ps1 # Budget vs actual per subscription
│ ├── Get-SavingsRealized.ps1 # Savings from existing RIs, SPs, and AHB
│ ├── Get-TagRecommendations.ps1 # CAF tag compliance check
│ ├── Get-PolicyInventory.ps1 # Policy assignments + compliance (Resource Graph → per-sub fallback)
│ ├── Get-PolicyRecommendations.ps1 # 15 curated FinOps policies gap analysis
│ ├── Deploy-PolicyAssignment.ps1 # Deploy policy assignments via ARM REST API
│ └── Get-BillingStructure.ps1 # Billing accounts, profiles, invoice sections, cost allocation
├── gui/
│ ├── MainWindow.xaml # WPF layout (Azure-themed, virtualized grids, trend chart)
│ ├── app.ico # Azure cloud window icon
│ └── skeleton.pbit # Power BI template skeleton with pre-built report layout
└── README.md
Each module exports a single function and returns PSCustomObjects.
The main script dot-sources all modules and orchestrates the scan via
a DispatcherTimer so the UI updates between stages.
| Stage | Module | API Used | Time |
|---|---|---|---|
| 1 | Verify tenant context | Uses auth from Choose Tenant step | <1s |
| 2 | Get-TenantHierarchy | Get-AzManagementGroup -Expand -Recurse |
~3s |
| 3 | Get-ContractInfo | REST: /providers/Microsoft.Billing/... |
~1s |
| 4 | Get-CostData | REST: Cost Management Query (MG scope) | ~5s |
| 5 | Get-ResourceCosts | REST: Cost Management Query (per sub) | ~10s |
| 6 | Get-TagInventory | Search-AzGraph (cross-subscription) |
~3s |
| 7 | Get-CostByTag | REST: Cost Management Query + tag group | ~5s |
| 8 | Get-CostTrend | REST: Cost Management (Monthly, 6 months) | ~3s |
| 9 | Get-AHBOpportunities | Search-AzGraph (3 queries) |
~3s |
| 10 | Get-CommitmentUtilization | REST: Reservation Summaries + Benefit Util API | ~5s |
| 11 | Get-OrphanedResources | Search-AzGraph (6 KQL queries) |
~3s |
| 12 | Get-IdleVMs | REST: Azure Monitor Metrics (CPU + Network, 14-day) | ~5-15s |
| 13 | Get-StorageTierAdvice | REST: Azure Monitor Metrics (Transactions + Capacity, 30-day) | ~5-15s |
| 14 | Get-ReservationAdvice | Search-AzGraph (advisorresources) + Reservation Recs API |
~3s |
| 15 | Get-OptimizationAdvice | Search-AzGraph (advisorresources) |
~3s |
| 16 | Get-BudgetStatus | REST: Consumption Budgets API (per sub) | ~3s |
| 17 | Get-SavingsRealized | REST: Cost Management (ActualCost + AmortizedCost) + ARG; skipped if no commitments detected in stage 10 | ~5s |
| 18 | Get-TagRecommendations | Local comparison + tag location map | <1s |
| 19 | Get-PolicyInventory | ARM REST API (all effective) + Resource Graph compliance | ~3s |
| 20 | Get-PolicyRecommendations | Local comparison (no API call) | <1s |
| 21 | Get-BillingStructure | REST: Billing Accounts/Profiles/Sections | ~3s |
Performance Note: The tool is adaptive — it detects tenant size and optimizes accordingly. Cost queries try management-group scope first (one call for all subs). Policy inventory uses Resource Graph (
policyresourcestable) for a single cross-tenant query. Advisor recommendations useadvisorresourceswith automatic per-sub REST fallback. For large tenants (50+ subs) where MG-scope fails, the tool uses sample-first strategies: test 3 subs before iterating 300+, skip forecast queries, and short-circuit when data is absent. Budget queries sample 10 subs first — if no budgets exist, remaining subs are skipped. The UI stays responsive during long iterations via inline status updates. Small tenant (1-10 subs): under 1 minute. Large tenant (300+ subs): 2-5 minutes with MG-scope, 5-10 minutes with per-sub fallback.
| Decision | Why |
|---|---|
| MG-scope cost queries with per-sub fallback | One call covers all subs; auto-fallback if RBAC blocks MG scope |
| MonthToDate → TheLastMonth fallback | Cost-by-tag auto-retries with last month if current month has no data (early month) |
| Column-aware API parsing | Reads column headers from Cost Management responses instead of hardcoded indices |
| ARM ResourceId cost lookup | Constructs full ARM resource path for cost matching in optimization grids |
| Lock icon on Choose Tenant | Visual feedback — unlocked while picking, locked once connected |
| Resource Graph for Advisor | Single cross-tenant query via advisorresources table; REST fallback if ARG unavailable |
| Resource Graph for tags + AHB | Cross-subscription, fast (KQL), paginated |
| API pagination (nextLink) | Cost Management caps results at ~5000 rows; pagination captures all resources |
| List<T> instead of array += | O(n) vs O(n^2) — critical for tenants with 1000s of resources |
| DataGrid virtualization | WPF only renders visible rows; prevents UI freeze on large datasets |
| Dynamic resource spend threshold | Resource grid includes all resources above 0.1% of total actual spend (min $1); avoids arbitrary caps while filtering noise |
| DispatcherTimer staged loading | UI stays responsive between data-collection stages |
| Modular .ps1 files | Each module testable independently; clean path to C# conversion |
| CAF tag baseline | Comparison against Microsoft's own recommended tags, not arbitrary |
| Fallback on every module | If an API fails (RBAC, throttling), the scan continues gracefully |
| UTF-8 BOM on all .ps1 files | Ensures PS 5.1 reads special characters correctly |
| 100% ASCII XAML | All non-ASCII replaced with XML entities to avoid WPF encoding issues |
| Explicit Azure environment buttons | Separate Commercial Tenant / Gov Tenant buttons — no auto-probing of Gov endpoints |
| Separate tenant / Scan buttons | Auth happens once via Commercial Tenant or Gov Tenant; scan is repeatable without re-auth |
| WPF minimize during browser auth | MSAL browser login needs the foreground; scanner minimizes then restores |
| Tenant picker dialog | WPF ListBox shows all accessible tenants; supports 30+ tenants cleanly |
| LoginExperienceV2 suppressed | $env:AZURE_LOGIN_EXPERIENCE_V2=Off prevents Az.Accounts 12+ console subscription picker |
| Contract type quotaId fallback | Infers EA/MCA/PAYGO/Internal from ARM subscription quotaId when Billing API is inaccessible |
| 4-column optimization grids | Each recommendation shows Actual (MTD), Forecast, With-X savings, and Annual Savings |
| Pure WPF bar chart | Cost trend drawn with Canvas + Rectangles — no NuGet charting libraries needed |
| Idle VM detection via Azure Monitor | 14-day avg CPU + total network bytes per running VM; idle (<5% CPU, <1MB/day net) and underutilized (<10%) classification |
| Storage tier advice via Azure Monitor | 30-day transaction count + blob capacity per hot-tier storage account; recommend Cool (<1000 tx) or Archive (<100 tx) |
| Resources tab (static links) | Curated FinOps links populated at dashboard build time — no API calls needed; includes orphaned resources workbook |
| Tag removal retry with backoff | 500 errors during tag removal trigger up to 3 retries with 1s/2s exponential backoff |
| Tag deployment via ARM Tags API | PATCH merge to add/update; PATCH delete to remove tags; preserves other existing tags |
| Tag removal via ARM Tags API | Delete operation removes a single tag by name without affecting other tags |
| Policy deployment via ARM PUT | Deploy recommended FinOps policies with user-selected effect (Audit/Deny/etc.) |
| Policy unassignment via ARM DELETE | Remove policy assignments directly from the recommendations grid |
| Inline action buttons in grids | Tag and policy recommendation grids use programmatic TemplateColumns with Add/Remove and Deploy/Unassign buttons per row |
| Policy remediation via REST | Trigger remediation tasks for DeployIfNotExists/Modify assignments via Policy Insights API (2021-10-01) |
| Budget policy deployment | Deploy budget enforcement policies (AuditIfNotExists / DeployIfNotExists) at subscription or MG scope from the Budgets tab |
| User-defined budget thresholds | Budget deploy supports up to 4 threshold entries with Actual/Forecasted type selectors |
| Dev/Test subscription inclusion | Dev/Test subscriptions are no longer skipped during scanning |
| Tag inventory includes resourcecontainers | Resource Graph tag queries union the resourcecontainers table to capture subscription and resource-group-level tags |
| Orphan savings in scorecard | Subscription scorecard and cost table include an Orphan Savings column showing estimated annual waste from orphaned resources |
| Lazy scope loading for tag deploy | Subscription/RG list fetched on first tag deploy click, cached for session |
| Background runspace for tag deploy | Tag deployment runs in a background runspace with 30s timeout; UI stays responsive via DispatcherFrame polling |
| MG-scope fail-once flag | First cost module that gets 401/403 at MG scope sets a shared flag; all subsequent modules skip to per-sub instantly instead of retrying |
| 429 throttle retry | Invoke-AzRestMethodWithRetry wraps all REST calls with automatic retry on HTTP 429, exponential backoff, and DispatcherFrame UI-responsive wait |
| Resource Graph safe wrapper | Search-AzGraphSafe runs all Resource Graph calls in background runspaces with 60s timeout, 429 retry, and JSON round-trip to preserve nested properties |
| Tag compliance location column | Tag recommendations grid shows where each present tag is deployed (Subscription / Resource Group) |
| MG-inherited policy discovery | ARM REST API GET policyAssignments returns all effective policies including those inherited from management groups and tenant root |
| Billing structure discovery | Queries billing accounts, profiles, invoice sections, EA departments, and cost allocation rules |
| Commitment utilization tracking | Queries Reservation Summaries + Benefit Utilization APIs to show RI/SP usage % |
| Orphaned resource detection | 6 Resource Graph KQL queries find waste: orphaned disks, unattached IPs/NICs, deallocated VMs, empty ASPs, old snapshots |
| Budget vs actual monitoring | Per-subscription budget query with risk levels: Over Budget, At Risk, Watch, On Track |
| Savings realized calculation | Compares ActualCost vs AmortizedCost by charge type to quantify RI/SP/AHB savings |
| Orphaned resource cost enrichment | Each orphan shows Cost (MTD) and Est. Annual waste from the shared resource cost map; summary totals estimated waste across all costed resources |
| Shared resource cost map | Build-ResourceCostMap builds a single ARM-path-keyed lookup shared by Optimization and Orphan sections — avoids duplicate work |
| CAF-aligned policy recommendations | Policy recs expanded to include Azure Security Benchmark v3, Secure Transfer, Diagnostic Settings, and Allowed Locations; distinguishes Policy vs Initiative assignments |
| Non-priority tag backfill | Cost-by-tag queries the priority list first, then backfills discovered tags up to 5 total (skipping system prefixes like hidden-, aks-managed-) |
| CAF allocation tag alignment | Tag recommendations, cost-by-tag, and maturity scoring all use the same 7 CAF tags: CostCenter, BusinessUnit, ApplicationName, WorkloadName, OpsTeam, Criticality, DataClassification |
| Weighted tag scoring | Allocation score uses per-tag weights: CostCenter and BusinessUnit = 3 pts each, ApplicationName = 2 pts, remaining 4 tags = 1 pt each (12 pts total from tags out of 20) |
| Commitment-aware savings skip | Stage 15 (SavingsRealized) checks commitment data from stage 10; if no RIs or Savings Plans exist, all Cost Management savings queries are skipped (saves 2-120 API calls) |
| Cost anomaly flagging | Per-subscription MoM delta computation; flags 25%+ changes for investigation |
| Subscription scorecard | Composite per-sub view combining cost, tags, optimizations, orphans, budget, trend |
| Adaptive large-tenant scanning | Sample-first, cross-tag short-circuit, budget sampling, forecast skip for 50+ subs |
| Resource Graph for policy inventory | policyresources table replaces per-sub REST; MG-scope compliance in 1 call |
- Add a tag to recommendations: Edit
Get-TagRecommendations.ps1→$recommendedTagsarray (updateGet-CostByTag.ps1$targetTagsand the Allocation scoring inStart-FinOpsMultitool.ps1to match) - Change theme colors: Edit
gui/MainWindow.xaml→Window.Resourcesbrushes - Add a new data module: Create
modules/Get-YourData.ps1, dot-source inStart-FinOpsMultitool.ps1, add a scan stage
The current tool covers FinOps discovery and remediation — the first two capabilities in a larger vision.
The longer-term direction is an agentic FinOps layer: a set of autonomous agents that don't just surface findings but act on them continuously. Discovery, anomaly detection, remediation, optimization recommendations, and reporting — orchestrated as a team rather than a one-time scan.
The Azure FinOps Multitool is the foundation that makes that possible: a proven, field-tested data layer with direct write-back to Azure. The first step toward agents has already shipped — an MCP server exposes the scan modules as tools an AI agent can call — and a headless Azure Function runs the same scan unattended. Full multi-agent orchestration comes next.
-
Custom tag deployment— Deploy any user-defined tag from the Tags tab -
Scheduled/headless scan mode— runs unattended as an Azure Function companion (AzureFinOpsFunction) on a timer or HTTP trigger, sharing the same scan modules as the GUI -
Email report delivery— the headless Function emails the scan report on a daily schedule - PDF export with charts
- C# / WPF conversion (full async, MVVM architecture)
- [~] Agentic orchestration layer (anomaly agent, optimization agent, reporting agent) — first step shipped: an MCP server exposes the scan modules as tools so an AI agent can query cost, governance, and optimization data on demand; full multi-agent orchestration still to come
-
Power BI integration— auto-generates a.pbittemplate with 4-page dashboard (Cost Overview, Subscriptions, Optimization, Governance) styled after the FinOps toolkit reports
-
Firewalled-storage export creation—New-CostExportnow attaches a system-assigned managed identity +locationto the export so Cost Management can grant it Storage Blob Data Contributor and deliver to storage accounts behind a firewall (matching the Step 2 help text, which previously over-promised). It also registers theMicrosoft.CostManagementExportsresource provider on the storage account's subscription before creating the export (the portal does this automatically; an API caller must do it explicitly), resolves the storage region when the caller doesn't supply it, and returns clearer messaging about firewall/roleAssignments/writerequirements and realistic data-landing time (a few hours, not minutes) -
RI/SP recommendation de-duplication— Azure Advisor frequently emits several identical recommendation records that differ only by recommendation GUID (overlapping generation cycles), which made a single buy appear 3–6 times in the Reserved Instance grid and inflated the estimated-savings total.Get-ReservationAdvicenow captures thedisplaySKU,region, anddisplayQtyextended properties and collapses records on the meaningful tuple (subscription + resource type + term + SKU + region + qty + savings), keeping one row per distinct recommendation with aDuplicateCount. The RI and SP grids now surface SKU, Region, and Qty columns so each row is self-explanatory -
6-month trend multi-month read— the trend now re-reads each export with a 6-month lookback, gathering the newest run folder per billing month instead of reusing the single current-month dataset the cost/resource/tag tabs consume. Each month lives in its ownYYYYMMDD-YYYYMMDDfolder and is re-emitted daily, so the trend now renders every available month (e.g. Mar–Jun) rather than one. Live-query mode (and the headless Function, which already queries Cost Management for the monthly grain) were unaffected -
Export reader month-selection fix— the FinOps Hub / Cost Management export reader now selects the run folder whose date-range covers the current billing month instead of the one with the newest blob timestamp. Hubs re-emit the prior (closed) month daily, so picking "newest LastModified" could read last month's full-month total as if it were month-to-date (and then 10× it in the forecast). Selection is now driven by theYYYYMMDD-YYYYMMDDdate-range in the blob path, tie-broken by newest run -
MACC consumption accuracy fix— consumed/remaining now derived from the Consumption Lots API the same way as the Cost Management data factory sample (consumed =originalAmount−closedBalance); the Lots API never returned ausedAmount, so the prior calculation always reported 0 consumed / full remaining -
Budget vs. actual comparison per subscription— Budget Status module with risk levels -
Cost trend chart (last 6 months)— WPF Canvas bar chart with per-subscription filter -
Anomaly detection (spike alerts)— 25%+ MoM cost change flagging per subscription -
Azure Policy compliance overlay— Policy tab with inventory, compliance %, CAF policy and initiative recs, deploy from GUI -
Orphaned resource cost data— Per-resource Cost (MTD) and Est. Annual waste columns with total waste summary -
CAF policy alignment— Policy recommendations expanded to CAF-aligned policies and initiatives (Security Benchmark v3, Secure Transfer, Diagnostic Settings, Allowed Locations) -
Non-priority tag backfill— Cost-by-tag auto-discovers additional tags beyond the priority list (up to 5 total) -
CAF allocation tag alignment— Tag list, cost-by-tag, and scoring all aligned to 7 CAF allocation tags with weighted maturity scoring -
Commitment-aware savings skip— SavingsRealized skips Cost Management queries when no RIs/SPs exist (stage 10 data reuse) -
Separate Commercial / Gov tenant buttons— Explicit environment selection; Gov cloud is opt-in, no longer auto-probed -
User-defined budget thresholds— Deploy budgets with up to 4 custom thresholds (Actual/Forecasted type per threshold) -
Budget policy deployment— Deploy budget enforcement policies (AuditIfNotExists / DeployIfNotExists) from the Budgets tab -
Policy remediation tasks— Trigger remediation for DeployIfNotExists/Modify policy assignments from the Policy tab -
Orphan savings in scorecard— Subscription scorecard and cost table show estimated annual waste from orphaned resources -
Tag inventory includes subscription/RG-level tags— Resource Graph queries unionresourcecontainerstable -
Dev/Test subscription inclusion— Dev/Test subs are no longer excluded from scans -
Inline tag management— Add/Remove buttons directly in the tag recommendations grid -
Inline policy management— Deploy/Unassign buttons directly in the policy recommendations grid -
Idle VM detection— 14-day Azure Monitor metrics flag running VMs with <5% CPU and minimal network -
Storage tier optimization— Hot-tier storage accounts flagged for Cool/Archive migration -
Resources tab— Curated links to FinOps Framework, Azure Workbooks, orphaned resources workbook -
Tag Inventory Remove button— Delete any tag directly from the Tag Inventory grid -
Session action log in HTML report— Exported reports include tags deployed/removed and policies assigned/unassigned during the session
The Guidance tab calculates a 0-100 maturity score based on the FinOps Foundation Maturity Model and Microsoft Cloud Adoption Framework. The score is broken into five categories:
| Points | Criteria |
|---|---|
| 0-10 | Tag coverage (% of resources tagged, scaled) |
| 5 | Cost data retrieved from Cost Management API |
| 5 | 6-month cost trend data available |
| 5 | Resource-level cost breakdown available |
Uses per-tag weights so the tags that matter most for chargeback/showback carry more points:
| Points | Criteria |
|---|---|
| 3 | CostCenter tag present |
| 3 | BusinessUnit tag present |
| 2 | ApplicationName tag present |
| 1 | WorkloadName tag present |
| 1 | OpsTeam tag present |
| 1 | Criticality tag present |
| 1 | DataClassification tag present |
| 4 | Cost-by-tag data returns results |
| 4 | Azure Cost Allocation Rules configured |
Tag variations are recognized (e.g., cost-center, cc, bu, dept, application).
| Points | Criteria |
|---|---|
| 5 | At least one budget exists |
| 0-5 | Budget coverage across subscriptions (% scaled) |
| 5 | No subscriptions over budget (3 if some at-risk but none over) |
| Points | Criteria |
|---|---|
| 5 | RI/SP utilization >= 80% (3 if >= 60%, 2 if no commitments) |
| 5 | Savings realized from existing commitments |
| 0-5 | Low Advisor recommendation count (5 if zero, 3 if <= 3, 1 if <= 10) |
| 0-5 | Low orphaned resource count (5 if zero, 3 if <= 3, 1 if <= 10) |
| Points | Criteria |
|---|---|
| 5 | Has Azure Policy assignments |
| 0-5 | FinOps-recommended policies assigned (% scaled) |
| 5 | Policy compliance >= 80% (3 if >= 50%) |
| 5 | Management group hierarchy exists (2 if flat subs only) |
| Score | Grade |
|---|---|
| 85-100 | Excellent |
| 70-84 | Good |
| 50-69 | Developing |
| 30-49 | Foundational |
| 0-29 | Getting Started |
Zac Larsen — Personal project (not an official Microsoft product)
This tool queries only public Azure APIs (Cost Management, Resource Graph, Advisor, Billing) against your own Azure subscriptions. It reads subscription metadata (such as subscription IDs/names, regions, budgets, and usage) and writes results locally (console output and HTML/CSV exports); it does not transmit this data off your machine except as required to call Azure APIs.
- Issues & PRs: Welcome! Please do not include subscription IDs, tenant IDs, internal URLs, or any confidential information.
- Azure support: For Azure platform issues or outages, contact Azure Support — not this repository.
- Exported files: Review HTML/CSV exports before sharing externally — they may contain subscription IDs, region information, budgets, and usage details for your environment.
This project may access or process Azure Cost Management, Policy, Resource Graph, or Subscription metadata through Azure APIs.
Execution of this tool may initiate:
- Resource discovery
- Policy evaluation
- Cost data queries
- Tagging analysis
- Configuration inspection
Ensure that least‑privilege access is used when running this utility.
- FinOps Framework
- Azure FinOps Toolkit
- Cloud Adoption Framework — Tagging
- Azure Cost Management
- Azure Advisor
- Azure Hybrid Benefit
- Reservation Recommendations
This repository contains sample tooling developed by a Microsoft employee and is provided for informational and educational purposes only.
This is not an official Microsoft product, service, or supported offering.
This project is provided "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO:
- Production readiness
- Security hardening
- Tenant compatibility
- Governance alignment
- Cost optimization outcome guarantees
- Policy compliance assurance
Microsoft does not provide support for this project under any Microsoft support agreement, Premier/Unified Support plan, or Azure support contract.
No Microsoft service level agreements (SLAs), warranties, or product commitments apply to this repository or any derivative use of its contents.
Execution of this tool within an Azure tenant may result in configuration, cost visibility, tagging analysis, governance evaluation, or policy‑related outcomes depending on permissions granted.
Users are solely responsible for validating all scripts and automation prior to execution in production environments.