AWS cost & waste audit

Account Acme Retail (synthetic demo) · regions ap-south-1, us-east-1 · 02 Nov 2026, 11:39 UTC
Estimated savings / month
$616.37
Estimated savings / year
$7,396.44
Findings
20
4 high · 7 medium · 5 low · 4 info
Low-risk quick wins
1
$2.28 / month

Summary

The scan ran 12 read-only checks and found 20 items worth reviewing. The largest single opportunity is Unattached EBS volume (vol-61b83803386704d04, about $208.10/month). 1 fix is low-risk and can be done this week. Nothing was changed in the account: every fix below is a command for you to review and run.

Top opportunities

PriorityWhatResourceRegionSaving / month
highUnattached EBS volume
100 GiB io1, 3,000 provisioned IOPS (perf-test), not attached to any instance
vol-61b83803386704d04ap-south-1$208.10
highUnattached EBS volume
1000 GiB gp2 (forgotten-ml-dataset), not attached to any instance
vol-d5ad5396436612ce5us-east-1$100.00
highInstance looks idle
t2.large (legacy-reporting): average CPU 2.7%, peak 7.5% over 13 days
i-1222cd8a252d8edc1ap-south-1$72.42
highDatabase has had no connections
db.t3.medium postgres 100 GiB: 0 connections in 13 days
analytics-copyap-south-1$63.96
mediumUnattached EBS volume
500 GiB gp3 (migration-temp), not attached to any instance
vol-8f632793cccc4eedcap-south-1$45.60
mediumNAT gateway barely used
moved 0.51 GB (in + out) in 14 days (VPC vpc-0527c2ef9d79f9cd3)
nat-95a718f3d35a4e2d8ap-south-1$40.88
mediumUnattached EBS volume
200 GiB gp2 (old-jenkins-data), not attached to any instance
vol-021fe5f4debe2e734ap-south-1$22.80
mediumLoad balancer has no targets
application load balancer with 1 target group(s) and 0 registered targets
old-promo-albap-south-1$16.43
mediumInstance stopped for a long time
t3.large (old-build-agent) stopped for 75 days, 1 volume(s) attached
i-8891acb15ab0eabd4ap-south-1$13.68
mediumOld snapshot not used by any AMI
410 days old, from a 200 GiB volume. pre-migration backup 2025
snap-623da09cda0b9887aap-south-1$10.00

Findings by check

EBS-001 · Unattached EBS volumes (4)

Volumes in the 'available' state are billed every month but serve nothing - often left behind by deleted instances.

