All cloud projects
LIVE us-east-1 2026
CLOUD PROJECT · 2026

FinOps: live cost dashboard

A nightly Lambda pulls spend-by-service from AWS Cost Explorer in a single API call and publishes it same-origin, so the site can show — publicly and honestly — exactly what the platform costs to run each month. No database, no backfill: Cost Explorer's own 13-month history does the work.

Cost ExplorerLambdaEventBridgeS3CloudFrontAWS BudgetsTerraform

The problem

Cost optimization is the competency everyone claims and few can show. “I’m cost-aware” is a line on a résumé; a public chart of your own AWS bill, updated nightly, is proof. So the goal for this project was narrow and concrete: put the platform’s real monthly spend, broken down by service, on a page anyone can load — and make the mechanism that produces it as cheap as the bill it reports.

The trap is that the tool for querying spend, the Cost Explorer API, costs money per call ($0.01 per request). A naive implementation that polls on every page view, or fans out a request per service, would ironically make the cost dashboard a line item on the very bill it’s trying to shrink. The whole design is shaped by that one constraint.

The architecture

A single Lambda runs once a night on an EventBridge schedule. It makes one ce:GetCostAndUsage call — MONTHLY granularity, grouped by SERVICE, over the trailing 13 months — and writes the aggregated result as one JSON document to the shared S3 data bucket, served same-origin through the existing CloudFront /data/* behavior. The page fetches /data/costs.json as a first-party request and hand-renders a stacked bar chart in the browser.

EventBridgeCollector λCost Explorer1 call / nightnightlycosts.json (S3)CloudFront /data/*/costs/ pageBudgets → SNSwatches spend
Nightly path: EventBridge → collector Lambda → one Cost Explorer call → costs.json in the S3 data bucket → CloudFront /data/* → the /costs/ page. AWS Budgets watches the same spend independently and pages SNS.

Key decisions & trade-offs

No database, no backfill — Cost Explorer is the store. The obvious design stores a daily row somewhere (DynamoDB) and backfills history so the chart isn’t empty on launch. But Cost Explorer already retains ~13 months and returns the whole series in one query, so the database and the backfill job are both dead weight. Dropping them removed two resources, an IAM surface, and a class of “the snapshot job missed a night” bugs. The nightly query also self-corrects: as each month’s bill finalizes, the next run overwrites the estimate with the settled number. The one thing I gave up is history beyond Cost Explorer’s window — for a portfolio platform, 13 rolling months is plenty; a business would tee the same data into S3/Athena for long-term retention.

One call a night, because the API isn’t free. ce:GetCostAndUsage bills per request, so the collector is scheduled, not on-demand, and makes exactly one grouped call rather than one-per-service. That holds the Cost Explorer line to roughly $0.30/month regardless of how many people load the page — the page reads a static JSON object from CloudFront, never the API. An error alarm on the Lambda pages the same SNS topic the observability canary uses, so a stuck, looping invocation can’t quietly run up CE charges.

Least privilege, within what CE allows. Cost Explorer has no resource-level permissions — ce:GetCostAndUsage can only be granted on *. The role gets that one read action and nothing else, plus s3:PutObject scoped to the data/ prefix and its own log group. It cannot read objects, touch other buckets, or call any other billing API.

It reuses 002’s plumbing. The data bucket, the /data/* CloudFront behavior, and the SNS alert topic already existed from the observability project. FinOps adds a Lambda, a schedule, and an alarm — nothing else. That’s the point of treating the site as one evolving platform: each project gets cheaper to add.

Cost breakdown

ComponentMonthly cost
Cost Explorer API (1 call/night)~$0.30
Lambda (~30 invocations)~$0 (free tier)
S3 object + CloudFront /data/*fractional cents
Error alarm (reuses 002 topic)$0
Total≈ $0.30/mo

And the number this project exists to publish — the platform’s actual all-in monthly spend — is live on the cost dashboard, next to the AWS Budgets guardrail that keeps it honest.

What I’d do differently at scale

For a real organization, the nightly aggregate becomes a pipeline: Cost Explorer (or the far cheaper Cost and Usage Report dumped to S3) → Athena/QuickSight, with allocation by tag, team, and environment rather than raw service. I’d add anomaly detection (AWS Cost Anomaly Detection is free) to catch a spend spike the day it happens instead of at month-end, and I’d enforce a tagging policy with AWS Config or SCPs so untagged spend can’t accumulate in an “unattributable” bucket. The pattern here — pull once, publish cheaply, alarm on failure — scales; the storage and the slicing are what grow.