Case Study

FinOps Reporting & Budget Platform

A cloud financial operations platform I built to replace manual spreadsheet reporting with automated spend tracking, forecasting, and executive scorecards across AWS, Azure, and GCP — covering $48M+ in annual cloud spend.

3 Cloud Providers
100+ Subscriptions
$48M+ Annual Spend
129 Unit Tests

The Problem

Cloud cost reporting at enterprise scale was a manual, error-prone process.

Actuals were copied from Ternary into Excel every month. Budget variance analysis meant pivoting through provider billing consoles. Year-end projections were educated guesses. With 100+ cloud subscriptions across 16 solutions and three providers, nobody had a single view of where the money was going — or where it was headed.

Leadership needed answers to questions like: Are we going to hit budget this year? Which subscriptions are driving overruns? What changed month over month? And they needed those answers without waiting for someone to update a spreadsheet.

Scale

MetricValue
Cloud Providers3 (AWS, Azure, GCP)
Solutions Tracked16+
Cloud Subscriptions100+
Historical DataFY23 (July 2022) through present
Vendors Tracked11 (3 with detailed solution-level breakdowns)
Unit Tests129 (80 backend + 49 frontend)

Core Capabilities

Six systems working together to turn raw billing data into executive-ready answers.

Automated Actuals
Direct Ternary API integration
  • Pulls actuals and forecasts automatically on every app restart — no manual exports
  • Azure uses EffectiveCost (amortized); AWS and GCP use BilledCost
  • Three alias maps normalize provider names, subscription codenames, and business unit codes
  • Deduplicates records and upserts into PostgreSQL with no manual intervention
  • Budgets deliberately come from the authoritative Ops Committee workbook, not from Ternary
  • Adding a new alias is a one-line config change and redeploy
Forecasting Engine
Linear regression on 6-month actuals
  • Current month extrapolated from month-to-date daily average
  • Future months projected using current daily rate adjusted for days in month
  • Rolling forecast and year-end projections powered by linear regression on last 6 months
  • Budget auto-fill strategies: last actuals, 3-month average, or linear trend
Executive Dashboard
Structured on the FinOps Framework
  • Headline KPIs: Annual Budget, Projected Year-End Spend, Budget Remaining, Active Subscriptions
  • One card per FinOps domain — Understand, Quantify, Optimize, Manage — each with a red/amber/green status and a one-line verdict
  • Every card scrolls to a detail band that drills through to the page proving the number
  • Headline data loads in a single call; heavier per-section detail lazy-loads so first paint isn’t blocked
  • COGS vs. Non-COGS breakdown with trend analysis for gross margin reporting
Solution Scorecard
Letter grades A+ through D−
  • Budget Adherence (45%), Risk & Anomalies (20%), Spend Stability (15%), Cost Efficiency (10%), Forecast Accuracy (10%)
  • Over-budget penalized at 2× the rate of under-budget to reflect asymmetric financial risk
  • Magnitude-based penalty scales with absolute dollar variance, not just percentage
  • Methodology transparency — every grade component is visible and explained
Actionable Insights
Eight analysis tabs
  • Month-over-month and year-over-year cost movement with configurable thresholds
  • Anomaly detection: spending spikes (>25% MoM) and dormant spend (<$1K/month over 3+ months)
  • Rolling forecast, year-end projection, and budget burn-down
  • Budget alerts categorized as Critical, At Risk, Warning, or On Track
  • COGS analysis for gross margin reporting
Vendor Tracking
11 vendors with solution-level breakdowns
  • Monthly budget vs. actual per vendor with COGS/Non-COGS breakdown
  • Cumulative variance tracking across the fiscal year
  • Detailed solution-level spend for complex vendors (Elastic Cloud, Okta CIAM, New Relic)
  • Up to 34 solutions and 9 sub-subscriptions tracked within a single vendor
Monthly Report Generation
The deck, built from live data
  • Renders the Monthly Cloud Report’s chart and table pages directly from the database
  • Sized and styled for snipping straight into the executive deck — in WellSky deck colors
  • Cumulative actual, budget, and forecast per cloud, with fiscal-YTD variance headers
  • Replaced the hand-built Excel charts that used to be rebuilt every month
  • Reconciles to the published deck figures line for line
AI & Token Spend
Per-user AI cost attribution
  • Service-level AI spend by business unit, alongside disaster recovery spend
  • Two BigQuery-backed views attribute AI coding-tool cost down to the individual user
  • Billed cost is reconciled against telemetry, with an explicit “unattributed” row so totals always tie
  • Every BigQuery scan is cached for ten minutes and bounded to a trailing window — the query itself costs money
  • Caches invalidate automatically after each AI spend sync
Ask It Instead — ARIA

Twelve tools in ARIA, the FinOps AI agent I built on top of this platform, read from these APIs — spend, budget variance, scorecards, vendors, COGS, over-budget, AI and DR spend. Anyone who doesn’t want to find the right tab can ask in plain English and get an answer that links back to the exact filtered view here.