ResourceDetailRecommendationRegionNow / monthSaving
vol-61b83803386704d04100 GiB io1, 3,000 provisioned IOPS (perf-test), not attached to any instance Confirm with the owner, snapshot it (cheap insurance), then delete the volume. The command waits for the snapshot to finish (up to 10 minutes); if the wait times out, delete the volume once the snapshot shows 'completed'. Delete the snapshot too after a month or two if nobody needs it.
Command (risk: medium)
SNAP=$(aws ec2 create-snapshot --region ap-south-1 --volume-id vol-61b83803386704d04 --description 'billdoctor: backup before delete' --query SnapshotId --output text) && aws ec2 wait snapshot-completed --region ap-south-1 --snapshot-ids "$SNAP" && aws ec2 delete-volume --region ap-south-1 --volume-id vol-61b83803386704d04
ap-south-1$208.10$208.10
vol-d5ad5396436612ce51000 GiB gp2 (forgotten-ml-dataset), not attached to any instance Confirm with the owner, snapshot it (cheap insurance), then delete the volume. The command waits for the snapshot to finish (up to 10 minutes); if the wait times out, delete the volume once the snapshot shows 'completed'. Delete the snapshot too after a month or two if nobody needs it.
Command (risk: medium)
SNAP=$(aws ec2 create-snapshot --region us-east-1 --volume-id vol-d5ad5396436612ce5 --description 'billdoctor: backup before delete' --query SnapshotId --output text) && aws ec2 wait snapshot-completed --region us-east-1 --snapshot-ids "$SNAP" && aws ec2 delete-volume --region us-east-1 --volume-id vol-d5ad5396436612ce5
us-east-1$100.00$100.00
vol-8f632793cccc4eedc500 GiB gp3 (migration-temp), not attached to any instance Confirm with the owner, snapshot it (cheap insurance), then delete the volume. The command waits for the snapshot to finish (up to 10 minutes); if the wait times out, delete the volume once the snapshot shows 'completed'. Delete the snapshot too after a month or two if nobody needs it.
Command (risk: medium)
SNAP=$(aws ec2 create-snapshot --region ap-south-1 --volume-id vol-8f632793cccc4eedc --description 'billdoctor: backup before delete' --query SnapshotId --output text) && aws ec2 wait snapshot-completed --region ap-south-1 --snapshot-ids "$SNAP" && aws ec2 delete-volume --region ap-south-1 --volume-id vol-8f632793cccc4eedc
ap-south-1$45.60$45.60
vol-021fe5f4debe2e734200 GiB gp2 (old-jenkins-data), not attached to any instance Confirm with the owner, snapshot it (cheap insurance), then delete the volume. The command waits for the snapshot to finish (up to 10 minutes); if the wait times out, delete the volume once the snapshot shows 'completed'. Delete the snapshot too after a month or two if nobody needs it.
Command (risk: medium)
SNAP=$(aws ec2 create-snapshot --region ap-south-1 --volume-id vol-021fe5f4debe2e734 --description 'billdoctor: backup before delete' --query SnapshotId --output text) && aws ec2 wait snapshot-completed --region ap-south-1 --snapshot-ids "$SNAP" && aws ec2 delete-volume --region ap-south-1 --volume-id vol-021fe5f4debe2e734
ap-south-1$22.80$22.80

EBS-002 · gp2 volumes that should be gp3 (1)

gp3 is about 20% cheaper than gp2 per GiB and has a 3,000 IOPS baseline regardless of size. The change is online.

ResourceDetailRecommendationRegionNow / monthSaving
vol-7128ec531f6695580100 GiB gp2 attached to i-1222cd8a252d8edc1 Modify the volume to gp3 (no downtime).
Command (risk: low)
aws ec2 modify-volume --region ap-south-1 --volume-id vol-7128ec531f6695580 --volume-type gp3
ap-south-1$11.40$2.28

EBS-003 · Old EBS snapshots (2)

Snapshots older than the retention you need, and not used by an AMI, keep costing storage forever.

ResourceDetailRecommendationRegionNow / monthSaving
snap-623da09cda0b9887a410 days old, from a 200 GiB volume. pre-migration backup 2025 Keep only what your retention policy needs (e.g. 30-90 days) - set up Amazon Data Lifecycle Manager instead of manual snapshots. Saving shown is an upper bound (snapshots are incremental).
Command (risk: medium)
aws ec2 delete-snapshot --region ap-south-1 --snapshot-id snap-623da09cda0b9887a
ap-south-1$10.00$10.00
snap-d93c9fe1018b66a97220 days old, from a 200 GiB volume. manual backup before upgrade Keep only what your retention policy needs (e.g. 30-90 days) - set up Amazon Data Lifecycle Manager instead of manual snapshots. Saving shown is an upper bound (snapshots are incremental).
Command (risk: medium)
aws ec2 delete-snapshot --region ap-south-1 --snapshot-id snap-d93c9fe1018b66a97
ap-south-1$10.00$10.00

EC2-001 · Unassociated Elastic IPs (2)

Since February 2024 every public IPv4 address costs $0.005/hour; an Elastic IP attached to nothing is pure waste.

ResourceDetailRecommendationRegionNow / monthSaving
eipalloc-0076a7c9852db02a4127.184.176.136 is not attached to anything Release it unless an allow-list somewhere depends on this exact IP.
Command (risk: medium)
aws ec2 release-address --region ap-south-1 --allocation-id eipalloc-0076a7c9852db02a4
ap-south-1$3.65$3.65
eipalloc-689a9541a3f86cde3127.43.70.107 is not attached to anything Release it unless an allow-list somewhere depends on this exact IP.
Command (risk: medium)
aws ec2 release-address --region us-east-1 --allocation-id eipalloc-689a9541a3f86cde3
us-east-1$3.65$3.65

