Cloud costScenario based on common patterns

The Azure bill nobody could explain

Spend crept up for a year and nobody owned it. Tracing it resource by resource turned up idle VMs, orphaned disks and dev running all weekend.

problem

Monthly Azure spend kept rising with no new features to explain it.

approach

Read-only access, 30 days of cost per resource, utilisation and Advisor data.

outcome

About a third of spend was waste, removed in a four-week plan.

stack

Cost Management, Resource Graph, Advisor

Reading 30 days of billing data.
audit.loglive
Numbers from the public sample report of a fictional company.

The scenario

A small SaaS team on Azure watched its bill climb month after month. Nobody had changed much, so nobody could say why.

The subscription had grown the way most do: a few people with Contributor access, test environments created for a sprint and never removed, and a production setup sized for a launch that never got the traffic it was planned for.

Symptoms

  • Spend up steadily for a year, with no matching growth in usage.
  • The cost view grouped by service showed “Virtual Machines” and “Storage” as the biggest lines, which explains nothing.
  • No tags, so no way to tell production from leftovers.

How it was found

The fix starts with cost per resource, not per service. Joining 30 days of actual cost to the resource inventory shows exactly what each thing costs. Then a handful of Resource Graph queries find the usual suspects.

kusto
// disks left behind after their VM was deleted
Resources
| where type =~ 'microsoft.compute/disks'
| where properties.diskState =~ 'Unattached'
| project name, resourceGroup, sku = tostring(sku.name), sizeGb = toint(properties.diskSizeGB)

The same approach finds VMs that are stopped but still allocated, public IPs attached to nothing, App Service plans with no apps, and non-production VMs that run 24 hours a day. CPU metrics over 14 days show which running machines are far bigger than they need to be.

What fixed it

  1. Week 1: deallocate stopped VMs, delete unattached disks, idle IPs and empty plans.
  2. Week 2: confirm old snapshots and parked VMs with their owners, then remove them.
  3. Weeks 2 to 3: auto-shutdown for dev and test, resize oversized VMs, move non-prod disks off Premium.
  4. Week 4: buy reservations for what’s left, sized on the smaller footprint.
bash
# stop paying for a VM that's already stopped
az vm deallocate --ids "$VM_ID"
# shut dev VMs down at 7 pm every day
az vm auto-shutdown --ids "$VM_ID" --time 1900

Result

In the public sample report this scenario is based on, the plan cuts $3,066 a month from $9,094, about 34% of spend. Commitments are counted at 70% so the headline isn’t inflated.

Lessons

  • Look at cost per resource. Cost per service hides the answer.
  • Clean up before you commit. Reservations bought on a wasteful footprint lock the waste in.
  • Tag by environment and owner from day one, and the next audit takes an hour.

crafted.data

New deep-dives by email

One email when a new article is out: databases, cloud and AI, with the code. Unsubscribe in one click.

Email signups open soon. Until then, new articles are announced on Instagram.

Short videos too: crafted.data on Instagram