How To

How to Audit Your AWS Costs with Native Tools (Cost Explorer, Trusted Advisor, Compute Optimizer)

A full AWS cost audit of all seven leaks using only Cost Explorer, Trusted Advisor, Compute Optimizer, CloudWatch and the CLI, with how to read each result. It includes one rightsizing recommendation verified end to end, and the four reasons a one-off audit can't be your strategy.

9 min read
Cover Image for How to Audit Your AWS Costs with Native Tools (Cost Explorer, Trusted Advisor, Compute Optimizer)

How to Audit Your AWS Costs with Native Tools (Cost Explorer, Trusted Advisor, Compute Optimizer)

Compute Optimizer says an m5.2xlarge is over-provisioned and should be an m5.xlarge. Do you trust it? The answer depends on whether its 14-day window saw the month-end batch job that pins that instance at 90% for two days. That question, repeated across every finding, is what a real AWS cost audit is made of.

Part one covered the seven places AWS bills leak: unattached EBS volumes and orphaned snapshots, idle or oversized EC2, RI and Savings Plans coverage gaps, unused Elastic IPs and idle load balancers, over-provisioned RDS, data transfer, and non-production environments running 24/7.

This part is the audit itself, covering all seven with tools you already have: Cost Explorer, Trusted Advisor, Compute Optimizer, CloudWatch and the AWS CLI. Nothing to install. Budget 3 to 5 hours for a first pass on a multi-account setup, and expect the findings document to pay for that time many times over.

We'll go tool by tool, because that's how you'll actually do it. Each tool covers several leaks at once.

01Map

Cost Explorer

Shape of the bill, commitment coverage, data transfer, non-prod spend.

02Checks

Trusted Advisor

Pre-built cost checks for idle EC2, EBS, Elastic IPs, load balancers and RDS.

03Sizing

Compute Optimizer

Rightsizing for EC2, EBS and RDS, with a performance-risk score.

04Verify

CloudWatch

Raw metrics behind every finding before anyone acts on it.

05Sweep

AWS CLI

Orphan inventory in every region, whatever your support plan.

Five passes, in the order this guide runs them. Each one covers several of the seven leaks.

Tool 1: Cost Explorer, the map

Cost Explorer answers "where is the money going" and covers leaks #3 (coverage gaps), #6 (data transfer) and #7 (non-prod). If you've never opened it, enable it under Billing and Cost Management → Cost Explorer. Data appears within 24 hours (Cost Explorer docs).

Step 1: Establish the shape of the bill

Console: Cost Explorer → New report → Cost and usage. Set the range to the last 3 months, granularity Monthly, and Group by: Service.

You're looking for two things: which services dominate (EC2, RDS and data transfer usually make up 60 to 80% at SaaS companies), and which ones grow faster than your traffic. Write down the top five. They set your audit priorities.

Step 2: Check Savings Plans and RI coverage (leak #3)

Console: Billing and Cost Management → Savings Plans → Coverage report, and if you hold RIs, Reservations → Coverage.

CLI:

aws ce get-savings-plans-coverage \
  --time-period Start=2026-06-01,End=2026-07-01 \
  --granularity MONTHLY

How to read it. The number that matters is coverage of your stable baseline: compute that runs 24/7 whatever the load. Healthy targets are 70 to 85% coverage for steady workloads, with the rest on-demand for elasticity. Below 60%, you're overpaying materially. Also open Savings Plans → Recommendations: AWS computes a suggested hourly commitment from your own last 7, 30 or 60 days. Treat it as a floor, since it's deliberately conservative.

Check utilization while you're there. Coverage above 85% with utilization below roughly 95% means you over-committed. That's a different problem, and you want to know about it before buying more.

Step 3: Isolate data transfer (leak #6)

Console: Cost Explorer → Group by: Usage Type, then filter Usage Type Group to EC2: Data Transfer - Inter AZ. Repeat for usage types containing NatGateway.

CLI:

aws ce get-cost-and-usage \
  --time-period Start=2026-06-01,End=2026-07-01 \
  --granularity MONTHLY --metrics UnblendedCost \
  --group-by Type=DIMENSION,Key=USAGE_TYPE \
  --filter '{"Dimensions":{"Key":"USAGE_TYPE_GROUP","Values":["EC2: Data Transfer - Inter AZ"]}}'

How to read it. There's no universal healthy number, so use a ratio. If inter-AZ transfer plus NAT processing is more than 3 to 5% of the total bill, something architectural deserves a look. The usual suspects: replicated data stores talking across AZs (often unavoidable), and S3 or DynamoDB traffic routed through a NAT gateway (almost always avoidable with free gateway VPC endpoints). ECR image pulls count here too: the layers come from S3, so an S3 gateway endpoint removes most of that traffic.

Step 4: Measure non-prod spend (leak #7)

If your resources carry an environment tag (activate it first under Billing → Cost allocation tags):

aws ce get-cost-and-usage \
  --time-period Start=2026-06-01,End=2026-07-01 \
  --granularity MONTHLY --metrics UnblendedCost \
  --group-by Type=TAG,Key=environment

How to read it. Multiply each non-prod environment's compute cost by 0.36 (60 business hours out of 168). The difference between that and what you pay is your scheduling opportunity. Look at the untagged bucket too. If it's more than about 10% of spend, tagging discipline is itself a finding.

Tool 2: Trusted Advisor, the pre-built checks

Trusted Advisor covers leaks #1 (EBS), #2 (idle EC2), #4 (Elastic IPs and idle load balancers) and parts of #5 (idle RDS) with no setup. The catch: the full cost optimization checks need a Business, Enterprise On-Ramp or Enterprise support plan (Trusted Advisor docs). On Basic or Developer support you get a small subset, so skip to the CLI section and do it by hand.

Console: Trusted Advisor → Recommendations → Cost optimization. The checks to open, mapped to the leaks:

Trusted Advisor checkLeak
Low Utilization Amazon EC2 Instances#2 idle or oversized EC2
Underutilized Amazon EBS Volumes#1 unattached EBS
Unassociated Elastic IP Addresses#4 unused Elastic IPs
Idle Load Balancers#4 idle load balancers
Amazon RDS Idle DB Instances#5 RDS
Savings Plan and RI recommendations#3 coverage

How to read it. The thresholds are generous. "Low Utilization" means CPU at or below 10% and network I/O at or below 5 MB on 4 or more of the last 14 days. Anything it flags is a strong candidate, and plenty of waste passes under its thresholds, which is why the CloudWatch pass comes later. Every check exports to CSV. Download them all: they're the backbone of your findings document.

You can also pull results through the AWS Support API (Business support or above, us-east-1 endpoint). Look up the check ID by name rather than hard-coding it:

# List all cost checks and their IDs
aws support describe-trusted-advisor-checks --language en --region us-east-1 \
  --query 'checks[?category==`cost_optimizing`].{id:id,name:name}' --output table

# Pull one check's results by name
CHECK_ID=$(aws support describe-trusted-advisor-checks --language en --region us-east-1 \
  --query "checks[?name=='Low Utilization Amazon EC2 Instances'].id" --output text)
aws support describe-trusted-advisor-check-result \
  --check-id "$CHECK_ID" --language en --region us-east-1

Tool 3: Compute Optimizer, the rightsizing engine

Compute Optimizer covers leaks #2 (EC2 rightsizing), #1 (EBS volume types) and #5 (RDS). It's free, but you have to opt in (Compute Optimizer console → Get started) and wait about 24 hours. By default it analyzes 14 days of CloudWatch metrics (Compute Optimizer docs).

Pull every over-provisioned instance:

aws compute-optimizer get-ec2-instance-recommendations \
  --filters name=Finding,values=Overprovisioned \
  --query 'instanceRecommendations[*].{Arn:instanceArn,Current:currentInstanceType,Recommended:recommendationOptions[0].instanceType}' \
  --output table

Repeat with get-ebs-volume-recommendations (flags gp2-to-gp3 conversions and oversized IOPS) and get-rds-database-recommendations.

How to read it. EC2 findings come back as Optimized, Overprovisioned or Underprovisioned. Each recommendation option includes projected utilization and a performance-risk score. One size down with low performance risk is usually safe to act on. Treat multi-step downsizes (2xlarge to medium) with more care and verify against 30 days, not 14. Enhanced infrastructure metrics, a paid add-on, extends the lookback to 93 days and catches the monthly batch spikes the default window misses.

What Compute Optimizer won't tell you is whether an instance should exist at all. It rightsizes. It doesn't question. That call is yours.

Tool 4: CloudWatch, the verification layer

Before acting on any finding above, check it against raw metrics. CloudWatch is also the main tool for leak #5 (RDS), where a CPU-only view misleads.

For a suspect RDS instance, pull CPU, connections and memory together:

for metric in CPUUtilization DatabaseConnections FreeableMemory; do
  aws cloudwatch get-metric-statistics \
    --namespace AWS/RDS --metric-name $metric \
    --dimensions Name=DBInstanceIdentifier,Value=your-db-name \
    --start-time $(date -u -d '30 days ago' +%Y-%m-%dT%H:%M:%SZ) \
    --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
    --period 86400 --statistics Average Maximum
done

(On macOS, use date -u -v-30d +%Y-%m-%dT%H:%M:%SZ for the start time.)

How to read it. A downsize candidate has average CPU under 15%, maximum under 50%, connections well below the class limit, and FreeableMemory steadily above roughly 40% of instance RAM. If memory is tight but CPU is idle, move within the memory-optimized family rather than down a size. For NAT gateways (leak #6), use the same pattern with --namespace AWS/NATGateway --statistics Sum, once for BytesInFromSource (traffic from your VPC heading out) and once for BytesInFromDestination (responses coming back). AWS charges data processing on every GB through the gateway, in either direction, so add the two together for the month, multiply by $0.045/GB (us-east-1, VPC pricing) to price the traffic, then go find out what it is. The metric definitions are in the NAT gateway CloudWatch docs.

Tool 5: AWS CLI sweeps, the orphan hunt

Last, the direct inventory checks for leaks #1 and #4. These catch everything regardless of support plan. Run them in every active region:

# Unattached EBS volumes
aws ec2 describe-volumes --filters Name=status,Values=available \
  --query 'Volumes[*].{ID:VolumeId,Size:Size,Type:VolumeType,Created:CreateTime}' --output table

# Old snapshots you own (adjust the date to your retention policy)
aws ec2 describe-snapshots --owner-ids self \
  --query 'Snapshots[?StartTime<=`2026-01-01`].{ID:SnapshotId,Vol:VolumeId,Size:VolumeSize}' --output table

# Unassociated Elastic IPs
aws ec2 describe-addresses \
  --query 'Addresses[?AssociationId==null].{IP:PublicIp,AllocId:AllocationId}' --output table

# All load balancers (then check RequestCount for each in CloudWatch)
aws elbv2 describe-load-balancers \
  --query 'LoadBalancers[*].{Name:LoadBalancerName,Type:Type,Created:CreatedTime}' --output table

To loop across regions:

for r in $(aws ec2 describe-regions --query 'Regions[*].RegionName' --output text); do
  echo "== $r =="
  aws ec2 describe-volumes --region $r --filters Name=status,Values=available \
    --query 'Volumes[*].VolumeId' --output text
done

How to read it. Every available volume gets a snapshot-then-delete ticket. Every Elastic IP with no association gets released. Every load balancer with near-zero RequestCount over 14 days gets an owner asked "can this go?", and if nobody answers in a week, it goes.

After five passes you'll have a findings document covering all seven leaks, with dollar estimates. On a typical first audit of a mid-market account, that document is worth 15 to 30% of the monthly bill.


One finding, verified end to end

Back to the opening question. Here's what verifying a single Compute Optimizer finding looks like. The details are illustrative.

Compute Optimizer flags i-0a1b (m5.2xlarge, tagged service:reporting) as over-provisioned, recommending m5.xlarge at low performance risk. Saving: about $140 a month. Three readings fit:

  • Truly oversized. Predicts low CPU and memory every day of the month.
  • Spiky. Predicts a periodic peak the 14-day window missed, like a month-end batch.
  • Memory-bound. Predicts low CPU but high memory use, which Compute Optimizer can only see if the CloudWatch agent publishes memory metrics.

Check the spikes. Pull 35 days of hourly Maximum CPU instead of daily averages:

aws cloudwatch get-metric-statistics \
  --namespace AWS/EC2 --metric-name CPUUtilization \
  --dimensions Name=InstanceId,Value=i-0a1b \
  --start-time $(date -u -d '35 days ago' +%Y-%m-%dT%H:%M:%SZ) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
  --period 3600 --statistics Maximum

Two days at the start of last month peak at 88%. The rest of the month sits under 12%. The 14-day window missed the month-end run. Reading two.

Check memory. The instance publishes mem_used_percent through the CloudWatch agent. It peaks at 61% during the same two days. Halving RAM would push the batch into swap.

Conclusion. Don't downsize. The right action is moving the month-end job to a scheduled, right-sized instance or a batch service, and then rightsizing what's left. Compute Optimizer's recommendation wasn't wrong about 28 days of the month. It just couldn't see the other two. This is the judgment the audit asks of you, once per finding.

What the native tools cover, and what they don't

LeakBest native toolWhat's left for a person
#1 Unattached EBS, old snapshotsCLI sweep, Trusted AdvisorConfirming nothing restores from that snapshot
#2 Idle or oversized EC2Compute Optimizer, CloudWatchSpikes, memory, and whether it should exist
#3 Coverage gapsSavings Plans coverage and recommendationsChoosing a commitment you can live with
#4 Elastic IPs, idle load balancersCLI sweep, Trusted AdvisorFinding an owner to say "it can go"
#5 Over-provisioned RDSCompute Optimizer, CloudWatchScheduling the failover window
#6 Data transferCost Explorer usage types, NAT metricsWorking out what the traffic is
#7 Non-prod 24/7Cost Explorer by tagBuilding and owning the schedule

The deeper difference: the native tools are excellent at finding. Every row's right column is a decision plus an action, and neither one is automated.

The honest part: where the DIY audit falls short

You should run this audit. You should also know its four structural limits before you rely on it as a strategy:

01Limit 1

Point in time

It describes the account on audit day. Waste starts regrowing the next sprint.

02Limit 2

Engineer hours

Three to five hours the first time, never free after, so monthly becomes quarterly.

03Limit 3

Decay

A recommendation built on last month’s metrics can be wrong after the next traffic shift.

04Limit 4

No remediation

Nothing here deletes, resizes or buys. Every finding becomes a ticket that needs an owner.

Run the audit anyway. Just treat it as a baseline, not a strategy.

It's a point-in-time snapshot. It describes the account on audit day. Next sprint's deploys, terminations and scale-ups start regrowing waste immediately, because every one of the seven leaks is a process, not a one-off mess.

It costs real engineer time, every time. The first pass is 3 to 5 hours. Repeats are faster but never free, and they compete with feature work. In practice, "monthly audit" becomes "quarterly audit" by Q3.

Findings decay. A rightsizing recommendation built on last month's metrics can be stale after the next traffic shift. Trusted Advisor's 14-day windows and Compute Optimizer's default lookback both trail reality. A finding you act on 45 days after the data was collected may simply be wrong.

Detection isn't remediation. Nothing above deletes a volume, resizes an instance or buys a Savings Plan. Every finding becomes a ticket, and tickets without owners are where savings go to die. Most teams recover well under half of the audited savings for this reason.

None of this makes the audit worthless. It makes it a baseline. And it always leads to the same question: how do you run these checks continuously, with remediation attached?

What comes next

Everything you did by hand here can run continuously. CloudThinker Optimize performs the same checks, all seven leaks, through read-only AWS access, every day instead of every quarter. Each finding arrives with the evidence you gathered above, and every fix goes through Auto Mode, so you decide what runs on its own and what waits for approval. That's part three.

Related reading

Or skip ahead and see your own account's findings: the free tier includes 100 premium credits, no card required.

awscostoptimizationfinopscostexplorertrustedadvisorcomputeoptimizerawscli