What it is
A virtual machine that was correctly stopped (deallocated) weeks or months ago, whose attached Premium SSD or Standard SSD managed disks keep billing at their full tier price. The compute meter is at zero, which is the part everyone checks. The disks sit in the Reserved state (attached to a deallocated VM) and bill exactly as they did when the VM was busy. Because the VM is already off, converting those disks to a cheaper SKU, or dropping a Premium performance tier, is the rare Azure change with zero disruption: nothing is reading them.
Why it happens
Azure's billing model for a parked VM is correct but incomplete in the way people remember it. Deallocation releases the compute host, so the VM's hourly rate stops; this is the fix for the "Stopped but still billing" trap (AZF-0010), and teams rightly learn to do it. What nobody adds to the lesson is that managed disks bill on provisioned tier, not on I/O. A P30 attached to a VM that has been deallocated since March bills the same $135 a month it billed when the VM was serving production.
The disks are easy to miss because they are not orphaned. Unattached-disk reports (AZF-0003) skip them, since managedBy still points at the VM and diskState is Reserved, not Unattached. The portal's VM list shows "Stopped (deallocated)" and a reassuring $0 compute line, and the disk costs land in a different line of the invoice under Storage. The VM is kept "just in case" (a decommissioned app, a seasonal workload, a DR target, a developer's abandoned test box), and "just in case" stretches into quarters.
The conversion rules are what make this the easiest saving on the list. A disk's SKU can change between Premium SSD, Standard SSD, and Standard HDD only while the attached VM is deallocated, which normally means scheduling downtime. Here the downtime already happened. The change is instantaneous, limited to twice per day per disk, and reversible: convert back to Premium before starting the VM if it needs the performance. Shared disks (maxShares > 1) are the exception, because another VM may still be using them. And note the one place Standard HDD no longer belongs: Microsoft retires Standard HDD as an OS disk type on 8 September 2028, so send the OS disk to Standard SSD and reserve HDD for data disks.
What it costs / blast radius
At list price in East US (LRS), a deallocated VM carrying a P30 OS disk and two P30 data disks bills about 3 × $135.17 = $405/month in disks alone, roughly $4,870/year for a machine doing nothing. The same three disks on Standard SSD E30 (~$76.80 each) bill about $230/month. Keep the OS disk on E30 and move the two data disks to Standard HDD S30 (~$40.96 each) and the total is about $159/month, a saving near $246/month. There are no transaction charges to subtract while the VM is deallocated, because the disks perform no operations. (Azure list prices; your contract and region may differ. A VM deallocated for good should simply be deleted along with its disks, after a snapshot if the data matters; see AZF-0003 and AZF-0011.)
The pattern compounds: a deallocated VM rarely travels alone. Dev/test environments, decommissioned tiers, and DR shells tend to be parked a dozen VMs at a time, each with several Premium disks, and each one bills at full tier until somebody converts or deletes it.
See it
Resources
| where type =~ 'microsoft.compute/virtualmachines'
| where properties.extended.instanceView.powerState.code =~ 'PowerState/deallocated'
| project vmId = tolower(id), vmName = name
| join kind=inner (
Resources
| where type =~ 'microsoft.compute/disks'
| where properties.diskState =~ 'Reserved'
| where sku.name in~ ('Premium_LRS', 'Premium_ZRS', 'StandardSSD_LRS', 'StandardSSD_ZRS')
| where toint(properties.maxShares) <= 1 or isnull(properties.maxShares)
| project vmId = tolower(managedBy), diskName = name, resourceGroup, subscriptionId,
sku = sku.name, tier = properties.tier, sizeGB = toint(properties.diskSizeGB),
isOsDisk = isnotempty(properties.osType)
) on vmId
| project vmName, diskName, isOsDisk, sku, tier, sizeGB, resourceGroup, subscriptionId
| order by vmName asc# Confirm the VM really is deallocated (not merely Stopped; see AZF-0010).
az vm get-instance-view --name vm-archive-01 --resource-group rg-prod \
--query "instanceView.statuses[?starts_with(code,'PowerState')].code" -o tsv
# PowerState/deallocated
# OS disk -> Standard SSD (Standard HDD OS disks retire 8 Sep 2028).
az disk update --name vm-archive-01_OsDisk --resource-group rg-prod \
--sku StandardSSD_LRS
# Data disks -> Standard HDD while parked. Conversions are instantaneous;
# limit is two SKU changes per disk per day.
az disk update --name vm-archive-01-data01 --resource-group rg-prod --sku Standard_LRS
az disk update --name vm-archive-01-data02 --resource-group rg-prod --sku Standard_LRS
# Need it back at full speed later? Convert first, then start.
# az disk update --name vm-archive-01-data01 --resource-group rg-prod --sku Premium_LRS
# az vm start --name vm-archive-01 --resource-group rg-prod
# Parked for good? Snapshot what matters and delete the VM and its disks instead.How StratoLens helps
StratoLens flags the disks still billing under deallocated VMs, with how long each VM has been parked and what the disks would cost on Standard SSD or Standard HDD, automatically and continuously across every subscription, inside your own tenant. A VM parked "for a couple of weeks" shows up with a dollar figure the moment the weeks turn into months.