Reference
Permissions
Every Azure role Infralign needs, its scope, and why — all read-only, all revocable.
Infralign’s default Cost API lane runs on exactly two Azure built-in roles, both read-only, both granted per subscription to a service principal in your own tenant. No Owner or Contributor role is ever needed, at any scope, at any stage — including engagements, where the architect prepares each change and your team merges it through your own pipeline.
Azure roles
Section titled “Azure roles”| Role | Scope | Covered operations | Why it’s needed |
|---|---|---|---|
| Cost Management Reader | Each subscription you include | Microsoft.CostManagement/generateCostDetailsReport (an action), plus the cost query used to validate it | The daily cost report is a POST action, not a read — plain Reader returns 403 on it. This role is what makes the Cost API lane work. Report blobs download over Cost-Management-signed SAS links, so no storage role is involved. |
| Reader | Each subscription you include | */read — Resource Graph inventory, Activity Log, Monitor metrics, Advisor recommendations | Resource metadata, change history, CPU utilisation, and Advisor signals. Never data inside resources. |
The app registration, client secret, assignment steps, and validation runbook are in Entra app registration.
Scope minimisation
Section titled “Scope minimisation”Both roles are assigned per subscription — the narrowest scope that works for the daily lane. Cost Management Reader sits on the subscription, never on the billing account. Start with one subscription and add others when you’re ready; the agent works with whatever you’ve included.
Nothing is assigned at management-group or tenant scope unless you choose to do so for your own convenience.
What these roles cannot do
Section titled “What these roles cannot do”- No role grants write access of any kind: no creating, modifying, or deleting resources, tags, or exports.
- Reader exposes resource metadata only — it cannot read database contents, VM disks, application data, or secrets in Key Vault.
- The service principal needs no Microsoft Graph API permissions and no admin consent, only these IAM role assignments. (The separate Infralign Portal sign-in app is what your tenant grants a one-time admin consent to — see Dashboard sign-in.)
- No agents or extensions are installed on your VMs, and no standing production privilege exists. See the safety model.
Why Reader, and what it cannot see
Section titled “Why Reader, and what it cannot see”Security teams reasonably ask whether Reader is too broad.
Reader’s */read is control plane only: resource existence, SKUs, tags, and configuration metadata. It cannot read data planes — no blob contents, no Key Vault secret values, no database rows, no VM disks. It cannot call action-style operations such as listKeys, so it cannot bootstrap itself into data access. Azure Resource Manager also never returns secure properties (passwords, connection strings) in GET responses to any principal, readers included.
The platform asks for that breadth because of Azure Resource Graph’s authorisation model: Microsoft.ResourceGraph/resources/read alone returns only the resources the identity can already read individually. A narrower role does not make queries fail — it makes resources silently vanish from inventory, so a blind spot reads as “no waste found” rather than “access denied”. A complete custom role therefore converges back towards */read, which is Reader. (Monitoring Reader is not a narrower alternative: its definition also includes */read.)
Least-privilege custom role (strict tenants)
Section titled “Least-privilege custom role (strict tenants)”For security-sensitive tenants, a custom role with an explicit action list works. Creating a role definition requires Microsoft.Authorization/roleDefinitions/write — Owner or User Access Administrator on the assignable scope, a higher privilege than assigning the built-in roles. Define the role, create it, then assign it per subscription:
{ "Name": "Infralign Estate Reader", "IsCustom": true, "Description": "Read-only inventory, metrics, and cost-advisory access for Infralign.", "Actions": [ "Microsoft.ResourceGraph/resources/read", "Microsoft.Resources/subscriptions/read", "Microsoft.Resources/subscriptions/resourceGroups/read", "Microsoft.Insights/metrics/read", "Microsoft.Insights/eventtypes/values/read", "Microsoft.Advisor/recommendations/read", "Microsoft.Compute/*/read", "Microsoft.Storage/*/read", "Microsoft.Sql/*/read", "Microsoft.Network/*/read", "Microsoft.Web/*/read", "Microsoft.ContainerService/*/read", "Microsoft.DBforPostgreSQL/*/read", "Microsoft.DocumentDB/*/read", "Microsoft.OperationalInsights/*/read" ], "NotActions": [], "DataActions": [], "NotDataActions": [], "AssignableScopes": [ "/subscriptions/<subscription-id-1>", "/subscriptions/<subscription-id-2>" ]}A custom role can only be assigned inside its AssignableScopes, so list every subscription you intend to analyse, or use a management-group scope such as /providers/Microsoft.Management/managementGroups/<group-id> that contains them.
Extend the provider list to match your estate: every provider you want analysed needs its own */read line. Common additions: Microsoft.KeyVault/*/read, Microsoft.ContainerRegistry/*/read, Microsoft.Cache/*/read, and Microsoft.DBforMySQL/*/read.
# Create the role definition from the JSON aboveaz role definition create --role-definition @infralign-estate-reader.json
# Assign it to the service principal, per subscriptionaz role assignment create \ --assignee-object-id <sp-object-id> \ --assignee-principal-type ServicePrincipal \ --role "Infralign Estate Reader" \ --scope /subscriptions/<subscription-id>Allow a few minutes for the new role definition and its assignments to propagate before validating — the same window as the runbook’s propagation note.
The trade-offs are yours to carry:
- you own maintaining the list as the estate evolves;
- any resource type not in the list silently drops out of inventory, utilisation metrics, and orphan detection;
- cost data still arrives regardless — Cost Management Reader covers billing for every resource — so a missed type shows its spend, but without the inventory or metrics context behind it;
- Cost Management Reader stays required either way.
The simpler lever is scope, not role: every role here is assigned per subscription, so a sensitive subscription can be left out of the engagement entirely.
Optional: Microsoft Teams
Section titled “Optional: Microsoft Teams”| Integration | Access | Why it’s needed |
|---|---|---|
| Microsoft Teams | Incoming webhook to a channel | Delivers the nightly pipeline summary card and failure-alert cards to your team. |
Teams is optional — the visibility tier and a meaningful audit work from the Azure roles alone.
Signing in to the dashboard
Section titled “Signing in to the dashboard”Your users sign in separately from the ingestion service principal, using their existing Microsoft work account through the multi-tenant Infralign Portal app, which a tenant admin consents to once. A per-tenant allowlist bound to your tenant ID controls who can sign in, and passwords are never shared with Infralign. The consent procedure, user list, and troubleshooting are in Dashboard sign-in.
Revoking access
Section titled “Revoking access”Access can be revoked at any time:
- Remove both role assignments from Access control (IAM) on each subscription, or delete the app registration outright.
- Rotate or remove the client secret from the app’s Certificates & secrets blade.
- If you set up the optional FOCUS lane, remove the storage grant and delete or pause the export.
Revocation is immediate and requires nothing from Infralign’s side.
Next: Security and data handling — where the data these roles read is hosted, and what leaves the EU.