The eight Conditional Access policies — a deep implementation guide

Entra ID  ·  Conditional Access  ·  Baseline Policies  ·  2026

Conditional Access Baseline Series  ·  Part 3 of 4
Series progress
Part 1
Baseline overview
Part 2
Foundations: break-glass & logging
Part 3  · You are here
The 8 policies — deep implementation guide
Part 4  · Coming soon
Report-only rollout & troubleshooting

Part 1 set the baseline. Part 2 put the break-glass, exclusions, and logging in place. Part 3 is the one you keep open on a second monitor: every baseline policy walked end to end — the exact UI path, the precise include/exclude scope, the grant or session control with its trade-offs, the common false positives and how to triage them in the sign-in logs, and the rollback pattern for when a policy bites in production. Each of the eight sections follows the same structure, so you can read linearly or jump straight to the one you are deploying today.

🧭
Every policy follows the same sub-structure. UI path · Scope · Grant/Session · Trade-offs · False positives & sign-in log triage · Rollback · Mini Graph PowerShell. Skim or deep-read — your call
🚦
Every policy ships in Report-only first. Enforcement only after at least seven days of real sign-in data, clean triage, and a signed-off exclusion review
🔑
Break-glass is excluded from every policy. That is Part 2's work — if CA-Exclusions-BreakGlass is not in place, stop and go back to Part 2 before enabling anything here
🧩
Core baseline = Policies 1–6, Extended baseline = Policies 7–8. The two sets are not equal in deployment friction; Policies 7 and 8 depend on Intune and generate more triage noise in young tenants
📦
Mini Graph PowerShell per policy, plus a consolidated appendix. Enough to version your baseline as code when you are ready, without forcing it on UI-first admins

How to read this article

Each of the eight H2 sections below is a self-contained policy implementation guide. The sub-structure is identical everywhere:

UI path
The exact click trail in the Entra admin center as of early 2026. Portal UI moves, but the policy shape does not.
Scope
Precise include/exclude. Named group references match the exclusion pattern from Part 2: CA-Exclusions-BreakGlass, CA-Exclusions-ServiceAccounts, CA-Exclusions-Travel, CA-Exclusions-TempAccess.
Grant / Session
The control that actually enforces the policy, plus the secondary choice (authentication strength, frequency, persistent browser) when relevant.
Trade-offs
Where the clean version of the policy rubs against real-world SMB reality. Licensing, platform coverage, preview features, and what Microsoft documents differently.
False positives & triage
The specific failure modes you will see in Report-only, and the sign-in log filters that tell you whether the match is legitimate, a misconfiguration, or a real attack signal.
Rollback pattern
The fastest safe way to turn this specific policy off, plus what to keep in place while you investigate. Every policy has one.
Graph PowerShell
A minimal New-MgIdentityConditionalAccessPolicy snippet with just the parameters that matter for this policy. The full reusable pattern (parameter splatting, session controls, auth context) lives in the appendix.
⚠️
A note on the Graph PowerShell snippets. The per-policy snippets in this article are intentionally minimal and illustrative. They are designed to show the shape of each policy, not to be copy-pasted into production. Before you automate any of these policies, validate IDs, aliases, filter expressions, and preview-dependent values against the current Microsoft documentation for your tenant. Conditional Access surface details — app filter names, authentication strength IDs, device filter syntax, preview/GA status — evolve over time, and the safest path is always to confirm in the latest docs.

The 2026 guidance assumes you have Part 2's foundations in place: two break-glass accounts with FIDO2 keys, the four CA-Exclusions-* groups populated and documented, and sign-in logs streaming into Log Analytics with at least one alert rule wired up. If any of those are missing, stop here and do Part 2 first — otherwise the policies below turn into your next incident rather than your next hardening win.


Policy 1 · Block legacy authentication

The first policy every tenant needs. Legacy auth (Basic Auth, Exchange ActiveSync, SMTP AUTH, IMAP/POP, older Office clients) cannot honour MFA, cannot present device state, and is the single most reliable way password-spray attacks still succeed in 2026. Block it before anything else — enforcement here is a prerequisite for Policy 2 to be meaningful.

