How to Clean Up Orphaned Azure Managed Disks and Unused Public IPs
Why Orphaned Disks and Idle Public IPs Hurt Your Bottom Line
Azure charges for every provisioned managed disk and every allocated public IP, even when they are not attached to a running VM. A 128 GB Standard SSD that sits empty still costs ~$5/month; a static public IP costs ~$3.60 per month. Multiply those across dozens of environments and the waste quickly eclipses the value of the resources they once served. Removing these orphans is one of the highest‑impact, lowest‑effort FinOps actions you can take.
Identify Orphaned Managed Disks
The Azure CLI makes it easy to list disks that have no managedBy relationship.
# List all managed disks that are not attached to any VM
az disk list \
--query "[?managedBy==null]" \
--output table
The output shows name, resourceGroup, sizeGb, and sku. If you have many subscriptions, prepend az account set --subscription <id> or add --subscription <id> to the command.
Quick sanity check
- Verify the disk truly has no data you need. Check the
creationDatafield for snapshots or images that might be referenced elsewhere. - Confirm the disk is not part of a Scale Set or a backup policy. Use:
bash az snapshot list --query "[?sourceResourceId!=null && contains(sourceResourceId, '<disk-id>')]"If the query returns results, investigate before deletion.
Delete Orphaned Managed Disks Safely
Once you are confident a disk is unused, delete it with a single CLI call. You can batch‑delete using jq or Azure CLI's --ids flag.
# Delete all unattached disks in the current subscription
az disk list \
--query "[?managedBy==null].id" \
-o tsv | while read diskId; do
echo "Deleting $diskId"
az disk delete --ids "$diskId" --yes
done
Best practice: Enable soft‑delete for disks (available via Azure Policy) so accidental deletions can be recovered within the retention window.
Find Unused Public IP Addresses
Static public IPs are billed whether they are associated with a NIC, Load Balancer, or Application Gateway. Use the following query to surface IPs with no ipConfiguration.
az network public-ip list \
--query "[?ipConfiguration==null]" \
--output table
The table includes name, resourceGroup, sku (Standard vs Basic), and ipAddress. Standard SKU IPs are more expensive, so prioritize them.
Verify No Hidden Associations
Sometimes an IP is reserved for future use but still attached to a resource that is stopped (e.g., a stopped VM). Run:
az network public-ip show --ids <public-ip-id> --query "ipConfiguration.id" -o tsv
If the result is empty, the IP is truly unattached.
Release Unused Public IPs
Delete the IP address directly, or if you prefer to keep the address for later use, deallocate it by converting it to a Basic SKU (which is free when not attached). However, most cost‑savvy teams simply delete.
# Delete all unattached public IPs
az network public-ip list \
--query "[?ipConfiguration==null].id" -o tsv | while read ipId; do
echo "Deleting $ipId"
az network public-ip delete --ids "$ipId" --yes
done
If you need to retain the IP for DNS purposes, consider moving it to a DNS zone and then deleting the Azure resource; the DNS record will persist.
Prevent Future Orphans
Cleaning up is a one‑time win, but prevention keeps the bill low long term.
- Tagging policy: Enforce tags like
Owner,CreatedDate, andTTL. Use Azure Policy to block creation of disks or IPs without required tags. - Automation with Azure Functions: Schedule a weekly function that runs the above CLI queries and posts a Slack message with any findings.
- Resource lock: Apply a
CanNotDeletelock on critical disks; for test environments, useDeletelock only after a review period. - Lifecycle policies: Enable Azure Disk Auto‑Delete for temporary disks created by Azure DevTest Labs or CI pipelines.
- Cost alerts: Set up an Azure Cost Management alert for
Public IPandManaged Diskspend exceeding a threshold (e.g.,$5/month).
Quick Reference Cheat Sheet
| Action | CLI Command | Notes |
|---|---|---|
| List unattached disks | az disk list --query "[?managedBy==null]" -o table |
Add --subscription if needed |
| Delete unattached disks | az disk delete --ids <id> --yes |
Batch via pipe |
| List unused public IPs | az network public-ip list --query "[?ipConfiguration==null]" -o table |
Check SKU |
| Delete unused IPs | az network public-ip delete --ids <id> --yes |
Batch via pipe |
| Set tag policy | Azure Policy definition RequireTag |
Enforce on creation |
How CloudBudgetMaster Automates This
CloudBudgetMaster continuously scans your Azure subscriptions, flags orphaned managed disks and unattached public IPs, shows the exact monthly dollar impact, and can trigger a cleanup run through a service‑principal with read‑write RBAC permissions, turning manual FinOps chores into a single‑click operation.
CloudBudgetMaster