Manage Subscription Access¶
The engineer's guide to who can do what in a vended subscription. Use this runbook to:
- Give a team Contributor, RBAC Administrator or Reader on a subscription.
- Take that access away.
- Activate the access when you need it.
- Renew eligibility that has expired.
This covers access inside one subscription. Platform-wide admin roles (Owner at a management group, Entra directory roles) are personas: see Manage Access Packages.
Groups, never people
Vending never adds a person to a subscription. You nominate a team's own Entra group, and vending makes it a member of the subscription's access group. Who is in the team is managed on the team's group, in one place, not in every subscription the team uses.
How access is built¶
Every vended subscription gets three Entra groups, and each holds one role on the subscription:
| Group | Role on the subscription | Assignment |
|---|---|---|
grp-<name>-contributor |
Contributor | PIM-eligible: members activate it when they need it |
grp-<name>-role-based-access-administrator |
Role Based Access Control Administrator | PIM-eligible |
grp-<name>-reader |
Reader | Standing: always on |
grp-payments-devs ──member of──▶ grp-payments-prod-westeu-00-contributor
(team's group) │ eligible
▼
Contributor on payments-prod-westeu-00
Nobody holds Owner on a vended subscription. The subscription's own
pipeline identity, sp-sub-tf-<name>-rw, holds Contributor and a locks-only
custom role, so the team's normal changes go through their GitLab pipeline,
not through activated access.
The projects¶
| Project | Lane | Holds |
|---|---|---|
subscription-vending-workload |
B | The rbac block in each workload subscription's <name>.tf. CI applies on merge. |
subscription-vending-platform |
A | The rbac block in each platform subscription's <name>.tf. You apply with ./apply.ps1 under pag-t0-platform-subscriptions. |
Grant a team access¶
📁
subscription-vending-*: 📃<name>.tf
Add the team's group display name to the tier it needs:
rbac = {
contributor = { group_names = ["grp-payments-devs"] }
role-based-access-administrator = { group_names = ["grp-payments-leads"] }
reader = { group_names = ["grp-payments-devs", "grp-finops-readers"] }
}
Pick the smallest tier that does the job:
- Reader to look around, read logs and check costs. Always on.
- Contributor to fix something by hand that the pipeline can't, e.g. during an incident. Needs activating.
- RBAC Administrator only for whoever has to grant access inside the subscription, e.g. a managed identity a role on a Key Vault. Needs activating.
The same group can be in more than one tier. A Contributor team usually also wants Reader, so it can see the subscription without activating.
Open an MR. The plan should show only azuread_group_member adds, one
per group per tier. Merge (and for a platform subscription, run
./apply.ps1).
The nominated group must already exist
Vending looks each group up by display name. If it can't find it, the
plan fails with a data.azuread_group.members error. Create the team's
group first. Display names must be unique in the tenant, or the lookup
is ambiguous.
Remove access¶
Remove the group name from the tier's list. The plan should show only
azuread_group_member destroys. Merge and apply.
Access ends as soon as the membership is gone. An activation already in progress lasts until it expires (up to its maximum duration).
To remove one person, take them out of the team's own group. Nothing changes in vending.
Use the access¶
Contributor and RBAC Administrator are eligible, not active. Before you use them:
- Go to Entra ID → Privileged Identity Management → My roles → Azure resources.
- Find the role on the subscription and select Activate.
- Give a justification and a duration. MFA is required.
-
Sign in again so the new token carries the role:
Deactivate it when you're done rather than waiting for it to expire.
Grant one principal eligibility directly¶
Sometimes a principal needs a role that isn't one of the three tiers, or
needs to be eligible without joining a team group (for example a break-glass
group as Owner). Use pim_eligible_role_assignments:
pim_eligible_role_assignments = {
break-glass-owner = {
principal_id = "00000000-0000-0000-0000-000000000000" # object ID, not display name
role_definition_name = "Owner"
}
}
Keep this rare and comment every entry with why. Never make assignments by hand in the portal: they're invisible to code review and show up as drift in the AzGovViz reports.
Renew expired eligibility¶
Every eligible assignment vending creates expires after 180 days. The tenant's PIM policy doesn't allow permanent eligibility. The next apply after it lapses creates it again, but nothing renews it in between.
If a subscription's file hasn't been applied for 180 days, its groups' members find nothing to activate in PIM. To renew:
- Workload: re-run the latest
mainpipeline ofsubscription-vending-workload. - Platform: re-run the
mainpipeline, then./apply.ps1.
The plan shows the expired azurerm_pim_eligible_role_assignment resources
being created again. Nothing else should change.
Check it worked¶
# Who is in the access group
az ad group member list --group grp-payments-prod-westeu-00-contributor \
--query "[].displayName" -o tsv
# Eligible assignments on the subscription (needs Reader)
az rest --method get \
--url "https://management.azure.com/subscriptions/<sub-id>/providers/Microsoft.Authorization/roleEligibilityScheduleInstances?api-version=2020-10-01&\$filter=atScope()" \
--query "value[].{principal:properties.expandedProperties.principal.displayName, role:properties.expandedProperties.roleDefinition.displayName, ends:properties.endDateTime}" -o table
When it fails¶
| Symptom | Cause | Fix |
|---|---|---|
Plan fails on data.azuread_group.members["<group>"] |
The group doesn't exist, or its name is misspelled, or two groups share the name | Create the group, fix the spelling, or rename the duplicate. |
| A member can't see the role in PIM | Eligibility expired after 180 days, or the member was added to the team group minutes ago | Renew it. Group membership can take a few minutes to show in PIM. |
RoleAssignmentRequestPolicyValidationFailed: ExpirationRule |
The tenant's maximum eligible duration for the role is now shorter than 180 days | Lower pim_eligible_duration_days in the bootstrap module to match the PIM role settings, release, and bump the solution ref. |
Activated Contributor but az still returns 403 |
The CLI token was issued before activation | az account clear and az login again. |
Workload pipeline: Insufficient privileges adding a group member |
The vending pipeline can only change groups it owns. The grp-<name>-* group was created by someone else, or its owner was changed. |
Make sp-pla-tf-subscription-vending-rw an owner of the group again (needs Groups Administrator). |