Architecture & Data Flow

A full-stack web app with two PostgreSQL databases, served directly from the Express backend — no separate frontend server required.

 Ternary API (FOCUS_BILLING)
AWS · Azure · GCP · BilledCost + EffectiveCost · 50,000-row daily feed
↓ on app startup — syncCurrentMonth()
 Express Backend (Node.js 20)
Provider normalization · Subscription alias mapping · Forecast projection · Serves React SPA
 PostgreSQL: cloud_budget
Financial records, subscriptions, budgets, forecasts
 PostgreSQL: vendor_spend
Detailed vendor breakdowns, sub-subscriptions
 React SPA
React 19 · Material-UI 7 · AG-Grid · Recharts · Served by Express from same container
 GKE (Skystage) + Okta SSO — VPN-only access

Data Flow

1

On app startup, syncCurrentMonth() fetches current-month actuals from the Ternary API, extrapolates a month-to-date forecast, and projects remaining fiscal year months.

2

A one-time backfill script populates FY23 through present from historical billing data. A marker file prevents re-runs.

3

FY gap-fill creates empty records for all 12 fiscal year months per subscription so the budget entry UI always has complete period coverage.

4

Budgets are entered manually through the app’s budget page. Admins can use auto-fill strategies (last actuals, 3-month average, linear trend) to pre-populate before adjusting.

5

Provider name normalization maps Ternary’s internal labels to canonical names via three configurable alias maps — provider, subscription, and business unit. Adding a new alias is a one-line config change.

Tech Stack

LayerTechnology
FrontendReact 19, Vite, Material-UI 7, AG-Grid, Recharts
BackendNode.js 20, Express 4
DatabasePostgreSQL 15 (2 databases: core financial + vendor spend)
Data SourceTernary API (FOCUS_BILLING with BilledCost + EffectiveCost)
AuthJWT + bcrypt (app-level), Okta SSO (platform-level)
TestingVitest (80 backend + 49 frontend tests)
DeploymentDocker multi-stage build, Kubernetes, ArgoCD

Key Technical Decisions

The “why” behind each major design choice — not just what was built, but the reasoning and trade-offs considered.

The original workflow required someone to export data from Ternary, transform it in Excel, and import it. I replaced it with a direct API integration — for actuals and forecasts:

  • Zero manual intervention — data pulls automatically on startup, no scheduled task required
  • Normalization pipeline — three alias maps handle provider codenames, subscription names, and business unit codes that vary across systems
  • Deduplication and upsert — safe to run repeatedly without creating duplicate records
  • Easy extensibility — adding a new subscription alias is one line of config, not a data migration

Budgets are the deliberate exception. Ternary has a budget feature, and syncing it would have been the easy call. I turned it off. Ternary’s budget entries are scoped too inconsistently to reconcile one-to-one against the Cloud Report Ops Committee workbook, which is the number leadership actually governs against. Two competing budget figures in one tool is worse than one manual import step, so budgets flow from the Ops Committee workbook through the import page and every budget-derived view reads from that single source.

Trade-off accepted: API dependency means if Ternary is down on startup, the app serves stale actuals. Acceptable given the daily cadence of budget reporting vs. the alternative of manual error-prone exports.

A naive “distance from budget” score treats 10% under-budget and 10% over-budget as equally bad. In practice, overspending is far more damaging than underspending. I designed the scorecard to reflect this:

  • 2× penalty for over-budget — the budget adherence component penalizes overspending at twice the rate of underspending
  • Magnitude-based scaling — a $500K overrun on a $1M budget scores worse than a $5K overrun on a $10K budget, even if the percentage is identical
  • Five weighted dimensions — budget adherence alone doesn’t tell the full story; stability, risk, efficiency, and forecast accuracy each contribute
  • Methodology transparency — every component of every grade is visible in the UI so stakeholders can see exactly why a subscription received its score

This design pushes decision-makers toward the right priorities without requiring manual judgment on every subscription.

Core financial data lives in cloud_budget. Detailed vendor spend breakdowns live in vendor_spend. This separation was intentional:

  • Different granularity — vendor spend (Elastic Cloud, Okta CIAM, New Relic) has 34 solutions and 9 sub-subscriptions that don’t map cleanly to the core subscription schema
  • Different update cadence — vendor breakdowns are entered manually on a different schedule than automated Ternary actuals
  • Independent schema evolution — adding a new vendor sub-subscription field doesn’t require a migration against the main financial database
  • Clean backup and restore paths — a vendor_spend restore doesn’t touch core financial records

Every component in the app thinks in fiscal years (July 1 through June 30), not calendar years. This was a foundational design decision made before the first line of code:

  • Date pickers default to FY ranges
  • Scorecards filter to the current FY automatically
  • Forecasts project through June of the current FY
  • Budget entry UI presents all 12 fiscal months grouped by quarter
  • Automatic rollover — when July 1 arrives, everything switches to the new FY with no manual configuration

Why it mattered: A calendar-year-aware system would have required constant manual correction every July. FY-native design meant the app matched how leadership actually thought about budgets from day one.

Budget entry for 100+ subscriptions × 12 months is inherently table-heavy work. Standard form inputs would have been unusable at this scale.

  • Excel-like inline editing — stakeholders could click a cell and type, matching the workflow they already knew from spreadsheets
  • Column sorting, filtering, and pinning — navigating 100+ rows without these is painful
  • Row selection and bulk operations — apply a budget value to a filtered set of subscriptions in one action
  • CSV/Excel export — finance team could pull a snapshot at any time without building a report

Trade-off accepted: AG-Grid Community license limits some Enterprise features. The missing features (server-side row models, advanced pivot) weren’t required for this use case.

Three roles with progressively more access, enforced on every write operation in the backend — not just the frontend:

  • Read-Only — view all dashboards, analysis, and reports
  • Tenant — read access plus forecast editing for their own solution area
  • Admin — full CRUD: budget entry, data imports, user management, Clean Slate + Backfill operations

Role checks happen in Express middleware on every write endpoint. Frontend role gating is a UX convenience only — the API rejects unauthorized requests regardless of what the client sends.

Features at a Glance

 Analysis & Reporting
  • Executive dashboard with budget utilization gauge and color-coded KPI cards
  • Solution scorecard with five-dimension grading and methodology transparency
  • Budget variance analysis at solution, subscription, and provider levels
  • MoM and YoY cost movement analysis with configurable thresholds
  • Anomaly detection: spending spikes and dormant spend
  • COGS vs. Non-COGS breakdown with trend for gross margin reporting
  • Rolling forecast with linear regression and year-end projection
 Data Management
  • Automated actuals from Ternary API with provider/subscription normalization
  • Manual budget and forecast entry with auto-fill strategies
  • CSV/Excel import supporting normalized and pivot table formats
  • Clean Slate + Backfill for full historical re-pull from Ternary
  • Vendor budget tracking with COGS/Non-COGS and cumulative variance
  • CSV export from every data table
 UX & Platform
  • Split-panel financial data view with resizable chart and AG-Grid panels
  • Light/dark mode with system preference detection
  • Role-based access: Admin (full CRUD), Tenant (read + forecast editing), Read-Only
  • Lazy-loaded pages with 30-second response caching
  • Swagger/OpenAPI docs on every endpoint
  • Visitor analytics tracking engagement across sessions

Security

Okta SSO
Platform-level authentication via Okta, VPN-only access. No public internet exposure.
JWT Authentication
24-hour token expiry with fresh role lookups on each request. bcrypt password hashing at cost factor 10.
Role-Based Authorization
All write operations require Admin role enforced in the backend. Frontend gating is UX only.
Parameterized SQL
No string interpolation in queries throughout the entire codebase. SQL injection is architecturally prevented.
Rate Limiting
100 requests per 15-minute window on all write endpoints.
Secret Manager
API key stored in GCP Secret Manager. No credentials in code or environment variables.

What I Learned

What Worked Well
  • PostgreSQL window functions eliminated complex application-side delta and cumulative calculation logic
  • Dual database architecture kept vendor data cleanly separated from core financials — independent migration paths and backup schedules
  • AG-Grid provided an Excel-like data entry UX that stakeholders were immediately comfortable with
  • Docker Compose made local development identical to production — no environment-specific bugs
  • Role-based access prevented accidental data changes by non-admin users without requiring complex UI workflows
  • Fiscal year-native design meant zero manual configuration when July 1 arrived
  • Automated Ternary sync eliminated the most error-prone step in the old monthly process
Challenges
  • Data normalization was the hardest part — cloud providers use different billing models, Ternary uses internal codenames, and the organization’s solution taxonomy didn’t match anyone else’s naming convention
  • CSV import mapping needed fuzzy matching because subscription names vary across systems and change over time
  • Dual database seeding required careful transaction management to keep both databases consistent on startup
  • Budget data entry for 100+ subscriptions × 12 months was tedious before bulk auto-fill tools were added
  • AG-Grid Community vs. Enterprise licensing limited some features that would have improved the bulk edit UX
Would Do Differently
  • Use TypeScript throughout — the backend especially would benefit from stronger type safety across the normalization pipeline
  • Add database backup and disaster recovery tooling earlier, not as an afterthought
  • Implement audit logging from day one — knowing who changed what budget figure and when became a frequent question
  • Deploy to GKE instead of Docker Compose from the start for better scalability and observability

This project taught me that the hardest part of FinOps tooling isn’t the technology — it’s the data normalization. Building a reliable pipeline that handles provider billing model differences, internal codenames, and a taxonomy that doesn’t match anyone else’s — and makes it easy to add new mappings when names change — was the foundation everything else depended on. The scorecard design also pushed me to think carefully about what “good” means in financial operations — a simple budget adherence percentage doesn’t capture the asymmetry between overspending and underspending, or the difference between a small subscription missing by 50% and a large one missing by 5%.

 Back to Projects