🚀 Executive Summary
TL;DR: Unchecked “Delete on Termination” flags and decoupled cloud resources lead to significant hidden costs from orphaned EBS volumes, Azure Disks, and other unattached services. This guide outlines three methods—CLI sweeps, policy enforcement via AWS DLM/Config or Azure Policy, and automated janitor functions—to identify and eliminate these forgotten resources, ensuring disciplined cloud cost management.
🎯 Key Takeaways
- Orphaned resources like EBS volumes, Azure Disks, Elastic IPs, and NAT Gateways are a primary source of hidden cloud costs, often due to default behaviors that preserve storage upon instance termination.
- Immediate cost control can be achieved using CLI commands such as `aws ec2 describe-volumes –filters Name=status,Values=available` for AWS and `az disk list –query “[?managedBy==null]”` for Azure to identify and manually delete unattached resources.
- Long-term prevention involves implementing policy enforcement using AWS Data Lifecycle Manager (DLM) or AWS Config’s `ec2-volume-inuse-check` rule, and Azure Policy’s “Audit unattached disks” definition, often enhanced by strict tagging policies for intelligent automation.
Tired of getting blindsided by a massive cloud bill? I’ll walk you through how to hunt down and eliminate the most common hidden costs in AWS and Azure, starting with the silent killer: orphaned resources.
Stop the Bleeding: Hunting Down Ghost Resources in Your Cloud Bill
I still remember the Monday morning meeting a few years back. The finance guy, bless his heart, was holding a printout of our AWS bill and looked like he’d just seen a ghost. A junior engineer, let’s call him Alex, had been testing a new database migration over the weekend. He spun up a massive 1TB io2 EBS volume with 64,000 provisioned IOPS for a high-performance test on an instance named prod-db-failover-test-01. The test failed, he terminated the EC2 instance, and went home. What he didn’t realize was that the “Delete on Termination” flag for the volume was unchecked. That multi-thousand-dollar-a-month volume just sat there, unattached to anything, racking up charges for three straight days. It’s a classic, painful story, and one I see happen time and time again.
The “Why” of Orphaned Resources
This isn’t a bug; it’s a feature, and a dangerous one if you’re not paying attention. Cloud providers decouple resources like storage volumes (EBS/Azure Disks), Elastic IPs, and NAT Gateways from the compute instances they serve. When you terminate an EC2 instance or a VM, the default behavior is often to preserve the attached storage volume. This is a safety net to prevent you from accidentally deleting critical production data. The problem is, in a fast-moving dev or test environment, this “safety net” becomes a money pit, littered with forgotten, unattached resources that you’re still paying for every single hour.
Three Ways to Stop the Bleeding
Alright, so you suspect you’re leaking money. Let’s get this under control. Here are three methods, from a quick fix to a permanent solution.
1. The Quick & Dirty CLI Sweep
This is your first-response, “stop the bleeding now” move. You need to find what’s unattached and costing you money, and you need to do it five minutes ago. The command line is your best friend here. We’re going on a hunt for any EBS volumes or Azure Disks in an ‘available’ or ‘unattached’ state.
For AWS, find those unattached EBS volumes:
aws ec2 describe-volumes --region us-east-1 --filters Name=status,Values=available --query "Volumes[*].[VolumeId,Size,VolumeType]" --output table
For Azure, find unmanaged disks (a common culprit):
az disk list --query "[?managedBy==null].[name,resourceGroup,diskSizeGb]" --output table
Run these commands. If you see a list of resources, you’ve found your money pit. Now you can investigate each one and delete it if it’s no longer needed.
2. The ‘Set It and Forget It’ Policy
Doing a manual cleanup is great, but you don’t want to be doing it every Friday afternoon. The real fix is automation and policy. You need to set up rules that prevent this from happening again.
In AWS, my go-to tool for this is the Amazon Data Lifecycle Manager (DLM) or AWS Config. With AWS Config, you can use the ec2-volume-inuse-check managed rule to automatically flag any EBS volume that hasn’t been attached to an instance for a specified period. You can then hook this up to a Lambda function to automatically delete it or, more safely, take a snapshot and then delete it.
In Azure, you can use Azure Policy. There’s a built-in policy definition called “Audit unattached disks” that you can assign to your subscriptions or resource groups. This will continuously monitor for orphaned disks and report them on your compliance dashboard. From there, you can build an automated remediation task using Azure Functions.
Pro Tip: This is where a strict tagging policy becomes your superpower. Enforce a tag like
ownerorexpiry-dateon all resources. Your cleanup scripts can then use these tags to make intelligent decisions, like notifying the owner before deleting a resource.
3. The ‘Nuclear Option’: The Automated Janitor
Sometimes, especially in chaotic dev environments, you need a bigger hammer. I’ve set this up a few times, and while it’s aggressive, it’s incredibly effective for keeping costs in check. The idea is to run a scheduled Lambda function or Azure Function—your “Cloud Janitor”—that scans your account every night.
The logic is brutally simple:
- Find all resources of a certain type (e.g., EBS volumes, EIPs, NAT Gateways).
- Check if they have a specific “do-not-delete” tag (e.g.,
env=prodorcleanup-exempt=true). - If a resource is unattached AND lacks the exemption tag, terminate it.
Here’s a conceptual Python snippet for a Lambda function to illustrate:
import boto3
def lambda_handler(event, context):
ec2 = boto3.client('ec2', region_name='us-west-2')
# Find all available (unattached) volumes
volumes = ec2.describe_volumes(Filters=[{'Name': 'status', 'Values': ['available']}])
for volume in volumes['Volumes']:
vol_id = volume['VolumeId']
tags = volume.get('Tags', [])
# Check for an exemption tag
is_exempt = any(tag['Key'] == 'cleanup-exempt' and tag['Value'] == 'true' for tag in tags)
if not is_exempt:
print(f"Deleting non-exempt, available volume: {vol_id}")
# ec2.delete_volume(VolumeId=vol_id) # Uncomment with extreme caution!
else:
print(f"Skipping exempt volume: {vol_id}")
WARNING: This approach is powerful and DANGEROUS. You can easily delete something important. Test it extensively, use soft-deletes (like snapshotting first) where possible, and NEVER run this on a production account without ironclad tagging and multiple safeguards. This is a hacky but effective way to enforce hygiene in non-critical environments.
| Method | Effort | Risk | Best For |
| 1. CLI Sweep | Low | Low (if you verify before deleting) | Immediate cost emergencies. |
| 2. Policy Enforcement | Medium | Low | Long-term, preventative cost management. |
| 3. Automated Janitor | High | Very High | Aggressively controlling costs in messy dev/test accounts. |
At the end of the day, cloud cost management isn’t about a single tool; it’s about discipline and visibility. Start with the CLI sweep to fix the immediate problem, but work towards implementing policies. It’ll save you from having to explain a surprise four-figure bill for a disk that wasn’t doing anything.
🤖 Frequently Asked Questions
âť“ What are orphaned cloud resources and why do they cost money?
Orphaned cloud resources are services like EBS volumes, Azure Disks, Elastic IPs, or NAT Gateways that remain provisioned and accrue charges even after the compute instances they were associated with have been terminated. This happens because cloud providers often decouple these resources, and default settings may preserve them, leading to forgotten, unattached assets that continuously incur costs.
âť“ How do the CLI sweep, policy enforcement, and automated janitor methods compare for managing cloud costs?
The CLI sweep is a low-effort, low-risk immediate fix for urgent cost emergencies. Policy enforcement (AWS DLM/Config, Azure Policy) offers a medium-effort, low-risk, long-term preventative solution for continuous cost management. The Automated Janitor is a high-effort, very high-risk method for aggressively controlling costs in non-critical dev/test environments, requiring extreme caution due to its potential for accidental deletion.
âť“ What is a common pitfall when implementing automated cleanup for orphaned resources, and how can it be avoided?
A common pitfall is accidentally deleting critical resources, especially when using aggressive “Automated Janitor” scripts. This can be avoided by implementing strict tagging policies (e.g., `cleanup-exempt=true`, `env=prod`), using soft-deletes like snapshotting before deletion, and thoroughly testing automation in non-production environments with multiple safeguards.
Leave a Reply