Identity Bytes // IB-ENTRA-SEC Track
Advanced Lab 03 of 12 Est. 120 minutes

Enforcing MFA for All Users with Conditional Access

Having a strong factor available is not the same as requiring it. In this lab you build the Conditional Access policy that enforces MFA at sign-in for every account, done the safe way: emergency access accounts first, a report-only rollout to see the blast radius, evidence-based validation, then enforcement, and a persona-based admin policy on top.

Section 1

Lab Metadata

Lab ID
IB-ENTRA-SEC-03
Difficulty
Advanced
Scenario Org
Northgate Financial
Estimated Time
120 minutes

Core technologies

Conditional Access Break-glass accounts Report-only mode Sign-in logs Directory role targeting Microsoft Graph PowerShell Entra ID P1
Section 2

Scenario and Description

IN PLAIN TERMS Owning a good lock is not the same as locking the door. Lab 02 gave everyone a strong key. Conditional Access is the rule that says the door is genuinely locked and you must use that key to come in. Because a rule that locks every door at once could trap you inside, you first cut two emergency keys that always work, the break-glass accounts, then run the new rule as a silent rehearsal to see exactly who it would stop, and only then switch it on for real.

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.

Section 3

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

ScopeWhy it is needed
Policy.ReadWrite.ConditionalAccessCreate and update Conditional Access policies
Application.Read.AllResolve application names when reading policy conditions and logs
User.ReadWrite.All, Group.ReadWrite.All, RoleManagement.ReadWrite.DirectoryCreate the break-glass accounts, their group, and assign Global Administrator
AuditLog.Read.AllRead sign-in logs to validate report-only impact
STOP - READ BEFORE YOU RUN ANYTHING Do not create any Conditional Access policy in this lab until Phase A is complete and you have confirmed the break-glass exclusion works. If you enforce an all-users MFA policy without a working excluded emergency account, and your own session is later challenged in a way you cannot satisfy, you can be permanently locked out of the tenant. Phase A exists to prevent exactly that.
Section 4

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.

DimensionWhy this matters
RiskPassword-only sign-in is the primary path to account takeover. Enforced MFA is the highest-impact single control you can deploy.
ComplianceAuditors expect enforced, not merely available, MFA, plus evidence of emergency-access governance. Both are produced here.
ProductivityReport-only mode lets you predict helpdesk impact and communicate before users are affected, avoiding a support surge.
Security postureThis 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.

Section 5

Skills Mapped to Production Solutions

Skill learned in this labReal-world enterprise application
Designing and provisioning break-glass accountsGuaranteed tenant recovery, a mandatory control in every mature Entra deployment
Authoring Conditional Access policy as code via GraphVersion-controlled, repeatable policy deployment across environments
Report-only rollout and sign-in log analysisPredicting and communicating change impact before enforcement
Persona-based policy targeting directory rolesStricter controls for administrators than for standard staff
Safe enforcement and rollbackChange-managed security control deployment without outages
Section 6

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.

Sign-in user + app + context CA evaluation Is user excluded? Which policies apply? What controls result? Break-glass excluded 2 emergency accounts bypass ALL policies CA001 - MFA for all users grant = require MFA CA002 - MFA for admins tighter, role-targeted Result grant with MFA, or block
PRODUCTION CONSIDERATION Microsoft's own guidance is to exclude at least one, ideally two, emergency access accounts from all Conditional Access policies, keep their long credentials in a physical vault or privileged secret store, and alert on every sign-in they make. Those alerts are how you tell a genuine emergency use from a compromise.
Section 7

Step-by-Step Implementation

Phase A - Build the safety net first

REAL WORLD ANALOGYA break-glass account is the fire axe behind glass in the corridor. It is deliberately kept outside every locked cabinet, because the whole point is that it must work when the normal keys and rules have failed. That is why these two accounts are excluded from every access rule you will ever write: an emergency tool locked behind the same doors it exists to break is no emergency tool at all.

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)" }
}
CRITICAL Copy both generated passphrases into a secure store now, a password manager or, in production, a sealed physical record. These accounts are your only guaranteed way back in. If you lose the passphrases and lock yourself out, recovery means a Microsoft support case.
VERIFICATION
Get-MgGroupMember -GroupId $bg.Id | ForEach-Object { $_.AdditionalProperties.userPrincipalName }
# Expected: breakglass1@... and breakglass2@...

Phase B - Author the policy in report-only

REAL WORLD ANALOGYReport-only mode is a silent fire drill. The new rule watches every person coming through the door and writes down who it would have stopped, but stops nobody. Enterprises run every significant access rule this way first, because the log of 'would have been blocked' is how you find the forgotten service account or the director's old laptop before they become a live outage.

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)"
VERIFICATION
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
VERIFICATION For 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.
INFO: the portal What If tool The Entra admin center has a "What If" tool under Conditional Access that simulates a sign-in for a chosen user and app and lists which policies would apply. It is the fastest visual check to run alongside the log query above, especially to confirm break-glass exclusion.

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
SECURITY WARNING From this moment MFA is required for all non-excluded users. Keep your break-glass credentials and one already-MFA'd admin session available until you have confirmed you can still sign in normally. If your own admin account was not yet registered for MFA, register it immediately at 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

REAL WORLD ANALOGYA bank checks a teller's ID once at the staff entrance, but the person entering the vault gets a second, stricter check at the vault door. Splitting admins into their own policy is building that second checkpoint: you can later demand a stronger grade of key from vault staff without changing anything for the tellers. Keeping the populations separate is what makes the Lab 04 upgrade a one-line change.

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)"
VERIFICATION Sign in as 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.
PRODUCTION CONSIDERATION In Lab 04 you replace 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.
Section 8

Testing and Validation

  1. Standard user: sign in as asmith in a private window. You are challenged for MFA. Approving the Authenticator prompt grants access.
  2. Break-glass: sign in as breakglass1. You are not challenged by CA001 or CA002 (result notApplied). This proves your safety net.
  3. Admin: sign in as jpatel. Both CA001 and CA002 evaluate, and MFA is required.
  4. Regression check: re-run the Lab 01 baseline. Your posture story is now "MFA available (Lab 02) and enforced (Lab 03)".
SymptomCauseResolution
You are locked out after enforcingYour admin account had no registered factor and was not excludedSign in with a break-glass account, register MFA for your admin, or temporarily set CA001 back to report-only
Break-glass gets challengedAccount not in the exclusion group, or group not referenced in the policyConfirm group membership and that excludeGroups holds the group id
Policy shows notApplied for everyoneStill in report-only, or an app or client filter is excluding sign-insConfirm state is enabled and applications is All
Admin policy does not triggerRole assignment is PIM-eligible but not activeRole-targeted CA applies to active assignments, activate the role or test with an active assignment
Section 9

Security Analysis

What makes this sound

Intentionally simplified for the lab

Production hardening

Section 10

Cleanup Instructions

INFO: preserve for the track Keep CA001, CA002 and the break-glass accounts, Lab 04 upgrades CA002 and Lab 07 relies on the break-glass pattern. Only remove them to reset the tenant.
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
SECURITY WARNING Only delete break-glass accounts in a lab. In production these accounts are permanent and their removal would remove your emergency recovery path.
Section 12

Key Takeaways and Next Lab

Identity Bytes // IB-ENTRA-SEC Track // Lab 03 of 12. British English. For lab and training use against a disposable tenant only.