EC2-002 · Long-stopped instances still paying for disks (1)

A stopped instance costs nothing for compute, but its EBS volumes are billed in full.

ResourceDetailRecommendationRegionNow / monthSaving
i-8891acb15ab0eabd4t3.large (old-build-agent) stopped for 75 days, 1 volume(s) attached If it is not coming back: create an AMI (keeps a restorable copy), then terminate the instance. Volumes with 'delete on termination' turned off survive the termination; delete them too (the AMI holds a copy) or they show up as unattached volumes next time.
Command (risk: high)
IMG=$(aws ec2 create-image --region ap-south-1 --instance-id i-8891acb15ab0eabd4 --name billdoctor-i-8891acb15ab0eabd4 --query ImageId --output text) && aws ec2 wait image-available --region ap-south-1 --image-ids "$IMG" && aws ec2 terminate-instances --region ap-south-1 --instance-ids i-8891acb15ab0eabd4
ap-south-1$13.68$13.68

EC2-003 · Idle running instances (1)

Instances averaging under 5% CPU (and never above 20%) for two weeks are usually forgotten or oversized.

ResourceDetailRecommendationRegionNow / monthSaving
i-1222cd8a252d8edc1t2.large (legacy-reporting): average CPU 2.7%, peak 7.5% over 13 days Confirm with the owner. Then stop it, schedule it (office hours only), or downsize. Saving shown assumes it can be stopped.
Command (risk: high)
aws ec2 stop-instances --region ap-south-1 --instance-ids i-1222cd8a252d8edc1
ap-south-1$72.42$72.42

EC2-004 · Previous-generation instance types (2)

Older families (t2, m4, c4, r4...) cost more for less performance than their current replacements.

ResourceDetailRecommendationRegionNow / monthSaving
i-1222cd8a252d8edc1t2.large -> t3.large Change the instance type to t3.large during a maintenance window (stop, change type, start). Newer types run on Nitro: very old AMIs need the ENA and NVMe drivers (current images have them). A stop/start changes the public IP unless it is an Elastic IP. Graviton (t4g/m7g) is cheaper still if the software runs on ARM.
Command (risk: medium)
aws ec2 stop-instances --region ap-south-1 --instance-ids i-1222cd8a252d8edc1 && aws ec2 wait instance-stopped --region ap-south-1 --instance-ids i-1222cd8a252d8edc1 && aws ec2 modify-instance-attribute --region ap-south-1 --instance-id i-1222cd8a252d8edc1 --instance-type Value=t3.large && aws ec2 start-instances --region ap-south-1 --instance-ids i-1222cd8a252d8edc1
ap-south-1$72.42$7.01
i-a6d48966bc3c7556bm4.large -> m6i.large Change the instance type to m6i.large during a maintenance window (stop, change type, start). Newer types run on Nitro: very old AMIs need the ENA and NVMe drivers (current images have them). A stop/start changes the public IP unless it is an Elastic IP. Graviton (t4g/m7g) is cheaper still if the software runs on ARM.
Command (risk: medium)
aws ec2 stop-instances --region ap-south-1 --instance-ids i-a6d48966bc3c7556b && aws ec2 wait instance-stopped --region ap-south-1 --instance-ids i-a6d48966bc3c7556b && aws ec2 modify-instance-attribute --region ap-south-1 --instance-id i-a6d48966bc3c7556b --instance-type Value=m6i.large && aws ec2 start-instances --region ap-south-1 --instance-ids i-a6d48966bc3c7556b
ap-south-1$73.00$2.92

ELB-001 · Load balancers with no targets (1)

A load balancer is billed by the hour even when nothing is registered behind it.

