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-4d2b74392f2aca509, 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-4d2b74392f2aca509ap-south-1$208.10
highUnattached EBS volume
1000 GiB gp2 (forgotten-ml-dataset), not attached to any instance
vol-e8b333159767c43f1us-east-1$100.00
highInstance looks idle
t2.large (legacy-reporting): average CPU 2.7%, peak 7.5% over 13 days
i-bd540e2a038f46d43ap-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-18086aeeb8ae02f33ap-south-1$45.60
mediumNAT gateway barely used
moved 0.51 GB (in + out) in 14 days (VPC vpc-663763701c56d4e86)
nat-fd7db26a016ea87b1ap-south-1$40.88
mediumUnattached EBS volume
200 GiB gp2 (old-jenkins-data), not attached to any instance
vol-2173027d8b7ee1d33ap-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-1a1444233b43272f1ap-south-1$13.68
mediumOld snapshot not used by any AMI
410 days old, from a 200 GiB volume. pre-migration backup 2025
snap-8f13d4c9317c14b7eap-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-4d2b74392f2aca509100 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-4d2b74392f2aca509 --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-4d2b74392f2aca509
ap-south-1$208.10$208.10
vol-e8b333159767c43f11000 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-e8b333159767c43f1 --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-e8b333159767c43f1
us-east-1$100.00$100.00
vol-18086aeeb8ae02f33500 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-18086aeeb8ae02f33 --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-18086aeeb8ae02f33
ap-south-1$45.60$45.60
vol-2173027d8b7ee1d33200 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-2173027d8b7ee1d33 --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-2173027d8b7ee1d33
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-62d334940312f5472100 GiB gp2 attached to i-bd540e2a038f46d43 Modify the volume to gp3 (no downtime).
Command (risk: low)
aws ec2 modify-volume --region ap-south-1 --volume-id vol-62d334940312f5472 --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-8f13d4c9317c14b7e410 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-8f13d4c9317c14b7e
ap-south-1$10.00$10.00
snap-5775104f2ff9157e7220 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-5775104f2ff9157e7
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-4ffa7c394b6d7eebe127.37.226.155 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-4ffa7c394b6d7eebe
ap-south-1$3.65$3.65
eipalloc-f6f25954c79c40702127.172.44.47 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-f6f25954c79c40702
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-1a1444233b43272f1t3.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-1a1444233b43272f1 --name billdoctor-i-1a1444233b43272f1 --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-1a1444233b43272f1
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-bd540e2a038f46d43t2.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-bd540e2a038f46d43
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-bd540e2a038f46d43t2.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-bd540e2a038f46d43 && aws ec2 wait instance-stopped --region ap-south-1 --instance-ids i-bd540e2a038f46d43 && aws ec2 modify-instance-attribute --region ap-south-1 --instance-id i-bd540e2a038f46d43 --instance-type Value=t3.large && aws ec2 start-instances --region ap-south-1 --instance-ids i-bd540e2a038f46d43
ap-south-1$72.42$7.01
i-9f757bf59aaf5d499m4.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-9f757bf59aaf5d499 && aws ec2 wait instance-stopped --region ap-south-1 --instance-ids i-9f757bf59aaf5d499 && aws ec2 modify-instance-attribute --region ap-south-1 --instance-id i-9f757bf59aaf5d499 --instance-type Value=m6i.large && aws ec2 start-instances --region ap-south-1 --instance-ids i-9f757bf59aaf5d499
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/679b32dd68cc9392
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-fd7db26a016ea87b1moved 0.51 GB (in + out) in 14 days (VPC vpc-663763701c56d4e86) 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-fd7db26a016ea87b1
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