A VIP forwards you a screenshot: a grey Entra page saying their organisation’s Conditional Access policy has blocked the sign-in, with a request ID and a timestamp and nothing else. The user swears nothing changed. Maybe nothing did on their side; something always changed somewhere. This is the most common identity ticket in any Entra-governed tenant, and it is maddening for the same reason an App Service 502 is maddening: the screen the user sees is the enforcement point’s verdict, not the reason. The block page tells the user almost nothing — surfacing policy logic to an unauthenticated browser would leak security detail — so the real answer lives somewhere else: the Entra sign-in logs.
This is the diagnostic playbook for that answer. Every sign-in event carries a top-level conditionalAccessStatus (did CA succeed, fail, or not apply?), an appliedConditionalAccessPolicies array that names every policy evaluated and its result for this sign-in, and a failure reason with a numeric error code (53003 blocked by policy, 50074 MFA required, 53000 device not compliant, 50158 an external grant control unmet) that pins the block to a specific control. Learn to read those three things and you stop guessing — localising any block to the exact policy, the exact control, and the exact condition that matched, using the Sign-in logs blade, the What If tool, Microsoft Graph, and KQL over SignInLogs.
By the end you will read a blocked sign-in the way a senior identity engineer does: open the event, glance at conditionalAccessStatus, expand the Conditional Access tab to find the one policy whose result is failure, read which grant control it required and which condition put the user in scope, and know within ninety seconds whether you face a genuine policy gap, a user who isn’t enrolled for MFA, a non-compliant device, an unexpected location, or a legacy-auth client that can’t satisfy modern controls at all. Knowing which is the difference between a five-minute fix and a day of poking at policies in production.
What problem this solves
Conditional Access hides enormous machinery so that “require MFA for admins” becomes a checkbox. That abstraction is a gift until a sign-in is blocked, then it becomes an opaque wall: the block page is intentionally vague, the user’s description (“it just stopped working”) is unreliable, and a tenant of any size runs a dozen overlapping policies whose combined effect on one sign-in from one device in one location is hard to predict in your head. The information you need is captured — Entra logs every policy it evaluated for every sign-in — but if you don’t know which field answers which question, you burn an hour clicking, or worse, start disabling policies in production to see what happens. That loosens a policy (a hole auditors find later), excludes a user (a standing exception nobody revisits), or has them “try again” until a transient MFA prompt succeeds — teaching everyone the wrong lesson.
Who hits this: every organisation that has rolled out Conditional Access — now most, since security defaults and baseline policies push MFA and legacy-auth blocking by default. It bites hardest right after a new policy ships (a wave of unexpected blocks), during migrations (a service account on legacy auth suddenly blocked), for travelling executives (location and device conditions), and for anyone enabling device-compliance or phishing-resistant-MFA controls without first watching the impact in report-only mode. The fix is almost never “turn the policy off” — it’s “read the log, find the control that wasn’t satisfied, and decide whether the user should satisfy it or be legitimately scoped out.”
To frame the whole field before the deep dive, here is every block class this article covers, the question it forces, and the one field to read first:
| Block class | What the enforcement point is saying | First question to ask | First field to read | Most common single cause |
|---|---|---|---|---|
| Hard block (access denied) | “A policy’s grant control was Block access” | Which policy returned failure with a Block control? |
appliedConditionalAccessPolicies[].result = failure |
A Block legacy authentication or Block by location/platform policy matched |
| Grant not satisfiable | “A required control couldn’t be met by this sign-in” | Was it MFA, compliant device, or a managed app? | Failure reason code (50074/53000/53003) |
User not MFA-registered, or device not compliant |
| MFA interrupt (not a block) | “Step-up required” — the user just didn’t complete it | Did the user actually finish the prompt? | status + conditionalAccessStatus |
Legitimate MFA challenge abandoned/failed |
| Unexpected scope-in | “A policy applied that you didn’t expect” | Which condition matched (app/location/risk/platform)? | conditionsNotSatisfied / the conditions tab |
Broad “All cloud apps” + a missing exclusion |
| Unexpected scope-out | “A policy you wanted didn’t apply” | Why was the user not in scope? | result = notApplied + the conditions |
An exclusion group, trusted location, or filter |
Learning objectives
By the end of this article you can:
- Read an Entra sign-in event end to end and name, from
conditionalAccessStatusand theappliedConditionalAccessPoliciesarray, exactly which policy and which control blocked or challenged a sign-in. - Map every common Conditional Access failure reason code (
53003,50074,50076,50079,53000,53001,53002,53004,50158) to the grant or session control that wasn’t satisfied — and to the right fix. - Distinguish a genuine hard block (a
Block accessgrant control) from a grant-not-satisfied outcome (MFA/compliant-device/app-protection required but not met) and from a plain MFA interrupt the user simply abandoned. - Drive the four core diagnostic tools fluently: the Sign-in logs blade (interactive, non-interactive, service-principal and managed-identity tabs), the What If tool, Microsoft Graph (
auditLogs/signIns), and KQL overSignInLogs. - Tell unexpected scope-in (a policy applied when it shouldn’t) from unexpected scope-out (a policy didn’t apply when it should) by reading the conditions and
notAppliedresults. - Use report-only mode and the workbook to predict the blast radius of a policy before you enforce it, so the next rollout doesn’t generate the very tickets this article diagnoses.
- Write the standing KQL queries that turn a recurring “why was I blocked” ticket into a two-minute lookup instead of a portal expedition.
Prerequisites & where this fits
You should understand the shape of Conditional Access: a policy is an if-then statement — if these assignments match (users/groups, target resources, and conditions like location, device platform, client app, sign-in risk) then apply these access controls (grant controls — require MFA, require compliant device, require app protection, or block access; and session controls — sign-in frequency, app-enforced restrictions). Know that CA requires Entra ID P1 (P2 adds risk-based conditions), that policies evaluate after first-factor auth succeeds, and that all matching policies combine — grant controls are ANDed across policies. You should be comfortable in the Entra admin center and able to run az/Graph queries and read JSON.
This sits in the Identity & Access track, squarely in its troubleshooting corner. It assumes you have policies deployed — ideally the way the companion piece Deploying Conditional Access Safely: Report-Only Rollout to Enforcement lays out — and it pairs with the deeper identity concepts in OIDC and OAuth2 on Entra ID: Choosing the Right Flow, because what kind of sign-in (interactive auth-code vs client-credentials vs device-code) decides which controls can even be satisfied. When the block involves MFA method registration, Authenticator Passkeys and Number Matching: Stopping MFA Fatigue Attacks is the adjacent topic; when it involves desktops, Conditional Access for AVD and Windows 365 covers that surface. If the logs themselves aren’t arriving in Log Analytics, that’s a different problem — see No Logs Showing Up? Troubleshooting Empty Log Analytics Tables.
A quick map of who confirms what during a sign-in incident, so you escalate to the right owner fast:
| Layer | What lives here | Who usually owns it | Block classes it can cause |
|---|---|---|---|
| First-factor auth | Password / passkey / federation | Identity team | 500xx auth errors before CA even runs (not CA) |
| CA policy evaluation | Assignments, conditions, grant/session controls | Identity / security | Hard block, grant-not-satisfied, scope surprises |
| MFA registration & methods | Authenticator, FIDO2, phone, OATH | Identity + helpdesk | 50074/53004 — user not registered / can’t step up |
| Device state | Intune compliance, Entra join/registration | Endpoint team | 53000/53001/53002 — device controls |
| App / client type | Browser, mobile/desktop app, legacy client | App + identity | 53003 block legacy auth; app-protection grant |
| Network / location | Named locations, trusted IPs, GSA | Network + identity | Location conditions scoping the policy in or out |
Core concepts
Five mental models make every later diagnosis obvious.
The block page names the enforcement point’s verdict, not the reason. The browser shows a generic interrupt with a request ID (the correlation ID) and a timestamp — by design. The reason — which policy, which control, which condition — is written to the sign-in log for that exact event. So the first move in every Conditional Access ticket is not “look at the policy”; it is “find the sign-in event” by user, time, and ideally request ID, and read what Entra recorded.
conditionalAccessStatus is the first fork. Every sign-in carries one of: success (CA applied, every required control satisfied), failure (CA applied, at least one control not satisfied — your block or unsatisfied grant), notApplied (no policy’s assignments matched, so CA did nothing), or notEnabled (CA not licensed/configured). “Did CA even fail?” versus “did CA not apply at all?” is the same first fork that 502-vs-503 is for App Service — it tells you instantly whether you’re chasing a policy that fired or one that should have fired but didn’t.
The appliedConditionalAccessPolicies array is the whole story. This per-sign-in array lists every policy Entra evaluated — not just the one that blocked — each with a result: success, failure, notApplied, notEnabled, or the report-only variants (reportOnlySuccess/Failure/NotApplied/Interrupted). For a block you scan for the row whose result is failure, then read its enforcedGrantControls (Mfa, Block, CompliantDevice, RequireAppProtection) and enforcedSessionControls to see what it demanded. One sign-in can have several policies in failure — the user must satisfy all of them.
Grant controls are ANDed across policies; “Block” wins outright. Within one policy you pick require all or require one of the selected controls. Across multiple applicable policies they combine with AND — the user must meet every required control from every policy in scope. A Block access control is absolute: if any applicable policy says Block, the sign-in is denied regardless of what else the user could satisfy. This is why “I have MFA, why am I blocked?” is common — MFA satisfied policy A, but policy B required a compliant device the user lacked, or policy C blocked the legacy client outright.
Failure reason ≠ error: the code is a diagnosis, not a bug. The error code names the control that wasn’t met, not a system fault. 53003 means a grant control denied access; 50074 means MFA was required and not completed; 53000 means a compliant device was required and this one isn’t. The code tells you the fix category before you open the policy: enrol the user, fix device compliance, scope the policy, or unblock the client.
The vocabulary in one table
Before the deep sections, pin down every moving part. The glossary at the end repeats these for lookup; this table is the mental model side by side:
| Concept | One-line definition | Where it lives | Why it matters to a block |
|---|---|---|---|
| Policy (CA) | An if-then rule: assignments → access controls | Entra → Conditional Access | The thing that blocks or challenges |
conditionalAccessStatus |
success / failure / notApplied for the sign-in | Sign-in log (top level) | The first fork: did CA fail or not apply? |
appliedConditionalAccessPolicies |
Array of every policy evaluated + its result | Sign-in log → Conditional Access tab | Names the policy and control that blocked |
result (per policy) |
success / failure / notApplied / reportOnly* | In the array | failure = this policy wasn’t satisfied |
| Grant control | Require MFA / compliant device / block, etc. | Policy → Grant | What the user had to satisfy |
| Session control | Sign-in frequency, app-enforced restrictions | Policy → Session | Re-auth cadence; app-side limits |
| Failure reason / code | Why it failed (53003, 50074, …) |
Sign-in log (Basic info) | Maps the block to a specific control |
| Named location | An IP range / country marked trusted or not | Entra → Named locations | Conditions that scope a policy in/out |
| Report-only | Policy evaluated but not enforced | Policy state | Predicts blocks without blocking |
| What If | Simulator for “which policies apply to X?” | Conditional Access → What If | Confirms scope without a real sign-in |
| Sign-in risk | Real-time/aggregate risk level (P2) | Identity Protection | A risk condition can scope a policy in |
| Filter for devices | Rule over device attributes | Policy condition | Scopes by device properties, not compliance |
How an Entra sign-in record is structured
You cannot diagnose what you cannot read, and the sign-in record has a lot in it. Open Entra admin center → Identity → Monitoring & health → Sign-in logs, click a single event, and you get a flyout with several tabs. The fields that matter for a Conditional Access block, and where each one lives:
| Tab / field | What it tells you | Why it matters to a block |
|---|---|---|
| Basic info → Status | Success / Failure / Interrupted | Top-line outcome of the whole sign-in |
| Basic info → Sign-in error code | Numeric code + failure reason | The diagnosis (53003, 50074, …) |
| Basic info → Conditional Access | Success / Failure / Not applied | conditionalAccessStatus at a glance |
| Basic info → Correlation / Request ID | The ID on the user’s block page | Lets you match the user’s screenshot to the event |
| Conditional Access tab | The appliedConditionalAccessPolicies array |
Per-policy result + the controls it enforced |
| Device info tab | Device ID, OS, compliant, managed, join type | Confirms a device-control failure |
| Authentication details tab | Which methods were tried/satisfied (MFA) | Confirms whether MFA actually completed |
| Location | IP, city, country, named-location match | Confirms a location condition scoped the policy |
| Additional details | Client app, app/resource, user agent | Confirms legacy auth, the target app, the platform |
The first three you read in two seconds: Status = Failure, Conditional Access = Failure, error code = 53003 is the canonical “blocked by policy” signature. But Status can be Interrupted (an MFA challenge that wasn’t completed) rather than Failure, and that distinction matters enormously — an interrupt is the user not finishing, a failure is a control that couldn’t be met. The combinations you will actually see, decoded:
| Status | Conditional Access | Error code | What it means | Where to go next |
|---|---|---|---|---|
| Success | Success | 0 | CA applied, all controls met | Nothing wrong — it worked |
| Failure | Failure | 53003 | A grant control denied access (Block, or unmet required control) | Conditional Access tab → the failure policy |
| Interrupted | Success | 50074 / 50076 / 50079 | MFA was required; user didn’t complete it (this attempt) | Authentication details; was the prompt finished? |
| Failure | Failure | 53000 / 53001 / 53002 | A device control wasn’t met (not compliant / not joined / not managed) | Device info tab + Intune compliance |
| Failure | Failure | 53004 | User must register for MFA before this works | Authentication methods registration |
| Failure | Failure | 50158 | An external/secondary grant control wasn’t satisfied (e.g. T&C, custom control) | The policy’s grant controls |
| Success | Not applied | 0 | No policy matched this sign-in | Expected — or a scope-out surprise |
| Failure | Not applied | various | The sign-in failed for a non-CA reason | It’s not Conditional Access — look at auth |
That last row is the most common false alarm: a wrong password (50126) or a disabled account (50057) is not a Conditional Access block, even though the user says they were blocked. If conditionalAccessStatus is notApplied and the error is a 500xx auth code, stop looking at policies — the failure happened before CA evaluated.
The Conditional Access failure-reason reference
This is the lookup table you scan first. Every code below is a real Entra AADSTS/sign-in error you will encounter; the column that earns its keep is how to confirm (so you don’t fix the wrong thing) and the control it maps to.
| Code | Failure reason (short) | Control / condition it maps to | How to confirm | First fix |
|---|---|---|---|---|
| 53003 | Access blocked by Conditional Access | A grant control denied (Block, or an unmet require control) | CA tab → policy with result=failure; read enforcedGrantControls |
Satisfy the control, scope the user out, or remove the Block |
| 50074 | Strong auth (MFA) required | Require MFA grant control | Authentication details — MFA not completed this attempt | Complete MFA; ensure user has a usable method registered |
| 50076 | MFA required (from a non-trusted location / re-auth) | Require MFA (often + location) | Location tab + CA tab | Complete MFA; check the location condition is intended |
| 50079 | MFA registration and challenge required | Require MFA, user has no method | Authentication methods → registration state | Register an MFA method (Authenticator/FIDO2/phone) |
| 53004 | User must enrol for MFA | Require MFA before first use | Registration campaign / methods blade | Drive the user through MFA registration |
| 53000 | Device is not compliant | Require compliant device | Device info → isCompliant=false; Intune |
Remediate compliance in Intune, or fix the policy scope |
| 53001 | Device is not Entra-joined / registered | Require Hybrid/Entra joined device | Device info → join type | Join/register the device, or relax the requirement |
| 53002 | App not approved / not managed | Require approved client app / app protection | Additional details → client app | Use an approved app; apply the app-protection policy |
| 50158 | External security challenge not satisfied | A custom/secondary grant control (T&C, custom controls) | CA tab → the policy’s grant controls | Accept terms / satisfy the custom control |
| 50097 | Device authentication required | Device condition / certificate | Device info; certificate presence | Provide device cert / join state the policy expects |
| 530032 | Blocked by security info registration interrupt | Security info / MFA registration policy | Registration policy state | Complete required security-info registration |
| 500121 | Authentication failed during MFA request | MFA method failed/timed out/denied | Authentication details — method + result | User retries; check method health (number match denied?) |
Three reading notes that save the most time:
| Distinction | The trap | How to tell them apart |
|---|---|---|
53003 (CA block) vs a 500xx auth error |
“Blocked” is overloaded; users say it for any failure | 53003 has conditionalAccessStatus = failure; 500xx auth errors have notApplied and happen before CA |
| MFA interrupt vs MFA failure | An abandoned prompt looks like a block | Status Interrupted + a later successful sign-in = user just didn’t finish; persistent failure = no usable method |
| Block-control vs unsatisfied-require-control | Both surface as 53003 |
Read enforcedGrantControls: Block = hard block; Mfa/CompliantDevice = a require the user can satisfy |
Anatomy of a hard block (access denied)
A hard block is the cleanest case: an applicable policy’s grant control is Block access, so the sign-in is denied no matter what the user could otherwise satisfy. Five distinct triggers. Scan the matrix, then read the detail for whichever row matches:
| # | Block trigger | Tell-tale signal | Confirm with | Real fix | Band-aid that masks it |
|---|---|---|---|---|---|
| 1 | Block legacy authentication matched | clientAppUsed = “Other clients” / IMAP/POP/SMTP |
Additional details → client app | Move the client to modern auth (OAuth) | Excluding the app (re-opens legacy auth hole) |
| 2 | Block by location | Sign-in from an untrusted country/IP | Location tab + named-location match | Add the location to trusted, or scope the policy | Disabling the location policy entirely |
| 3 | Block by device platform | Sign-in from an unsupported OS (e.g. Linux) | Device info → OS / platform condition | Use a supported platform, or scope the policy | Removing the platform condition wholesale |
| 4 | Block specific app / all but a few | Target app is in (or not excluded from) the policy | CA tab → target resources | Exclude the app intentionally, or grant access | Broadening “All cloud apps” exclusions blindly |
| 5 | Filter-for-devices block rule | Device attribute matched a block filter | Device info → attributes vs filter rule | Fix the device attribute or the filter expression | Deleting the filter (loses the protection) |
Trigger 1 — Block legacy authentication
The single most common intentional hard block. A Block legacy authentication policy targets the legacy authentication clients condition (Exchange ActiveSync and “Other clients” — IMAP, POP3, SMTP AUTH, MAPI over HTTP, older Office) and grants Block access. Legacy protocols can’t do interactive MFA, so they’re a favourite for password spray — blocking them is correct. But an old mail client, a printer’s scan-to-email, or a basic-auth script then gets a flat denial with no idea why.
Confirm. Event → Additional details → Client app shows a legacy value (IMAP4, POP3, SMTP, Exchange ActiveSync, or the catch-all “Other clients”); the Conditional Access tab shows the policy with result = failure and enforcedGrantControls = [Block]. In KQL:
// Legacy-auth sign-ins blocked by CA in the last 24h
SigninLogs
| where TimeGenerated > ago(24h)
| where ConditionalAccessStatus == "failure"
| where ClientAppUsed in ("Other clients","IMAP4","POP3","SMTP","Exchange ActiveSync","MAPI Over HTTP","Authenticated SMTP")
| project TimeGenerated, UserPrincipalName, AppDisplayName, ClientAppUsed, ResultType, Status
| order by TimeGenerated desc
Fix. Move the client to modern authentication (OAuth 2.0) — reconfigure the mail client or switch the script to a token flow. Do not simply exclude the app or user from the block: that re-opens the exact hole the policy closes. If a service genuinely cannot use modern auth, scope the exception as narrowly as possible — a single service identity, ideally a workload identity using the right OAuth flow — and document it.
Trigger 2 — Block by location
A location condition — “all locations, exclude trusted” with a Block grant, or “include these countries” — denies sign-ins from places you don’t operate. A travelling executive in a blocked country, or a user behind a VPN egressing from an unexpected region, hits a flat block.
Confirm. The Location tab shows the source IP, resolved city/country, and whether it matched a named location; compare against the policy’s location condition (Entra → Named locations).
// Blocks where a location condition was in play, with the source country
SigninLogs
| where TimeGenerated > ago(7d) and ConditionalAccessStatus == "failure"
| extend country = tostring(LocationDetails.countryOrRegion), city = tostring(LocationDetails.city)
| project TimeGenerated, UserPrincipalName, IPAddress, country, city, AppDisplayName, ResultType
| order by TimeGenerated desc
Fix. If the location is legitimate, add it as a trusted named location (or add the country to the allowed set) — don’t disable the policy. If the GeoIP is simply wrong (it happens), that’s a strong argument to base the condition on trusted IP ranges you control rather than country lookups — see the location grid below.
Trigger 3 — Block by device platform
A device platform condition (Windows / iOS / Android / macOS / Linux) with a Block grant denies unsupported platforms — e.g. a policy allowing only managed Windows and iOS quietly blocks a developer on Linux or a personal Chromebook. The Device info tab shows the OS; the platform is inferred from the user agent for browser sign-ins, so an unusual or spoofed agent can mis-match. Fix: add the platform to the allowed set if needed, or scope the policy — don’t strip the condition globally for one user.
Trigger 4 — Block by app / target resource
The policy’s target resources include the app the user is reaching — or it’s “All cloud apps” with the app not excluded — and a Block grant denies it. This is also how a “block all but the approved apps” policy works, and how it catches a new app nobody added to the allow list. Additional details shows the Application and Resource; match it against the policy. Find the surprise target in KQL:
// Which app is being blocked, grouped — find the surprise target
SigninLogs
| where TimeGenerated > ago(24h) and ConditionalAccessStatus == "failure" and ResultType == 53003
| summarize blocks = count() by AppDisplayName, ResourceDisplayName
| order by blocks desc
Fix: add the app to the exclusions deliberately if it should be allowed; otherwise the policy is working and the app simply isn’t permitted.
Trigger 5 — Filter for devices
A filter for devices rule scopes by device attributes (model, ownership, extensionAttribute, trustType) rather than compliance; a block filter (or an include the device doesn’t match) can deny an otherwise-fine device. The Device info tab shows the attributes — a mismatched extensionAttribute from a stale provisioning script is a frequent culprit. Fix: correct the attribute (re-stamp via Graph/Intune) or the filter expression, and test it in What If before re-enforcing.
The location-related controls deserve their own grid — what each is, and the gotcha:
| Location mechanism | What it is | Where set | Gotcha |
|---|---|---|---|
| IP-range named location | CIDR ranges you mark trusted | Named locations | Must be the egress (public) IP, not the client’s private IP |
| Country/region named location | Locations by GeoIP or sign-in country | Named locations | GeoIP can be wrong; VPN egress shifts the country |
| MFA trusted IPs (legacy) | Old per-tenant trusted-IP list | Per-user MFA service settings | Separate from named locations; easy to forget it exists |
| Compliant network | Microsoft-defined network compliance signal | Global Secure Access | Requires GSA; not the same as a named location |
| GPS-based country (Authenticator) | High-assurance location via the app | Named locations (GPS) | Prompts the user; not available for all sign-in types |
Anatomy of a grant-not-satisfied block
This is the larger, subtler family: the policy did not say “Block” — it required a control (MFA, a compliant device, an approved app, app protection) that this sign-in didn’t satisfy. The user could get in; they just haven’t met the bar yet. The surface code is usually still 53003 (or an MFA-specific 500xx), so you must read the enforced grant controls to know which.
| # | Unsatisfied control | Tell-tale signal | Confirm with | Real fix |
|---|---|---|---|---|
| 1 | Require MFA — user not registered | 50079/53004; no methods registered |
Authentication methods blade | Register a method (Authenticator/FIDO2/phone) |
| 2 | Require MFA — challenge not completed | Status Interrupted; 50074 |
Authentication details | User completes the prompt; check method health |
| 3 | Require compliant device | 53000; isCompliant=false |
Device info + Intune compliance | Remediate compliance; or scope the requirement |
| 4 | Require Entra/Hybrid-joined device | 53001; join type wrong |
Device info → join type | Join/register the device |
| 5 | Require approved app / app protection | 53002; client app not approved |
Additional details → client app | Use an approved app with an applied APP policy |
| 6 | Require phishing-resistant MFA | 53003 + Mfa strength unmet |
Authentication strength in the policy | Register a passkey/FIDO2/CBA method |
Cause 1 & 2 — Require MFA, but it wasn’t (or couldn’t be) completed
Two flavours hide here. In the first, the user has no usable MFA method registered, so when a policy requires MFA the sign-in fails with 50079 or 53004 (“you must register before you can do this”). In the second, the user is registered but didn’t complete the challenge this attempt — they dismissed the Authenticator push, denied the number match, let it time out, or hit a flaky network — and the sign-in shows Status = Interrupted with 50074. The second is not really a block at all; the next attempt usually succeeds.
Confirm. The Authentication details tab lists each method attempted and its result (“MFA denied; user declined the authentication”, “MFA successfully completed”, or no method at all). For registration state, Entra → Authentication methods → User registration details (or the userRegistrationDetails Graph API) shows whether the user has any method.
// MFA-driven interrupts vs hard failures in the last day
SigninLogs
| where TimeGenerated > ago(1d)
| where ResultType in (50074, 50076, 50079, 53004)
| project TimeGenerated, UserPrincipalName, ResultType, ResultDescription, Status, AppDisplayName
| order by TimeGenerated desc
Fix. For the unregistered user, drive registration — an MFA registration campaign, or a temporary report-only rollout that lets you find the unregistered population before you enforce. For the abandoned-prompt case, the fix is usually user education plus making the prompt easier to satisfy — number matching and passkeys reduce both fatigue and accidental denials. A genuinely repeating 500121 (MFA denied) from one user is worth a security glance: it can be the user reflexively denying a legitimate prompt, or an attacker triggering prompts the user is correctly rejecting.
Cause 3 & 4 — Require compliant or joined device
A policy requires a compliant device (Intune-managed and passing its compliance policy) or an Entra-joined / Hybrid-joined device, and the sign-in comes from one that isn’t — codes 53000 (not compliant) and 53001 (not joined/registered). The device may have been compliant then drifted (expired evaluation, a disk-encryption check that began failing, an OS version below the bar), so it’s blocked today with nothing the user did.
Confirm. The Device info tab shows Compliant: false, the join type, and the device ID; cross-check Intune → Devices → the device → Compliance to see which setting failed.
// Device-control blocks with the device state inline
SigninLogs
| where TimeGenerated > ago(2d) and ResultType in (53000, 53001, 53002)
| extend isCompliant = tostring(DeviceDetail.isCompliant), join = tostring(DeviceDetail.trustType), os = tostring(DeviceDetail.operatingSystem)
| project TimeGenerated, UserPrincipalName, ResultType, isCompliant, join, os, DeviceDetail.displayName
| order by TimeGenerated desc
Fix. Remediate the actual compliance failure in Intune (re-encrypt, update the OS, re-enrol). If the device should be exempt (a kiosk, a specific role), scope the policy rather than mark a non-compliant device compliant. A common operational trap: the device shows compliant in Intune but the sign-in still says 53000 because the compliance state hadn’t synced to Entra at sign-in time — a forced sync or a short wait resolves it, and that’s worth checking before you touch the policy.
Cause 5 & 6 — Require approved app / app protection / stronger MFA
A policy requires an approved client app or an app protection policy (APP / MAM) — common for mobile Exchange/SharePoint — and the user reaches the resource from an unapproved app (native iOS Mail instead of Outlook): code 53002. Or it demands an authentication strength (phishing-resistant MFA) the user’s method can’t meet — they did a form of MFA, but not a passkey/FIDO2/certificate. Additional details → Client app (and the policy’s grant controls / required strength) confirms which. Fix: move the user to an approved app with the APP policy applied, or get a phishing-resistant method registered — don’t downgrade the requirement to clear one ticket when it’s the security goal.
Scope surprises: when the wrong policies (or none) apply
Half of “why was I blocked” tickets aren’t about an unsatisfied control at all — they’re about scope: a policy applied that you didn’t expect (scope-in), or one you wanted didn’t apply (scope-out). Both are read from the conditions and the per-policy result.
| Surprise | What you see | Likely cause | Confirm with |
|---|---|---|---|
| Unexpected scope-in | A policy with result=failure you didn’t think applied |
Broad “All cloud apps”, a group the user is unexpectedly in, a matched condition | CA tab → that policy’s assignments; What If |
| Unexpected scope-out | A policy you wanted shows result=notApplied |
An exclusion group, a trusted location, a device filter, or the user not in the include set | CA tab → notApplied + conditions |
| “Required by both” confusion | Two policies in failure, user met one |
Grant controls AND across policies | Read every failure row’s enforcedGrantControls |
| Report-only “would have blocked” | reportOnlyFailure in the array |
A not-yet-enforced policy would block | Report-only results; the workbook |
| No policy at all | conditionalAccessStatus = notApplied |
Nothing matched — or it’s not a CA problem | Check it isn’t a 500xx auth error |
Unexpected scope-in
The user was blocked by a policy you assumed didn’t target them — almost always because it targets All cloud apps (so it applies by default), the user is in a group that’s in the include set (membership changed, or a nested group pulled them in), or a condition matched unexpectedly (untrusted location, device platform, risk). The dangerous version is a broad policy with a missing exclusion — a break-glass account that should be excluded but isn’t. The Conditional Access tab shows the offending policy’s assignments; the fastest non-destructive confirmation is What If (below), which lists exactly which policies would apply and why without a real sign-in.
Unexpected scope-out
The opposite, and more dangerous because it’s silent: a policy you intended to protect this sign-in shows result = notApplied, so a sign-in you wanted to challenge sailed through. Causes: the user (or their group) is excluded, the sign-in came from an excluded trusted location, a device filter excluded the device, or they’re not in the include set. Standing exclusions are the usual villain — a “temporary” one that became permanent. The notApplied result plus the conditions (and What If) tells you which assignment dropped them. This is the audit you run after a security review to prove a high-value app is actually covered.
The result values in the array, fully decoded — the report-only ones trip people up:
result value |
Meaning | Enforced? | What to do |
|---|---|---|---|
success |
Policy applied; controls satisfied | Yes | Nothing — working as intended |
failure |
Policy applied; a control was not satisfied | Yes | This is your block — read the controls |
notApplied |
Assignments didn’t match this sign-in | No | Expected, or a scope-out to investigate |
notEnabled |
Policy is Off | No | Turn it On if it should be active |
reportOnlySuccess |
Report-only; controls would be satisfied | No | Safe to enforce for this case |
reportOnlyFailure |
Report-only; would have blocked/challenged | No | This is the blast radius — fix before enforcing |
reportOnlyNotApplied |
Report-only; would not have applied | No | Out of scope when enforced |
reportOnlyInterrupted |
Report-only; would have interrupted (MFA) | No | The user would be challenged when enforced |
Driving the diagnostic tools
Four tools answer 95% of these questions. Knowing which one answers your question is the whole skill.
The Sign-in logs blade and its four tabs
The logs are split because not every sign-in is a person at a browser. The four tabs:
| Tab | What it shows | When CA blocks show here |
|---|---|---|
| Interactive user sign-ins | A human authenticating (browser, MFA prompts) | Most user-facing CA blocks |
| Non-interactive user sign-ins | Token refreshes, background app calls on a user’s behalf | Silent re-eval blocks (sign-in frequency, revoked) |
| Service principal sign-ins | App-only (client-credentials) sign-ins | Workload identity CA policies (P-Workload) |
| Managed identity sign-ins | MI token acquisitions | Rarely CA-gated; useful for completeness |
The trap: an admin searches Interactive sign-ins, finds nothing, and concludes “there’s no block” — when the failing sign-in is a non-interactive token refresh (a session-frequency policy forcing re-auth a background process can’t do) or a service principal call governed by a workload-identity policy. If an app “just stops working” with no prompt, check the non-interactive tab. Filter by user, time, status, application, and crucially Conditional Access = Failure to cut straight to blocks.
What If — simulate before you touch anything
Conditional Access → What If is the simulator. You specify a user, a target resource/app, and optional conditions — IP address (hence country/named-location match), device platform, client app, sign-in risk, user risk — and Entra evaluates all your policies against that hypothetical sign-in, returning which would apply (and the controls they’d enforce) and which would not apply, with the reason. It’s the safest way to answer “would this user be blocked from this app at this location?” without making them try and fail, and it’s how you prove a fix before you ship it.
The key caveat: What If evaluates policy logic, not live state. It will say “Require compliant device would apply” but doesn’t know whether the user’s laptop is compliant right now, run a real MFA check, or predict transient failures — for outcomes you still read a real sign-in or check Intune. Use What If for scope; use report-only and real logs for outcomes.
Microsoft Graph and the az CLI
For automation, bulk lookups, or when the portal is slow, query the sign-in logs via Microsoft Graph (/auditLogs/signIns). The CA detail rides along in appliedConditionalAccessPolicies.
# All failed-CA sign-ins for one user in the last day (Graph via az rest)
az rest --method get \
--uri "https://graph.microsoft.com/v1.0/auditLogs/signIns?\$filter=userPrincipalName eq 'aisha@contoso.com' and conditionalAccessStatus eq 'failure'&\$top=20" \
--query "value[].{time:createdDateTime, app:appDisplayName, status:status.errorCode, reason:status.failureReason, client:clientAppUsed}" -o table
# Drill into ONE sign-in's applied policies (paste the id from above)
az rest --method get \
--uri "https://graph.microsoft.com/v1.0/auditLogs/signIns/<signInId>" \
--query "appliedConditionalAccessPolicies[].{policy:displayName, result:result, grants:enforcedGrantControls, sessions:enforcedSessionControls}" -o json
Reading sign-in logs via Graph needs AuditLog.Read.All (plus Directory.Read.All) and a directory role that can read them (Reports Reader, Security Reader, or higher). If the call 403s, that’s an RBAC problem, not a CA one — see Troubleshooting AuthorizationFailed.
KQL over SignInLogs in Log Analytics
For trends, dashboards, and alerting, export the logs to Log Analytics via a diagnostic setting (the SignInLogs table; non-interactive arrives as AADNonInteractiveUserSignInLogs). The playbook then becomes queryable:
// Which policies are blocking the most, and which app/control
SigninLogs
| where TimeGenerated > ago(7d) and ConditionalAccessStatus == "failure"
| mv-expand pol = ConditionalAccessPolicies
| where tostring(pol.result) == "failure"
| extend policyName = tostring(pol.displayName), grants = tostring(pol.enforcedGrantControls)
| summarize blocks = count() by policyName, grants, AppDisplayName
| order by blocks desc
// Report-only policies that WOULD have blocked — your pre-enforcement blast radius
SigninLogs
| where TimeGenerated > ago(7d)
| mv-expand pol = ConditionalAccessPolicies
| where tostring(pol.result) in ("reportOnlyFailure","reportOnlyInterrupted")
| summarize impacted_users = dcount(UserPrincipalName), events = count() by policyName = tostring(pol.displayName)
| order by impacted_users desc
If SignInLogs is empty even though sign-ins are happening, the diagnostic setting isn’t wired up (or the workspace is wrong) — that’s an ingestion problem, covered in No Logs Showing Up?, and you should set up the workspace first per How to Create a Log Analytics Workspace.
Architecture at a glance
Hold one mental model and every diagnosis snaps into place: a sign-in flows through three gates in order, and Conditional Access is the second. Gate one — first-factor authentication: the user proves who they are (password, passkey, federated token); if that fails you get a 500xx error (50126 bad password, 50057 disabled, 50053 locked out) and CA never runs — conditionalAccessStatus stays notApplied, your signal the problem is upstream of CA. Gate two — Conditional Access: Entra gathers the sign-in’s signals (user and groups, target resource, location/IP, device state from Intune/Entra, client app, risk score), finds every policy whose assignments match, and combines their controls. Gate three — session: once every required grant control is satisfied (and nothing says Block), a token is issued and session controls (sign-in frequency, app-enforced restrictions) shape what follows.
The diagnostic method falls out of that order: at which gate did this die, and why? notApplied + a 500xx code = gate one, not a CA problem. failure = gate two, and the appliedConditionalAccessPolicies array names the policy while enforcedGrantControls names the control (Block = hard deny; Mfa/CompliantDevice/RequireAppProtection = a require the user didn’t meet). A re-prompt every hour with no error = gate three. The logs split into interactive / non-interactive / service-principal tabs because the same three gates apply to background refreshes and app-only sign-ins, not just humans — so when an app “just stops” silently, look at the non-interactive or service-principal tab for a gate-two or gate-three block the user never saw. Localise to the gate, read the field, apply the fix.
Real-world scenario
Northwind Logistics runs a 2,400-seat Entra tenant (P1, with P2 for the admin tier) governing Microsoft 365, a custom dispatch app, and a fleet-tracking SaaS. The identity team is three people; they had recently shipped a tightened Conditional Access baseline — Require MFA for all users, Block legacy authentication, and Require compliant device for the dispatch app — moving each from report-only to enforced over a fortnight.
The incident began on a Monday after a long weekend: 40 tickets before 09:30, all variations of “can’t get into email” and “the dispatch app blocked me.” Two distinct populations, but the tickets read identically because every user just saw the grey block page with a request ID. The on-call engineer’s first instinct was to suspect the newest policy — Require compliant device — and his hand hovered over the toggle. Instead he grabbed one request ID and found the matching sign-in.
That first event told a clear story: Status = Failure, Conditional Access = Failure, error 53003, and on the Conditional Access tab a single policy in failure — Block legacy authentication — with enforcedGrantControls = [Block] and Client app = “Other clients”. Not the new compliant-device policy at all. A KQL query filtered to ClientAppUsed in ("Other clients","IMAP4","Authenticated SMTP") returned 31 of the 40 — all on an old third-party mail app that still used IMAP basic auth, which had pushed a weekend update reverting a subset of accounts to legacy auth. The block was correct; the trigger was a client regression.
The other 9 showed a different code: 53000 — device not compliant — for the dispatch app. Device info showed isCompliant = false, yet Intune showed those devices as compliant. A sync lag: a Friday-evening compliance change hadn’t propagated the new compliant state to Entra for devices that were off all weekend. A forced Intune sync (and, for a few, waiting for the next evaluation) cleared all nine without touching a policy.
The fix landed in two tracks, and crucially no policy was loosened. For the 31: roll the third-party app back to its modern-auth version and re-authenticate with OAuth — the block policy stayed exactly as it was, because it had done its job. For the 9: a forced device sync, plus a standing alert on ResultType == 53000 so the next sync-lag wave is caught in minutes. The note on the wall afterward: “A block page is a request ID, not a diagnosis. Read the code before you reach for a toggle — 53003 and 53000 are completely different days.” From first ticket to all-clear: 70 minutes. Disabling the newest policy would have taken the dispatch app down and still not fixed the email blocks.
The incident as a timeline, because the order of moves is the lesson:
| Time | Symptom | Action taken | Effect | What it should have been |
|---|---|---|---|---|
| 09:15 | 40 “I’m blocked” tickets | (alerts/escalation) | — | Don’t assume the newest policy |
| 09:20 | Hand on the compliant-device toggle | Paused | No change | Read one real sign-in first |
| 09:25 | First request ID matched to a sign-in | Read code: 53003, legacy auth |
Root cause #1 found | This is the breakthrough |
| 09:35 | KQL on ClientAppUsed |
31 of 40 are legacy-auth | Population #1 scoped | — |
| 09:45 | Remaining 9 read | Code 53000, device not compliant |
Root cause #2 (sync lag) | Two causes, not one |
| 10:05 | Mitigated | App rollback + forced Intune sync | Both populations clear | No policy loosened |
| +1 day | Hardened | Standing alerts on 53003/53000 by app | Next wave caught in minutes | The durable fix |
Advantages and disadvantages
The signals-driven, log-everything model both causes this class of ticket and makes it diagnosable. Weigh it honestly:
| Advantages (why this model helps you) | Disadvantages (why it bites) |
|---|---|
Every policy evaluation is recorded per sign-in (appliedConditionalAccessPolicies) — you rarely lack data |
The user-facing block page is deliberately opaque; the reason lives elsewhere, so users can’t self-diagnose |
| What If lets you simulate scope without a real sign-in — test before you ship | What If reflects policy logic, not the user’s live MFA/device state — it can mislead if you forget that |
| Report-only mode predicts the blast radius before enforcement — no surprise ticket flood | Skipping report-only is easy and turns a rollout into an incident |
| Grant controls combine with AND across policies, giving precise layered security | “I have MFA, why am I blocked?” — a different policy’s compliant-device/Block control denied it; the interaction is non-obvious |
Failure-reason codes (53003, 53000, 50074) map cleanly to a fix category |
The codes are easy to confuse; 53003 covers both hard Block and unmet require controls |
| Logs split by sign-in type (interactive / non-interactive / SP) so app-only and background blocks are visible | Searching only the Interactive tab hides non-interactive and service-principal blocks — a classic dead end |
| Standing exclusions and trusted locations give legitimate escape valves | Those same exclusions silently create coverage gaps (scope-out) auditors later find |
The model is right for any organisation that wants policy-driven, auditable access decisions instead of static rules. It bites hardest right after a new policy ships (a wave of reportOnlyFailure-turned-real blocks), for chatty non-interactive workloads (session-frequency re-auth a background process can’t do), and for anyone who confuses an MFA interrupt with a block. Every disadvantage is manageable — but only if you read the log instead of the block page, which is the entire point of this article.
Hands-on lab
Create a deliberately blocking Conditional Access policy in report-only first, watch it predict a block, enforce it, trigger the block, then read the sign-in log to diagnose it — all reversible, no spend beyond an Entra ID P1 trial. Use a test user you don’t mind locking out of one app; never test on yourself or a break-glass account.
Step 1 — Pick a disposable test user and confirm baseline. In Entra → Users, note a test account (e.g. latency-test@yourtenant.onmicrosoft.com). Sign in as them to a target app (use the My Apps portal or Office) and confirm it works today.
Step 2 — Create a Block policy in report-only. Entra → Conditional Access → Create new policy.
- Name:
LAB - Block test user from My Apps (report-only) - Users: include only the test user (and nobody else — this is the safety rail).
- Target resources: select a single app you can live without for this user (e.g. a non-critical enterprise app), or “All cloud apps” only if the include is the single test user.
- Grant: Block access.
- Enable policy: Report-only.
# Confirm the policy exists and is report-only (Graph via az rest)
az rest --method get \
--uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies?\$filter=startswith(displayName,'LAB')" \
--query "value[].{name:displayName, state:state}" -o table
Expected: a row with state = enabledForReportingButNotEnforced.
Step 3 — Generate a sign-in and read the report-only result. Sign in as the test user to the target app again — it still works (report-only doesn’t enforce). Now in Sign-in logs, find that event → Conditional Access tab. Expected: the LAB policy shows reportOnlyFailure — Entra is telling you it would have blocked. This is the blast-radius preview the real world skips at its peril.
// See the report-only "would-block" in Log Analytics (if diagnostic settings are on)
SigninLogs
| where TimeGenerated > ago(1h) and UserPrincipalName has "latency-test"
| mv-expand pol = ConditionalAccessPolicies
| where tostring(pol.displayName) startswith "LAB"
| project TimeGenerated, result = tostring(pol.result), grants = tostring(pol.enforcedGrantControls)
Step 4 — Confirm scope with What If (no sign-in needed). Conditional Access → What If → pick the test user and the same app → What If. Expected: the LAB policy appears under policies that would apply, with grant control Block. Try a different user in What If — the policy should not apply (proving your scope is tight).
Step 5 — Enforce and trigger the real block. Edit the LAB policy → Enable policy: On → Save. Sign in as the test user to the target app again. Expected: the grey block page with a request ID.
Step 6 — Diagnose it from the log. In Sign-in logs, find the new failed event:
# Pull the test user's latest failed-CA sign-in and its applied policies
az rest --method get \
--uri "https://graph.microsoft.com/v1.0/auditLogs/signIns?\$filter=userPrincipalName eq 'latency-test@yourtenant.onmicrosoft.com' and conditionalAccessStatus eq 'failure'&\$top=1" \
--query "value[0].{code:status.errorCode, reason:status.failureReason, policies:appliedConditionalAccessPolicies[?result=='failure'].{p:displayName, g:enforcedGrantControls}}" -o json
Expected: code = 53003, and one policy (LAB - …) with enforcedGrantControls = ["Block"]. You just walked the full path: a block page → its sign-in event → the 53003 code → the exact policy and the Block control. That is the entire method on one user you control.
Validation checklist. You created a Block policy in report-only, saw reportOnlyFailure predict the block, confirmed scope with What If, enforced it, reproduced the grey page, and read 53003 + the Block grant from the log. The lab steps mapped to what each proves:
| Step | What you did | What it proves | Real-world analogue |
|---|---|---|---|
| 3 | Read reportOnlyFailure |
Report-only previews blocks without blocking | Every safe rollout |
| 4 | What If on two users | Scope is testable before a real sign-in | Pre-flighting a new policy |
| 5 | Enforce, get the grey page | Enforcement is what the user actually hits | The ticket itself |
| 6 | Read 53003 + Block from the log |
The block page → the diagnosis | The 90-second triage |
Cleanup (remove the lab policy so it can’t block anyone).
# Delete the LAB policy by id (get the id from Step 2's query, then:)
az rest --method delete \
--uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies/<policyId>"
Or delete it in the portal (Conditional Access → the LAB policy → Delete). Verify the test user can sign in to the app again.
Cost note. Conditional Access needs Entra ID P1 (a free trial covers the lab); there is no per-policy or per-evaluation charge. The only “cost” is the Log Analytics ingestion if you export SignInLogs — trivial for a lab, and you can skip the diagnostic setting and use the portal logs instead.
Common mistakes & troubleshooting
This is the playbook — the part you bookmark. First as a scannable table you can read while a user waits on the phone, then the same entries with the full confirm-command detail underneath. It spans the basic blocks (legacy auth, MFA) and the advanced ones (device sync lag, non-interactive re-auth, workload identity, stale exclusions).
| # | Symptom | Root cause | Confirm (exact cmd / portal path) | Fix |
|---|---|---|---|---|
| 1 | User blocked, but “I have MFA!” | A different applicable policy required a compliant device or Block — grants AND across policies | Sign-in → CA tab → find the failure policy’s enforcedGrantControls |
Satisfy that control (compliance), or scope that policy |
| 2 | Old mail client / printer flat-denied | Block legacy authentication matched (“Other clients”) | Additional details → Client app; KQL ClientAppUsed in ("Other clients",...) |
Move client to modern auth; never just exclude it |
| 3 | “Blocked” but it’s a wrong password | Not CA at all — conditionalAccessStatus = notApplied, error 50126 |
Sign-in → Status + code; CA = Not applied | Reset password / unlock; CA is innocent |
| 4 | Device worked yesterday, 53000 today |
Compliant state drifted, or Intune→Entra sync lag | Device info isCompliant=false vs Intune (compliant) |
Force Intune sync / wait an eval; remediate real failures |
| 5 | App “just stops”, no prompt, no error users see | Non-interactive token refresh blocked (session frequency / revoked) | Sign-in logs → Non-interactive tab; ResultType there | Re-auth interactively; review sign-in-frequency policy |
| 6 | Service/app integration suddenly 403/blocked | Workload identity CA policy on the service principal | Sign-in logs → Service principal tab | Adjust the workload-identity policy; use proper flow |
| 7 | MFA prompt appears but user can’t pass it | No usable method registered (50079/53004) |
Authentication methods → registration details | Register Authenticator/FIDO2/phone; run a campaign |
| 8 | Intermittent block, only from certain places | Location condition (untrusted country / VPN egress) | Location tab + Named locations; KQL on country | Add trusted location, or base on trusted IP ranges |
| 9 | Policy you built isn’t stopping anyone | Scope-out: user excluded, trusted location, or not in include set | CA tab → result=notApplied + conditions; What If |
Fix the exclusion / include set; re-test in What If |
| 10 | New policy caused a ticket flood on go-live | Enforced without report-only first | Report-only reportOnlyFailure count (had you checked) |
Roll back to report-only; size impact; re-enforce |
| 11 | Linux/Chromebook user blocked from one app | Device platform condition with Block | Device info → OS; CA tab platform condition | Allow the platform if needed, or scope the policy |
| 12 | Mobile user blocked from Exchange/SharePoint | Require approved app / app protection not met (53002) |
Additional details → client app | Move to Outlook/approved app with APP policy applied |
| 13 | “Strong auth required” loop, never resolves | Authentication strength (phishing-resistant) unmet | Policy → auth strength; Authentication details | Register passkey/FIDO2/CBA method |
| 14 | Block traced to a policy nobody remembers editing | Group membership change pulled the user into scope | CA tab → the policy’s group assignment; group audit | Adjust group membership or the policy’s include set |
| 15 | Graph/log query returns 403, not the sign-in | RBAC — caller lacks AuditLog.Read.All / reader role |
The 403 body; az ad signed-in-user show roles |
Grant Reports/Security Reader + the Graph permission |
The expanded form, with the full reasoning for the entries that bite hardest:
1. “I have MFA, why am I blocked?”
Root cause: MFA satisfied one policy, but a second applicable policy required something else — most often a compliant device or an outright Block — and grant controls combine with AND across all applicable policies, so one unmet control anywhere blocks the whole sign-in.
Confirm: Sign-in event → Conditional Access tab → find the row with result = failure (it won’t be the MFA one) and read its enforcedGrantControls.
Fix: Satisfy that control (e.g. make the device compliant), or — if the second policy shouldn’t apply here — fix its scope. Never assume the user’s own MFA status explains a block.
2. An old mail client or device is flatly denied. Block legacy authentication matched because the client shows as “Other clients” (IMAP/POP/SMTP/EAS), which can’t do interactive MFA. Additional details → Client app confirms it; the KQL above lists the whole affected population. Move the client to modern auth — do not exclude it, which re-opens the password-spray surface the policy closes.
3. “Blocked” that’s actually a bad password or locked account. It isn’t Conditional Access: first-factor auth failed before CA ran, so conditionalAccessStatus = notApplied and the code is a 500xx (50126 wrong password, 50053 locked out, 50057 disabled). Reset/unlock the account — the giveaway is notApplied + a 500xx code, not a policy.
4. A device that worked yesterday is blocked with 53000 today.
Root cause: Require compliant device is unmet because the device drifted out of compliance, or Intune marked it compliant but that state hadn’t synced to Entra at sign-in time (common after a weekend or a compliance-policy change).
Confirm: Device info tab shows isCompliant = false, while Intune → the device → Compliance may show compliant (the sync-lag tell) or reveal the failing setting.
Fix: If Intune already shows compliant, force a device sync / wait for the next evaluation. If a setting genuinely fails (encryption, OS version), remediate it. Scope the policy only if the device should be legitimately exempt.
5. An app “just stops working” with no prompt the user ever sees.
Root cause: The block is on a non-interactive sign-in — a silent token refresh that a sign-in-frequency session control forced to re-authenticate (which a background process can’t do), or a revoked/expired session being re-evaluated.
Confirm: Sign-in logs → Non-interactive user sign-ins tab (the Interactive tab will show nothing) and read the ConditionalAccessStatus/ResultType there.
Fix: Have the user re-authenticate interactively to mint a fresh token; review whether the sign-in-frequency policy should apply to that app/flow at all.
6. A service or app integration suddenly returns 403 / is blocked.
Root cause: A Conditional Access policy for workload identities is governing the service principal (app-only / client-credentials) sign-in — a newer capability that blocks or location-restricts app-only access.
Confirm: Sign-in logs → Service principal sign-ins tab; the appliedConditionalAccessPolicies there name the workload-identity policy.
Fix: Adjust the workload-identity policy (e.g. add the source IP to a trusted location, or scope the SP out if appropriate). Confirm the integration uses the correct OAuth flow.
7. The MFA prompt appears but the user can never pass it. They have no usable MFA method registered (codes 50079/53004), so a Require MFA policy can’t be satisfied. Entra → Authentication methods → User registration details (or the userRegistrationDetails Graph API) confirms no method. Register one — Authenticator, FIDO2/passkey, or phone — via a registration campaign so you catch the whole unregistered population, not one user at a time.
8. Blocks only from certain places. A location condition (untrusted country, or an IP outside your trusted ranges via VPN/travel) scopes a Block or step-up policy in. The Location tab shows the source and named-location match. Add the legitimate location as trusted, or base the condition on trusted IP ranges rather than fallible GeoIP — don’t disable the policy.
9. A policy you carefully built isn’t blocking anyone (scope-out). The user (or their group) is excluded, the sign-in is from an excluded trusted location, a device filter excluded the device, or they’re not in the include set. The policy’s row shows result = notApplied; What If states the exclusion reason explicitly. Correct the exclusion/include set and re-validate — this is the audit that proves a high-value app is actually covered.
10. A new policy caused a flood of blocks on go-live. It was enforced without a report-only soak, so its blast radius (unregistered users, non-compliant devices, legacy clients) hit production at once. Roll it back to report-only, size the impact (reportOnlyFailure count by policy), remediate, then re-enforce — the discipline in Deploying Conditional Access Safely.
14. A block traces to a policy nobody remembers changing. The policy didn’t change — the user’s group membership did, pulling them into scope (often a nested or dynamic group whose rule started matching them). Check the failure policy’s group assignment and the user’s membership/membershipRule. Fix the membership or rule if wrong; otherwise the user is now correctly in scope. The dynamic groups cookbook covers rules that don’t surprise you.
15. Your own query for the sign-in returns 403. Not a CA problem — you lack permission to read sign-in logs. Graph needs AuditLog.Read.All plus a reader role (Reports Reader, Security Reader, or higher); the 403 body names the missing permission. The method for reading your own effective permissions is in Troubleshooting AuthorizationFailed.
Best practices
- Read the code before you touch a policy. A request ID is the start of an investigation, not the end —
53003,53000,50074and a 500xx are completely different days. - Always roll out in report-only first. Watch
reportOnlyFailure/reportOnlyInterruptedfor a full business cycle (including a weekend) to size the blast radius. This single discipline prevents most go-live floods. - Keep policies single-purpose and named for what they do (Require MFA — all users, Block legacy auth). When one shows
failurein a log, the name should already tell you the control. - Never fix a block by widening an exclusion blindly. Excluding a user/app from a Block legacy auth policy re-opens a real attack surface — fix the client or scope precisely instead.
- Maintain and audit your break-glass exclusions. Every “All users” policy needs emergency-access accounts excluded; review those exclusions quarterly so they don’t become unmonitored gaps.
- Search the right sign-in tab. If a block isn’t in Interactive, check Non-interactive and Service principal before concluding “no block exists.”
- Base location conditions on trusted IP ranges you control where possible; GeoIP is fallible and VPNs shift egress — treat country blocks as coarse, not precise.
- Expect Intune→Entra compliance sync lag. Alert on
53000so a sync-lag wave is caught in minutes, and don’t assume “Intune says compliant” means “Entra knew at sign-in time.” - Export
SignInLogsto Log Analytics from day one. Portal logs suit one ticket; trends, dashboards and alerting need the table queryable. - Wire What If into your change process. Before enabling or editing a policy, run it for a representative user and a break-glass account to prove scope — including that it does not apply where it shouldn’t.
- Alert on leading indicators, not “the user complained”: spikes in
53003by policy,53000by app, legacy-auth blocks, andreportOnlyFailurefor any policy you’re about to enforce. - Document every standing exception with an owner and an expiry. A “temporary” exclusion with no expiry is the scope-out gap an auditor finds next year.
The alerts worth wiring before the next incident — leading indicators, not the lagging “user complained”:
| Alert on | Signal (KQL field) | Threshold (starting point) | Why it’s leading |
|---|---|---|---|
| Legacy-auth blocks | ClientAppUsed legacy + ResultType 53003 |
Any sustained, or a sudden spike | A client regression or spray attempt |
| Device-compliance blocks | ResultType == 53000 |
Spike vs baseline | Sync lag or a bad compliance push |
| MFA-unregistered failures | ResultType in (50079,53004) |
> 0 for active users | A population that will be locked out when you enforce |
| Report-only would-block | result == "reportOnlyFailure" |
dcount(users) before enforcing | The pre-enforcement blast radius |
| Block-control denials by policy | ResultType 53003 grouped by policy |
Spike on any one policy | A scope change or new client population |
| Workload-identity blocks | Service-principal tab failures | Any new occurrence | A service integration just broke |
Security notes
- The opacity of the block page is a feature — keep it. The user sees a request ID, not “policy X requires a compliant device.” Don’t add custom error pages that leak policy logic to unauthenticated browsers; put the detail in the logs, where only authorised staff read it.
- Least privilege for log readers. Reading sign-in logs needs Reports Reader or Security Reader (and
AuditLog.Read.Allfor Graph), not Global Administrator — reading blocks shouldn’t require the power to change policy. Use just-in-time elevation via PIM for Entra roles for the rarer admin tasks. - Treat “fix the block by excluding the user” as a security change, not a helpdesk action. Exclusions and disabled policies weaken posture; route them through change control, not an ad-hoc toggle during a call.
- Protect break-glass accounts and watch them. Excluded from blocking policies by necessity, they are high-value — alert on every break-glass sign-in and store credentials offline.
- Don’t loosen authentication strength to clear a ticket. If a policy demands phishing-resistant MFA, the fix is a passkey/FIDO2 method for the user — not dropping the requirement for everyone.
- Validate that high-value apps are actually covered. Use What If and
notAppliedaudits to prove no exclusion or trusted-location carve-out has quietly removed a sensitive app from protection (the silent scope-out). - Block legacy authentication and keep it blocked — the highest-leverage control against password spray. Every exception is a documented, owned, time-boxed risk, never a permanent convenience.
Cost & sizing
Conditional Access is a licensing line, not a metered one — the levers are licences and the log pipeline, and “sizing” means access tier and log retention, not instances.
- Entra ID P1 is the floor and the dominant cost. CA (policies, What If, report-only, named locations) requires P1; P2 adds risk-based conditions via Identity Protection and PIM. Both are per-user/month and usually bundled in Microsoft 365 E3 (P1) and E5 (P2), so the marginal cost is often zero if you have those suites. There is no per-policy or per-evaluation charge.
- Log Analytics ingestion is the operational cost of making the playbook queryable. Exporting
SignInLogs(andAADNonInteractiveUserSignInLogs) via a diagnostic setting bills per GB ingested and per GB retained beyond free retention. Interactive volume is modest; non-interactive (token-refresh) volume can be large, so export it deliberately and set sane retention. - Retention vs investigation window. The Entra portal keeps interactive sign-in logs for a limited window (commonly 30 days on P1/P2). If you need longer for audits or baselines, that’s exactly why you export to Log Analytics — match retention to your audit requirement, not “forever,” and design the workspace once (per the workspace guide) so RBAC and cost are right from the start.
A rough monthly picture for a 2,000-seat tenant already on M365 E3/E5: CA itself adds nothing beyond the suite licence; the only incremental spend is Log Analytics ingestion for the sign-in tables — often a few hundred to a couple of thousand rupees a month, driven by whether you export the chatty non-interactive table and how long you keep it. The cost drivers and what each buys you:
| Cost driver | What you pay for | Rough scale | What it buys | Watch-out |
|---|---|---|---|---|
| Entra ID P1 (per user) | Conditional Access, What If, report-only | Bundled in M365 E3 | All non-risk CA | No risk-based conditions on P1 |
| Entra ID P2 (per user) | Risk conditions, Identity Protection, PIM | Bundled in M365 E5 | Risk-based CA, JIT roles | Only needed where you use risk/PIM |
SignInLogs ingestion |
Interactive sign-in log GB | Modest | Queryable interactive blocks | Per-GB; grows with tenant size |
| Non-interactive log ingestion | Token-refresh log GB | Large | Visibility into silent blocks | Can dwarf interactive volume — export deliberately |
| Log Analytics retention | GB-months beyond free | Your choice | Longer investigation/audit window | “Forever” retention is a real bill |
Interview & exam questions
1. A user is blocked and insists “but I have MFA.” What’s the most likely explanation and how do you confirm it? Grant controls combine with AND across all applicable policies, so MFA satisfied one policy while a different applicable policy required something the user didn’t meet — commonly a compliant device or an outright Block. Confirm on the sign-in event’s Conditional Access tab: find the policy whose result is failure (it won’t be the MFA one) and read its enforcedGrantControls.
2. What is the difference between conditionalAccessStatus values failure and notApplied? failure means at least one applicable policy’s required control was not satisfied (a block or unmet grant) — a genuine CA outcome. notApplied means no policy’s assignments matched this sign-in, so CA did nothing; if the sign-in still failed, it failed for a non-CA reason (e.g. a 500xx auth error before CA ran).
3. A user reports “Conditional Access blocked me” but the sign-in shows error 50126. Is it a CA problem? No. 50126 is invalid username or password — a first-factor authentication failure that happens before Conditional Access evaluates, so conditionalAccessStatus is notApplied. The fix is a password reset/unlock; looking at policies wastes time. The tell is notApplied + a 500xx code.
4. Which field names every policy that was evaluated for a sign-in, and what does its result of failure mean? The appliedConditionalAccessPolicies array (the Conditional Access tab in the portal). A result of failure for a policy means that policy applied and one of its required controls was not satisfied — read enforcedGrantControls to see whether it was a hard Block or a require (MFA, compliant device, app protection) the user could meet.
5. What does error code 53003 tell you, and what does it not tell you? 53003 means access was blocked by Conditional Access — a grant control denied the sign-in. It does not by itself distinguish a hard Block access control from an unmet require control (MFA/compliant device); for that you must read the enforcedGrantControls of the failure policy. Block is a hard deny; a require is something the user can satisfy.
6. A scheduled integration suddenly starts failing with a block, but the user-interactive sign-in logs show nothing. Where do you look? The Non-interactive user sign-ins or Service principal sign-ins tab. A background token refresh blocked by a sign-in-frequency session control appears under non-interactive; an app-only (client-credentials) sign-in governed by a workload-identity Conditional Access policy appears under service principal. Searching only Interactive sign-ins is the classic dead end.
7. How do you predict which users a new blocking policy will affect before enforcing it? Deploy the policy in report-only mode and watch the appliedConditionalAccessPolicies results for reportOnlyFailure/reportOnlyInterrupted over a full business cycle, ideally via KQL over SignInLogs (count distinct users impacted by policy). Complement with the What If tool to validate scope for specific users. Never enforce a new blocking policy without one of these.
8. What is the limitation of the What If tool that catches people out? What If evaluates policy logic only — which policies would apply to a hypothetical sign-in and why — but it does not know the user’s live state (actual MFA registration, real device compliance). It will say “Require compliant device would apply” without knowing whether the device is compliant right now. For real outcomes you still read an actual sign-in or check Intune.
9. A device worked yesterday and is blocked with 53000 today, yet Intune shows it as compliant. What’s happening? A compliance state sync lag between Intune and Entra: the device became (or was re-marked) compliant in Intune, but that state hadn’t propagated to Entra at sign-in time — common after a weekend or a compliance-policy change. Confirm by comparing the sign-in’s isCompliant=false against Intune’s compliant status; fix with a forced device sync or by waiting for the next evaluation, not by editing the policy.
10. Why is excluding a user from a Block legacy authentication policy a bad fix, and what should you do instead? Excluding them re-opens the basic-auth attack surface (legacy protocols can’t do interactive MFA and are a primary password-spray vector) — a security regression to fix a usability ticket. The right fix is to move the client to modern authentication (OAuth); if a service truly can’t, scope the exception to a single documented, owned, time-boxed identity, ideally a workload identity.
11. What grant control combination makes “require one of MFA or compliant device” possible, and how does that differ across two policies? Within one policy, selecting both controls with “Require one of the selected controls” lets either satisfy it. Across two separate policies, controls are ANDed — the user must meet both policies’ requirements — so you cannot express “either/or” by splitting controls into two policies; you must put the “or” inside a single policy.
12. Where do you go first when a sign-in event itself returns 403 to your Graph query for it? That’s an RBAC problem, not a Conditional Access one: reading sign-in logs via Graph needs AuditLog.Read.All and a directory role such as Reports Reader or Security Reader. The 403 body names the missing permission. Grant least-privilege read access rather than escalating to Global Administrator.
These map to SC-300 (Identity and Access Administrator) — plan, implement and manage Conditional Access, sign-in diagnostics, Identity Protection — and AZ-500 (Security Engineer) — manage identity and access, Conditional Access, MFA. The reporting/log angle also touches SC-200. A compact cert-mapping for revision:
| Question theme | Primary cert | Exam objective area |
|---|---|---|
| CA policy logic, grant control combination | SC-300 | Plan & implement Conditional Access |
| Reading sign-in logs, failure codes | SC-300 / SC-200 | Monitor identity; investigate sign-ins |
| Report-only, What If, blast-radius sizing | SC-300 | Implement & manage CA safely |
| Device-compliance & app-protection grants | SC-300 / MD-102 | Conditional Access with Intune signals |
| Legacy-auth blocking, MFA strength | SC-300 / AZ-500 | Secure authentication; MFA |
| Least-privilege log access (RBAC/Graph) | AZ-500 / SC-300 | Manage identity & access |
Quick check
- A user is blocked and says “but I have MFA.” Which field do you read to find the actual control that denied them, and why isn’t it the MFA policy?
- A sign-in shows
conditionalAccessStatus = notAppliedand error50126. Is this a Conditional Access block? What’s the fix? - What does error code
53003tell you, and what crucial detail must you still read to know whether it was a hard block or a satisfiable require? - An app integration silently stops working with no prompt the user sees, and the Interactive sign-in logs show nothing. Name two tabs to check.
- You’re about to enforce a new blocking policy. What mode do you use first, and which
resultvalue tells you the blast radius?
Answers
- Read the
appliedConditionalAccessPoliciesarray (the Conditional Access tab) and find the policy whoseresultisfailure— then read itsenforcedGrantControls. It isn’t the MFA policy because grant controls AND across all applicable policies, so MFA satisfied one policy while a different one (commonly compliant device or Block) was the one that denied access. - No —
50126is an invalid username/password, a first-factor failure that happens before CA runs, which is whyconditionalAccessStatusisnotApplied. The fix is a password reset / account unlock, not a policy change. 53003means access was blocked by Conditional Access (a grant control denied it). You must still read thefailurepolicy’senforcedGrantControls: aBlockis a hard deny, whereasMfa/CompliantDevice/RequireAppProtectionis a require the user can satisfy.- The Non-interactive user sign-ins tab (a background token refresh blocked by a sign-in-frequency control) and the Service principal sign-ins tab (an app-only sign-in governed by a workload-identity policy).
- Deploy it in report-only mode first; the
reportOnlyFailure(andreportOnlyInterrupted) results — countable by distinct users over a business cycle — are your pre-enforcement blast radius.
Glossary
- Conditional Access (CA) policy — an if-then rule: if these assignments (users, target resources, conditions) match, then apply these access controls (grant and/or session).
conditionalAccessStatus— the sign-in’s top-level CA outcome:success,failure,notApplied, ornotEnabled.appliedConditionalAccessPolicies— the per-sign-in array listing every policy evaluated and itsresult; the field that names the blocking policy and its controls.result(per policy) —success,failure,notApplied,notEnabled, or the report-only variants (reportOnlySuccess/Failure/NotApplied/Interrupted).- Grant control — what a policy requires to allow access: Require MFA, Require compliant device, Require Entra/Hybrid-joined device, Require approved client app, Require app protection policy, Require password change, or Block access.
- Session control — what a policy shapes after sign-in: sign-in frequency, persistent browser, app-enforced restrictions, continuous access evaluation.
- Failure reason / error code — the numeric code (
53003,50074,53000,50158, …) and text naming the control/condition that wasn’t met. - 53003 — “Access has been blocked by Conditional Access policies” — a grant control (Block, or an unmet require) denied the sign-in.
- 50074 / 50076 / 50079 — MFA required / required from this context / required and the user must register a method.
- 53000 / 53001 / 53002 — device not compliant / not Entra-or-Hybrid-joined / app not approved (managed) — the device and app grant controls.
- Named location — an IP range or country/region you define and optionally mark trusted; used in location conditions.
- Report-only — a policy state where CA is evaluated and logged (
reportOnly*results) but not enforced; the safe way to size impact. - What If — the Conditional Access simulator that evaluates all policies against a hypothetical sign-in (user + app + conditions) to show which would apply and why.
- Interactive vs non-interactive sign-in — a human authenticating (with prompts) vs a background/silent token acquisition on the user’s behalf; logged in separate tabs.
- Service principal sign-in — an app-only (client-credentials) sign-in; can be governed by workload-identity Conditional Access policies.
- Authentication strength — a grant control requiring a specific MFA quality (e.g. phishing-resistant: FIDO2/passkey/certificate), beyond “any MFA.”
- Filter for devices — a policy condition that scopes by device attributes (model, ownership,
extensionAttribute,trustType) rather than by compliance. - Scope-in / scope-out — a policy applying when you didn’t expect it (in) vs not applying when you wanted it to (out); both read from the conditions and
result.
Next steps
You can now turn a grey block page into a precise diagnosis. Build outward:
- Next: Deploying Conditional Access Safely: Report-Only Rollout to Enforcement — the rollout discipline that stops these blocks from becoming a ticket flood in the first place.
- Related: OIDC and OAuth2 on Entra ID: Choosing the Right Flow — what kind of sign-in it is decides which controls can even be satisfied.
- Related: Authenticator Passkeys and Number Matching: Stopping MFA Fatigue Attacks — fix the MFA-interrupt and phishing-resistant-strength cases at the source.
- Related: Conditional Access for AVD and Windows 365 — the desktop sign-in surface, where device and platform conditions bite hardest.
- Related: No Logs Showing Up? Troubleshooting Empty Log Analytics Tables — when
SignInLogsis empty and the playbook has nothing to query. - Related: PIM for Entra Roles: Just-in-Time Activation, Approvals, and Security Alerts — least-privilege, time-boxed access for the admin tasks behind these investigations.