When you delete a VM in Azure, it doesn't automatically delete everything associated with it. Disks, network interfaces, and public IPs often get left behind - still billing you every month.
We call these "orphaned resources," and they're in almost every Azure environment we assess.
What Gets Orphaned?
Managed Disks - The most common culprit. Delete a VM and its OS disk and data disks remain, still incurring storage costs.
Network Interfaces - NICs persist after VM deletion. They're cheap individually but add up.
Public IP Addresses - These aren't free. A static public IP costs around £3/month whether it's attached to anything or not.
Network Security Groups - Often created per-VM and left behind.
Snapshots - Taken for backup purposes and forgotten about.
Unattached Premium SSDs - These are expensive. A P30 (1TB) costs ~£100/month whether it's attached or not.
Finding Orphaned Resources
Azure Portal
Disks:
- Go to Disks
- Add filter: "Disk state" = "Unattached"
- Review and delete
Public IPs:
- Go to Public IP addresses
- Add column: "Associated to"
- Look for blanks
NICs:
- Go to Network interfaces
- Add column: "Virtual machine"
- Look for blanks
Azure Resource Graph
More powerful for larger environments. Run these queries in Resource Graph Explorer:
Unattached Disks:
Resources
| where type == "microsoft.compute/disks"
| where properties.diskState == "Unattached"
| project name, resourceGroup, sku.name, properties.diskSizeGB, subscriptionId
Unassociated Public IPs:
Resources
| where type == "microsoft.network/publicipaddresses"
| where properties.ipConfiguration == ""
| project name, resourceGroup, subscriptionId
Orphaned NICs:
Resources
| where type == "microsoft.network/networkinterfaces"
| where isnull(properties.virtualMachine)
| project name, resourceGroup, subscriptionId
Azure Advisor
Azure Advisor now flags some orphaned resources under Cost recommendations. Check there as a starting point, but it doesn't catch everything.
Real World Example
A recent client had:
- 47 unattached managed disks
- 23 orphaned NICs
- 12 unassociated public IPs
- 8 old snapshots from 2023
Total monthly waste: £340
Not a massive amount, but it's pure waste. Over a year, that's £4,000 - enough to fund some actual improvements.
Cleanup Process
Before deleting anything:
- Document what you find - Export the list to CSV
- Check with resource owners - That "orphaned" disk might be deliberately kept for recovery
- Check for dependencies - Some disks are detached temporarily for maintenance
- Delete in batches - Don't select-all and delete
Safe Deletion Script
For disks, I usually use a two-stage process:
# First, list what we'd delete (dry run)
az disk list --query "[?diskState=='Unattached'].{Name:name, RG:resourceGroup, Size:diskSizeGb}" -o table
# Then delete with confirmation
az disk list --query "[?diskState=='Unattached'].id" -o tsv | while read id; do
echo "Deleting: $id"
az disk delete --ids "$id" --yes
done
Prevention
Better than cleaning up is not creating orphans in the first place:
Delete associated resources with VMs: When deleting a VM in the portal, there's a checkbox to delete the NIC, public IP, and disks. Use it.
Use Terraform/Bicep: Infrastructure as Code ensures resources are created and destroyed together.
Regular audits: Run the Resource Graph queries monthly. Add them to your FinOps routine.
Tagging: Tag resources with owners and expiry dates. Makes cleanup decisions easier.
Want us to find your orphaned resources? Our free savings snapshot includes a complete orphaned resource audit.