DEV Community

Vikas Tripathi
Vikas Tripathi

Posted on

How to audit your AWS account for waste in 5 minutes using Python

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": "*"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode

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}")
Enter fullscreen mode Exit fullscreen mode

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')
Enter fullscreen mode Exit fullscreen mode

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
==================================================
Enter fullscreen mode Exit fullscreen mode

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

  1. EBS volumes — snapshot then delete if unused
  2. Elastic IPs — release immediately if not needed
  3. 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)