Skip to content

Instantly share code, notes, and snippets.

@andaryjo
Last active February 4, 2025 15:15
Show Gist options
  • Save andaryjo/425b2cd1ea82eefbb33741bddb952ab9 to your computer and use it in GitHub Desktop.
Save andaryjo/425b2cd1ea82eefbb33741bddb952ab9 to your computer and use it in GitHub Desktop.
Azure Tales: How many APIs does it take to see your Cloud Carbon footprint?

Azure Tales: How many APIs does it take to see your Cloud Carbon footprint?

Unfortunately it's more complicated than you might assume.

In this Azure tale I want to take a look at the different ways to figure out your Azure workload's CO2e emissions. I can already tell you, there's many different ways to do this and all of them have their own limitations.

This article has been written during January 2025. There's a lot of change in this area; all the products and APIs are in preview and regularly release new versions, changes in behavior and of course new bugs.

When it comes to Azure workloads, there are two product suites that allow you to extract emissions data:

Microsoft Cloud for Sustainability (MCFS)

MCFS is a suite of tools to manage your organization's ESG data, not limited to Azure or just workload emissions. To get a better understanding of the products I recently attended the MCFS Technical Summit, but one could argue that it turned out to actually be more of a Copilot summit.

What I know is that all of the MCFS services providing data for Azure workload emissions have two arguably major limitations:

  1. They only work for customers with EA / MCA contracts, meaning when you get billed through Microsoft directly. If your Azure services get sold to you through a third party, you won't get any data. We will later find out that this is a completely arbitrary limitation.
  2. You need to be Billing Account Administrator on your Azure's billing account to have access to these services which makes this unfeasible for your developers to use.

MCFS Azure Emissions Impact Dashboard

The Azure Emissions Impact Dashboard is a Power BI app you can install, but it requires a PowerBI Pro license. So this is basically a paywalled feature and currently costs around 10€ per user who wants to use it (which is probably not that many since each of them needs to be a Billing Account Administrator).

If this dashboard works (which it did not for some time during my testing phase), it might be useful to you if you already know your way around Power BI. Besides just displaying emissions data per Azure subscription, region or even service type, the dashboard comes with funny features managers would love, for example it can magically guesstimate the carbon emissions you have saved by switching to Azure compared to on-premises data centers with terrible PUEs.

The dashboard could even break down your Azure emissions per GHG scope. I'm saying "could" because it actually only shows you GHG scope 1 and 3 emissions. But we'll get into that later.

As an engineer, I touched Power BI for the first time ever and I just don't feel home here. And I'm sure there's a reason this Power BI app is currently rated with 2.1 out of 5 stars on Microsoft AppSource. At least you can export your data and get a somewhat usable Excel sheet. But let's check out the APIs.

MCFS APIs (Preview)

