← All entries
Web app

AWS Cost Calculator

Build an EC2 / S3 / RDS / Lambda estimate on live AWS On-Demand prices — per-resource cards, a grand total in monthly and annual terms, and CSV export.

Web app awspricingcost-estimatereactcsvec2s3rdslambda

◎ What it solves

Gives a cost estimate provenance and a shelf life — every line traces back to the AWS price list version it came from, instead of being a number in a spreadsheet nobody can date.

⚠ Limitations & Risks

Every catalog entry must acknowledge at least one limitation or risk.

What it solves

An estimate is easy to produce and hard to trust. Built by hand — clicking through the AWS pricing calculator service by service, or keeping a spreadsheet someone once typed prices into — you end up with a single number and no way to answer the two questions that matter about it: which prices is this based on, and are they still true?

Both failures are silent. A spreadsheet does not announce that it is eight months old, and AWS does not tell you it moved a price. The estimate keeps looking authoritative while quietly drifting away from reality, and the next scenario means rebuilding the whole thing from scratch.

So the point here is not the arithmetic — it’s the provenance and the shelf life. Prices are pulled weekly from the public Price List Bulk API (no AWS credentials involved), stamped with the price-list version they came from, and every service on screen links out to its AWS pricing page and the raw list behind it. A separate weekly job diffs fresh prices against the committed snapshot and opens a PR when anything moves more than 1%, so a change gets noticed rather than absorbed.

A hand-built estimate drifting from real prices, versus a dated and traceable one

Before: a hand-typed estimate sits frozen while the real AWS prices beside it move, and nothing records which price list it used. After: a weekly pull from the public price list produces a versioned snapshot, every estimate line links to its source, and a review job raises a pull request when a price moves more than one percent.

BEFORE — a number with no originhand-built estimateEC2 m5.xlarge$140S3 5 TB$115RDS$210total$465 / moAWS today$152$118$236prices typed in · 8 months ago⚠ which price list? from when? still true?next scenario = rebuild from scratchAFTER — dated and traceablePrice List Bulk APIpublic · no credentialsweeklypricing_<region>v2026-08-14estimate — every line traces to its sourceEC2 m5.xlarge$152🔗 sourceS3 5 TB$118🔗 sourceRDS$236🔗 sourcetotal$506 / mo · $6,072 / yr⚙ review PR opened when a price moves >1%

The arithmetic is the easy half. What changes on the right is that the number is dated — stamped with the price-list version behind it, refreshed on a schedule, and loud when a price moves — instead of a figure whose age nobody can establish.

What it does

Add the resources you are planning — EC2, S3, RDS, Lambda, each with an editable name — plus flat “Estimate” line items, tune the inputs, and watch a live per-card cost and grand total update in both monthly and annual terms. Export the whole estimate as CSV.

A “Where these prices come from” panel hyperlinks every service to its AWS pricing page, the raw price list it was pulled from, and the version — so a number on screen can always be traced back.

Where the data comes from

aws-pricing/generate_aws_pricing.py pulls the public Price List Bulk API with no AWS credentials (stdlib urllib; it streams the ~473 MB EC2 region file rather than buffering it) and .github/workflows/aws-pricing.yml publishes compact JSON to the aws-pricing/data branch weekly. Regions accumulate.

The SPA reads index.jsonpricing_<region>.json at runtime, preferring a commit-SHA raw URL to dodge raw.githubusercontent’s ~5 minute branch cache, then the branch path, then the bundled public/data/ snapshot.

A separate weekly review workflow diffs fresh prices against the committed snapshot and opens a PR whose body calls out any change over 1%.

Run it locally

cd cost-dashboard
npm install
npm run dev

# Prove the SKU filters reject decoys, offline:
python aws-pricing/test_generate.py