Lab Metadata
Core technologies
Scenario and Description
Lab 02 made Microsoft Authenticator available and migrated Northgate off SMS. But a factor being registered does not mean it is demanded. Entra will only challenge a user for MFA when something tells it to, and that something is Conditional Access. Right now a user at Northgate with a leaked password can still sign in without a second factor, because nothing requires one. This lab closes that gap.
Conditional Access is the policy engine that evaluates each sign-in against signals (who, what app, what device, what location, what risk) and applies controls (require MFA, require a compliant device, block). It is the heart of a Zero Trust posture and the mechanism behind the brief's requirement to "ensure MFA is enforced on all user accounts".
Conditional Access is also the single most effective way to lock yourself out of your own tenant. A policy that requires MFA from all users, applied to all apps, with no exclusions, can trap every administrator, including you, if their session is challenged and cannot respond. This lab therefore front-loads the two controls that make Conditional Access safe: dedicated break-glass emergency accounts that are excluded from policy, and a report-only rollout that shows you the impact before a single user is actually blocked.
Prerequisites
Prior labs
Labs 01 and 02 are required. The personas and the migrated Authenticator methods from Lab 02 mean users have a factor to satisfy the policy you are about to enforce. Conditional Access requires Entra ID P1, which your Lab 01 trial provides.
Graph scopes introduced
| Scope | Why it is needed |
|---|---|
Policy.ReadWrite.ConditionalAccess | Create and update Conditional Access policies |
Application.Read.All | Resolve application names when reading policy conditions and logs |
User.ReadWrite.All, Group.ReadWrite.All, RoleManagement.ReadWrite.Directory | Create the break-glass accounts, their group, and assign Global Administrator |
AuditLog.Read.All | Read sign-in logs to validate report-only impact |
Real-World Problem Statement
Every organisation needs MFA enforced, and every organisation needs a guaranteed way back in if MFA infrastructure fails or a policy misfires. These two needs pull against each other, and the resolution is a deliberate design: enforce broadly, exclude a tiny, monitored set of emergency accounts, and never roll out blind.
| Dimension | Why this matters |
|---|---|
| Risk | Password-only sign-in is the primary path to account takeover. Enforced MFA is the highest-impact single control you can deploy. |
| Compliance | Auditors expect enforced, not merely available, MFA, plus evidence of emergency-access governance. Both are produced here. |
| Productivity | Report-only mode lets you predict helpdesk impact and communicate before users are affected, avoiding a support surge. |
| Security posture | This is the policy that turns "MFA-capable" from Lab 01 into "MFA-enforced", the number the board actually cares about. |
Concrete scenario: Northgate's CISO wants MFA enforced for the whole company by month end, with a written guarantee that the security team cannot be locked out. You deliver a report-only policy this week, a validated impact report, then enforcement, plus two governed break-glass accounts documented for the audit file.
Skills Mapped to Production Solutions
| Skill learned in this lab | Real-world enterprise application |
|---|---|
| Designing and provisioning break-glass accounts | Guaranteed tenant recovery, a mandatory control in every mature Entra deployment |
| Authoring Conditional Access policy as code via Graph | Version-controlled, repeatable policy deployment across environments |
| Report-only rollout and sign-in log analysis | Predicting and communicating change impact before enforcement |
| Persona-based policy targeting directory roles | Stricter controls for administrators than for standard staff |
| Safe enforcement and rollback | Change-managed security control deployment without outages |
Architecture Overview
A sign-in is evaluated against every enabled Conditional Access policy. The design here layers two policies over a foundation of excluded emergency accounts: a broad "MFA for all users" policy, and a stricter "MFA for admins" policy. Break-glass accounts are excluded from both.
Step-by-Step Implementation
Phase A - Build the safety net first
1Create two break-glass accounts and an exclusion group
Purpose: guarantee a route back into the tenant that no Conditional Access policy can block.
Context: a "break-glass" or emergency access account is a cloud-only Global Administrator, not tied to any individual, with a very long passphrase, used only when normal admin access fails. You exclude it from every policy so a misfire cannot trap you.
Create the accounts, the group, and assign Global Administrator
Connect-MgGraph -Scopes 'Policy.ReadWrite.ConditionalAccess','Application.Read.All', 'User.ReadWrite.All','Group.ReadWrite.All','RoleManagement.ReadWrite.Directory', 'AuditLog.Read.All' -NoWelcome $domain = (Get-MgOrganization).VerifiedDomains | Where-Object IsDefault | Select-Object -ExpandProperty Name # 1. Exclusion group that break-glass accounts belong to. $bg = New-MgGroup -DisplayName 'CA-Exclude-BreakGlass' -MailEnabled:$false ` -MailNickname 'ca-exclude-breakglass' -SecurityEnabled:$true # 2. Two emergency accounts with long random passphrases. Add-Type -AssemblyName System.Web 1..2 | ForEach-Object { $upn = "breakglass$_@$domain" $pw = [System.Web.Security.Membership]::GeneratePassword(40,8) $u = New-MgUser -DisplayName "Break Glass $_" -UserPrincipalName $upn ` -MailNickname "breakglass$_" -AccountEnabled ` -PasswordProfile @{ Password=$pw; ForceChangePasswordNextSignIn=$false } ` -UsageLocation 'GB' New-MgGroupMember -GroupId $bg.Id -DirectoryObjectId $u.Id Write-Host "Created $upn" Write-Host " RECORD THIS PASSPHRASE SECURELY: $pw" -ForegroundColor Yellow } # 3. Assign Global Administrator (template 62e90394-69f5-4237-9190-012177145e10) to both. $gaTemplate = '62e90394-69f5-4237-9190-012177145e10' $gaRole = Get-MgDirectoryRole -All | Where-Object RoleTemplateId -eq $gaTemplate if (-not $gaRole) { $gaRole = New-MgDirectoryRole -RoleTemplateId $gaTemplate } foreach ($n in 1..2) { $u = Get-MgUser -Filter "userPrincipalName eq 'breakglass$n@$domain'" New-MgDirectoryRoleMemberByRef -DirectoryRoleId $gaRole.Id -BodyParameter @{ '@odata.id' = "https://graph.microsoft.com/v1.0/directoryObjects/$($u.Id)" } }
Get-MgGroupMember -GroupId $bg.Id | ForEach-Object { $_.AdditionalProperties.userPrincipalName }
# Expected: breakglass1@... and breakglass2@...
Phase B - Author the policy in report-only
2Create "MFA for all users" without enforcing it
Purpose: define the policy and observe its effect, while it blocks nobody.
Context: the policy state enabledForReportingButNotEnforced is report-only. Entra evaluates the policy on every sign-in and records what would have happened, without applying the control. This is your blast-radius preview.
Create the report-only policy, excluding break-glass
$params = @{
displayName = 'CA001 - Require MFA for all users'
state = 'enabledForReportingButNotEnforced' # report-only
conditions = @{
users = @{
includeUsers = @('All')
excludeGroups = @($bg.Id) # break-glass bypass
}
applications = @{ includeApplications = @('All') }
clientAppTypes = @('all')
}
grantControls = @{
operator = 'OR'
builtInControls = @('mfa') # require multi-factor authentication
}
}
$ca001 = New-MgIdentityConditionalAccessPolicy -BodyParameter $params
"Created policy $($ca001.Id) in state $($ca001.State)"
Get-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $ca001.Id |
Select-Object DisplayName,State
# Expected State: enabledForReportingButNotEnforced
What just happened? A policy now exists that says "require MFA from everyone except the break-glass group, for every app". Because it is report-only, no user is challenged yet. You have defined the intent and can now measure it before you commit.
Phase C - Validate the blast radius
3Generate sign-ins and read the report-only results
Purpose: confirm the policy would apply to normal users and would not apply to break-glass, before enforcing.
Trigger a sign-in, then inspect report-only outcomes in the logs
# Sign in as asmith in a private browser at https://office.com to create a log entry. # Sign-in logs can take a few minutes to surface. Then query them: $since = (Get-Date).AddHours(-1).ToString('yyyy-MM-ddTHH:mm:ssZ') $signins = Get-MgAuditLogSignIn -Filter "createdDateTime ge $since" -Top 50 foreach ($s in $signins) { foreach ($p in $s.AppliedConditionalAccessPolicies) { if ($p.DisplayName -eq 'CA001 - Require MFA for all users') { [pscustomobject]@{ User = $s.UserPrincipalName App = $s.AppDisplayName Result = $p.Result # reportOnlySuccess / reportOnlyFailure / reportOnlyInterrupted / notApplied } } } } | Format-Table -AutoSize
asmith the result is reportOnlySuccess or reportOnlyInterrupted (the policy would apply and require MFA). If you sign in with a break-glass account, the result for CA001 must be notApplied, proving the exclusion works. Do not proceed to enforcement until you have seen notApplied for a break-glass sign-in.
Phase D - Enforce
4Flip the policy to enabled
Purpose: move from observing to enforcing, now that the blast radius is confirmed and break-glass is verified excluded.
Enable the policy
Update-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $ca001.Id `
-BodyParameter @{ state = 'enabled' }
Get-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $ca001.Id |
Select-Object DisplayName,State
# Expected State: enabled
aka.ms/mfasetup.
What just happened? The policy is live. Every ordinary sign-in without a satisfied second factor is now challenged, and a leaked password alone no longer grants access. This is the control that moves Northgate's board metric from "MFA-capable" to "MFA-enforced".
Phase E - Persona-based: stricter policy for admins
5Require MFA specifically for directory-role holders
Purpose: apply a dedicated, tighter policy to privileged roles, the foundation Lab 04 upgrades to phishing-resistant methods.
Context: you target includeRoles by role template ID. Admins should always be held to the strongest controls, separate from the all-users baseline, so you can tighten them independently.
Create a role-targeted MFA policy in report-only
# Common privileged role template IDs: # 62e90394-69f5-4237-9190-012177145e10 Global Administrator # 194ae4cb-b126-40b2-bd5b-6091b380977d Security Administrator # e8611ab8-c189-46e8-94e1-60213ab1f814 Privileged Role Administrator # fe930be7-5e62-47db-91af-98c3a49a38b1 User Administrator $adminParams = @{ displayName = 'CA002 - Require MFA for admin roles' state = 'enabledForReportingButNotEnforced' conditions = @{ users = @{ includeRoles = @( '62e90394-69f5-4237-9190-012177145e10', '194ae4cb-b126-40b2-bd5b-6091b380977d', 'e8611ab8-c189-46e8-94e1-60213ab1f814', 'fe930be7-5e62-47db-91af-98c3a49a38b1' ) excludeGroups = @($bg.Id) } applications = @{ includeApplications = @('All') } clientAppTypes = @('all') } grantControls = @{ operator = 'OR'; builtInControls = @('mfa') } } $ca002 = New-MgIdentityConditionalAccessPolicy -BodyParameter $adminParams "Created $($ca002.DisplayName) in state $($ca002.State)"
jpatel (made User Administrator in Lab 01). The CA002 result in the sign-in logs should show the policy applying to the admin. Validate in report-only, then enforce with the same state = 'enabled' flip from Phase D.
builtInControls = @('mfa') on this admin policy with an authentication strength that demands phishing-resistant methods (FIDO2 or Windows Hello for Business), so administrators cannot satisfy the challenge with a phishable factor. Keeping admins in a separate policy now is what makes that upgrade a one-line change later.
Testing and Validation
- Standard user: sign in as asmith in a private window. You are challenged for MFA. Approving the Authenticator prompt grants access.
- Break-glass: sign in as breakglass1. You are not challenged by CA001 or CA002 (result
notApplied). This proves your safety net. - Admin: sign in as jpatel. Both CA001 and CA002 evaluate, and MFA is required.
- Regression check: re-run the Lab 01 baseline. Your posture story is now "MFA available (Lab 02) and enforced (Lab 03)".
| Symptom | Cause | Resolution |
|---|---|---|
| You are locked out after enforcing | Your admin account had no registered factor and was not excluded | Sign in with a break-glass account, register MFA for your admin, or temporarily set CA001 back to report-only |
| Break-glass gets challenged | Account not in the exclusion group, or group not referenced in the policy | Confirm group membership and that excludeGroups holds the group id |
Policy shows notApplied for everyone | Still in report-only, or an app or client filter is excluding sign-ins | Confirm state is enabled and applications is All |
| Admin policy does not trigger | Role assignment is PIM-eligible but not active | Role-targeted CA applies to active assignments, activate the role or test with an active assignment |
Security Analysis
What makes this sound
- Recoverability by design: excluded, monitored break-glass accounts mean a policy error is never terminal.
- Evidence before enforcement: report-only converts a risky change into a measured one, with logs proving impact first.
- Separation of populations: admins sit in their own policy, so their controls can be tightened without touching everyone.
Intentionally simplified for the lab
- Break-glass sign-in alerting is described but not built. Production wires these accounts to a high-priority alert in your SIEM or via a Log Analytics alert rule.
- The all-users policy uses only the MFA control. Real deployments layer device compliance, sign-in risk and session controls.
- Legacy authentication protocols are not yet blocked. A dedicated "block legacy auth" policy is standard and pairs with this one.
Production hardening
- Add a policy that blocks legacy authentication protocols, which bypass MFA entirely.
- Alert on any creation or change of Conditional Access policies and on any break-glass sign-in.
- Review policies with the What If tool after every change, and keep policy definitions in source control.
Cleanup Instructions
Remove the policies, break-glass accounts and group
# Delete the two policies. foreach ($name in 'CA001 - Require MFA for all users','CA002 - Require MFA for admin roles') { $p = Get-MgIdentityConditionalAccessPolicy -Filter "displayName eq '$name'" if ($p) { Remove-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $p.Id } } # Remove break-glass accounts and their group. $domain = (Get-MgOrganization).VerifiedDomains | Where-Object IsDefault | Select-Object -ExpandProperty Name foreach ($n in 1..2) { $u = Get-MgUser -Filter "userPrincipalName eq 'breakglass$n@$domain'" -ErrorAction SilentlyContinue if ($u) { Remove-MgUser -UserId $u.Id } } $g = Get-MgGroup -Filter "displayName eq 'CA-Exclude-BreakGlass'" if ($g) { Remove-MgGroup -GroupId $g.Id } Disconnect-MgGraph
Recommended Learning Links
Key Takeaways and Next Lab
- Conditional Access is what actually enforces MFA. Availability without enforcement is not protection.
- Build the safety net first: excluded, monitored break-glass accounts are non-negotiable before any all-users policy.
- Report-only mode turns a dangerous change into a measured one, always validate the blast radius before enforcing.
- Keep admins in their own policy so their controls can be tightened independently of the estate.
Identity Bytes // IB-ENTRA-SEC Track // Lab 03 of 12. British English. For lab and training use against a disposable tenant only.