These APIs are currently in closed preview and you have to apply for access. Once admitted (which in my case took around 6 weeks) you'll get access to the MCFS API Dashboard, can enable the Azure data source and issue an API key to use the APIs. To access the APIs you'll need both this API key and an OAuth Bearer Token of an Entra ID principal (although I'm not sure what permission this principal needs - the docs are not clear on that one). For my testing purposes it was the easiest to use the MCFS API Developer Dashboard to retrieve a token (simply select authorization_code as authorization flow in the "Try it" panel).

MCFS OData API

The MCFS OData API will return emissions data for all eligible Azure subscriptions and each item will be the emissions per subscription per Azure service per region per month. Since this is an OData API you can now use OData queries to filter the data. Following request will give you all emissions for a single subscription for Virtual Machines and GHG scope 3:

See API request
GET https://api.mcfs.microsoft.com/api/v1.0/instances/[mcfsInstanceId]/enrollments/[billingAccountID]/emissions?$filter=(subscriptionId eq '[subscriptionID]' and scopeId eq 3 and azureServiceName eq 'Virtual Machines')

{
  "@odata.context": "https://api.mcfs.microsoft.com/api/v1.0/instances/[mcfsInstanceId]/enrollments/[billingAccountID]/$metadata#emissions",
  "value": [
    {
      "rowNum": 6864,
      "dateKey": 20240901,
      "enrollmentId": "[billingAccountID]",
      "orgName": "[...]",
      "subscriptionId": "[subscriptionID]",
      "subscriptionName": "[...]",
      "azureServiceName": "Virtual Machines",
      "subService": "Azure Compute",
      "azureRegionName": "West Europe",
      "scope": "Scope3",
      "scopeId": 3,
      "totalEmissions": 9.188330188217006E-05,
      "pk": "[...]"
    },
    {
      "rowNum": 498,
      "dateKey": 20241001,
      "enrollmentId": "[billingAccountID]",
      "orgName": "[...]",
      "subscriptionId": "[subscriptionID]",
      "subscriptionName": "[...]",
      "azureServiceName": "Virtual Machines",
      "subService": "Azure Compute",
      "azureRegionName": "West Europe",
      "scope": "Scope3",
      "scopeId": 3,
      "totalEmissions": 0.00036919577795397517,
      "pk": "[...]"
    },
  ]
}

The totalEmissions are in metric tons, so in October 2024 Virtual Machines in West Europe in this subscription were responsible for 0,37kg CO2e of GHG scope 3 emissions.

OData is pretty powerful and you can manipulate the response in many ways. For example, to aggregate all emissions for all Azure services per month for a single subscription you could use this query:

$filter=subscriptionId eq '[subscriptionID]'&$apply=groupby((dateKey, subscriptionId), aggregate(totalEmissions with sum as totalEmissionsPerMonth))

Or simply get the sum of all emissions of all time (the data is limited to the last 5 years according to the docs) for all subscriptions and group by GHG scope:

GET https://api.mcfs.microsoft.com/api/v1.0/instances/[mcfsInstanceId]/enrollments/[billingAccountID]/emissions?$apply=groupby((scope), aggregate(totalEmissions with sum as totalEmissions))

[
  {
    "scope": "Scope1",
    "totalEmissions": 0.0066298144971553155
  },
  {
    "scope": "Scope2",
    "totalEmissions": 0
  },
  {
    "scope": "Scope3",
    "totalEmissions": 0.8666006957148362
  }
]

MCFS Export API

There's also the MCFS Export API which you can use in case you do not want to do live API requests and deal with pagination. Using one API endpoint you can trigger an export job, then use another endpoint to check the job's status and once the data has been exported to a Microsoft-managed Azure Storage Account you have to use yet another API endpoint to get a SAS token for this Storage Account to access your data using a tool like azcopy.

The data gets exported in .parquet files and once you have converted them to actually usable CSV files you'll figure out that it is the exact same data as the one returned by the OData API. If you think this way is easier, go for it.

Azure Carbon Optimization Service (Preview)

The Azure Carbon Optimization Service is a product that shows you the emissions of your Azure workloads and even makes recommendations on how to reduce them (for example by downsizing certain VMs). It is Azure-native and uses Azure RBAC, so you could actually grant your developer teams roles to view this data. All you need to view the data is the Carbon Optimization Reader role on the subscriptions you want to view.

And even though the docs say that this service uses the same underlying data and calculation methodology as MCFS suite, somehow it can even provide you data for Azure subscriptions which have not been purchased directly through Microsoft, so for example for your CSP subscriptions. In my testing I confirmed that the data provided by the Azure Carbon Optimization Service and by MCFS is exactly the same for our MCA subscriptions, down to the last decimal for certain Azure services and months.

The only major drawback I found with this service (besides it being buggy and the API being poorly documented) is that the data is limited to the past 12 months.

Carbon Optimization Dashboard

There's a pretty neat Dashboard which is doing somewhat the same as the MCFS Dashboard but is more intuitive to use and actually shows you the information you want to see. You can query based on Azure subscriptions, GHG scopes, Azure services and even resource groups.

In theory you can also group by Azure regions, but somehow that data got corrupted and now all emissions get attributed to "Other". This is not an error in the dashboard but in its underlying API. Microsoft announced a recalculation of historical data in February 2025, so maybe that will fix this issue.

The Dashboard allows you to export data to CSV files which is nice, but let's check out its API.

Carbon Optimization API

The dashboard uses this API which is officially documented (even though in preview). But that documentation is not good. For example, for many query parameters it will simply tell you that they are supposed to be a string, but not what the valid values are. The result is 400 Bad Request errors raining down on you until you figure out how to use the API. But as always, I recommend you to simply inspect the network traffic from the Azure Portal to figure out what parameters to supply.

All we need to access item is the OAuth Bearer Token of a principal which has at least the Carbon Optimization Reader role on the subscriptions we want to read data for.

Using this API, we can now retrieve the the Azure workload emissions for a specific subscription grouped by Azure Service and month:

See API request
POST https://management.azure.com/providers/Microsoft.Carbon/carbonEmissionReports?api-version=2023-04-01-preview
{
  "reportType": "TopItemsMonthlySummaryReport",
  "subscriptionList": [
    "[subscriptionID]"
  ],
  "resourceGroupUrlList": [],
  "categoryType": "ServiceType",
  "topItems": 25,
  "carbonScopeList": [
    "Scope3"
  ],
  "dateRange": {
    "start": "2024-10-01",
    "end": "2024-10-01"
  }
}

{
  "value": [
    [...]
    {
      "dataType": "TopItemsMonthlySummaryData",
      "itemName": "Storage",
      "categoryType": "ServiceType",
      "date": "2024-10-01",
      "totalCarbonEmission": 0.01927401388757731,
      "totalCarbonEmission12MonthsAgo": 0,
      "totalCarbonEmissionLastMonth": 0.004844239051011249,
      "changeRatioFor12Months": 0,
      "changeRatioForLastMonth": 2.9787495382899825,
      "changeValueMonthOverMonth": 0
    },
    {
      "dataType": "TopItemsMonthlySummaryData",
      "itemName": "Virtual Machines",
      "categoryType": "ServiceType",
      "date": "2024-10-01",
      "totalCarbonEmission": 0.3691957779539752,
      "totalCarbonEmission12MonthsAgo": 0,
      "totalCarbonEmissionLastMonth": 0.09188330188217006,
      "changeRatioFor12Months": 0,
      "changeRatioForLastMonth": 3.0180943696105635,
      "changeValueMonthOverMonth": 0
    },
    [...]
  ]
}

As you already could see with the MCFS OData API, in October 2024 Virtual Machines in West Europe in this subscription were responsible for 0,37kg CO2e of GHG scope 3 emissions.

When comparing these aggregations (or monthly summary reports) to the MCFS OData API, this API is less powerful when it comes to queries and data aggregation. For example, we cannot group by both region and Azure service at the same time. It's emissions either per region per month or per service per month.

But there are other report types, for example the ItemDetailsReport which can give you the emissions for single Azure resources and rank them across up to 100 Azure subscriptions at the same time:

See API request
POST https://management.azure.com/providers/Microsoft.Carbon/carbonEmissionReports?api-version=2023-04-01-preview
{
  "reportType": "ItemDetailsReport",
  "subscriptionList": [
    "[subscriptionID1]",
    "[subscriptionID2]"
  ],
  "categoryType": "Resource",
  "orderBy": "totalCarbonEmission",
  "carbonScopeList": [
    "Scope1",
    "Scope2",
    "Scope3"
  ],
  "dateRange": {
    "start": "2024-12-01",
    "end": "2024-12-01"
  },
  "groupCategory": "",
  "sortDirection": "Desc",
  "pageSize": 50
}

{
  "value": [
    {
      "dataType": "ResourceItemDetailsData",
      "itemName": "[resourceName]",
      "categoryType": "Resource",
      "groupName": "",
      "subscriptionId": "[subscriptionID1]",
      "resourceGroup": "[resourceGroupName]",
      "resourceId": "/subscriptions/[subscriptionID1]/resourcegroups/[resourceGroupName]/providers/microsoft.dbforpostgresql/flexibleservers/[resourceName]",
      "resourceType": "microsoft.dbforpostgresql/flexibleservers",
      "totalCarbonEmission": 1.7547028498966462,
      "totalCarbonEmission12MonthsAgo": 0,
      "totalCarbonEmissionLastMonth": 1.6932236301499184,
      "changeRatioFor12Months": 0,
      "changeRatioForLastMonth": 0.03630897812433932,
      "changeValueMonthOverMonth": 0.0614792197467278
    },
  ],
  "skipToken": ""
}

Data limitations

Market-based data

You might have noticed in the examples above that the GHG scope 2 emissions for our Azure workloads are zero even though we have significant GHG scope 3 emissions. This behavior is consistent across all dashboards and APIs.

GHG scope 2 classifies emissions that are indirect emissions caused by the production for the energy required to run a product. These emissions being zero effectively means that Azure is telling us that no CO2e emissions get produced to power our Azure workloads.

That is - of course - not entirely correct. When you look at the specifications of Azure data centers you can see that they do not get powered by 100% renewable energy. For example, Azure West Europe (located near Amsterdam) has a renewable energy coverage of 79% (see Azure Netherlands sustainability fact sheet). And this is a pretty high value when compared to other Azure data centers. For most of them this information isn't even available, so Microsoft probably isn't too proud of many of the coverage values.

Not being covered by 100% renewable energy means that there are some emissions-producing energy sources in the mix. To get the actual GHG scope 2 emissions you would now need to know the energy consumption of your Azure resources and multiply it with the data center's energy grid's carbon intensity at the time the resources were running, but I can already tell you that this is more than zero.

A clue gives the above linked sustainability fact sheet. While Azure claims they are net-zero (when it comes to GHG scope 2 emissions), they also say that they "Match 100 percent of electricity consumption, 100 percent of the time, with zero-carbon energy purchases". What they are doing here is called Carbon Offsetting through Virtual Power Purchase Agreements. The resulting emissions data is then called marked-based data because the zero-emissions energy for these workloads has been purchased somewhere.

While the effectiveness of Carbon Offsetting is highly controversial, they are far from the only company doing it. The actual issue here is that they don't tell you anywhere in their docs that these "zero" emissions get achieved through carbon offsetting and that they do not provide data on the actual location-based emissions (which for example Google Cloud is doing).

But it hasn't always been like that. I was able to get my hands on the Azure Emissions Impact Dashboard for an organization that has data going back several years and for 2021 and prior the Dashboard shows us GHG scope 2 emissions data. Maybe the data back then was calculated differently or they didn't yet offset the GHG scope 2 emissions.

I reached out to Azure to confirm these assumptions but have not received an answer yet.

Co-located data centers

Not all Azure regions are data centers operated by Azure. Some of them are so-called co-located data centers operated by another provider. Azure is not able or willing to provide data for these data centers.

Unfortunately, next to US government regions, this includes all regions in Germany (see docs). And in fact, none of our workloads in Germany get represented in the data.

Comparison

MCFS Carbon Optimization
Data Market-based data only Marked-based data only
GHG scopes all scopes (technically) all scopes (technically)
Regions excludes co-located regions excludes co-located regions
Subscriptions EA/MCA subscriptions only all subscriptions
Historical data 5 years 1 year
Data granularity Subscription Single resource
Access Requires Billing Account privileges Has dedicated Azure RBAC role
Dashboard Dashboard
Generally available Yes Public Preview
Costs Requires Power BI Pro License Free
Data Export Yes Yes
API API
Generally available Private Preview Public Preview
Costs Free Free
Data Export Yes No
Custom queries OData queries No

As you can see, both product suites have their own limitations and both are best in different use cases or for different data. But even with two different dashboards and three APIs you still won't be able to retrieve data for your CSP subscriptions that's older than a year.

There's still a lot changing with these products, so let's hope Microsoft will in future consolidate these two product suites and build one API that can actually provide all of the data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment