Most AWS accounts have hundreds of dollars in monthly waste
hiding in plain sight. The problem isn't that it's hard to
find — it's that nobody automates the looking.
Here's a 5-minute audit you can run right now.
What You'll Need
- Python 3.x
- boto3 installed (
pip install boto3) - Read-only AWS credentials
Step 1: Create Read-Only IAM User
Create a user with this policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:DescribeVolumes",
"ec2:DescribeAddresses",
"ec2:DescribeSnapshots",
"cloudwatch:GetMetricStatistics",
"sts:GetCallerIdentity"
],
"Resource": "*"
}
]
}
Step 2: Find Unattached EBS Volumes
These are the silent killers. Volumes that exist but
aren't attached to anything — charging you every month.
import boto3
def find_unattached_ebs(region='us-east-1'):
ec2 = boto3.client('ec2', region_name=region)
volumes = ec2.describe_volumes(
Filters=[{'Name': 'status', 'Values': ['available']}]
)
monthly_waste = 0
findings = []
pricing = {'gp2': 0.10, 'gp3': 0.08, 'io1': 0.125}
for vol in volumes['Volumes']:
size = vol['Size']
vol_type = vol['VolumeType']
rate = pricing.get(vol_type, 0.10)
monthly_cost = size * rate
monthly_waste += monthly_cost
findings.append({
'id': vol['VolumeId'],
'size_gb': size,
'type': vol_type,
'monthly_cost': monthly_cost
})
return findings, monthly_waste
findings, waste = find_unattached_ebs()
print(f"Found {len(findings)} unattached volumes")
print(f"Monthly waste: ${waste:.2f}")
for f in findings:
print(f" {f['id']} - {f['size_gb']}GB {f['type']} - ${f['monthly_cost']:.2f}/month")
Step 3: Find Unused Elastic IPs
Each idle Elastic IP costs $3.65/month. Sounds small
until you find 20 of them.
def find_idle_eips(region='us-east-1'):
ec2 = boto3.client('ec2', region_name=region)
response = ec2.describe_addresses()
idle_eips = []
for addr in response['Addresses']:
if 'AssociationId' not in addr:
idle_eips.append({
'ip': addr['PublicIp'],
'id': addr['AllocationId'],
'monthly_cost': 3.65
})
total_waste = len(idle_eips) * 3.65
return idle_eips, total_waste
eips, waste = find_idle_eips()
print(f"Found {len(eips)} idle Elastic IPs")
print(f"Monthly waste: ${waste:.2f}")
for e in eips:
print(f" {e['ip']} - ${e['monthly_cost']}/month")
Step 4: Find Old Snapshots
Snapshots older than 90 days that nobody remembers
creating.
from datetime import datetime, timezone, timedelta
def find_old_snapshots(region='us-east-1', days=90):
ec2 = boto3.client('ec2', region_name=region)
sts = boto3.client('sts', region_name=region)
account_id = sts.get_caller_identity()['Account']
snapshots = ec2.describe_snapshots(OwnerIds=[account_id])
old_snapshots = []
monthly_waste = 0
cutoff = datetime.now(timezone.utc) - timedelta(days=days)
for snap in snapshots['Snapshots']:
if snap['StartTime'] < cutoff:
size = snap['VolumeSize']
monthly_cost = size * 0.05
monthly_waste += monthly_cost
old_snapshots.append({
'id': snap['SnapshotId'],
'size_gb': size,
'age_days': (datetime.now(timezone.utc) - snap['StartTime']).days,
'monthly_cost': monthly_cost
})
return old_snapshots, monthly_waste
snaps, waste = find_old_snapshots()
print(f"Found {len(snaps)} old snapshots")
print(f"Monthly waste: ${waste:.2f}")
Step 5: Full Audit In One Script
def full_audit(region='us-east-1'):
print(f"\n🔍 Scanning {region}...\n")
ebs, ebs_waste = find_unattached_ebs(region)
eips, eip_waste = find_idle_eips(region)
snaps, snap_waste = find_old_snapshots(region)
total = ebs_waste + eip_waste + snap_waste
print(f"{'='*50}")
print(f"AUDIT RESULTS - {region}")
print(f"{'='*50}")
print(f"Unattached EBS volumes: {len(ebs)} (${ebs_waste:.2f}/month)")
print(f"Idle Elastic IPs: {len(eips)} (${eip_waste:.2f}/month)")
print(f"Old snapshots: {len(snaps)} (${snap_waste:.2f}/month)")
print(f"{'='*50}")
print(f"TOTAL MONTHLY WASTE: ${total:.2f}")
print(f"{'='*50}")
full_audit('us-east-1')
Sample Output
==================================================
AUDIT RESULTS - us-east-1
==================================================
Unattached EBS volumes: 4 ($48.00/month)
Idle Elastic IPs: 3 ($10.95/month)
Old snapshots: 12 ($18.40/month)
==================================================
TOTAL MONTHLY WASTE: $77.35
==================================================
Automate It
If you want this as an API you can plug into your
pipelines without managing the script yourself, I
built Cloud Waste Detector which does this scan
via a single API call with a free tier:
[Cloud Waste Detector on RapidAPI] https://rapidapi.com/quimztech-solutions-baselayer-quimztech-solutions-baselayer-default/api/cloud-waste-detector1
What To Do With The Results
- EBS volumes — snapshot then delete if unused
- Elastic IPs — release immediately if not needed
- Snapshots — review and delete if no longer needed
Run this monthly. Set a calendar reminder. The waste
accumulates fast and silently.
What other AWS resources should I add to this audit?
Top comments (0)