By the end of this you have two hardened break-glass accounts carrying phishing-resistant keys, the Entra groups that will carry every Azure role you ever assign, RBAC placed on the management-group tree with a worked example, the one un-automatable elevation that lets you assign at the top of the tree, Privileged Identity Management stood up if you licensed it or a disciplined standing model if you did not, a naming convention that keeps the whole thing legible, and an end-to-end test that proves a role assigned once, high on the tree, is inherited at a subscription nobody touched. The portal walk is the teaching spine; a consolidated set of commands reproduces each step for anyone who would rather run it than click it. The running example is Northfork Supply Co., tenant northforksupply.onmicrosoft.com, with a single Production subscription under the tree from the governance article.
Before Step 1: you need Global Administrator to create the emergency accounts and to perform the root elevation in Step 3; the management-group tree from the governance build already in place; a decision made on Entra ID P2 or Entra ID Governance licensing, because Step 5 forks on it; two hardware security keys for the break-glass accounts; and the Azure CLI plus the Microsoft Graph PowerShell module on your workstation. Set a few identifiers once so the commands below read cleanly: the management-group scope is /providers/Microsoft.Management/managementGroups/mg-northfork, and the Production subscription is referenced as PROD_SUB_ID. Substitute the object IDs the earlier steps hand you where the commands say so.
Step 1: Create and harden the two break-glass accounts
Create two cloud-only accounts on the tenant’s onmicrosoft.com domain, give each a long random password that lives only in an offline store split so no one person can open it alone, and assign each the Global Administrator role as a standing, permanent assignment. Standing Global Admin is correct here and nowhere else, because the entire purpose of these accounts is to work on the day everything else has failed. What makes them safe is not the absence of privilege but the presence of a strong credential and the absence of brittle dependencies: register a passkey on a hardware security key to each account, and exclude both accounts from the restrictive Conditional Access policies and the Microsoft-managed policies that could otherwise lock them out, while leaving them subject to the mandatory multifactor requirement they now satisfy with the key.
| Setting | Value | Why |
|---|---|---|
| Account domain | [email protected] and bg02@… | Cloud-only on the tenant domain, dependent on nothing federated or synced from on-premises |
| Directory role | Global Administrator, permanent active | The one place standing top privilege is correct, because the account exists for total-failure days |
| Credential | Passkey on a hardware security key, plus a long random password held offline | Mandatory MFA does not exempt these accounts; a password-only account cannot sign in under enforcement |
| Conditional Access | Excluded from restrictive and Microsoft-managed policies, not from the MFA requirement itself | A misfiring policy must never be able to lock out every path into the tenant at once |
| Monitoring | An alert on any sign-in by either account | These accounts should never sign in during normal operations; every use is an event a human reviews |
The account and its role assignment script cleanly; the security key does not, because registering a physical key is a deliberate interactive act performed once per account at the account’s own security information. Create the accounts and grant the role with the commands below, then register the keys by hand and record where they are stored.
Connect-MgGraph -Scopes "User.ReadWrite.All","RoleManagement.ReadWrite.Directory"
# Create one emergency account (repeat for bg02); set a long random password held offline
$pw = @{ Password = "REPLACE_WITH_LONG_RANDOM"; ForceChangePasswordNextSignIn = $false }
$u = New-MgUser -DisplayName "Break Glass 01" `
-UserPrincipalName "[email protected]" `
-MailNickname "bg01" -AccountEnabled -PasswordProfile $pw
# Grant Global Administrator (role template 62e90394-69f5-4237-9190-012177145e10)
$role = Get-MgDirectoryRole -Filter "RoleTemplateId eq '62e90394-69f5-4237-9190-012177145e10'"
if (-not $role) { $role = New-MgDirectoryRole -RoleTemplateId "62e90394-69f5-4237-9190-012177145e10" }
New-MgDirectoryRoleMemberByRef -DirectoryRoleId $role.Id `
-BodyParameter @{ "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$($u.Id)" }
Expected result: two accounts exist, each showing Global Administrator, each with a registered security key. Failure mode to avoid: creating the accounts and never registering the key, which leaves you with emergency accounts that cannot pass mandatory MFA on the day you reach for them. The registration is the step people skip because it is manual, and it is the step that makes the account real.
Step 2: Create the groups that will carry every role
Every Azure role you assign in this series lands on a group, never on a person, so the groups come before the assignments. Create a small, named set that maps to function rather than to individuals, and put the two human administrators into the platform-admins group as members. Do not put the break-glass accounts in any group; their power comes from their standing directory role, and pooling them into an operational group would defeat the isolation that makes them emergency accounts.
| Group | Carries | Members |
|---|---|---|
| NF-Azure-Platform-Admins | Contributor on the Production subscription; Owner/UAA eligible if licensed | The two administrators who run the foundation |
| NF-Azure-Readers | Reader at the top of the tree | Anyone needing estate-wide visibility without change rights |
| NF-Security-Readers | Security Reader at the top of the tree | The security or MDR function reviewing posture |
| NF-Workload-FileServer-Admins | Contributor at the file-server resource group only | The operators of one workload, scoped to it and nothing wider |
Make the privileged group, NF-Azure-Platform-Admins, a role-assignable group. That property hardens it so that only higher-privileged roles can change its membership, which matters for a group that will hold Owner eligibility. Creating a role-assignable group needs only the Entra ID P1 that Business Premium already includes. Note one thing that has changed and that older guides get wrong: Privileged Identity Management for Groups no longer requires a group to be role-assignable, so you do not need this property merely to make the group eligible later; you set it here because it is good hardening for a privileged group, not because PIM demands it.
Connect-MgGraph -Scopes "Group.ReadWrite.All"
# The privileged group, hardened as role-assignable (P1 is enough to create it)
New-MgGroup -DisplayName "NF-Azure-Platform-Admins" -MailNickname "NF-Azure-Platform-Admins" `
-SecurityEnabled -MailEnabled:$false -IsAssignableToRole
# The remaining function groups (ordinary security groups)
foreach ($g in "NF-Azure-Readers","NF-Security-Readers","NF-Workload-FileServer-Admins") {
New-MgGroup -DisplayName $g -MailNickname $g -SecurityEnabled -MailEnabled:$false
}
# Add the two administrators to the platform-admins group by their user object IDs
New-MgGroupMember -GroupId PLATFORM_ADMINS_OBJID -DirectoryObjectId ADMIN1_OBJID
New-MgGroupMember -GroupId PLATFORM_ADMINS_OBJID -DirectoryObjectId ADMIN2_OBJID
Expected result: four groups, each named for its audience, with the two admins in the platform group and the emergency accounts in none. Keep the object IDs the commands return; Steps 4 and 5 assign roles to these groups by ID.
Step 3: The exceptional elevation at the root of the tree
To place a role assignment at the top of your management-group tree, you need authority at a scope no ordinary administrator holds by default. Azure provides exactly one bridge for this, and it is meant to be exceptional rather than standing. A Global Administrator grants themselves the User Access Administrator role at the root scope, the single slash above every management group and subscription in the tenant, makes the assignments the tree needs, and then removes that elevation. This is the same elevation the governance build relied on to create the hierarchy, and the discipline is the whole point: one person, briefly, for a specific task, stepped back down the moment the task is done.
| Setting | Value | Why |
|---|---|---|
| Root elevation method | Entra Properties toggle, or the elevateAccess endpoint from a script | Either one grants User Access Administrator at root scope so you can assign at the top of the tree |
| Who performs it | A Global Administrator, one person | Root-scope authority is the most powerful state in the tenant; it is not a standing condition |
| Duration | Only as long as Step 4 takes | Elevated root access left on is the widest blast radius that exists; turn it off deliberately |
You can perform the elevation either way, and neither path is more privileged than the other. In the portal it is a directory property change: Microsoft Entra ID, Properties, Access management for Azure resources set to Yes, and back to No when you are finished. From a script it is a single call against the elevateAccess endpoint, which grants the same root User Access Administrator role, and the assignment it creates is removed exactly the way you remove any role assignment at root scope. Reach for whichever fits how you are running the rest of this build, then verify the assignment is gone afterward, because root User Access Administrator left in place is the widest standing authority in the tenant.
# Elevate: grant yourself User Access Administrator at the root scope (/)
az rest --method post \
--url "/providers/Microsoft.Authorization/elevateAccess?api-version=2016-07-01"
# ... make the Step 4 assignments at mg-northfork and below ...
# Step back down: remove the root User Access Administrator assignment when finished
az role assignment delete \
--assignee "[email protected]" \
--role "User Access Administrator" \
--scope "/"
Step 4: Assign RBAC on the tree, with the blast radius you can accept
Now place the standing assignments, each at the scope whose blast radius you are willing to own. Reader and Security Reader go high, at mg-northfork, because visibility across the whole estate is useful and harmless. Contributor for the platform admins goes on the Production subscription, not on the management group above it, so that the ceiling on standing change-power is one subscription rather than the entire tree. Workload Contributor goes lower still, on the single resource group its operators need. Nobody is assigned Owner anywhere, standing; that right is handled in Step 5. In the portal each assignment is the same move: open the scope, open its Access control, add a role assignment, choose the role, and assign it to the group rather than to a person. The consolidated commands do the same thing in one block.
| Scope | Group | Role | Why here |
|---|---|---|---|
| mg-northfork (top of tree) | NF-Azure-Readers | Reader | Estate-wide visibility, inherited everywhere, changes nothing |
| mg-northfork (top of tree) | NF-Security-Readers | Security Reader | Posture review across the estate for the security function |
| Production subscription | NF-Azure-Platform-Admins | Contributor | Full resource management with the ceiling held at one subscription, and no right to grant access |
| File-server resource group | NF-Workload-FileServer-Admins | Contributor | Workload operators scoped to their workload and no wider |
# Reader and Security Reader at the top of the tree (cascade to everything below)
az role assignment create --assignee-object-id READERS_OBJID --assignee-principal-type Group \
--role "Reader" --scope "/providers/Microsoft.Management/managementGroups/mg-northfork"
az role assignment create --assignee-object-id SECREADERS_OBJID --assignee-principal-type Group \
--role "Security Reader" --scope "/providers/Microsoft.Management/managementGroups/mg-northfork"
# Contributor for the platform admins, ceiling held at the Production subscription
az role assignment create --assignee-object-id PLATFORM_ADMINS_OBJID --assignee-principal-type Group \
--role "Contributor" --scope "/subscriptions/PROD_SUB_ID"
# Workload Contributor at a single resource group
az role assignment create --assignee-object-id FILESERVER_ADMINS_OBJID --assignee-principal-type Group \
--role "Contributor" \
--scope "/subscriptions/PROD_SUB_ID/resourceGroups/rg-fileserver-prod-wus2-001"
Expected result: four standing assignments, two at the tree top and two below, none of them Owner. Passing --assignee-principal-type Group is worth doing rather than omitting, because it stops the command from failing silently when Entra replication has not yet caught up with a freshly created group. One design limit to keep in view rather than to fear: a subscription accepts a few thousand role assignments and a management group accepts several hundred, both fixed ceilings. Assigning groups keeps you orders of magnitude clear of them; assigning individuals across resources is how estates actually hit the wall.
Step 5: Make Owner eligible, or run a disciplined standing model
This is the step the licensing decision forks. If you bought Entra ID P2 or Entra ID Governance for the administrators, use Privileged Identity Management to make Owner and User Access Administrator eligible on the Production subscription for NF-Azure-Platform-Admins, rather than assigning either as standing. In the portal this lives under Privileged Identity Management, Azure resources, the Production subscription, its roles, where you add an eligible assignment for the group and then set the activation rules on that role: a maximum activation of a few hours, a required justification, multifactor at activation, and an approval step on Owner specifically. The eligible assignment and the activation policy are two separate settings, and the policy is where just-in-time actually earns its keep, so do not stop at making the role eligible without also tightening how it activates.
| Activation setting | Value | Why |
|---|---|---|
| Maximum activation duration | Two to four hours | Long enough to do the work, short enough that a forgotten activation expires on its own |
| Require justification | On | Every activation writes a reason into the record, which is what makes the log worth reading |
| Require multifactor at activation | On | The privilege is claimed with a fresh proof of identity, not on the strength of a morning sign-in |
| Require approval (Owner) | On, approver is the other administrator | The most dangerous role keeps a second human in the loop; a two-person team can still do this |
Privileged Identity Management is configured in the portal by default, and that is the honest recommendation for the activation policy, which has no clean command-line equivalent worth typing. The eligible assignment itself can be created through the API when you want it in a script, against the role eligibility schedule request resource; the shape is below for the automator, with the activation rules still set in the portal afterward.
# Create an eligible Owner assignment for the group via the PIM API
# (activation policy: max duration, justification, MFA, approval are set in the portal)
az rest --method put \
--url "https://management.azure.com/subscriptions/PROD_SUB_ID/providers/Microsoft.Authorization/roleEligibilityScheduleRequests/NEW_GUID?api-version=2020-10-01" \
--body '{
"properties": {
"principalId": "PLATFORM_ADMINS_OBJID",
"roleDefinitionId": "/subscriptions/PROD_SUB_ID/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635",
"requestType": "AdminAssign",
"scheduleInfo": { "expiration": { "type": "NoExpiration" } }
}
}'
If you did not license P2, you do not get eligibility, and the correct response is not to fall back to standing Owner, it is to design around its absence. Nobody holds standing Owner at all. The platform admins keep Contributor from Step 4, which manages every resource but cannot grant access, and the rare operation that genuinely requires Owner or User Access Administrator is done through a break-glass account or the Step 3 root elevation, deliberately and with a record. You then buy back the visibility the platform is not giving you: an Activity Log alert that fires on any role-assignment write across the subscription, so that a new privileged assignment anywhere is something a person sees immediately. This model asks more of the operator, but it is a real design rather than a gap, and it is far better than standing Owner nobody chose.
# No-PIM fallback: alert on any role-assignment change in the subscription
az monitor activity-log alert create \
--name "alert-role-assignment-write" \
--resource-group "rg-management-prod-wus2-001" \
--scope "/subscriptions/PROD_SUB_ID" \
--condition category=Administrative and operationName=Microsoft.Authorization/roleAssignments/write \
--action-group AG_OPS_EMAIL_ID
A worked example
Northfork runs with two administrators and no dedicated security staff. Both are members of NF-Azure-Platform-Admins, which holds Contributor on the Production subscription, so day to day they build and manage everything without anyone holding a standing right to hand out access. Northfork bought two Entra ID P2 licenses, one per administrator, so Owner and User Access Administrator are eligible on the subscription with a four-hour cap, a required justification, and approval on Owner routed to whichever administrator is not activating. When Northfork needs to add a new role assignment for a vendor, one administrator activates User Access Administrator, makes the change, and lets it expire; the other approved it and the log holds the reason. The two break-glass accounts sit untouched with their keys in the safe, monitored so that any sign-in raises an alert. If Northfork had not bought P2, the same two admins would run on Contributor alone, reach for a break-glass account on the rare day an Owner-only action is unavoidable, and rely on the role-assignment alert to catch anything that appeared without their doing it. Same estate, one licensing decision, two coherent operating models.
Naming convention
Name groups for scope and function so a glance at the group list reads as an access plan: the NF prefix ties them to the organization, then Azure or Workload states the plane, then the scope or workload, then the function, giving names like NF-Azure-Platform-Admins and NF-Workload-FileServer-Admins. Azure does not let you name a role assignment, so keep the assignment map itself, the four-row table in Step 4, in the same document as the rest of the foundation, and update it whenever a scope gains or loses a group. That table is your assignment inventory; the estate stays legible because the map is maintained, not because the platform remembers your intent.
The end-to-end test
Prove the one thing that makes the whole model real: a role assigned once, high on the tree, reaching a subscription that was never touched. Reader was assigned to NF-Azure-Readers at mg-northfork and to no subscription at all, so list the group’s effective assignments at the Production subscription and confirm Reader appears as inherited from the management-group scope. If it does, inheritance is working and the governance backbone is genuinely governing. If you licensed P2, run the second half of the proof as well: have an administrator activate Owner through Privileged Identity Management, confirm the role becomes active with the justification recorded, do nothing, and confirm it expires on its own at the cap. A right that appears on demand, is logged, and disappears by itself is the design working exactly as drawn.
# Prove inheritance: Reader assigned at the MG shows as inherited at the subscription
az role assignment list --assignee READERS_OBJID \
--scope "/subscriptions/PROD_SUB_ID" --include-inherited \
--query "[].{role:roleDefinitionName, scope:scope}" -o table
# Expect: Reader, with scope showing the mg-northfork management-group path, not the subscription
Before you scale it
- Two break-glass accounts created cloud-only on the onmicrosoft.com domain, each with standing Global Administrator, each carrying a registered hardware security key, excluded from restrictive and Microsoft-managed Conditional Access, and monitored for any sign-in.
- Function groups created and named for scope and function, the two administrators added to the platform-admins group, and the break-glass accounts left out of every group.
- Root elevation used once to assign at the top of the tree and then turned off.
- Standing RBAC placed at the scope whose blast radius you accept: Reader and Security Reader at the tree top, Contributor at the Production subscription, workload Contributor at a resource group, and no standing Owner anywhere.
- Owner and User Access Administrator made eligible through Privileged Identity Management with a capped, justified, approved activation policy, or the no-PIM fallback run deliberately with a role-assignment alert in place.
- End-to-end test passed: Reader assigned at the management group shows as inherited at the subscription, and if licensed, an activated Owner role expires on its own.
With identity placed on the tree, the foundation can finally be trusted to enforce rather than merely describe. What it cannot yet do is tell you what happened on it. The next article turns to security posture and central logging: the single Log Analytics workspace everything reports into, Defender for Cloud watching the estate, and the diagnostic settings that make an incident reconstructable instead of a guess. That is where the foundation starts to keep records, and it is next.
Azure Landing Zones
‹ Previous: [LZ 5] The Control Plane You Were Already Standing On
Next: [LZ 6] The Estate You Can See ›




