GrandLine Architecture Intelligence. Multi-Cloud FinOps Primer
Revision: 2026-Q2 · Audience: FinOps leads, platform engineers, and architects who are asked to "tie cost to architecture" across AWS, Azure, and GCP.
This is not a FinOps methodology document. the FinOps Foundation already writes those better than we could. This paper is about how the three clouds expose cost data, how GrandLine normalises it, and how to align tags, rules, and ownership so your finance team gets the numbers they actually need.
1. Why multi-cloud FinOps is awkward
Each cloud publishes cost data in a different shape, on a different cadence, with different granularity, at a different price. A "resource" in AWS's CUR is not the same row as a "resource" in Azure's Cost Management export, and neither is the same as an entry in a GCP Billing BigQuery dataset. The terms of art. reservation, commitment, committed use. do not line up. Even "tag" does not mean the same thing in Azure (no case enforcement historically) as it does in AWS (case-sensitive since birth).
A team running on one cloud can build a dashboard directly on the native export. A team running on three clouds ends up with a semantic integration problem that looks simple from ten feet away and takes six months to get right. GrandLine does this integration once, normalises it, and pins the results to the architecture graph so cost is a property of a resource, not a row in a spreadsheet.
2. How the three clouds expose cost
2.1 AWS. Cost and Usage Report (CUR 2.0)
- Format: Parquet, delivered to S3 hourly or daily.
- Granularity: line-item per hour, per resource (when
RESOURCESandSPLIT_COST_ALLOCATION_DATAare enabled on the CUR). - Latency: final data 24 hours after usage.
- Identifiers:
line_item_resource_id(often the ARN, often empty for non-resource charges like data transfer or tax). - Reserved Instances and Savings Plans: surfaced as
pricing_term = Reservedandsavings_plan_*columns. Amortization is handled by CUR 2.0 natively if you opt in. - Gotchas: region tax charges, cross-account Reserved Instance and Savings Plan sharing, Marketplace charges (third-party AMIs etc.) show up as separate rows with no
line_item_resource_id.
2.2 Azure. Microsoft Cost Management + Billing exports
- Format: CSV, delivered to a blob container in a Storage Account daily or weekly.
- Granularity: one row per meter per resource per day. Amortized and actual views are separate exports.
- Latency: 24–72 hours.
- Identifiers:
ResourceId(the full ARM path),SubscriptionId,ResourceGroup. - Commitments: Azure Reservations and Azure Savings Plans for Compute are first-class rows; amortised view attributes commitment usage back to the consuming resource.
- Gotchas: tag data only appears once you enable tag inheritance at the management-group level; pre-2022 data lacks case consistency; Marketplace charges lack ARM path.
2.3 GCP. Billing BigQuery export
- Format: a streaming BigQuery dataset, not a file dump.
- Granularity:
gcp_billing_export_v1_contains per-SKU, per-day, per-project rows. Detailed export (gcp_billing_export_resource_v1_) adds a resource-level row for services that support it (Compute Engine, GKE, Cloud Storage, a few others). - Latency: hours, often sub-day for fresh data; reconciliation up to 3 days.
- Identifiers:
resource.name,resource.global_name. Many services still do not emit resource-level rows; Cloud Run and BigQuery itself were late additions. - Commitments: Committed Use Discounts (CUDs) appear as credits applied to matching SKUs; spend-based CUDs are attributed broadly across eligible usage.
- Gotchas: no single "invoice total" query. you assemble it. Partial detailed coverage means "resource-level cost" is a partial claim for GCP until you know which services are covered.
2.4 Side-by-side
| Property | AWS CUR 2.0 | Azure Cost Management | GCP Billing BQ |
|---|---|---|---|
| Native destination | S3 (Parquet) | Storage Account blob container (CSV) | BigQuery (streaming table) |
| Granularity | Line-item per hour | Meter per resource per day | SKU per project (+ resource for some services) |
| Tags surfaced | Resource tags + Cost allocation tags | Resource tags + inheritance config | Project labels + some resource labels |
| Amortised commitments | Yes (CUR 2.0) | Yes (amortised export) | Yes (credit rows) |
| Reconciliation | 24 hours | 24–72 hours | up to 3 days |
| Tie-back to inventory | ARN | ARM resource id | resource.name (partial) |
The upshot: AWS has the cleanest per-resource picture, Azure the most uniform tag story, and GCP the freshest data but the most partial resource coverage. You cannot write one query that works on all three; you write one abstraction that does.
3. How GrandLine normalises it
GrandLine ingests all three and writes rows to a single table. CostDaily in the primary schema. with the columns:
(tenantId, accountId, resourceId?, service, date, costUsd, usageQty?, usageUnit?)
Key normalisation decisions:
- Currency is USD. We convert Azure and GCP rows using the cloud's own daily FX (from the native export) so we match the customer's invoice. Non-USD primary currencies are supported on Enterprise. the dashboard then shows both the USD normalised value and the customer's native currency.
- Service names are canonicalised. AWS
AmazonEC2becomesec2, AzureVirtual Machinesbecomescompute, GCPCompute Enginebecomescompute. The full provider-native service name is preserved inmetadatafor drill-down. - Resource linkage is best-effort. When the native row carries a resource identifier, we join to
Resource.nativeId. When it does not (AWS "data transfer" without ARN, GCP SKU with no resource export), we attribute to a synthetic resource per(accountId, service)so the charge is still visible but never falsely pinned to a specific VM. - Amortised view by default. We default the dashboard to the amortised commitment view because that is what CFOs ask for.
- Idempotent ingestion. The daily ingestion job is idempotent on
(tenantId, accountId, resourceId, service, date). reruns do not double-count.
Implementation: packages/connectors-/src/cost/ for the provider readers, packages/finops-core/src/normalise.ts for the canonicalisation, apps/worker/src/cost-ingest.ts for the scheduled job.
4. Tagging strategy. the part your finance team cares about
A good tag strategy is worth more than any cost-optimisation feature. We push our customers (politely) toward four mandatory tag keys across all three clouds:
env.prod/staging/dev/sandbox.owner. an email alias or team handle that resolves to a human.cost_center. a finance code used by the GL.app. the application name the resource belongs to.
Why these four and not eight: a tag policy with more than four mandatory keys is not followed. We have seen this repeatedly. Four is the ceiling for consistent adoption.
4.1 Enforcement
- AWS. Organizations
Tag Policiesto enforce values (e.g.,env∈ {prod, staging, dev}); SCPs to denyec2:RunInstancesmissing theenvtag. - Azure. Policy definitions
require-tagon Management Group root; inherited automatically through subscriptions. - GCP. Labels (not tags. Google's "tags" are a different, IAM-ish concept); Organization Policy constraints to require specific label keys on projects.
4.2 What GrandLine does with them
- Cost-by-owner, cost-by-app, cost-by-env. all derive from these four tags.
- Chargeback reports. generated per
cost_centerand delivered as a DOCX or CSV on a schedule you configure. - Untagged resources. surfaced as a first-class finding (
RULE-FINOPS-001. resource missing required tags), with severitymediumby default. This is usually the most impactful change a customer makes in the first month.
5. Aligning cost to architecture
The architecture graph is the frame every cost number hangs on.
- Every resource in the graph has a
costUsdattribute. MTD, prior-month, and 30-day trailing. - Every container node (account, VPC, cluster) aggregates the cost of its children.
- Every edge can optionally show data-transfer cost (we only draw this on the cost-analysis view. it would clutter the architecture view otherwise).
This lets architects and FinOps practitioners ask. and get answers to. questions that native consoles handle poorly:
- "How much does this VPC cost?" → click the VPC, see the aggregate.
- "How much of that spend is inter-AZ data transfer?" → the cost-analysis view surfaces transfer charges as labelled edges.
- "Which services in this account are growing fastest?" → the per-service trend card filters on the account.
6. Recommendations that make sense
The quickest way to lose FinOps credibility is to produce recommendations that are wrong. GrandLine ships a small, conservative set.
6.1 Rightsizing
- Scope: EC2 / EBS / RDS on AWS; VMs / VMSS / Managed Disks / Azure SQL Database / Azure SQL Managed Instance / Azure Database for PostgreSQL flexible server / Azure Database for MySQL flexible server on Azure; Compute Engine / Persistent Disk / Cloud SQL on GCP.
- Method: p95 CPU and memory utilisation over a 14-day trailing window, with explicit guardrails (no recommendation on instances < 14 days old; no recommendation on instances tagged
critical=truewithout supervisor approval). - Output: a suggested instance family + size, the estimated annualised savings, and the confidence (high/medium/low). We never output a savings number without a confidence.
- Trust: we do not execute changes. The customer applies the change through their own pipeline.
6.2 Commitment insight (NOT automatic purchase)
- We report on current commitment coverage, utilisation, and the gap between the two.
- We model how a given SP / RI / CUD purchase would change the cost profile based on the trailing 90 days.
- We do not automatically purchase anything. Committing capital is a finance decision, not a software decision. We say this explicitly in the UI and in every report.
6.3 Idle / orphaned resources
- Elastic IPs without an ENI, unattached EBS / Managed Disks / Persistent Disks, idle NAT Gateways (bytes < 10 GB/month), stale snapshots (> 180 days), unused load balancers (0 requests 30 days).
- Severity:
lowtomediumdepending on cost. Every finding carries the estimated monthly saving but the customer must still make the judgment call about whether the resource is truly idle (some snapshots are compliance artefacts, some NAT gateways are DR-only).
6.4 What we do NOT claim
- We do not claim to generate six-figure savings from a template. Real savings come from process changes. better tagging, reviewing idle resources monthly, consolidating RIs across the organisation. We support those; we do not replace them.
- We do not claim to optimise BigQuery slot purchases, Snowflake warehouses, or other SaaS products outside the three cloud billing exports. FinOps for SaaS is a different problem.
- We do not sell "executable" recommendations as a product feature. That would require write access to the customer cloud; GrandLine is read-only by design.
8. Exports, reports, and integrations
- Scheduled reports. monthly FinOps PDF / DOCX per scope (tenant, account,
cost_center), delivered to a configurable email distribution list and/or S3 prefix. - CSV export. the raw
CostDailyrows, so your finance team can pull them into Excel, Power BI, or a data warehouse. - API. REST endpoints for every view surfaced in the UI. FinOps teams typically pull through the API into their own internal portal.
- Webhooks. we can push a "new high-severity finding" or "monthly report ready" event to any HTTPS endpoint or Slack channel.
9. What to read next
docs/11-connector-aws.md. the AWS connector, including cost-export configuration.docs/12-connector-azure.md. the Azure connector, including Cost Management export wiring.docs/13-connector-gcp.md. the GCP connector, including Billing BQ setup.docs/06-data-model.md. theCostDailytable and how it joins toResource.docs/20-operations-white-paper.md. how we run the ingestion pipeline at scale.
10. Contact
FinOps questions and feature requests: [email protected]. If you are already a customer, file the ticket. FinOps feedback is one of the easiest ways to influence the roadmap because the changes are usually small and contained.