UI path
Entra admin center → Protection → Conditional Access → Policies → New policy. Name: CA001 — Block legacy authentication.
Scope
Users: Include All users. Exclude CA-Exclusions-BreakGlass and CA-Exclusions-ServiceAccounts. Target resources: Cloud apps → All cloud apps. Conditions → Client apps: Select Exchange ActiveSync clients and Other clients. Leave browser and modern auth clients unticked.
Grant / Session
Grant: Block access. No session control. This policy is a gate, not a friction point.
Trade-offs
Basic Auth for Exchange Online has been disabled by default for new tenants for some time and Microsoft continues to retire the remaining protocols. The policy belt-and-braces that retirement — a blocked sign-in attempt is a cleaner audit signal than a protocol that silently does not exist. The only real trade-off is identifying which internal apps or devices still use SMTP AUTH before they fail Monday morning.
False positives & triage
In Report-only you will see hits for: line-of-business apps using SMTP AUTH for outbound mail (MFPs, CRM notifiers, on-prem scanners), old ActiveSync profiles on personal phones, and service accounts you did not know existed. Triage query in Sign-in logs → User sign-ins (non-interactive): filter by Status = Failure, Conditional Access = Report-only: Failure, and sort by Client app. Anything reporting Authenticated SMTP, IMAP4, POP3, Exchange ActiveSync, or Other clients is either a migration candidate or an exclusion candidate for CA-Exclusions-ServiceAccounts — with a documented reason and an end date.
Rollback pattern
Set the policy to Off in the portal. The effect is immediate. Do not delete the policy — keep it versioned and off. If a specific sender is the cause, the right fix is rarely to weaken this policy; it is to migrate the sender to OAuth2 / high-trust connector / Graph mail submission, or to scope it through CA-Exclusions-ServiceAccounts with an expiry.
Graph PowerShell
CA001 — minimal policy shape PowerShell
New-MgIdentityConditionalAccessPolicy -BodyParameter @{
  displayName  = "CA001 — Block legacy authentication"
  state        = "enabledForReportingButNotEnforced"
  conditions   = @{
    users         = @{ includeUsers = @("All"); excludeGroups = @("<BreakGlass-GroupId>","<ServiceAccts-GroupId>") }
    applications  = @{ includeApplications = @("All") }
    clientAppTypes = @("exchangeActiveSync","other")
  }
  grantControls = @{ operator = "OR"; builtInControls = @("block") }
}

Policy 2 · Require MFA for all users

This is the policy that replaces Security Defaults' MFA story with something you can actually tune. Use authentication strengths (MFA strength as the default) rather than the legacy "Require multi-factor authentication" checkbox — strengths are how Microsoft intends the control to evolve, and they let you escalate specific scenarios later without rewriting the policy.

UI path
Protection → Conditional Access → Policies → New policy. Name: CA002 — MFA for all users.
Scope
Users: Include CA-InScope-AllUsers (dynamic group that contains all member users). Exclude CA-Exclusions-BreakGlass, CA-Exclusions-ServiceAccounts, and CA-Exclusions-TempAccess. Target resources: Cloud apps → All cloud apps. Conditions: leave default (all locations, all devices, all client apps) — Policy 1 already handles legacy clients.
Grant / Session
Grant: Require authentication strength → Multifactor authentication. Do not mix in "Require device to be compliant" here — that belongs in admin-specific policies (Policy 4). Keep the MFA-for-everyone policy deliberately simple.
Trade-offs
Authentication strengths are the modern control surface, but they require a one-time mental shift for admins used to the legacy checkbox. The "MFA" built-in strength covers Microsoft Authenticator push, passkeys, FIDO2, and phone-based options; it is the right default for everyone. Admins get a stricter strength in Policy 3.
False positives & triage
Main causes in Report-only: users who have never registered an MFA method (they pass today via Security Defaults but fail once CA takes over), shared mailboxes incorrectly enabled as sign-in accounts, and automation service principals not properly scoped to the service-accounts exclusion. Triage: Sign-in logs → Interactive sign-ins, filter by Conditional Access = Report-only: Failure and add the Authentication requirement column. Cross-reference any repeat failure with the MFA registration report (Get-MgReportAuthenticationMethodUserRegistrationDetail, covered in Part 2's pre-deployment hygiene) — unregistered users must enrol before enforcement.
Rollback pattern
Set the policy to Off. Security Defaults remains off (assumed, per Part 1's handover) so MFA is temporarily absent — this is why the rollback plan for Policy 2 is not "revert to Security Defaults". The correct interim fallback is a narrower Policy 2 scoped only to CA-InScope-AllUsers minus a temporary CA-Exclusions-TempAccess containing the specific users you are unblocking, while you resolve the registration gap. Document the temp exclusion with an end date.
Graph PowerShell
CA002 — minimal policy shape PowerShell
New-MgIdentityConditionalAccessPolicy -BodyParameter @{
  displayName  = "CA002 — MFA for all users"
  state        = "enabledForReportingButNotEnforced"
  conditions   = @{
    users        = @{
      includeGroups = @("<InScope-AllUsers-GroupId>")
      excludeGroups = @("<BreakGlass-GroupId>","<ServiceAccts-GroupId>","<TempAccess-GroupId>")
    }
    applications = @{ includeApplications = @("All") }
  }
  grantControls = @{
    operator                    = "OR"
    authenticationStrength      = @{ id = "<MFA-StrengthId>" }
  }
}

Policy 3 · Phishing-resistant MFA for admins

The policy that stops adversary-in-the-middle (AiTM) phishing kits from succeeding against your most dangerous accounts. Phishing-resistant methods — FIDO2 security keys, Windows Hello for Business, device-bound passkeys, and certificate-based auth — are the only ones that cryptographically bind the sign-in to the origin, which is what AiTM defeats.

UI path
Protection → Conditional Access → Policies → New policy. Name: CA003 — Phishing-resistant MFA for admins.
Scope
Users → Directory roles: include the privileged roles Microsoft highlights — Global Administrator, Privileged Role Administrator, Security Administrator, Exchange Administrator, SharePoint Administrator, User Administrator, Conditional Access Administrator, Application Administrator, Cloud Application Administrator, Authentication Administrator, Privileged Authentication Administrator, Helpdesk Administrator, Billing Administrator, and any role assigned permanent Global Reader used for audit. Exclude CA-Exclusions-BreakGlass. Target resources: All cloud apps.
Grant / Session
Grant: Require authentication strength → Phishing-resistant MFA. Every admin registers a FIDO2 key or a device-bound passkey before this moves out of Report-only.
Trade-offs
Phishing-resistant MFA is the correct ceiling for admins and CISA agrees. The friction is the key procurement and enrolment — two FIDO2 keys per admin (primary + backup), enrolment done on a known workstation, and a re-issue process for lost keys that does not involve calling Microsoft support. Budget 30 minutes per admin for first enrolment. This is a one-time cost, not an ongoing one.
False positives & triage
The only legitimate failure in Report-only is an admin who has not yet registered a phishing-resistant method. Triage: MFA registration report filtered to Directory roles; anyone with Passkey (FIDO2) or Windows Hello for Business not in their methods list is an enrolment gap. An illegitimate failure — an admin signing in from an unregistered device attempting an AiTM session — is exactly what this policy exists to stop. Both look similar in the log; the difference is usually country, UA string, and whether the account was expecting to sign in.
Rollback pattern
Do not turn this policy off under pressure — that is exactly what an attacker with admin-phishing kits wants. Instead, the correct rollback is to narrow scope temporarily: add the specific admin blocked by an enrolment gap to CA-Exclusions-TempAccess with an expiry of 24 hours, complete their enrolment, then remove. Document each temp exclusion in the ticket. The policy itself stays on.
Graph PowerShell
CA003 — minimal policy shape PowerShell
New-MgIdentityConditionalAccessPolicy -BodyParameter @{
  displayName  = "CA003 — Phishing-resistant MFA for admins"
  state        = "enabledForReportingButNotEnforced"
  conditions   = @{
    users = @{
      includeRoles  = @("62e90394-69f5-4237-9190-012177145e10","<otherRoleIds>")  # Global Admin template ID shown
      excludeGroups = @("<BreakGlass-GroupId>")
    }
    applications = @{ includeApplications = @("All") }
  }
  grantControls = @{
    operator               = "OR"
    authenticationStrength = @{ id = "<PhishResistant-StrengthId>" }
  }
}

Policy 4 · Compliant device for admin portals

Policy 3 ensures admins authenticate from a phishing-resistant method. Policy 4 ensures they do it from a known, managed device. The two combine to mean: admin work happens on an Intune-compliant workstation with a FIDO2 key plugged in — not on a personal laptop, not on a browser session at home, not on a shared machine at a client site.

UI path
Protection → Conditional Access → Policies → New policy. Name: CA004 — Compliant device for admin portals.
Scope
Users: Same directory roles as Policy 3. Exclude CA-Exclusions-BreakGlass. Target resources → Cloud apps: apply the Microsoft Admin Portals filter (preview at time of writing — check current GA status before enforcing). This covers Entra admin center, Intune, Microsoft 365 admin center, Defender portal, Purview, and the family of admin URLs. Alternatively, include the individual service apps (Microsoft Graph, Azure Management, etc.) — the admin portals filter is easier to keep in sync as Microsoft adds consoles.
Grant / Session
Grant: Require device to be marked as compliant OR Require Hybrid Azure AD joined device. Operator: Require one of the selected controls. Combine with Policy 3's phishing-resistant MFA only if you are deliberately chaining both into a single policy — cleaner to keep them separate so each has its own rollback path.
Trade-offs
This is the first policy that genuinely depends on Intune (or a compatible compliance provider). If your tenant does not yet have Intune enrolment for admin workstations, Policy 4 cannot enforce anything meaningful — and until enrolment is complete, Report-only will fire on every admin sign-in. Sequence matters: enrol admin laptops into Intune, confirm compliance reporting is working, then turn this on in Report-only. The Microsoft Admin Portals filter is a preview capability; watch the feature status and test after Microsoft changes it.
False positives & triage
Expected failures: an admin signing in from their phone to approve something (no compliant device state), an admin on a home desktop that is not Intune-enrolled, and new admin hires whose laptop has not yet completed Autopilot enrolment. Triage: Sign-in logs → Interactive sign-ins, filter by Application = Microsoft Azure Management (or the portal you want to check), add the Device ID and Device compliance columns. A blank compliance field means the device is unknown to Intune — that is the enrolment gap. Resolve with enrolment, not with exclusions.
Rollback pattern
Safest rollback is to move the policy back to Report-only, not to Off. That way admins regain access while you still capture the failed-compliance signal in the logs. For a targeted fix, add the named admin to CA-Exclusions-TempAccess with a same-day expiry, and make completing Intune enrolment a blocker for removing that exclusion.
Graph PowerShell

If you automate this policy with Graph, confirm the current app filter or application identifier in the latest Microsoft documentation before use. The Microsoft Admin Portals filter is a preview-adjacent capability, and the alias below is illustrative — the exact value and shape can change.

CA004 — minimal policy shape (illustrative) PowerShell
New-MgIdentityConditionalAccessPolicy -BodyParameter @{
  displayName  = "CA004 — Compliant device for admin portals"
  state        = "enabledForReportingButNotEnforced"
  conditions   = @{
    users        = @{ includeRoles = @("<AdminRoleIds>"); excludeGroups = @("<BreakGlass-GroupId>") }
    applications = @{
      includeApplications = @("<AdminPortals-AppFilterOrIds>")  # validate in Microsoft docs
    }
  }
  grantControls = @{
    operator        = "OR"
    builtInControls = @("compliantDevice","domainJoinedDevice")
  }
}

Policy 5 · Require MFA for guests

Guest users — external partners, contractors, auditors — sign into your tenant with credentials you do not control. You cannot reason about their home-tenant hygiene, whether they have MFA, or whether their account has been compromised somewhere else. The only sensible position is to assert MFA at your door for every guest sign-in, regardless of what their home tenant does.

UI path
Protection → Conditional Access → Policies → New policy. Name: CA005 — MFA for guests.
Scope
Users → Guest and external users: select All guest and external users, or for a more precise posture use the individual sub-categories (B2B collaboration guests, B2B direct connect, B2B collaboration members, local guests, service provider users, other external users). Exclude CA-Exclusions-BreakGlass. Target resources: All cloud apps.
Grant / Session
Grant: Require authentication strength → Multifactor authentication. Do not use phishing-resistant MFA here: you cannot force a guest to enrol a FIDO2 key, and the policy would block legitimate partner access.
Trade-offs
Cross-tenant access settings (inbound trust) can make this policy smoother: if your partner tenants are trusted to satisfy MFA on their side, Entra honours their claim and does not re-prompt. Without cross-tenant trust, guests get an MFA prompt the first time they sign in to your tenant even if their home tenant already challenged them — slight friction, genuine security gain. Worth reviewing cross-tenant access settings for the 3–5 partner tenants that cover 90% of your guest sign-ins.
False positives & triage
Most common Report-only hits: guests whose accounts in their home tenant have no MFA methods registered (your prompt forces first registration in their tenant, which can confuse them). Triage: Sign-in logs → Interactive sign-ins, filter User type = Guest and Conditional Access = Report-only: Failure. Reach out to the guest's sponsor in your tenant — they usually have a contact at the partner who can resolve the registration.
Rollback pattern
Rollback to Report-only rather than Off — you still want the telemetry. For a specific stuck guest, the fix is on their side (register MFA in their home tenant), not on yours. Resist the temptation to carve per-guest exclusions; maintain a clean "guests are external and must MFA" posture.
Graph PowerShell
CA005 — minimal policy shape PowerShell
New-MgIdentityConditionalAccessPolicy -BodyParameter @{
  displayName  = "CA005 — MFA for guests"
  state        = "enabledForReportingButNotEnforced"
  conditions   = @{
    users = @{
      includeGuestsOrExternalUsers = @{
        guestOrExternalUserTypes = "b2bCollaborationGuest,b2bCollaborationMember,b2bDirectConnectUser,otherExternalUser"
        externalTenants          = @{ membershipKind = "all" }
      }
      excludeGroups = @("<BreakGlass-GroupId>")
    }
    applications = @{ includeApplications = @("All") }
  }
  grantControls = @{
    operator               = "OR"
    authenticationStrength = @{ id = "<MFA-StrengthId>" }
  }
}

Policy 6 · Block sign-ins from unexpected countries

Most SMB tenants sign in from a handful of countries: the home country, one or two where the sales team travels, and occasionally a client location. Everything else is noise — and specifically, everything else is where password-spray and credential-stuffing traffic comes from. A named-location allow-list blocks the noise before MFA even becomes relevant, and gives you a clean signal in the logs when someone travels unexpectedly.

UI path
Pre-req: Protection → Conditional Access → Named locations → Countries location. Create Allowed countries with the countries your users legitimately sign in from. Determine Location by IP address (IPv4/IPv6) is the safer option; GPS-based determination can be noisy on mobile. Then: Policies → New policy. Name: CA006 — Block unexpected countries.
Scope
Users: Include CA-InScope-AllUsers. Exclude CA-Exclusions-BreakGlass, CA-Exclusions-ServiceAccounts, and CA-Exclusions-Travel. Target resources: All cloud apps. Conditions → Locations: Include Any location; Exclude Allowed countries (your named location).
Grant / Session
Grant: Block access. Combining block with MFA here is tempting and wrong — block is definitive and cheaper than forcing MFA for traffic that should not exist.
Trade-offs
IP-based geolocation is not perfect: VPNs, mobile carriers with unusual routing, and small-business broadband occasionally geolocate to the wrong country. This is why the CA-Exclusions-Travel group exists — documented, time-boxed travel. It is also why this policy is Report-only for longer (often 14 days rather than 7): you want to see a full pay-cycle of legitimate signal before enforcement.
False positives & triage
Expected Report-only hits: the CEO's Madrid hotel wifi, the developer on a weekend trip to Porto, the consultant in Zurich for a client workshop. Triage: Sign-in logs → Interactive sign-ins, filter by Conditional Access = Report-only: Failure and add the Location and IP address columns. Cross-reference the user, the app, and the time — legitimate travel looks like a single country for 3–10 days with consistent app usage; attack traffic looks like ten countries in an hour. Legitimate travel → add the user to CA-Exclusions-Travel with an end date. Attack traffic → confirm the account's password hygiene and force a credential reset.
Rollback pattern
Set to Report-only, not Off — the signal is useful even when not enforced. If a specific traveller is blocked, do not widen the named location globally; add them to CA-Exclusions-Travel with a documented expiry. Expanding the allow-list permanently is almost always the wrong answer; most SMBs find that 90% of their travellers are <10 people and a time-boxed exclusion is cleaner.
Graph PowerShell
CA006 — minimal policy shape PowerShell
New-MgIdentityConditionalAccessPolicy -BodyParameter @{
  displayName  = "CA006 — Block unexpected countries"
  state        = "enabledForReportingButNotEnforced"
  conditions   = @{
    users        = @{
      includeGroups = @("<InScope-AllUsers-GroupId>")
      excludeGroups = @("<BreakGlass-GroupId>","<ServiceAccts-GroupId>","<Travel-GroupId>")
    }
    applications = @{ includeApplications = @("All") }
    locations    = @{
      includeLocations = @("All")
      excludeLocations = @("<AllowedCountries-NamedLocId>")
    }
  }
  grantControls = @{ operator = "OR"; builtInControls = @("block") }
}

⚠️
Core baseline ends here. Policies 7 and 8 are the extended baseline. They depend on Intune (app protection) and device state logic. The value is real, but the triage load in Report-only is higher than Policies 1–6 generated. Deploy these after the core six are enforced and stable — not in parallel.

Policy 7 · App protection + approved apps on mobile

Mobile is where corporate data leaks without anyone noticing. A user adding their work account to the iOS native Mail app, the Android default Gmail client, or a third-party calendar tool means corporate mail, attachments, and links are now sitting in an app your tenant cannot wipe, cannot enforce PIN on, and cannot block copy-paste from. Policy 7 closes that door: on mobile, you use approved clients (Outlook, Teams, OneDrive) and those clients respect an Intune app protection policy.

UI path
Pre-req: at least one Intune → Apps → App protection policies configured for iOS and Android (PIN, jailbreak/root block, copy-paste boundary, save-as restrictions, minimum OS version). Then: Protection → Conditional Access → Policies → New policy. Name: CA007 — App protection on mobile.
Scope
Users: Include CA-InScope-AllUsers. Exclude CA-Exclusions-BreakGlass and CA-Exclusions-ServiceAccounts. Target resources → Cloud apps: Office 365 as a bundle, or the individual Microsoft 365 apps (Exchange Online, SharePoint, Teams, OneDrive) if you want tighter scoping. The bundle is simpler to keep in sync as Microsoft evolves the service; individual scoping is more precise but adds maintenance. Either way, validate the exact target resource behaviour for your workloads in the Microsoft documentation before enforcing. Conditions → Device platforms: Include iOS and Android. Leave Windows / macOS / Linux out — they are managed by Policy 4 and (eventually) Windows-specific policies.
Grant / Session
Grant: Require approved client app AND require app protection policy. Operator: Require all the selected controls. The AND is important — "approved client app" alone is weaker than pairing it with the app protection requirement.
Trade-offs
This policy requires Intune plus an app protection policy; without those, the grant cannot be satisfied. It is the policy most likely to generate a flurry of "why can't I read my work email in the iPhone Mail app?" tickets in week one of enforcement. Communicate the change two weeks ahead, provide an Outlook install guide, and make the migration from native mail a planned activity rather than a surprise.
False positives & triage
Expected failures: iOS native Mail clients, Android Gmail / Samsung Email, third-party calendar apps, older Outlook mobile versions without app protection support, and devices with jailbreak or root detection triggering. Triage: Sign-in logs → Interactive sign-ins, filter by Device platform = iOS or Android, Conditional Access = Report-only: Failure. Group by Client app. The counts by client app become your communication plan: "87% of blocked sign-ins are iOS Mail — here is the Outlook migration guide". Genuine false positives are rare; most Report-only hits are users you need to migrate off unmanaged apps.
Rollback pattern
Set to Report-only if the migration scale is bigger than anticipated. Do not carve per-user exclusions — that fractures the mobile security story. If a specific team (e.g. field technicians with ruggedised devices) genuinely cannot move to Outlook, that is a platform/vendor decision you handle separately, not a CA exclusion.
Graph PowerShell
CA007 — minimal policy shape (illustrative) PowerShell
New-MgIdentityConditionalAccessPolicy -BodyParameter @{
  displayName  = "CA007 — App protection on mobile"
  state        = "enabledForReportingButNotEnforced"
  conditions   = @{
    users        = @{
      includeGroups = @("<InScope-AllUsers-GroupId>")
      excludeGroups = @("<BreakGlass-GroupId>","<ServiceAccts-GroupId>")
    }
    applications = @{
      includeApplications = @("<Office365-OrIndividualAppIds>")  # bundle or per-app — validate in Microsoft docs
    }
    platforms    = @{
      includePlatforms = @("iOS","android")
    }
  }
  grantControls = @{
    operator        = "AND"
    builtInControls = @("approvedApplication","compliantApplication")
  }
}

Policy 8 · Session controls for unmanaged browsers

The final baseline policy — and the one most likely to trigger "but why?" questions from users. When someone signs into Microsoft 365 from an unmanaged browser (a client's shared PC, a kiosk, a personal laptop), the session cookie that gets issued can be stolen and replayed. Session controls limit the damage window: sign-in frequency forces re-authentication after a set interval, and disabling persistent browser cookies stops "stay signed in" working on untrusted devices.

UI path
Protection → Conditional Access → Policies → New policy. Name: CA008 — Session controls on unmanaged browsers.
Scope
Users: Include CA-InScope-AllUsers. Exclude CA-Exclusions-BreakGlass. Target resources: All cloud apps. Conditions → Client apps: Browser. Conditions → Filter for devices: exclude devices that are compliant or hybrid-joined — i.e. the filter fires only on devices that are not known to Intune. An illustrative expression is device.isCompliant -ne True -and device.trustType -ne "ServerAD", but you must validate the expression in your own tenant and confirm the exact filter syntax and available attributes in the current Microsoft documentation — the portal and Graph representations can change.
Grant / Session
Grant: Require authentication strength → MFA (same as Policy 2 — it is the baseline floor, not a stricter gate). Session: Sign-in frequency = 8 hours (adjust to your tolerance), Persistent browser session = Never persistent. The combination limits a stolen cookie's shelf life and forces fresh auth on each new browser day.
Trade-offs
Sign-in frequency in hours is a GA control; filter for devices and certain session behaviours have evolved over time, with preview-adjacent capabilities shipping alongside GA ones. Confirm the current feature state, filter syntax, and Graph representation in Microsoft documentation before enforcing, and expect that specific filter expressions may need adjustment over time. 8 hours is a sensible default; 4 hours is stricter (noticeable to users), 24 hours is looser (practical for tenants with lots of browser-only contractors). Persistent browser "Never" is the default — changing it back to "Always persistent" defeats the point.
False positives & triage
Unlike Policies 1–7, this is less a pass/fail policy and more a friction policy — users experience more re-auth prompts on their personal browsers. Triage focuses on detecting excessive friction rather than blocked access. Sign-in logs → Interactive sign-ins, filter by Client app = Browser, group by user, and look at re-auth frequency. Users re-authenticating every 30 minutes on a personal laptop is a sign the filter is matching too aggressively — usually a misnamed trustType in the filter expression. Users on a compliant Windows laptop should see the long-lived session they expect.
Rollback pattern
Ease the sign-in frequency first (e.g. 8h → 24h) rather than turning the policy off — most complaints are friction complaints, not access complaints. If the device filter is firing on devices that should be treated as managed, fix the filter expression (especially the hybrid-join syntax, which is the most commonly miswritten). Last resort is to scope the policy to a smaller CA-InScope-BrowserSessions group until the filter is proven in the wild.
Graph PowerShell
CA008 — minimal policy shape (illustrative) PowerShell
New-MgIdentityConditionalAccessPolicy -BodyParameter @{
  displayName  = "CA008 — Session controls on unmanaged browsers"
  state        = "enabledForReportingButNotEnforced"
  conditions   = @{
    users        = @{ includeGroups = @("<InScope-AllUsers-GroupId>"); excludeGroups = @("<BreakGlass-GroupId>") }
    applications = @{ includeApplications = @("All") }
    clientAppTypes = @("browser")
    devices = @{
      deviceFilter = @{
        mode = "exclude"
        rule = "<DeviceFilterExpression>"  # validate syntax + attributes in Microsoft docs
      }
    }
  }
  grantControls = @{
    operator               = "OR"
    authenticationStrength = @{ id = "<MFA-StrengthId>" }
  }
  sessionControls = @{
    signInFrequency           = @{ value = 8; type = "hours"; isEnabled = $true }
    persistentBrowser         = @{ mode  = "never";           isEnabled = $true }
  }
}

Graph PowerShell appendix — baseline as code

The per-policy snippets above are deliberately minimal. When you are ready to version the baseline as code rather than clicking through the portal, the pattern below is the one I reuse: resolve all the group and role IDs once, then pass them as a parameter splat into each New-MgIdentityConditionalAccessPolicy call. That way the eight policy definitions are short, reviewable, and diff-friendly in git.

Resolve IDs once — reuse across all eight policies PowerShell
# Requires the Microsoft.Graph.Identity.SignIns module
Connect-MgGraph -Scopes "Policy.ReadWrite.ConditionalAccess","Policy.Read.All","Group.Read.All","Directory.Read.All"

# Groups created in Part 2
$ids = @{
  BreakGlass    = (Get-MgGroup -Filter "displayName eq 'CA-Exclusions-BreakGlass'").Id
  ServiceAccts  = (Get-MgGroup -Filter "displayName eq 'CA-Exclusions-ServiceAccounts'").Id
  Travel        = (Get-MgGroup -Filter "displayName eq 'CA-Exclusions-Travel'").Id
  TempAccess    = (Get-MgGroup -Filter "displayName eq 'CA-Exclusions-TempAccess'").Id
  InScopeAll    = (Get-MgGroup -Filter "displayName eq 'CA-InScope-AllUsers'").Id
}

# Authentication strengths (built-in)
$mfaStrength         = (Get-MgPolicyAuthenticationStrengthPolicy | ? displayName -eq "Multifactor authentication").Id
$phishResistStrength = (Get-MgPolicyAuthenticationStrengthPolicy | ? displayName -eq "Phishing-resistant MFA").Id

# Named location
$allowedCountriesLocId = (Get-MgIdentityConditionalAccessNamedLocation |
  ? displayName -eq "Allowed countries").Id

# Privileged roles (template IDs — stable across tenants)
$adminRoleIds = @(
  "62e90394-69f5-4237-9190-012177145e10"  # Global Administrator
  "e8611ab8-c189-46e8-94e1-60213ab1f814"  # Privileged Role Administrator
  "194ae4cb-b126-40b2-bd5b-6091b380977d"  # Security Administrator
  "29232cdf-9323-42fd-ade2-1d097af3e4de"  # Exchange Administrator
  "f28a1f50-f6e7-4571-818b-6a12f2af6b6c"  # SharePoint Administrator
  "fe930be7-5e62-47db-91af-98c3a49a38b1"  # User Administrator
  # add the remaining privileged roles your tenant uses
)

With those variables resolved, each per-policy snippet from the sections above becomes a one-call deployment. Store the whole script in a private repo, keep the outputs of Get-MgIdentityConditionalAccessPolicy as a JSON snapshot (see Part 1) before and after each change, and you have a lightweight but defensible "baseline as code" setup without taking on the full operational weight of a GitHub Actions pipeline.

💡
State management tip. Keep new or edited policies in state = "enabledForReportingButNotEnforced" from the script, and only promote to "enabled" after the seven-day Report-only review. Do not write both states in the same script run — the deploy step and the enforce step are separate decisions that deserve separate audit trails.

Sign-in log triage — the two queries that cover 80% of cases

Each policy above has its own triage guidance. Two cross-cutting queries are worth running at least weekly during rollout, regardless of which policy you are enabling. Both assume sign-in logs are streaming into Log Analytics (Part 2's foundation work).

Report-only failures, grouped by policy and user KQL
SigninLogs
| where TimeGenerated > ago(7d)
| mv-expand ConditionalAccessPolicies
| extend policyName   = tostring(ConditionalAccessPolicies.displayName)
| extend policyResult = tostring(ConditionalAccessPolicies.result)
| where policyResult in ("reportOnlyFailure", "reportOnlyInterrupted")
| summarize fail_count = count() by policyName, UserPrincipalName, AppDisplayName, ClientAppUsed
| order by fail_count desc
Enforced failures, grouped by policy and failure reason KQL
SigninLogs
| where TimeGenerated > ago(7d)
| mv-expand ConditionalAccessPolicies
| extend policyName   = tostring(ConditionalAccessPolicies.displayName)
| extend policyResult = tostring(ConditionalAccessPolicies.result)
| where policyResult == "failure"
| summarize enforced_fail_count = count() by policyName, ResultType, ResultDescription
| order by enforced_fail_count desc

Run the first query after 24 hours in Report-only — that tells you the scale of what the policy would block. Run it again at day seven to confirm the count has stabilised and the top users are explainable. Only then promote to Enabled. The second query is your ongoing operational query — run weekly after enforcement to spot new failure modes (a new device fleet, a new third-party integration, a new office in an unexpected country) before they escalate into tickets.

Per-policy readiness checklist

Before flipping any policy from Report-only to Enabled, work this checklist from top to bottom. It is the same checklist I run against a client tenant before signing off a baseline, and it is short for a reason — eight items is enough.

  • Break-glass tested in the last 30 days Documented sign-in with the FIDO2 key, from a clean browser, confirming the exclusion is honoured.
  • All four CA-Exclusions-* groups populated and reviewed Members match reality; every exclusion has an owner, a reason, and an end date (where applicable).
  • Sign-in logs streaming to Log Analytics with at least one alert wired up Break-glass sign-in alert is live and has been test-fired in the last 90 days.
  • Policy is in Report-only for at least seven days Fourteen for Policy 6 (locations) if the organisation travels, and for Policy 7 (app protection) if mobile device population is heterogeneous.
  • Top-10 Report-only failures triaged and accounted for Every user/app pair in the top ten has a resolution path (migrate, enrol, exclude with expiry, accept).
  • Rollback path documented for this specific policy One-paragraph runbook: how to disable, what to preserve, who to notify, what tickets to open.
  • Change window communicated to the right audience Admins for Policies 3/4, all users for Policies 2/5/6/8, mobile users for Policy 7. One email, one Teams post, one help-desk heads-up.
  • Graph PowerShell snapshot of current CA policies captured The "before" JSON, committed or archived. Future-you will want this.
Eight policies, eight rollback paths, one checklist. If every policy you deploy has passed this gate, you have something defensible — to users, to your own on-call rotation, and to any auditor who asks. Part 4 takes the next step: the full Report-only rollout cadence, What If tool usage, and the troubleshooting patterns for the specific failures that only show up under enforcement.
Next in the series  ·  Part 4 of 4
Report-only rollout & troubleshooting
The disciplined rollout cadence for moving all eight policies from Report-only to Enabled without incidents — What If scenarios, the seven-day review ritual, communication templates, and the specific troubleshooting patterns for failures that only appear under enforcement.
Coming next →
Previous
Previous

Report-only rollout & troubleshooting - the disciplined path from Report-only to Enabled

Next
Next

Conditional Access foundations: break-glass accounts, exclusion groups, and the logging you want before any policy