Case Study

FinOps Reporting & Budget Platform

A platform I built to replace a monthly spreadsheet cycle with automated actuals, forecasts and graded scorecards across AWS, Azure and GCP. The hard part was never the charts. It was making three providers’ billing say the same thing.

$48M+ Annual Cloud Spend
100+ Subscriptions
5 Scorecard Dimensions
129 Unit Tests

The Monthly Spreadsheet Cycle

Actuals were copied into Excel every month, and year-end projections were educated guesses.

100+Cloud subscriptions, each pivoted through a provider billing console to find variance
16Solutions across the organization, with a taxonomy that matched nobody else’s naming
3Providers, and no single view of where the money was going or where it was headed

Leadership needed answers to three questions. Are we going to hit budget this year? Which subscriptions are driving overruns? What changed month over month?

And they needed them without waiting for someone to update a spreadsheet. That turned the project into a data pipeline first and a dashboard second.

From Export To Dashboard

Left to right, with no manual export anywhere in the path.

Ternary API

FOCUS billing feed for AWS, Azure and GCP, up to 50,000 rows a day. Azure uses EffectiveCost (amortized); AWS and GCP use BilledCost.

Normalize

Three alias maps translate provider names, subscription codenames and business unit codes. Records are de-duplicated and upserted.

PostgreSQL

Two databases: core financials (cloud_budget) and detailed vendor spend (vendor_spend).

Express And React

One Express server serves the API and the React app from the same container: dashboard, scorecard, insights and report pages.

Two competing budget figures in one tool is worse than one manual import step.

Ternary has a budget feature, and syncing it would have been the easy call. I turned it off. Its budget entries are scoped too inconsistently to reconcile against the finance-owned operations workbook, which is the number leadership actually governs against. Budgets now flow from that workbook through one import page, and every budget-derived view reads from that single source.

What Goes Into A Grade

Each solution gets a letter from A+ through D−, built from five weighted dimensions.

DimensionWeightHow It Is Measured
Budget Adherence45%Actual against budget, asymmetric by design (see below)
Risk & Anomalies20%Anomaly and risk signals; the insights tabs flag spikes over 25% month over month and dormant spend under $1K a month across 3 or more months
Spend Stability15%How steady monthly spend is, so volatile subscriptions grade lower
Cost Efficiency10%Computed in code from the same actuals
Forecast Accuracy10%Computed in code from the same actuals

Same Percentage, Different Grade

CaseBudgetOverrunPercent OverRelative Grade
Small subscription$10K$5K50%Better
Large subscription$1M$500K50%Worse

Forecasts And Flags

Year-end projections stopped being guesses once each horizon had its own method.

HorizonMethod
Current MonthExtrapolated from the month-to-date daily average
Future MonthsThe current daily rate, adjusted for days in the month
Rolling And Year-EndLinear regression on the last 6 months of actuals
Budget Pre-FillLast actuals, a 3-month average, or a linear trend, chosen by the admin

The Fiscal Year Is The Calendar

Every component thinks in fiscal years, July 1 through June 30. That was settled before the first line of code.

ComponentFiscal Year Behavior
Date PickersDefault to fiscal year ranges
ScorecardsFilter to the current fiscal year automatically
ForecastsProject through June of the current fiscal year
Budget EntryAll 12 fiscal months, grouped by quarter
July 1Everything rolls to the new fiscal year with no manual configuration

One Dataset, Five Outputs

Once the pipeline was trusted, everything else became a view over it.

OutputWhat It Does
Monthly ReportRenders the Monthly Cloud Report’s chart and table pages straight from the database, sized for the executive deck, and reconciles to the published deck figures line for line. It replaced charts that were rebuilt by hand in Excel every month.
Vendor Tracking11 vendors with monthly budget versus actual, COGS and non-COGS splits and cumulative variance. Three have solution-level detail, up to 34 solutions and 9 sub-subscriptions in one vendor.
AI Token SpendService-level AI spend by business unit next to disaster recovery spend. Two BigQuery-backed views attribute AI coding tool cost to the individual user, with an explicit “unattributed” row so totals always tie.
Analysis TabsEight tabs of analysis, including month-over-month and year-over-year movement, anomaly detection and budget burn-down.
ARIATwelve tools in ARIA, the FinOps AI agent I built on top, read from these APIs and answer in plain English with a link back to the exact filtered view.

Who Can Change What

Roles are enforced in the backend on every write. The frontend only mirrors them.

RoleCan Do
Read-OnlyView all dashboards, analysis and reports
TenantRead access, plus forecast editing for their own solution area
AdminFull create, read, update and delete: budget entry, data imports, user management and Clean Slate with Backfill

Why Two Databases And A Grid

Two structural choices shaped the app more than any framework did.

React 19 Vite Material-UI 7 AG-Grid Recharts Node.js 20 Express 4 PostgreSQL 15 Vitest Docker Kubernetes ArgoCD

Four Things I Would Change

Each one is a lesson about what becomes expensive later.

  1. Use TypeScript Throughout

    The backend especially would benefit from stronger type safety across the normalization pipeline, where a misnamed field is a wrong number.

  2. Log Who Changed What From Day One

    Knowing who changed which budget figure, and when, became a frequent question. Audit logging added late cannot recover the history that came before it.

  3. Build Backup And Recovery Early

    Database backup and disaster recovery tooling arrived as an afterthought. Dual-database seeding also needed careful transaction management to keep both databases consistent on startup.

  4. Deploy To Kubernetes From The Start

    The early Docker Compose setup matched production for development, but moving to GKE sooner would have bought scalability and observability earlier.

The hardest part of FinOps tooling is not the technology. It is the data normalization.

A reliable pipeline that handles different billing models, internal codenames and a taxonomy that matches nobody else’s, and makes new mappings easy when names change, was the foundation everything else depended on. The scorecard taught me to think about what “good” means in financial operations: a plain adherence percentage cannot tell a small subscription missing by 50% from a large one missing by 5%.
 Back to Projects