The Multi-Cloud Cost Visibility Challenge
Organizations running workloads across Azure, AWS, and Google Cloud face a fundamental reporting problem: each provider uses different schemas, different terminology, and different pricing models. A column called “Cost” in Azure means something slightly different from “BlendedCost” in AWS or “cost” in GCP billing exports. Resource types, service categories, and billing periods all use provider-specific names and structures. Combining this data into a unified report without manual cleanup requires a normalization step that standardizes the schema before analysis begins.
The FinOps Open Cost and Usage Specification (FOCUS) was created to solve exactly this problem. It defines a standardized schema for cost and usage data across cloud providers, making it possible to combine Azure, AWS, and GCP billing data into a single dataset that can be queried, visualized, and reported on without per-provider transformations. Azure Cost Management supports FOCUS as a native export format, making it the natural starting point for multi-cloud cost reporting.
This guide covers how to export Azure cost data in FOCUS format, normalize it for multi-cloud consumption, combine it with data from other providers, and build reports that give leadership a single view across all cloud spending.
Understanding the FOCUS Specification
FOCUS stands for FinOps Open Cost and Usage Specification. It is governed by the FinOps Foundation (part of the Linux Foundation) and defines a common schema that cloud providers, SaaS vendors, and data center cost tools can adopt. Major providers including Microsoft Azure, AWS, Google Cloud, Oracle Cloud, and several others now produce FOCUS-compatible cost data.
Key FOCUS Columns
The specification standardizes column names and their semantics:
| FOCUS Column | Description | Azure Equivalent |
|---|---|---|
| BilledCost | Amount billed for the charge | CostInBillingCurrency |
| EffectiveCost | Amortized cost including commitment discounts | CostInBillingCurrency (amortized view) |
| ChargePeriodStart | Start of the charge period (UTC) | Date |
| ChargePeriodEnd | End of the charge period | N/A (derived) |
| ServiceCategory | High-level service classification (Compute, Storage, etc.) | ServiceFamily |
| ServiceName | Specific service name | MeterCategory |
| ResourceId | Unique identifier for the resource | ResourceId (ARM path) |
| ResourceName | Display name of the resource | ResourceName |
| Provider | Cloud provider name | “Microsoft” |
| ChargeCategory | Type of charge (Usage, Purchase, Tax, Credit) | ChargeType |
| CommitmentDiscountId | Reservation or savings plan identifier | BenefitId |
| PricingUnit | Unit that pricing is based on | UnitOfMeasure |
The power of FOCUS is in these standardized names. When your reporting queries use ServiceCategory and BilledCost, those queries work identically against Azure data, AWS data, and GCP data — no conditional logic, no provider-specific column mappings.
Exporting Azure Cost Data in FOCUS Format
Azure Cost Management supports FOCUS as a native export type called FocusCost. Configure it through the portal or REST API.
Portal Configuration
Navigate to Cost Management → Exports → Add. Select Cost and usage details (FOCUS) as the dataset type. Configure a daily schedule with Parquet format and Snappy compression for optimal storage and query performance. The FOCUS export combines both actual and amortized cost perspectives into a single dataset, eliminating the need for separate exports.
REST API Configuration
# Create FOCUS export for multi-cloud reporting
$scope = "/subscriptions/your-subscription-id"
$exportName = "focus-multicloud-daily"
$token = (Get-AzAccessToken -ResourceUrl https://management.azure.com).Token
$body = @{
properties = @{
format = "Parquet"
compressionMode = "snappy"
partitionData = $true
dataOverwriteBehavior = "OverwritePreviousReport"
definition = @{
type = "FocusCost"
timeframe = "MonthToDate"
dataSet = @{ granularity = "Daily" }
}
deliveryInfo = @{
destination = @{
type = "AzureBlob"
container = "multicloud-costs"
resourceId = "/subscriptions/$subId/resourceGroups/rg-finops/providers/Microsoft.Storage/storageAccounts/stmulticloudcosts"
rootFolderPath = "azure"
}
}
schedule = @{
status = "Active"
recurrence = "Daily"
recurrencePeriod = @{
from = "2026-04-01T00:00:00Z"
to = "2029-04-01T00:00:00Z"
}
}
}
} | ConvertTo-Json -Depth 10
$headers = @{ Authorization = "Bearer $token"; "Content-Type" = "application/json" }
$uri = "https://management.azure.com$scope/providers/Microsoft.CostManagement/exports/${exportName}?api-version=2025-03-01"
Invoke-RestMethod -Uri $uri -Method PUT -Headers $headers -Body $body
Note the storage path uses azure as the root folder, keeping Azure data separate from other providers’ exports in the same storage account.
Collecting Data from Other Cloud Providers
AWS Cost and Usage Report (CUR 2.0)
AWS supports FOCUS alignment through CUR 2.0 (Cost and Usage Report version 2). Configure a CUR export in the AWS Billing Console, selecting FOCUS-compatible columns. The export lands in an S3 bucket as Parquet files. To bring this data into your Azure-based reporting pipeline, set up a scheduled copy from the S3 bucket to your Azure Storage account using Azure Data Factory, or use a Logic App with the S3 connector.
Google Cloud Billing Export
GCP exports billing data to BigQuery, and FOCUS alignment is available through the standard billing export schema. Export the data from BigQuery to Google Cloud Storage in Parquet format, then transfer to your Azure Storage account. Google Cloud offers a Storage Transfer Service that can push data to Azure Blob Storage on a schedule.
Storage Organization
Organize the multi-cloud cost data in your storage account by provider:
multicloud-costs/
├── azure/
│ └── focus-export/[YYYYMMDD-YYYYMMDD]/[RunID]/
├── aws/
│ └── cur-focus/[YYYYMMDD-YYYYMMDD]/
└── gcp/
└── billing-focus/[YYYYMMDD-YYYYMMDD]/
Normalizing Non-FOCUS Data
While major providers support FOCUS, you may encounter data sources that do not: older AWS CUR v1 exports, SaaS billing data, or on-premises infrastructure costs. These require normalization to the FOCUS schema before they can be combined.
Azure Data Factory Transformation
Data Factory’s mapping data flows provide a visual interface for schema transformation. Create a pipeline that reads the source data, maps columns to FOCUS equivalents, standardizes date formats and currency codes, and writes the output in Parquet format to the same storage account.
Common transformations include:
- Mapping AWS
line_item_blended_costto FOCUSBilledCost - Mapping AWS
line_item_usage_start_dateto FOCUSChargePeriodStart - Standardizing service names (AWS “Amazon Elastic Compute Cloud” → “Compute” service category)
- Adding a
Providercolumn with the source cloud name
PowerShell Normalization Script
For simpler sources, a PowerShell script handles the transformation:
# Normalize AWS CUR v1 data to FOCUS schema
$awsData = Import-Csv "aws-cur-export.csv"
$normalized = $awsData | ForEach-Object {
[PSCustomObject]@{
Provider = "AWS"
BilledCost = [double]$_.line_item_blended_cost
EffectiveCost = [double]$_.line_item_unblended_cost
ChargePeriodStart = [datetime]$_.line_item_usage_start_date
ServiceCategory = switch -Wildcard ($_.product_product_family) {
"Compute*" { "Compute" }
"Storage*" { "Storage" }
"Data Transfer*" { "Networking" }
default { "Other" }
}
ServiceName = $_.line_item_product_code
ResourceId = $_.line_item_resource_id
ResourceName = ($_.line_item_resource_id -split '/')[-1]
ChargeCategory = "Usage"
Region = $_.product_region
Currency = $_.line_item_currency_code
}
}
$normalized | Export-Csv "aws-focus-normalized.csv" -NoTypeInformation
Building Cross-Cloud Reports
Once all provider data sits in FOCUS format within a shared store, reporting becomes straightforward because every query uses the same column names.
KQL: Total Spend by Provider
// Monthly spend comparison across cloud providers
MultiCloudCosts
| where ChargePeriodStart >= ago(180d)
| summarize MonthlyCost = sum(BilledCost)
by Provider, Month = startofmonth(ChargePeriodStart)
| order by Month asc, Provider
| render columnchart with (kind=stacked, title="Monthly Cloud Spend by Provider")
KQL: Service Category Comparison
// Compare compute, storage, and networking costs across providers
MultiCloudCosts
| where ChargePeriodStart >= startofmonth(ago(30d))
| where ServiceCategory in ("Compute", "Storage", "Networking")
| summarize TotalCost = sum(BilledCost) by Provider, ServiceCategory
| render barchart with (kind=stacked, title="Cost by Service Category per Provider")
These queries produce identical results regardless of which provider sourced the data, because FOCUS standardizes the column names and value semantics. A “Compute” charge from Azure and a “Compute” charge from AWS appear in the same service category without any join logic or mapping tables.
Handling Multi-Cloud Complexity
Currency Normalization
Different billing accounts may use different currencies. The FOCUS specification includes a BillingCurrency column that identifies the currency for each record. For consolidated reporting in a single currency, add a currency conversion step using daily exchange rates. Azure Data Factory or a simple lookup table in ADX can handle this transformation.
Service Name Harmonization
While FOCUS standardizes ServiceCategory at a high level (Compute, Storage, Networking, Databases), the ServiceName column retains provider-specific names. Azure reports “Virtual Machines” while AWS reports “Amazon Elastic Compute Cloud” for equivalent services. If your reports need provider-equivalent grouping at the service level, maintain a mapping table:
// Service mapping table approach
let ServiceMap = datatable(ServiceName:string, NormalizedService:string) [
"Virtual Machines", "Compute - VMs",
"Amazon Elastic Compute Cloud", "Compute - VMs",
"Compute Engine", "Compute - VMs",
"Azure SQL Database", "Database - Relational",
"Amazon Relational Database Service", "Database - Relational",
"Cloud SQL", "Database - Relational"
];
MultiCloudCosts
| lookup ServiceMap on ServiceName
| extend Service = coalesce(NormalizedService, ServiceName)
| summarize Cost = sum(BilledCost) by Service, Provider
| render barchart
Commitment Discount Normalization
Reserved instances in Azure, Savings Plans in AWS, and Committed Use Discounts in GCP all serve the same purpose but use different mechanics. The FOCUS CommitmentDiscountId and CommitmentDiscountType columns provide a common framework for analyzing commitment-based discounts across providers. Use these to build consolidated commitment utilization reports.
Multi-Cloud Reporting Architecture
A production-grade multi-cloud reporting architecture typically includes these components:
- Data collection — Scheduled exports from each provider landing in a centralized storage account, organized by provider
- Normalization — Data Factory pipelines that transform non-FOCUS data into FOCUS format
- Storage — Azure Data Lake Storage Gen2 as the unified cost data lake
- Analytics — Azure Data Explorer for KQL-based analysis, or Microsoft Fabric for integrated analytics and reporting
- Visualization — Power BI dashboards connected to the analytics layer, with provider comparison views
The Microsoft FinOps toolkit’s FinOps hub architecture supports this pattern natively. It accepts FOCUS exports from multiple providers and surfaces the combined data through pre-built Power BI reports that include provider comparison views.
Practical Recommendations
Start by enabling FOCUS exports in Azure since it requires no additional tooling — just select the FocusCost export type. Even before you add data from other providers, the FOCUS format gives you a future-proof schema that will not require migration when you go multi-cloud.
For the AWS side, switch to CUR 2.0 (which supports FOCUS) if you are still using CUR v1. The FOCUS-aligned export eliminates most of the normalization work. For GCP, use the BigQuery billing export and transform to FOCUS schema during the data transfer process.
Invest in a service mapping table early. The high-level ServiceCategory standardization in FOCUS covers most executive-level reporting needs, but engineering teams want provider-equivalent comparison at the service level. A maintained mapping table prevents ad-hoc and inconsistent translations across different reports.
Start simple with provider-level comparison (total cost by provider month-over-month) before drilling into cross-provider service comparison. The first report that shows all cloud spending in one chart changes the conversation from “how much do we spend on Azure” and “how much do we spend on AWS” asked separately to “how much do we spend on cloud” asked once, with the breakdown visible on the same page.
Multi-cloud cost reporting is not about choosing a winner between providers. It is about having accurate, normalized data that supports informed decisions about where to run workloads, how to negotiate rates, and how to allocate shared cloud investment across the business units that benefit from it. FOCUS makes that goal achievable without building a custom ETL pipeline for every new data source.
For more details, refer to the official documentation: What is Microsoft Cost Management.