Platform Identity & RBAC¶
The platform identity model defines who can access what, at what scope, using Entra ID security groups rather than individual role assignments. Groups are assigned at management group scope so access automatically covers every subscription that exists now and any added in the future.
- GRINNTEC: Platform Engineering
- GRINNTEC: Subscription Vending
- Azure Identity Management
- Microsoft Entra ID documentation
The model¶
Access in the Grinntec tenant is structured in two layers:
Platform groups — managed in azure-platform-identity, assigned at mg-grinntec scope. These give team-level access across all subscriptions simultaneously.
Per-subscription groups — created automatically by the subscription bootstrap module when a new subscription is vended. These give workload-specific access scoped to a single subscription.
Platform groups are nested into per-subscription groups so that platform team members automatically have access to every subscription without per-subscription configuration.
graph TD
PE["grp-platform-engineers<br/>Owner at mg-grinntec"]
FR["grp-finops-readers<br/>Cost Management Reader at mg-grinntec"]
TR["grp-tenant-readonly<br/>Reader at mg-grinntec"]
subgraph "Per subscription (auto-created by bootstrap)"
SC["grp-{sub}-contributor<br/>Contributor"]
SR["grp-{sub}-reader<br/>Reader"]
end
PE -->|nested member| SC
FR -->|nested member| SR
Platform groups¶
These three groups cover the platform team's permanent access needs. All are managed in azure-platform-identity.
grp-platform-engineers¶
Role: Owner at mg-grinntec
Inherits to: Every subscription in the tenant
Platform engineers need Owner to manage role assignments (e.g., granting cross-subscription access for workload SPs) and to create and configure resources across the entire estate. Owner at the intermediate root rather than the tenant root keeps this within the Grinntec-controlled scope.
grp-finops-readers¶
Role: Cost Management Reader at mg-grinntec
Inherits to: Every subscription in the tenant
Gives the finance and audit team visibility into cost data, usage, and budgets across all subscriptions without the ability to create or modify resources. Scoped to Cost Management Reader — not Reader — so it grants cost visibility only.
locals {
finops_reader_upns = toset([
"justine.frischmann@grinntec.net",
"liam.gallagher@grinntec.net",
"louise.wener@grinntec.net",
"mark.morriss@grinntec.net",
])
}
grp-tenant-readonly¶
Role: Reader at mg-grinntec
Inherits to: Every subscription in the tenant
Tenant-wide read access for stakeholders who need visibility across the estate — senior management, security reviewers, or external auditors. Can view all resources and configurations but cannot make any changes.
locals {
tenant_readonly_upns = toset([
"cerys.matthews@grinntec.net",
"damon.albarn@grinntec.net",
"jarvis.cocker@grinntec.net",
])
}
Per-subscription groups¶
When a subscription is vended, the bootstrap module automatically creates three Entra ID security groups scoped to that subscription:
| Group | Role | Purpose |
|---|---|---|
grp-{name}-owner |
Owner | Break-glass or privileged access. Assign sparingly. |
grp-{name}-contributor |
Contributor | Day-to-day workload engineers. |
grp-{name}-reader |
Reader | Auditors, read-only oversight. |
Membership in these groups is managed via the rbac variable in the subscription's bootstrap module call — not as separate Terraform resources. This keeps everything in one place:
module "bootstrap_gt_mkdocs_prod_westeu" {
...
rbac = {
owner = { user_upns = ["mark.morriss@grinntec.net"], group_names = [] }
contributor = { user_upns = ["richard.ashcroft@grinntec.net", "damon.albarn@grinntec.net"], group_names = [] }
reader = { user_upns = ["tim.burgess@grinntec.net"], group_names = [] }
}
}
Subscription memberships¶
The subscription-memberships.tf file in azure-platform-identity nests platform groups into per-subscription groups. This is what gives platform engineers contributor access to every managed subscription — through group nesting rather than direct role assignments.
locals {
subscriptions = {
"gt-sandbox-dev-westeu-01" = {
contributor_groups = ["grp-platform-engineers"]
reader_groups = ["grp-finops-readers"]
}
"gt-mkdocs-prod-westeu" = {
contributor_groups = ["grp-platform-engineers"]
reader_groups = ["grp-finops-readers"]
}
}
}
When a new subscription is vended, add an entry here to nest the appropriate platform groups into its RBAC groups.
Note
grp-platform-engineers gets Owner at mg-grinntec via the management group role assignment — which already gives Owner on every subscription. The subscription-level contributor nesting via subscription-memberships.tf is belt-and-braces, and also useful for any future subscriptions that might land outside mg-grinntec scope.
Adding and removing members¶
Members are managed as a list of UPNs (email addresses) in the relevant grp-*.tf file. Terraform looks up each UPN in Entra ID to get the object ID — no GUIDs to find or paste.
To add a member to grp-platform-engineers:
- Add their UPN to the
platform_engineer_upnsset ingrp-platform-engineers.tf - Open a Merge Request in
azure-platform-identity - Review the plan — it will show one
azuread_group_memberto be created - Merge — the pipeline applies
To remove a member, delete their UPN from the list and follow the same process.
How it all fits together¶
graph LR
subgraph "azure-management-groups"
MG["mg-grinntec<br/>mg-platform / mg-online<br/>mg-sandboxes..."]
end
subgraph "azure-platform-identity"
PE["grp-platform-engineers → Owner at mg-grinntec"]
FR["grp-finops-readers → Cost Mgmt Reader at mg-grinntec"]
TR["grp-tenant-readonly → Reader at mg-grinntec"]
SM["subscription-memberships.tf<br/>nests platform groups into sub groups"]
end
subgraph "azure-subscriptions (per sub)"
BS["Bootstrap module creates:<br/>grp-{sub}-owner<br/>grp-{sub}-contributor<br/>grp-{sub}-reader"]
end
MG -->|role scope| PE
MG -->|role scope| FR
MG -->|role scope| TR
BS -->|groups exist| SM
SM -->|nesting| BS
The three deployments are independent — each with its own service principal and pipeline. A change to platform identity does not touch subscription infrastructure, and vice versa.
Service principal permissions¶
The sp-terraform-azure-platform-identity service principal that runs the pipeline requires:
| Permission | Where | Why |
|---|---|---|
| Groups Administrator | Entra ID (tenant) | Create security groups and manage membership |
| User Access Administrator | mg-grinntec (management group) |
Assign roles to groups at management group scope |
It has no Contributor or Owner on any subscription — it cannot create or modify Azure resources, only Entra groups and role assignments.