Stop Guessing: How to Actually Identify Your Azure Billing Structure
- Shannon

- Jan 30
- 9 min read
Note, the companion code and corresponding full GitHub repository is located here.
Azure costs are tricky enough without licensing contracts adding another layer of confusion on top. One of my long-running jokes from my time working at Microsoft was that you needed an advanced degree just to understand Azure billing, and then a second one to understand how that billing intersects with real-world consumption in Azure. What tends to catch teams off guard is that the agreement your organization has in place with Microsoft directly affects how you view costs, how you report costs, what scopes your dashboards should use, and how commitments are measured over time.
Ask a room full of engineers, architects, and finance partners what agreement they are under and you will usually hear answers like EA, MACC, CSP, or Pay As You Go. None of those answers are entirely wrong, but most of them are answering different questions. That overlap is exactly how teams end up with dashboards that look right but don't reconcile, renewal forecasts that drift, and Finance is usually left wondering why the numbers seem to change depending on which portal view someone is using.
This post is about ending the guessing because guessing is leading to more confusion. I'll level set the language, explain where each of these terms actually lives, then prove what you have using Azure CLI and PowerShell. From there, we'll turn that understanding into something operational so FinOps stops being a debate and starts acting as a system.
Before We Touch Any Scripts: Level Setting the Language
The most important thing to understand about Azure billing is that these terms don't all live at the same layer. Azure billing has an agreement layer, a commitment layer, and a subscription offer layer. Most confusion comes from casually using words from all three layers as if they were interchangeable (they're not interchangeable).
Here's how it breaks down:
Agreements define the legal and commercial contract you have with Microsoft.
Commitments define whether you've promised to spend a certain amount of money over time and how Microsoft tracks that promise.
Subscription offers define how individual subscriptions are created, priced, and sometimes credited.
Once you separate those layers mentally, everything else in this post becomes much easier to follow.
Enterprise Agreement (EA)
An Enterprise Agreement is the traditional enterprise contract model that many organizations have been running for years. If your company adopted Azure early or entered through a large Microsoft enterprise deal, there's a good chance at least part of your environment still sits under an EA. From a billing perspective, EA introduces enrollments and enrollment accounts, and those concepts show up directly when you scope Cost Management queries or exports.
What trips teams up is that EA often becomes shorthand for everything related to Azure billing. In reality, EA only answers one question: what agreement type does this billing account use? It doesn't automatically tell you whether you have a consumption commitment, how Marketplace purchases behave, or how newer subscriptions are billed.
It's also increasingly rare to see environments that are purely EA. Many organizations have older subscriptions under EA and newer subscriptions created under a different agreement. That mixed state is often the root cause of inconsistent reporting.
Microsoft Customer Agreement (MCA)
The Microsoft Customer Agreement is the modern contract Microsoft uses for most new Azure customers. If you're buying Azure directly from Microsoft today, there's a very strong chance you're under MCA. This agreement introduces billing accounts, billing profiles, and invoice groupings that differ from the EA model.
MCA matters because it's the foundation for modern Azure billing constructs. If you have a Microsoft Azure Consumption Commitment, it exists because you're under an MCA. When you query a billing account and see the agreement type returned as MicrosoftCustomerAgreement, that tells you which billing hierarchy and APIs apply, regardless of how your subscriptions were created or named.
This is also where people get surprised. Pay As You Go subscriptions, Visual Studio Enterprise subscriptions, and many CSP scenarios ultimately roll up to MCA billing constructs. The subscription label doesn't change the agreement underneath it.
Microsoft Azure Consumption Commitment (MACC)
MACC is the term that causes the most confusion, largely because it's talked about as if it were an agreement or a subscription type. It's neither.
A Microsoft Azure Consumption Commitment is exactly what it sounds like: a promise to consume a certain dollar amount of Azure services over a defined period of time, usually one to three years. It lives at the billing account or billing profile level, not at the subscription level. MACC doesn't change pricing by itself. Discounts still come from negotiated rates, Reserved Instances, and Savings Plans. MACC simply tracks how quickly you're burning down a committed spend.
This distinction matters because MACC burn down doesn't always behave the way people expect. Credits, Azure prepayment, and certain Marketplace purchases can all affect what decrements the commitment. When someone says "the numbers don't match," MACC behavior is often the reason.
PAYG, CSP, and Subscription Offers
Pay As You Go (PAYG) is not an agreement. It's a subscription offer. PAYG subscriptions are billed monthly based on usage and typically sit under an MCA billing account. An organization can have PAYG subscriptions and still have a MACC at the billing account level. Treating PAYG as synonymous with "no commitments exist" is a very common mistake.
CSP is also not an agreement. CSP describes the commercial channel through which you buy Azure. Under the hood, CSP subscriptions often still rely on MCA billing constructs, but billing scopes, permissions, invoicing, and Marketplace eligibility vary by partner. This is why two CSP customers can have completely different FinOps experiences even though both say "we are CSP."
Visual Studio Enterprise, DevTest, sponsorships, and trial subscriptions all live at the subscription offer layer. These offers affect pricing or credits, but they don't change what agreement you're under or whether a commitment exists. Visual Studio credits are a classic example where usage exists but MACC burn down doesn't move, which is confusing until you understand the layering.
Why You Should Figure This Out Before Building Dashboards
If you don't know what agreement you're under, you don't know which billing scopes to use for exports and queries. If you don't know whether a MACC exists, you can build a beautiful burn down chart that is fundamentally meaningless. If you don't detect CSP correctly, Marketplace assumptions tend to sneak in and reporting becomes "mostly right," which is the worst kind of wrong.
Clarity at this layer is what makes everything downstream stable.
Step 1: Make Sure You Have the Right Billing Role
Before running any scripts represented in this blog, you need permissions at the billing account scope. Subscription Owner or Contributor is usually not enough because billing access is separate from resource access. A good baseline is Billing Account Reader on the billing account.
If your scripts return nothing or fail without a clear error, permissions are the first thing to check.
Step 2: Identify Agreement Type Using Azure CLI or PowerShell
Rather than guessing, start with the agreement layer and let Azure tell you the truth.
Azure CLI
az billing account list \
--query "[].{BillingAccount:name, Agreement:agreementType, DisplayName:displayName}" \
-o tablePowerShell
$accounts = az rest `
--method get `
--url "https://management.azure.com/providers/Microsoft.Billing/billingAccounts?api-version=2024-04-01" `
| ConvertFrom-Json
$accounts.value | Select-Object name, `
@{Name="AgreementType";Expression={$_.properties.agreementType}}, `
@{Name="DisplayName";Expression={$_.properties.displayName}} |
Format-Table -AutoSizeIf the agreement type is EnterpriseAgreement, you're under EA. If it's MicrosoftCustomerAgreement, you're under MCA.
Step 3: Check Whether a MACC Exists
Next, confirm whether a consumption commitment is actually being tracked.
Azure CLI
# Replace <billingAccountId> with your actual billing account ID
az rest \
--method get \
--url "https://management.azure.com/providers/Microsoft.Billing/billingAccounts/<billingAccountId>/providers/Microsoft.Consumption/lots?api-version=2024-08-01" \
--query "value[].{ID:name, Source:properties.source, Amount:properties.originalAmount.value, Currency:properties.originalAmount.currency, Status:properties.status}" \
-o tablePowerShell
$billingAccountId = "<your-billing-account-id>"
$lots = az rest `
--method get `
--url "https://management.azure.com/providers/Microsoft.Billing/billingAccounts/$billingAccountId/providers/Microsoft.Consumption/lots?api-version=2024-08-01" `
2>$null | ConvertFrom-Json
if ($lots.value -and $lots.value.Count -gt 0) {
Write-Host "MACC detected!" -ForegroundColor Green
$lots.value | Select-Object `
@{Name="ID";Expression={$_.name}}, `
@{Name="Source";Expression={$_.properties.source}}, `
@{Name="Amount";Expression={$_.properties.originalAmount.value}}, `
@{Name="Currency";Expression={$_.properties.originalAmount.currency}}, `
@{Name="Status";Expression={$_.properties.status}} |
Format-Table -AutoSize
} else {
Write-Host "No MACC found" -ForegroundColor Yellow
}If lots are returned with balances and dates, MACC exists. The source property will typically show ConsumptionCommitment.
Step 4: Start From a Subscription When That's All You Have
Most engineers start with a subscription ID. You can trace that back to billing.
Azure CLI
subscriptionId="<your-subscription-id>"
az rest \
--method get \
--url "https://management.azure.com/subscriptions/$subscriptionId?api-version=2020-01-01" \
--query "{SubscriptionId:id, BillingScopeId:subscriptionPolicies.billingScopeId, Offer:subscriptionPolicies.quotaId}" \
-o jsonPowerShell
$subscriptionId = "<your-subscription-id>"
$sub = az rest `
--method get `
--url "https://management.azure.com/subscriptions/$subscriptionId?api-version=2020-01-01" `
| ConvertFrom-Json
[PSCustomObject]@{
SubscriptionId = $sub.id
SubscriptionName = $sub.displayName
BillingScopeId = $sub.subscriptionPolicies.billingScopeId
OfferType = $sub.subscriptionPolicies.quotaId
} | Format-ListThe billingScopeId string tells you whether the subscription rolls up to an EA enrollment, an MCA billing profile, or a CSP customer. Here's what to look for:
EA: Contains /providers/Microsoft.Billing/billingAccounts/{enrollmentId}/enrollmentAccounts/{accountId}
MCA Direct: Contains /providers/Microsoft.Billing/billingAccounts/{accountId}/billingProfiles/{profileId}/invoiceSections/{sectionId}
CSP: Contains /customers/ in the scope path
Step 5: Detect CSP Explicitly
CSP doesn't have a single flag. You infer it by checking if the billing account has customers beneath it.
Azure CLI
billingAccountId="<your-billing-account-id>"
az rest \
--method get \
--url "https://management.azure.com/providers/Microsoft.Billing/billingAccounts/$billingAccountId/customers?api-version=2024-04-01" \
--query "value[].{CustomerName:properties.displayName, CustomerId:name}" \
-o tablePowerShell
$billingAccountId = "<your-billing-account-id>"
$customers = az rest `
--method get `
--url "https://management.azure.com/providers/Microsoft.Billing/billingAccounts/$billingAccountId/customers?api-version=2024-04-01" `
2>$null | ConvertFrom-Json
if ($customers.value -and $customers.value.Count -gt 0) {
Write-Host "CSP detected - this billing account has customers" -ForegroundColor Cyan
$customers.value | Select-Object `
@{Name="CustomerName";Expression={$_.properties.displayName}}, `
@{Name="CustomerId";Expression={$_.name}} |
Format-Table -AutoSize
} else {
Write-Host "No CSP customers found" -ForegroundColor Yellow
}If customers exist, CSP is involved. In a CSP model, each customer represents a separate entity that the partner manages.
Step 6: One Script to Classify Everything
At this point in the post, you have all the building blocks. You can list billing accounts, check agreement type, look for consumption commitment lots, and trace a subscription back to its billing scope. The problem is that doing those steps manually is fine for one subscription, but it gets old fast when you have dozens or hundreds. This is the point where people quietly give up and go back to assumptions.
So here's the practical version. One script. Run it once. It walks every subscription you can see, pulls the billing scope, infers whether it looks like CSP, maps the subscription back to its billing account, records the billing account's agreement type, and checks whether that billing account has consumption commitment lots. The output is a clean table, and it also exports a CSV so you can feed Power BI or hand it to Finance without having to explain what you did.
Understanding the Output
The "One Script to Classify Everything" script produces a CSV with the following columns:
Column | Description |
SubscriptionName | The display name of the subscription |
SubscriptionId | The subscription GUID |
OfferType | The subscription offer (e.g., MS-AZR-0017P for PAYG, MS-AZR-0148P for Enterprise) |
AgreementType | Either EnterpriseAgreement or MicrosoftCustomerAgreement |
IsCSP | Boolean indicating if CSP indicators were detected |
CSPEvidence | Details of why CSP was detected (if applicable) |
HasMACC | Boolean indicating if consumption commitment lots exist |
BillingAccountId | The billing account ID this subscription rolls up to |
BillingScopeId | The full billing scope path |
Common Patterns You'll See
After running the script, you'll typically see a few patterns emerge:
Pattern 1: Pure EA Environment
All subscriptions show EnterpriseAgreement
No CSP indicators
MACC is false (EA uses monetary commitment, not MACC)
Billing scopes contain /enrollmentAccounts/
Pattern 2: Modern MCA Environment
All subscriptions show MicrosoftCustomerAgreement
No CSP indicators
MACC may be true or false depending on your contract
Billing scopes contain /billingProfiles/ and /invoiceSections/
Pattern 3: CSP Environment
Agreement type shows MicrosoftCustomerAgreement
CSP is true
Billing scopes contain /customers/
Multiple customer IDs may be present
Pattern 4: Hybrid Environment (Most Common)
Mix of EnterpriseAgreement and MicrosoftCustomerAgreement
Some subscriptions may show CSP indicators
MACC detection varies by billing account
This is where reporting gets complicated without proper classification
What to Do With This Information
Once you know your billing structure, you can:
Scope Cost Management queries correctly - Use enrollment accounts for EA, billing profiles for MCA
Build accurate MACC burn-down dashboards - Only include subscriptions that actually decrement MACC
Set up proper RBAC - Billing permissions work differently under EA vs MCA
Configure exports appropriately - Export scopes differ based on agreement type
Forecast renewals accurately - Understand what commitments exist and when they expire
Troubleshoot billing anomalies - Know which portal and API to use for each subscription
Troubleshooting Common Issues
Issue: Script returns empty results
Solution: Check your permissions. You need at least Billing Account Reader role at the billing account scope, not just at the subscription level.
Issue: Some subscriptions show no billing account
Solution: This typically happens with trial subscriptions, sponsored subscriptions, or subscriptions that were created but not fully provisioned. These won't roll up to standard billing constructs.
Issue: CSP is detected but you don't think you're in CSP
Solution: You might be in an indirect CSP relationship. Check with your procurement team. Sometimes organizations buy through a CSP partner without the engineering team knowing.
Issue: MACC shows false but Finance says you have one
Solution: The commitment might be tracked outside of Azure (e.g., at the enterprise agreement level for EA customers). MACC specifically refers to MCA consumption commitments tracked via the lots API.
Closing Thoughts
EA and MCA are agreements. MACC is a commitment. PAYG, CSP, Visual Studio Enterprise, and DevTest are subscription level constructs layered on top. They're not competing labels or concepts. They're different parts of the same billing system, and Azure only becomes confusing when those layers get blurred together.
The moment you stop guessing and start proving, Azure billing stops being a procurement mystery and becomes a mechanical system you can automate. That's when dashboards reconcile, forecasts stabilize, and Finance + engineering finally start looking at the same reality.
If nothing else, take this with you: Assumptions are expensive. Scripts are cheap.
Additional Resources
Feedback and Contributions
Found this helpful? Have improvements? The scripts in this post are living documents on my GitHub page, with the repository located here (and referenced above). As Azure billing APIs evolve, so should these detection methods. If you find edge cases or improvements, I'd love to hear about them.




I read the post and it clearly explains how to break down your Azure billing so you stop guessing what costs come from where and make smarter budget decisions. When I was struggling with my own tech classes I once had to pay someone to complete sophia course work because I could not figure out complex cloud concepts on my own. That made me see that learning the basics first really helps with advanced topics.