Cloud · Tooling

AWS Cost Optimizer

A read-only Python CLI that scans every region of an AWS account for idle EC2 instances, unattached EBS volumes, outdated snapshots, unused Elastic IPs and idle RDS databases, using real CloudWatch utilisation rather than guesses, and reports the waste with a monthly and annual figure attached.

Status
Working tool
Area
Cloud
Period
2025 to 2026
Role
Design, build, document
Stack
PythonBoto3CloudWatchEC2EBSRDSYAML configSlack webhooks
Flow diagram: inventory every region, measure with CloudWatch, detect against thresholds, report in three formats
How a finding is made: inventory, measure, detect, report. The tool never changes the account; the decision to act stays with a person. Click to open full size.
5Resource types
3Report formats
7 dMetric window
0Write permissions

The problem

Cloud waste is rarely dramatic. It is a stopped project’s volumes nobody detached, an Elastic IP that outlived its instance, a database that has had no connections since the demo, and snapshots from a retention policy nobody wrote down. Each is a few dollars a month. Together, across regions, they are the line item that makes the finance team ask what the platform team is doing.

I wanted a tool that finds those things with evidence, not heuristics, and that is safe to run against any account because it never changes anything.

What it does

Point it at an AWS profile and it scans every region (or the ones you list) in parallel, then writes a timestamped report set.

Resource Flagged when Evidence collected
EC2 instances Average CPU below 5% over 7 days, and older than 24 hours CPU utilisation from CloudWatch
EBS volumes Unattached for longer than the configured days Size, age, encryption state
Snapshots Older than the retention period (default 90 days), unless an AMI depends on them Age, size
Elastic IPs Not associated with any resource Address, region (about $3.60 a month each)
RDS instances CPU below 5% and under one connection over 7 days CPU and connection metrics

Every threshold lives in config/config.yaml, so “idle” means what your organisation says it means.

Output

Three formats from one run, because three audiences read them:

  • JSON for automation and CI pipelines, with every metric the analyzers used.
  • CSV, one file per resource type plus an executive summary, for the people who live in spreadsheets.
  • HTML, an interactive report with cost calculations for the people who need to be shown.

An optional Slack webhook posts the summary when a run finishes, with the total potential saving per month and per year.

Decisions worth explaining

Read-only by construction. The IAM policy the tool needs is Describe*, List* and Get* only. It cannot stop, resize or delete anything, which is what makes it safe to hand to someone who is not the account owner, and what keeps the decision to act with a human who can see the evidence.

Measured, not inferred. “Idle” comes from CloudWatch, not from instance size or tags. A small instance that is busy is not waste; a large one that has been at 2% CPU for a week is.

Evidence before estimate. Each finding carries the metric that triggered it and the saving it represents, so the report reads as an argument rather than a list.

What is next

  • Right-sizing recommendations for the instances that are busy but oversized.
  • Cost Explorer integration for spend trends alongside the point-in-time findings.
  • A scheduled mode that diffs runs, so the report shows what changed since last week rather than the whole picture every time.
Evidence

What it looks like when it is actually configured.

Screenshots from the build, named and captioned so each one can be traced to the configuration it proves.