Connect Microsoft Azure
CloudQuell reads your Azure spend through a Microsoft Entra ID service principal with read-only cost roles. Two connect paths are available: Cost Management exports (recommended — invoice-accurate FOCUS data delivered to your own storage account) or quick connect (API pull, no export setup). Nothing is written back to your Azure tenant, and once connected, Azure spend flows into the same ledger, dashboards, reports, and allocation views as your other providers. Subscriptions appear as their own sub-accounts — the Azure analog of AWS linked accounts.
Prerequisites
Section titled “Prerequisites”- A CloudQuell admin — creating integrations requires the
super_adminoradminrole. See roles. - The Azure integration enabled for your CloudQuell organization (see the note above).
- An Azure administrator who can create app registrations in Microsoft Entra ID and assign roles at your billing scope — subscription, MCA billing account, or EA enrollment, depending on your account type.
The five steps
Section titled “The five steps”1. Create the service principal
Section titled “1. Create the service principal”The fastest path is the Azure CLI:
az ad sp create-for-rbac -n "cloudquell"Record three values from the output — the wizard asks for all of them:
appId— the Application (client) ID.password— the client secret value. It’s shown once; copy it now.tenant— the Directory (tenant) ID.
Secrets created this way expire after one year by default (pass --years to
change it). Note the expiry date — the wizard records it and CloudQuell warns you
before it lapses.
Prefer the portal?
- Open Microsoft Entra ID. On the Overview page, click + Add → App registration (or use Manage → App registrations → New registration).
- Name it
cloudquell, keep the defaults, and click Register. - On the app’s Overview, copy the Application (client) ID and Directory (tenant) ID.
- In the left menu, expand Manage → Certificates & secrets → + New client secret. Set an expiry, click Add, then copy the secret’s Value column (not the Secret ID) — it’s shown only once.
2. Grant cost read access
Section titled “2. Grant cost read access”What to grant depends on your billing account type — pick the one that matches.
Pay-as-you-go / MOSP (subscription scope)
Section titled “Pay-as-you-go / MOSP (subscription scope)”Assign Cost Management Reader (or Reader) to the app on each subscription you want CloudQuell to read.
Portal:
- Open Subscriptions and click the subscription name to open it.
- Left menu → Access control (IAM) → + Add → Add role assignment.
- Role tab → search Cost Management Reader → select it → Next.
- Members tab → User, group, or service principal → + Select members → search
cloudquell→ Select. - Review + assign (and confirm on the final screen — the assignment isn’t saved until then).
Repeat per subscription.
CLI:
az role assignment create \ --assignee <APP_ID> \ --role "Cost Management Reader" \ --scope "/subscriptions/<SUBSCRIPTION_ID>"Microsoft Customer Agreement (MCA)
Section titled “Microsoft Customer Agreement (MCA)”Grant access at the billing account (or billing profile) in the portal: Cost Management + Billing → select your billing account → Access control (IAM) → Add → “Billing account reader”, then select the app you registered in step 1.
The wizard asks for your billing account ID — copy it from Cost Management + Billing → Properties. For billing-profile scope, use the profile’s full resource path instead, from Billing profiles → your profile → Properties.
Enterprise Agreement (EA)
Section titled “Enterprise Agreement (EA)”The EnrollmentReader billing role can only be assigned over REST — there’s no
portal or CLI path. As an Enterprise Administrator, PUT a billing role assignment
(role definition 24f8edb6-1668-4659-b5e2-40bb5f3a7d7e is EnrollmentReader):
curl -X PUT \ "https://management.azure.com/providers/Microsoft.Billing/billingAccounts/<ENROLLMENT_ID>/billingRoleAssignments/<NEW_GUID>?api-version=2019-10-01-preview" \ -H "Authorization: Bearer <ARM_ACCESS_TOKEN>" \ -H "Content-Type: application/json" \ -d '{ "properties": { "principalId": "<APP_OBJECT_ID>", "principalTenantId": "<TENANT_ID>", "roleDefinitionId": "/providers/Microsoft.Billing/billingAccounts/<ENROLLMENT_ID>/billingRoleDefinitions/24f8edb6-1668-4659-b5e2-40bb5f3a7d7e" } }'Placeholders:
<ENROLLMENT_ID>— your EA enrollment number (the billing account name), shown under Cost Management + Billing → Properties. The wizard asks for this same value.<NEW_GUID>— any new GUID you generate (uuidgen) to name the assignment.<APP_OBJECT_ID>— the app’s Enterprise Application object ID (Entra ID → Enterprise applications → your app → Object ID), not the Application (client) ID. CLI:az ad sp show --id <APP_ID> --query id -o tsv.<TENANT_ID>— your Directory (tenant) ID.<ARM_ACCESS_TOKEN>— an Enterprise Administrator’s token:az account get-access-token --query accessToken -o tsv.
3. Choose a connect path
Section titled “3. Choose a connect path”The wizard’s first step asks which path you’re taking.
Cost Management exports — recommended
Section titled “Cost Management exports — recommended”Invoice-accurate billed and amortized cost at any scale.
Create the export (portal):
- In Cost Management, open Reporting + analytics → Exports → + Create.
- Under Select a template, choose Cost and usage (FOCUS). Set an Export prefix, keep the Daily frequency, then Next.
- On Destination: Azure blob storage → Use existing, then pick the
Subscription, Storage account, and Container you own, and set a
Directory (e.g.
cloudquell). - Leave Format = Parquet and Compression = Snappy — CloudQuell reads Parquet (CSV and CSV.gz also work) — then Review + create.
Note the storage account, container, and Directory — the wizard asks for all three. The wizard’s Directory field is the export’s Destination → Directory value, not the Datasets-tab Export prefix.
For history, use Export selected dates to create one-time exports for past months — one calendar month per export (the 1st to the last day of the same month).
Grant the app read access to the container. The role must be Storage Blob Data Reader — the plain Reader role does not grant blob-data access.
Portal:
- Open the storage account (or the specific container) → Access Control (IAM) → + Add → Add role assignment.
- Role tab → search Storage Blob Data Reader → select it → Next.
- Members tab → User, group, or service principal → + Select members →
cloudquell→ Select → Review + assign.
CLI:
az role assignment create \ --assignee <APP_ID> \ --role "Storage Blob Data Reader" \ --scope "/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP>/providers/Microsoft.Storage/storageAccounts/<STORAGE_ACCOUNT>/blobServices/default/containers/<CONTAINER>"Quick connect (API)
Section titled “Quick connect (API)”No export setup — you just list the subscriptions to read, and CloudQuell pulls cost data from Azure’s API. Up to 13 months of history backfills on connect. One caveat: on this path billed cost equals amortized cost (the API pull carries the amortized view only). You can connect exports later on the same integration for invoice-accurate billed cost — CloudQuell automatically prefers export data for any month where both exist, so nothing double-counts.
For Enterprise Agreement (EA) accounts specifically, the API dataset omits a list-cost column, so CloudQuell derives list cost from the pay-as-you-go unit price × quantity. Azure leaves that unit price blank on reservation-covered and Marketplace rows, so commitment coverage and realized-savings figures are partial on the EA quick-connect path — connect a FOCUS export for complete savings/coverage. (MCA accounts carry list cost directly and aren’t affected.)
4. Optional — commitments roles
Section titled “4. Optional — commitments roles”To see your Azure reservations and savings plans on the Commitments page, grant two tenant-level read roles.
Portal:
- Reservations Reader — search Reservations → Role assignments (top
toolbar) → Add → Reservations Reader → select
cloudquell. - Savings Plan Reader — search Savings plans → Role assignments → Add
→ Savings Plan Reader → select
cloudquell.
CLI:
az role assignment create \ --assignee <APP_ID> \ --role "Reservations Reader" \ --scope "/providers/Microsoft.Capacity"
az role assignment create \ --assignee <APP_ID> \ --role "Savings Plan Reader" \ --scope "/providers/Microsoft.BillingBenefits"These are optional and never block the connection — the wizard flags a missing role as a warning, not a failure. You can grant them any time later; they’re picked up automatically on the next daily sync.
5. Connect in CloudQuell
Section titled “5. Connect in CloudQuell”In the CloudQuell admin, go to Integrations, open the Microsoft Azure tile, and click Connect. The wizard walks you through the path choice, the credentials from step 1 (plus your billing scope), and the export details or subscription list — then runs live checks against Azure: credentials, billing-scope access, storage or per-subscription cost access, and the commitments roles. Nothing is saved until the checks pass, so a missing role fails immediately with the exact fix — not on the first sync.
What data appears, and when
Section titled “What data appears, and when”- Azure reports usage with an 8–24 hour lag, and restates month-to-date daily — so the most recent day or two may shift slightly before settling.
- On the export path, data appears after your export completes its first run (check Exports in Cost Management for run status). Historical months appear as their one-time exports complete.
- On the quick connect path, the backfill (up to 13 months) starts the moment you connect.
- After that, Azure spend syncs daily at 07:30 UTC.
Each subscription appears as its own sub-account, so you can filter, report, and allocate per subscription. Resource groups and invoice sections arrive as tags, filterable anywhere tags are.
Rotate the client secret
Section titled “Rotate the client secret”Client secrets expire — it’s the most common way an Azure connection breaks. CloudQuell shows a warning on the integration card when the secret has less than 30 days left.
- In Entra ID → App registrations → your app → Certificates & secrets, create a new client secret and copy its value.
- In CloudQuell, open the integration’s Manage dialog and update the client secret. It’s validated live against Azure before it’s swapped in.
Syncs continue on the old secret until the rotation succeeds, so rotating before expiry means no gap in data. Rotation never resets or interrupts your ingested data.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Likely cause | Fix |
|---|---|---|
Sync fails with AADSTS7000222 |
The client secret has expired | Create a new secret in Entra ID and rotate it from Manage — see Rotate the client secret. |
| Billing-scope check fails (403) | Role grant doesn’t match your scope — often assigned on the billing account instead of the subscription | For subscription scope, grant Cost Management Reader on the subscription (Subscriptions → the subscription → Access control (IAM)), not on the billing account. MCA needs Billing account reader on the billing account; EA needs the EnrollmentReader REST assignment — see step 2. |
| Storage check fails (403) | The app can’t read the export container, or you granted plain Reader | Grant Storage Blob Data Reader — not the generic Reader role — on the container or storage account (step 3), then re-run the checks. |
| A check still fails right after you granted the role | RBAC hasn’t propagated yet | Wait a few minutes and Run checks again. Data-plane roles like Storage Blob Data Reader can take 5–10 min — longer than subscription-scope roles. |
| Connected, but no data yet | The first export run hasn’t completed, or you’re inside Azure’s 8–24 h reporting lag | Wait for the export’s first run, then the next daily sync picks it up. On quick connect, data appears after the first sync. |
| A backfill month never shows up | The one-time export covered multiple months, so ingestion skipped it | Recreate the historical export as one month per export, then re-sync. |
Remove the integration
Section titled “Remove the integration”Deleting the Azure integration is a permanent hard delete: it removes all ingested Azure cost data and the stored client secret from Secrets Manager. This can’t be undone — see manage your integrations. The app registration and role assignments in Azure are yours to remove; deleting the app registration in Entra ID revokes all access.
Query it from an agent
Section titled “Query it from an agent”Azure spend lands in the same normalized ledger as everything else, so a connected agent can query it in plain language alongside your AWS costs — by subscription, service, resource-group tag, or day.