Here’s a question that sounds simple until you try to answer it across multiple clouds:

“How much did we spend on compute last month?”

On AWS, compute spend is buried in lineItem/UsageType with values like BoxUsage:c5.large, EBSOptimized:m5.xlarge, and DataTransfer-Regional-Bytes. On Azure, it’s in MeterCategory with values like Virtual Machines, Bandwidth, and Storage. On GCP, it’s service.description: "Compute Engine" combined with sku.description.

Same question, three completely different schemas, three completely different values. Writing one query that answers it across all three requires either deeply provider-specific logic in every analysis, or a normalisation layer that unifies them first.

We chose the normalisation layer. And we chose an existing standard for it rather than inventing our own.


What FOCUS Is

FOCUS — the FinOps Open Cost and Usage Specification — is an open standard maintained by the FinOps Foundation. Version 1.2 defines a common schema for cloud billing data with 37 standardised columns that every major cloud provider’s billing export should map to.

The core insight: the underlying concepts are the same across every cloud. Every provider has services, resources, charges, time periods, and costs. FOCUS names them consistently:

FOCUS ColumnAWS equivalentAzure equivalentGCP equivalent
ServiceNamelineItem/ProductCodeMeterCategoryservice.description
ResourceIdlineItem/ResourceIdResourceIdresource.name
ChargeTypelineItem/LineItemTypeChargeTypetype
BilledCostlineItem/BlendedCostCostInBillingCurrencycost
ChargePeriodStartlineItem/UsageStartDateDateusage.start_time

Once billing data is in FOCUS format, a single query runs across all three clouds. Anomaly detection, budget tracking, SLA credit calculations, recommendations — all of it is written once.


How We Store It

Every billing record ingested from any provider goes through a transformation pipeline that maps it to FOCUS 1.2 before storage:

class FocusBillingRecord(Base):
    __tablename__ = "focus_billing_records"

    # FOCUS 1.2 required columns
    billed_cost = Column(DECIMAL(12, 2), nullable=False)
    billing_currency = Column(String(3), nullable=False)
    billing_period_start = Column(DateTime(timezone=True), nullable=False)
    billing_period_end = Column(DateTime(timezone=True), nullable=False)
    charge_period_start = Column(DateTime(timezone=True), nullable=False)
    charge_period_end = Column(DateTime(timezone=True), nullable=False)
    charge_type = Column(String(64), nullable=False)    # Usage, Purchase, Tax, etc.
    provider_name = Column(String(64), nullable=False)  # AWS, Azure, GCP
    service_name = Column(String(256), nullable=False)
    resource_id = Column(String(512), nullable=True)
    region_id = Column(String(64), nullable=True)
    # ... 30+ more columns

The pipeline transforms each provider’s native format into these columns before writing. When data is queried, it looks the same regardless of origin.

AWS CURAzure UsageGCP BillingFOCUS 1.2Transform Pipeline37 standard columnsfocus_billing_recordsOne schema.All three providers.

What This Unlocks

Unified anomaly detection

Our anomaly detection runs z-score analysis on daily spend grouped by service. With FOCUS normalisation, “EC2” from AWS, “Virtual Machines” from Azure, and “Compute Engine” from GCP all get the same treatment. One algorithm, one code path.

Accurate SLA credit calculations

When we calculate the credit value for a breach, we look up monthly spend for the affected service. With FOCUS, that query is:

SELECT SUM(billed_cost)
FROM focus_billing_records
WHERE tenant_id = :tenant
  AND service_name ILIKE :service
  AND charge_period_start >= :month_start
  AND charge_period_end <= :month_end

The same query works for AWS, Azure, and GCP. No provider-specific code in the credit calculation.

Budget tracking

Budgets can be defined against any service name — and they work across providers automatically. A budget for “compute” aggregates EC2, Azure VMs, and GCP Compute Engine without the customer needing to know the provider-specific names.


The Real Cost of Not Having a Standard

Before we adopted FOCUS, we had provider-specific billing tables: aws_billing_records, azure_billing_records, gcp_billing_records. Every analysis that needed to cross clouds required a UNION of three queries with manual field mapping. Anomaly detection was three separate implementations. Budget queries were three separate implementations.

Every feature that touched billing data was 3x the code. 3x the tests. 3x the bugs.

Adopting FOCUS was a painful migration — six weeks of data transformation work, rewriting ingestion pipelines, migrating historical data. But it eliminated a class of complexity that would have compounded indefinitely.


FOCUS Isn’t Perfect

The standard is still evolving. Some provider-specific concepts don’t map cleanly (AWS Reserved Instance billing has no clean FOCUS equivalent yet). Some columns are optional in the spec and inconsistently populated in practice.

We keep provider-specific metadata in a JSONB column alongside the standardised fields — so nothing is lost, even when it doesn’t fit the standard.

But the 80% that FOCUS covers is precisely the 80% that matters for cost analysis, anomaly detection, and credit calculation. The standard earns its complexity.


Fintropy is a multi-cloud FinOps platform in private beta. Learn more at nuvikatech.com