The Three Most Common CA Mistakes
After deploying Conditional Access across Ericsson, Körber, Metro Lisboa, and others, the same mistakes appear in almost every environment I walk into.
Mistake 1: Deploying CA without a break-glass account
A Conditional Access policy scoped to "All Users" with no exclusions is an outage waiting to happen. If your Global Admin account is locked out by a misconfigured policy, you cannot log in to fix it. Without a break-glass account excluded from all CA policies, you are one misconfiguration away from a complete tenant lockout requiring Microsoft Support intervention.
I have seen this happen. It is not recoverable quickly.
Mistake 2: Skipping report-only mode
Report-only mode lets you see exactly which sign-ins a policy would affect before it enforces. There is no legitimate reason to deploy a policy directly to enforcement without running it in report-only first. The argument that "it's a simple policy" is not valid — simple policies affect service accounts, shared mailboxes, and legacy applications in ways that are not obvious until you check the logs.
Mistake 3: Applying policies to service accounts and shared mailboxes without exclusions
Service accounts that authenticate via SMTP or IMAP do not support modern authentication. An MFA policy applied to these accounts will immediately break any application that uses them. Shared mailboxes are frequently configured with flow automation (Power Automate, third-party integrations) that break silently when CA policies intercept the authentication.
The correct approach: audit every service account before deployment, create a dedicated exclusion group, add them to it, and document why each account is excluded.
# Find all accounts using legacy authentication in the last 30 days
$cutoff = (Get-Date).AddDays(-30).ToString("yyyy-MM-ddTHH:mm:ssZ")
Get-MgAuditLogSignIn -Filter "createdDateTime ge $cutoff" -All |
Where-Object {
$_.ClientAppUsed -notin @("Browser","Mobile Apps and Desktop clients") -and
$_.UserType -eq "Member"
} |
Select-Object UserPrincipalName, ClientAppUsed, AppDisplayName |
Sort-Object UserPrincipalName -Unique |
Export-Csv ".\legacy-auth-users.csv" -NoTypeInformation
Any account in this export needs to be in your exclusion group until the legacy auth dependency is resolved.
Report-Only Mode: Your Safety Net
Every Conditional Access policy I deploy starts in report-only mode. This is not optional.
# Check current state of all CA policies
Get-MgIdentityConditionalAccessPolicy -All |
Select-Object DisplayName, State |
Sort-Object State
# State values: enabled, disabled, enabledForReportingButNotEnforced
enabledForReportingButNotEnforced is report-only. In this state, the policy evaluates every sign-in and records what it would have done — but takes no action.
How to read report-only results in the sign-in log:
Navigate to: Entra ID → Monitoring → Sign-in logs → Filter by "Conditional access: Report-only"
For each sign-in, you see:
- Which policies evaluated in report-only
- What result they would have produced (Success / Failure / Not Applied)
- Which controls would have been required (MFA, compliant device, etc.)
Run the policy in report-only for a minimum of 14 days before enforcement. Look specifically for:
- Service accounts that are unexpectedly in scope
- Applications that would fail device compliance (printers, MFPs, kiosk accounts)
- VPN or PAW workflows that would break
# Extract report-only failures from sign-in logs (Azure Monitor required for volume)
# From Entra sign-in logs filtered to report-only failures:
$policyName = "Require MFA for All Users"
Get-MgAuditLogSignIn -Filter "conditionalAccessStatus eq 'reportOnlyFailure'" -All |
ForEach-Object {
$_.AppliedConditionalAccessPolicies |
Where-Object { $_.DisplayName -eq $policyName -and $_.Result -eq "reportOnlyFailure" } |
ForEach-Object {
[PSCustomObject]@{
UPN = $_.UserPrincipalName
App = $_.AppDisplayName
ClientApp = $_.ClientAppUsed
Timestamp = $_.CreatedDateTime
}
}
} | Export-Csv ".\report-only-failures.csv" -NoTypeInformation
Every row in that export is a user or application that would be blocked or challenged by the policy. Work through each one before enabling enforcement.
Policy Ordering and Precedence
Conditional Access policies do not have a strict execution order — they all evaluate in parallel. The result is the most restrictive outcome of all matching policies.
This has one important implication: you cannot use one policy to override another. If Policy A blocks access and Policy B grants it, access is blocked. The policies are additive, not sequential.
What this means in practice:
- Design your policy stack as a system, not as individual policies
- Avoid creating "allow" policies to counter "block" policies — they don't work that way
- Use exclusion groups on individual policies rather than creating countering policies
The one exception is the enforcement order within a single session — session policies (app-enforced restrictions, sign-in frequency) apply to the session after the grant controls have allowed the sign-in.
Common stack design:
| Priority | Policy | Scope | Controls | |---|---|---|---| | 1 | Block legacy auth | All users, legacy clients | Block | | 2 | Admin MFA | Directory roles | Phish-resistant MFA, compliant device | | 3 | All users MFA | All users, all apps | MFA | | 4 | Device compliance | All users, all apps | Require compliant device OR hybrid join | | 5 | Risk-based | All users | Block (high risk), MFA + pw reset (medium) |
The block legacy auth policy must be in place before MFA policies are effective. Legacy auth bypasses MFA entirely.
Break-Glass Accounts: Non-Negotiable
Two break-glass accounts. Cloud-only. No MFA required. Excluded from all CA policies. Long, randomly generated passwords stored offline in a sealed envelope in a physically secured location.
# Create a break-glass account
$bgPassword = [System.Web.Security.Membership]::GeneratePassword(32, 8)
$passwordProfile = @{
Password = $bgPassword
ForceChangePasswordNextSignIn = $false
}
New-MgUser -DisplayName "Break-Glass Account 01" `
-UserPrincipalName "breakglass01@contoso.onmicrosoft.com" `
-PasswordProfile $passwordProfile `
-AccountEnabled $true `
-MailNickname "breakglass01"
# Assign Global Administrator
$gaRole = Get-MgDirectoryRole | Where-Object { $_.DisplayName -eq "Global Administrator" }
New-MgDirectoryRoleMember -DirectoryRoleId $gaRole.Id `
-AdditionalProperties @{ "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$((Get-MgUser -UserId 'breakglass01@contoso.onmicrosoft.com').Id)" }
Monitor break-glass accounts:
Any sign-in from these accounts should trigger an immediate alert. If someone is logging in with a break-glass account, something has gone very wrong.
// Sentinel: Alert on break-glass sign-in
SigninLogs
| where UserPrincipalName in ("breakglass01@contoso.onmicrosoft.com", "breakglass02@contoso.onmicrosoft.com")
| where ResultType == 0 // Successful sign-in
| project TimeGenerated, UserPrincipalName, IPAddress, Location, AppDisplayName
Named Locations and Trusted IP Management
Named locations provide network context for Conditional Access decisions. They are useful but require careful management.
What named locations are good for:
- Defining corporate IP ranges for compliance reporting
- Excluding known-trusted locations from MFA requirements (carefully)
- Blocking access from high-risk countries (with careful impact assessment)
What named locations are not:
- A substitute for device compliance — a corporate IP address does not mean a compliant device
- A long-term MFA bypass — "trusted location" exclusions tend to expand over time as exceptions accumulate
# List all named locations and their IP ranges
Get-MgIdentityConditionalAccessNamedLocation | ForEach-Object {
$loc = Get-MgIdentityConditionalAccessNamedLocation -NamedLocationId $_.Id
[PSCustomObject]@{
Name = $loc.DisplayName
IPs = ($loc.AdditionalProperties.ipRanges | ForEach-Object { $_.cidrAddress }) -join ", "
Trusted = $loc.AdditionalProperties.isTrusted
}
} | Format-Table -AutoSize
Review named locations quarterly. I have found IP ranges in production Conditional Access named locations that were decommissioned years earlier. An attacker who controls an IP in a trusted named location bypasses MFA.
Phased Enforcement: Pilot → Staged → Broad
Never enable a new policy for all users simultaneously. The correct sequence:
Phase 1 — Pilot (IT team only, 5–10 users, 7 days)
Create a pilot group. Add the IT team. Enable the policy for that group only (not all users). Run for 7 days in enforcement. Fix any issues discovered.
Phase 2 — Staged (10–20% of users, 14 days)
Expand the inclusion to a representative cross-functional sample. Include users from each department, each application, each device type in your estate. The goal is to catch department-specific or application-specific breakage before it hits production.
Phase 3 — Broad enforcement (all users)
After Phase 2 runs cleanly for 14 days with no unresolved incidents, expand to all users. Keep monitoring for 7 days post-expansion.
# Move a policy from pilot group to all users
$policyId = "your-policy-id"
Update-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $policyId `
-Conditions @{
Users = @{
IncludeUsers = @("All")
ExcludeGroups = @("break-glass-group-id", "service-accounts-group-id")
}
}
The exclusion groups — break-glass and service accounts — must remain. Do not remove them.
Decision Criteria Before You Enforce
Before any Conditional Access policy moves out of report-only, validate five design questions:
- What authentication methods are actually in use? If administrators and priority users are still relying on SMS or app OTP, the policy may improve compliance without materially reducing phishing risk.
- Which workloads still depend on legacy protocols? Exchange Online migrations, multifunction devices, SMTP relay flows, and older line-of-business integrations are frequent blockers.
- Which device states are genuinely trusted? Requiring a compliant or hybrid-joined device only works when Intune compliance, hybrid join, and registration health are already operationally reliable.
- Which accounts must be excluded temporarily, and who owns the remediation? Every exclusion needs an owner, a reason, and a retirement date.
- What is the rollback path if user impact appears after enforcement? If support cannot quickly disable or scope down a policy, the deployment is not ready.
These questions sound procedural, but they are what separate a resilient control model from a policy stack that looks good in a workshop and fails in production.
Common Failure Modes in Enterprise CA Programmes
The same patterns recur in large tenants:
- Using Conditional Access to compensate for weak identity hygiene. CA cannot fix stale accounts, weak admin role design, or a poor MFA registration baseline.
- Treating device compliance as an identity control. A compliant device signal is useful, but it is only as trustworthy as the Intune baseline, check-in health, and remediation process behind it.
- Leaving exceptions undocumented. The real risk in most tenants is not the baseline policy. It is the slowly growing set of undocumented exclusions no one revisits.
- Ignoring operational ownership. Security designs fail when the service desk, messaging team, identity team, and endpoint team do not agree on who owns the fallout.
If you cannot explain who reviews report-only results, who signs off on exclusions, and who investigates post-enforcement failures, the design is incomplete.
Recommended Path for Most Microsoft 365 Tenants
For most enterprise tenants, the pragmatic rollout path is:
- Remove standing admin risk first: break-glass, PIM, stronger admin MFA.
- Eliminate legacy authentication dependencies or isolate them behind a documented exception plan.
- Deploy a small, clean baseline policy set rather than dozens of overlapping policies.
- Validate sign-in impact in report-only for at least two working weeks.
- Enforce in rings and review incidents daily until the post-change window closes.
What to validate before each expansion:
- sign-in failures by application and client app
- service accounts unexpectedly in scope
- unmanaged device access patterns
- helpdesk ticket volume and repeat causes
- named-location exceptions that are masking design problems
Where to Start in a Messy Tenant
If the tenant already contains overlapping policies, undocumented exclusions, and weak admin controls, do not begin by rewriting everything at once. The safer pattern is:
- Inventory every policy, exclusion group, and named location.
- Protect administrative access first.
- Remove or isolate the exceptions that have no owner.
- Rationalise the policy stack into a smaller set of baseline controls.
That sequence reduces risk without creating a second outage while you are trying to fix the first design problem.
A well-designed CA policy stack is invisible to legitimate users and impenetrable to attackers. A badly designed one causes Monday morning outages and helpdesk tickets. Book a discovery call to review yours.
Need help with this in your environment?
Book a discovery call