Test programme, September–October 2026
Tested on a real Azure estate, then checked against the bill
Test programme, September–October 2026
Before offering this to anyone, I built a real Azure estate, planted waste in it, and checked that the method finds exactly that waste, no more and no less, and prices it the way Azure actually bills it.
- 21 of 21 detection checks found exactly the planted waste; 6 of 6 look-alikes were left alone.
- A predicted saving matched Azure’s own bill to within 0.5%.
- Costs matched independently worked-out values, with one miss explained.
Prediction registered 2 October, before deletion; pass band ±10%
A saving in the report should show up on the bill
Prediction registered 2 October, before deletion; pass band ±10%
Four unattached disks billed every day in the test estate: three orphaned, and one Site Recovery look-alike. Before deleting two of the orphaned ones, I registered a prediction from Microsoft’s list prices: the four disks’ daily cost would fall by 49.8%. The other two were kept as controls.
- Predicted fall: 49.8% of the four disks’ daily cost. Billed fall: 50.0%. The billed saving came within 0.5% of the prediction, inside the ±10% band set in advance.
- The two control disks billed exactly the same before and after, so the drop came from the deletion.
- The disks were deleted with the exact command the report prints, which checked that the instructions work as written.
21 detection checks, 6 negative controls
What the test estate showed
21 detection checks, 6 negative controls
- 21 of 21 detection checks returned exactly the resources that were planted.
- 6 of 6 look-alikes, resources that resemble waste but aren’t, were correctly left out of the report.
- Of 13 priced lines: 7 within 0.5% of the independent value, 1 inside its wider band, 4 with nothing to save correctly priced at zero, and 1 miss, explained below.
Deployed from code; every resource billed by Azure
A real Azure subscription with planted waste
Deployed from code; every resource billed by Azure
Every resource was real and billed by Azure, so its cost data has the same shape as a client’s export.
| Resource | Why it’s waste |
|---|---|
| Managed disks left behind when their VM was removed | Billed every month, attached to nothing |
| A static public IP with nothing behind it | Billed by the hour, unused |
| A VM shut down from inside the operating system | Looks off but still bills compute; only deallocating stops the charge |
| Orphaned network interfaces, security groups and route tables | Free, but they hide real waste in a cluttered estate |
| An Application Gateway with an empty backend pool | Billed by the hour while serving nothing |
| A load balancer with rules but an empty backend pool | Billed per rule-hour; free in this lab’s first year, and flagged anyway |
| A SQL elastic pool with no databases | Billed by the day, holding nothing |
| Full-size disk snapshots | Billed per GB-month and easily forgotten |
| Windows VMs without Azure Hybrid Benefit set | Paying for the Windows licence in the hourly rate |
| A VM tagged as development, running around the clock | Paying for nights and weekends nobody uses |
| A Kubernetes (AKS) cluster with no Spot capacity | Flagged for review: interruptible work could run on Spot; no saving is claimed without knowing the workload |
A report that tells you to delete the wrong thing is worse than no report. So the estate also held look-alikes that a careless query would flag:
| Resource | Why it isn’t waste |
|---|---|
| A disk belonging to Azure Site Recovery | Looks unattached; deleting it breaks disaster recovery |
| A properly deallocated VM | Already not billing for compute |
| That VM’s operating-system disk | Still attached, not orphaned |
| A Linux VM | No Windows licence to save |
| A production VM running 24/7 | Expected to run all the time |
| A SQL Server Developer edition VM | The licence is already free, so there is nothing to save |
Expected values written separately, without seeing the assessment’s code or output
Costs checked against independent numbers
Expected values written separately, without seeing the assessment’s code or output
The expected monthly cost of every planted item was worked out separately, from Microsoft’s public price list and the estate’s inventory. The assessment’s figures were then compared line by line. Tolerance: ±5%, or a wider band where a free allowance makes the exact figure unknowable in advance.
| Item | Difference | Result |
|---|---|---|
| Windows licence on a production VM (no Hybrid Benefit) | −0.5% | Within tolerance |
| VM stopped but not deallocated | −0.5% | Within tolerance |
| Windows licence on that stopped VM¹ | 0 | Both zero |
| Unattached managed disks | −0.5% | Within tolerance |
| Load balancer with an empty pool (free tier in the lab) | 0 | Both zero |
| Unused static public IP | −0.5% | Within tolerance |
| Full-size snapshots² | −18.6% | Inside its wider band |
| Orphaned network interfaces, security groups, route tables (no charge) | 0 | Both zero |
| Development VM running around the clock | −0.5% | Within tolerance |
| Idle Application Gateway | +108% | Miss: estimate wrong, bill right |
| Empty SQL elastic pool | −0.5% | Within tolerance |
| Windows licence on the SQL Server VM | −0.5% | Within tolerance |
| Kubernetes pool without Spot (flagged; no saving claimed) | 0 | Both zero |
¹ Counted once, under the stopped VM above, so it isn’t claimed twice. ² A wider band because the free snapshot allowance can’t be known in advance. Exact amounts for every line are available on request.
- The steady −0.5% comes from the exchange rate on the bill versus the list price, not from the method.
- The Application Gateway was the one miss. The expected value assumed an idle gateway bills almost nothing beyond its base rate; Azure’s bill showed about three capacity units an hour with no traffic at all. The bill was right and the assumption was wrong, so the assessment follows the bill.
- The comparison also caught a real defect before any client saw it: snapshot costs came out about a third too low, because Azure’s month-to-date estimates over-apply free allowances. It was fixed, and the check now passes.
Microsoft FinOps Toolkit queries, run side by side with mine
Where the standard queries fall short
Microsoft FinOps Toolkit queries, run side by side with mine
- The idle load balancer query looks for load balancers with no backend pools. A load balancer whose pool has no members, the more common case, passes unnoticed. My query counts pool members and catches it.
- One published query, at the version tested, failed to run as written and needed a fix.
Stated in every report
Checks a short-lived lab can’t test
Stated in every report
Some checks need months of history or usage patterns that a short-lived test estate can’t produce:
- VM rightsizing from Azure Advisor
- Over-provisioned Azure SQL
- Storage on the wrong access tier
- Log Analytics and Application Insights ingestion
- Reservation and savings-plan coverage
Every finding in a report states how it was derived, so you can see which numbers rest on tested arithmetic and which are estimates.