ResourceDetailRecommendationRegionNow / monthSaving
old-promo-albapplication load balancer with 1 target group(s) and 0 registered targets Delete it if the service is gone. First check DNS records pointing at it, and its listener rules: a load balancer without targets can still serve redirects or fixed responses.
Command (risk: medium)
aws elbv2 delete-load-balancer --region ap-south-1 --load-balancer-arn arn:aws:elasticloadbalancing:ap-south-1:123456789012:loadbalancer/app/old-promo-alb/3eacd4314c9ad45d
ap-south-1$16.43$16.43

LOGS-001 · Log groups that never expire (2)

The default retention is 'never expire', so log storage grows forever.

ResourceDetailRecommendationRegionNow / monthSaving
/aws/lambda/old-report-fn12.00 GB stored, retention: never expire Set a retention period that matches your needs (e.g. 30 days for app logs, longer for audit logs). Saving depends on how old the data is, so it is not estimated.
Command (risk: low)
aws logs put-retention-policy --region ap-south-1 --log-group-name '/aws/lambda/old-report-fn' --retention-in-days 30
ap-south-1$0.36-
/ecs/web48.00 GB stored, retention: never expire Set a retention period that matches your needs (e.g. 30 days for app logs, longer for audit logs). Saving depends on how old the data is, so it is not estimated.
Command (risk: low)
aws logs put-retention-policy --region ap-south-1 --log-group-name '/ecs/web' --retention-in-days 30
ap-south-1$1.44-

NAT-001 · Idle NAT gateways (1)

A NAT gateway costs ~$33-41/month before any data. Many dev VPCs keep one for traffic that never comes.

ResourceDetailRecommendationRegionNow / monthSaving
nat-95a718f3d35a4e2d8moved 0.51 GB (in + out) in 14 days (VPC vpc-0527c2ef9d79f9cd3) Delete it if private subnets do not need the internet; use VPC endpoints for S3/DynamoDB/ECR, or public subnets with tight security groups for small dev setups.
Command (risk: high)
aws ec2 delete-nat-gateway --region ap-south-1 --nat-gateway-id nat-95a718f3d35a4e2d8
ap-south-1$40.88$40.88

RDS-001 · Idle RDS databases (1)

A database with zero connections for two weeks is usually a forgotten test or migration copy.

ResourceDetailRecommendationRegionNow / monthSaving
analytics-copydb.t3.medium postgres 100 GiB: 0 connections in 13 days Take a final snapshot and delete it (stopping only lasts 7 days before AWS restarts it).
Command (risk: high)
aws rds delete-db-instance --region ap-south-1 --db-instance-identifier analytics-copy --final-db-snapshot-identifier analytics-copy-billdoctor-final
ap-south-1$63.96$63.96

S3-001 · S3 buckets without lifecycle rules (2)

Without lifecycle rules, incomplete multipart uploads and old object versions are kept (and billed) forever.

ResourceDetailRecommendationRegionNow / monthSaving
acme-uploads-demoincomplete multipart uploads are never cleaned up; versioning is on but old versions never expire Add a lifecycle rule: abort incomplete multipart uploads after 7 days and expire noncurrent versions after 30-90 days.
Command (risk: low)
aws s3api put-bucket-lifecycle-configuration --region ap-south-1 --bucket acme-uploads-demo --lifecycle-configuration '{"Rules":[{"ID":"billdoctor-hygiene","Status":"Enabled","Filter":{},"AbortIncompleteMultipartUpload":{"DaysAfterInitiation":7},"NoncurrentVersionExpiration":{"NoncurrentDays":60}}]}'  # WARNING: replaces existing rules - merge them first
ap-south-1--
acme-exports-demoincomplete multipart uploads are never cleaned up Add a lifecycle rule: abort incomplete multipart uploads after 7 days.
Command (risk: low)
aws s3api put-bucket-lifecycle-configuration --region ap-south-1 --bucket acme-exports-demo --lifecycle-configuration '{"Rules":[{"ID":"billdoctor-hygiene","Status":"Enabled","Filter":{},"AbortIncompleteMultipartUpload":{"DaysAfterInitiation":7}}]}'  # WARNING: replaces existing rules - merge them first
ap-south-1--

Method and assumptions