Load Greenpixie data into CloudHealth (Azure)
How to integrate your enriched Azure cloud usage report data into CloudHealth.
This guide takes you from an enriched Azure cost file in your Azure Blob Storage container through to a fused cost and carbon dataset inside CloudHealth. It is written for a platform, FinOps or data engineer who already has the enriched file. That file carries the standard Azure cost-export columns plus Greenpixie's numeric sustainability columns for energy in kWh, carbon in tonnes CO₂e and water in litres. Where the procedure is Broadcom's or Microsoft's, this guide links to their documentation and covers what sustainability data changes.
The integration uses CloudHealth Data Connect, the Bring Your Own Data (BYOD) feature, to load the file as a custom dataset. Custom Datasets then joins it onto the native Azure cost model on shared keys such as resource ID and meter.
For an AWS estate, follow the AWS version of this guide.
Before you start
You need an enriched Azure cost file landing in a Blob Storage container you control, carrying both the standard Azure cost-export columns and Greenpixie's numeric sustainability columns.
Data Connect's own limits shape that file before you build it, and Bring Your Own Data with Data Connect is the authority on the current numbers. The limits that change what you produce:
- Data Connect is available only at the top-level organisational unit (TLOU) of the CloudHealth tenant. Create the connection there. A FlexOrg administrator without TLOU access will not find the feature.
- The schema cannot be edited after creation. The sample file you supply defines it, so get that file right the first time.
- Maximum 100 MB per file, as CSV or Parquet, and only one file per subscription per billing month. Together these mean the export has to be pre-aggregated down to one file per subscription-month.
- Column names must match
^[a-zA-Z][a-zA-Z0-9_-]*$, so letters, numbers, underscores and hyphens only, starting with a letter. Standard Azure export names such asResourceId,MeterIdandSubscriptionIdalready comply, as do Greenpixie's numeric sustainability columns.
Azure cost exports also differ by billing-account type, in that Enterprise Agreement (EA) and Microsoft Customer Agreement (MCA) use different account and subscription column names, and exports come as actual or amortized cost. Mapping your identifier columns onto CloudHealth's FOCUS fields should let the join work regardless of which your tenant uses.
Prepare the enriched file
- Confirm the identifier columns are present and clean. The join back to the cost model depends on columns that also exist in CloudHealth's native Azure dataset. Include at minimum:
ResourceId, the full Azure Resource Manager (ARM) resource URI, in the form/subscriptions/.../resourceGroups/.../providers/..., and the strongest join key.MeterIdand/orMeterName, the meter-level equivalent of an AWS SKU or usage type, for meter-level joins and where resource IDs are blank, such as some marketplace, data-transfer or tax lines.SubscriptionId, also writtenSubscriptionGuid, the subscription that owns the resource. On EA you may also carryAccountNameandAccountOwnerId; on MCA,billingProfileIdandinvoiceSectionName.- A period key,
BillingPeriodStartDateorDatetruncated to the day or month.
- Convert the period columns to
YYYY-MM-DD. CloudHealth infers a column as a Date only inYYYY-MM-DDform, and as a Timestamp only inYYYY-MM-DDTHH:MM:SSform. Anything else is stored as a string, and a string period key cannot be used as a date in reports or matched cleanly against CloudHealth's own period fields. Check the format your export actually writes forDate,BillingPeriodStartDateandBillingPeriodEndDate, and convert where it differs. - Fix the grain. Azure usage detail is granular to the meter, resource and day, while Greenpixie metrics are usually per-resource and often daily. Decide the join grain now, where resource ID, meter and day or month is the natural meeting point, and pre-aggregate the coarser side so you have exactly one row per key. A one-to-many match will multiply rows and inflate both cost and emissions.
- Keep sustainability columns strictly numeric so CloudHealth classifies them as measures, not dimensions, for example
usage_electricity_consumption_kwh,total_tonnes_co2e,total_water_litres. - Get each subscription-month under 100 MB. Because the folder structure allows one file per subscription per month and nothing finer, a single subscription-month that exceeds 100 MB in Parquet cannot be split. Reduce it instead, either by pre-aggregating to a coarser grain, so resource ID and month where you currently have resource ID and day, or by dropping columns that serve neither the join nor the report. Parquet is preferred for size and typed columns.
- Produce a representative sample file containing every column with realistic values, which is what defines the immutable schema.
Lay out the files in Blob Storage
Data Connect reads one fixed folder structure, set out on Broadcom's Bring Your Own Data page, and it does not collect a file written to a free-form prefix. Accepted extensions are .csv, .csv.gz and .parquet. A worked Greenpixie path is below.
BYOD/greenpixie_sustainability/00000000-0000-0000-0000-000000000000/2026-07/1/gpx-enriched.parquetRead against that structure, BYOD is a literal prefix, greenpixie_sustainability the Data Connect dataset name you enter under Metadata, the GUID the Azure subscription ID the data belongs to, the YYYY-MM folder the billing month, and 1 an incrementing version folder used when you re-upload the same month.
Grant CloudHealth access to your Blob Storage container
Where the AWS integration uses an Identity and Access Management (IAM) assume-role with an External ID, Azure authenticates through a service principal, which is an Entra ID or Azure AD app registration. Creating the registration is Microsoft's own procedure, documented at register an app and create a service principal. What this integration adds is the scope, being a Storage Blob Data Reader role on the container the enriched files land in.
az ad sp create-for-rbac \
--name "cloudhealth-greenpixie-reader" \
--role "Storage Blob Data Reader" \
--scopes "/subscriptions/<SUB_ID>/resourceGroups/<RG>/providers/Microsoft.Storage/storageAccounts/<ACCOUNT>/blobServices/default/containers/<CONTAINER>"The command returns appId, password and tenant, which CloudHealth's own fields call Client ID, Client Secret and Tenant ID. Capture the secret at this point, since it is not shown again.
Where the storage account restricts network access through firewall rules, private endpoints, or the "allow trusted Microsoft services" setting, permit CloudHealth's access. A blocked path shows up later, as a failure when CloudHealth tries to reach the storage location. Server-side encryption with customer-managed keys is transparent to a data-plane reader, so the service principal does not need Key Vault permissions to read encrypted blobs.
Note the storage account name, the container name, and the blob prefix where the enriched files land. Data Connect asks for the storage details alongside the Client ID, Client Secret and Tenant ID.
Set up Data Connect
Broadcom's Data Connect documentation covers the wizard under Setup & Configuration > Data Connect. Choose Custom File Upload as the connection type, since the automated connector option currently covers Databricks alone, and set Data Location to Azure Blob. The values it asks for that have to match what you wrote to Blob Storage:
- The dataset name under Metadata is the
<datasetName>folder in the path, sogreenpixie_sustainabilityin the example above. - The Account Identifier can be any unique identifier, and the subscription ID is the natural choice, matching the
<accountId>folder. - The Usage Month is given in
YYYY-MMformat and matches the month folder you wrote.
CloudHealth verifies it can reach the storage location before it opens the Schema section, so a storage firewall or private endpoint blocking the path shows up at that point. In the Schema section, enter the data location file path to your sample file and choose Get Schema.
Define the schema
CloudHealth reads the sample file, auto-classifies each column as a measure or a dimension, assigns a data type, and locks the result once you save. To change a saved schema, create a new connection with a corrected sample file. What needs correcting before you save:
usage_electricity_consumption_kwh,total_tonnes_co2eandtotal_water_litreshave to be numeric measures, andResourceId,MeterId/MeterName, subscription and period have to be dimensions.- The period column's inferred type has to be Date. Where it shows as a string, the file's date format is wrong and the sample needs regenerating before you save.
- Mapping Identifiers have to be set. These are CloudHealth's cloud-agnostic FOCUS fields, being ResourceId, ServiceName, Billingaccount and ChargePeriod. Map your identifier columns onto them, since that mapping is what lets the dataset line up with the native Azure data, and what the join depends on. Because the fields are cloud-agnostic, the join should hold whether your tenant's native dataset uses EA or MCA column names. Confirm it holds when you build the Custom Dataset.
The dataset imports and, within about 24 hours, appears as a Data Connect dataset in the Reports section and in classic FlexReports.
Load each new month
Write each new month's file into a new <YYYY-MM> folder under the same dataset and subscription path, using the same schema and column names. Data Connect collects on a 12-hour schedule, and Trigger Collection on the connection pulls a new file without waiting for the next scheduled run.
To correct a month already uploaded, write a complete replacement file containing every row for that month into a new <version> folder. CloudHealth does not merge partial updates, so the latest file must include all previously uploaded data for that month. Monitor row counts per period as a smoke test that new files are loading correctly.
Join onto the cost model
In Custom Datasets, create a new dataset over two sources, the native CloudHealth Azure cost dataset and your greenpixie_sustainability Data Connect dataset, then add a JOIN operation. Broadcom's Custom Datasets page covers the operations and the calculated columns.
Use a LEFT join with the Azure cost dataset as the left dataset, which keeps every cost line and attaches Greenpixie measures wherever the keys match. INNER restricts the result to resources Greenpixie has measured, which is rarely what you want.
Select join columns that match on both sides at the same grain, preferring the FOCUS mapping identifiers so the match does not depend on EA or MCA column naming.
- The primary key is resource ID,
ResourceId, which maps to the FOCUSResourceIdfield. - Add meter,
MeterIdorMeterName, for meter-level precision and to help on lines without a resource ID. - Add subscription and period, the FOCUS
BillingaccountandChargePeriodfields, to keep the match in scope and stop one row matching every period on the other side.
Compare the joined dataset's total cost against the unjoined Azure cost total as soon as the join runs; for a LEFT join they should be identical. If cost has inflated, a one-to-many match is fanning rows out, so revisit the grain or add join keys. Then add calculated columns for the metrics that matter, for example carbon_intensity_per_dollar = total_tonnes_co2e / cost, emissions_by_service, or kwh_per_service.
Report and dashboard
Build a FlexReport or Report on the joined custom dataset, grouping cost and emissions by service, subscription, team tag or meter on the same rows, and embed those reports in a CloudHealth dashboard for a unified cost-and-carbon view. For programmatic extraction, the GraphQL reporting API pulls the joined dataset into a BI tool.
References
- Bring Your Own Data (BYOD) with Data Connect
- Custom Datasets, covering JOIN, MERGE and calculated columns
- FlexReports
- GraphQL API guides
- CloudHealth API docs
- Azure, register an app and create a service principal
- Azure, Storage Blob Data Reader role
- Azure, Cost Management exports