Microsoft 365 Identity

Gating Microsoft 365 with Endpoint Conditional Access: Compliance Policies, Device Filters, and Require-Compliant Enforcement

The strongest Conditional Access (CA) control most tenants under-use is “require a compliant or hybrid-joined device.” MFA proves who is signing in; the compliant-device grant proves what they sign in from. This guide builds the full endpoint-gating stack — Intune compliance policy, compliance flowing into Entra ID, the CA grant, and device filters to scope it — staged so you don’t lock yourself out.

The device-based access model

Three independent pieces must line up before a “managed” device is allowed in:

  1. Device registration state in Entra ID — the device object’s trustType: Entra registered (BYO), Entra hybrid joined (on-prem AD + synced), or Entra joined (cloud-native). No object, no device grant can ever pass.
  2. Compliance state — Intune evaluates the device against a compliance policy and stamps isCompliant (true/false) onto the Entra device object. No Intune enrollment, no compliance signal.
  3. The CA grantRequire device to be marked as compliant and/or Require Microsoft Entra hybrid joined device. CA reads the device object at sign-in and decides.

The single most important fact: the compliant grant requires the device to be Intune-enrolled and evaluated; the hybrid-joined grant only requires hybrid join and does not need Intune. Mixing these up is the number-one cause of “my domain PCs are blocked” tickets.

Grant Needs Entra device object Needs Intune enrollment Typical fleet
Require marked compliant Yes Yes Intune-managed (cloud or co-managed)
Require hybrid joined Yes (hybrid trustType) No Legacy on-prem domain PCs
Require compliant OR hybrid Yes Only for the compliant half Mixed estates mid-migration

For a heterogeneous estate mid-migration, “compliant OR hybrid joined” with Require one of the selected controls is the pragmatic grant: cloud-managed machines pass on compliance, legacy domain machines on hybrid join, nothing breaks while you migrate.

Step 0 — Break-glass first, always

Before any device grant goes near On, confirm you have two cloud-only *.onmicrosoft.com Global Administrator accounts, each with a long random password, excluded from every CA policy, and covered by a sign-in alert. A device-compliance grant is especially dangerous: the break-glass admin will almost never sign in from a managed, compliant device, so if it isn’t excluded, your recovery account is the first thing the policy blocks. Verify the exclusion on the new policy every time.

Step 1 — Author the Intune compliance policy

A compliance policy is pass/fail rules plus actions for noncompliance (what happens, and when). Build one per platform at Intune admin center -> Devices -> Compliance -> Create policy. A sane Windows baseline:

Actions for noncompliance and grace periods

This separates a humane rollout from a help-desk flood. Each policy has an Actions for noncompliance tab:

Action When (schedule) Purpose
Mark device noncompliant Immediately (0 days) or after a grace period Controls when isCompliant actually flips
Send email to end user 0 / 1 / 3 days Warn before enforcement bites
Send push notification 0 / 1 days Same, on mobile
Remotely lock / retire Late (e.g. 30 days) Last-resort for abandoned devices

The “Mark device noncompliant” schedule is your grace period — there is no separate setting. Set it to 3 days during rollout: a freshly enrolled or briefly-failing device gets 3 days (and emails) before isCompliant flips to false and CA starts blocking. Drop it to 0 days (immediate) only once you’re confident in the estate.

The “Mark devices with no compliance policy assigned as” tenant setting (Compliance -> Compliance policy settings) is a second trap. Left at Compliant, every unmanaged or unevaluated device is treated as compliant and your CA grant is silently toothless. Set it to Not compliant — but only after your real policies are assigned and grace periods are in place, or you’ll instantly mark the whole fleet as failing. Set the Compliance status validity period (e.g. 30 days) there too, so a device that stops checking in eventually ages into noncompliance.

Assign the policy to a device group (or a user group — Intune supports both). Start with a pilot ring (KV-Pilot-Windows), not All Devices.

Step 2 — How compliance flows to Entra ID and CA

The chain is asynchronous; understanding the lag prevents most “why is it still blocked” confusion:

Device checks in to Intune
   -> Intune evaluates the assigned compliance policy
      -> Intune writes isCompliant to the Entra ID device object
         -> Conditional Access reads the device object at the NEXT sign-in / token request
            -> grant satisfied or blocked

Two timing facts to internalize:

Confirm the stamped state directly with Microsoft Graph:

Connect-MgGraph -Scopes "Device.Read.All"

# Look up the device object and the compliance flag CA actually reads
Get-MgDevice -Filter "displayName eq 'KV-LT-0421'" |
  Select-Object DisplayName, DeviceId, TrustType, IsCompliant, IsManaged, OperatingSystem

TrustType is the join type (Workplace = Entra registered, AzureAd = Entra joined, ServerAd = hybrid joined); IsCompliant is the exact bit the compliant grant evaluates. If it’s empty/false but Intune shows the device compliant, you’re in the sync lag — wait for the next check-in.

Step 3 — Build the require-compliant Conditional Access policy

Create it at Entra admin center -> Protection -> Conditional Access -> New policy, in Report-only first (Step 6).

The equivalent shape via Graph (POST /identity/conditionalAccess/policies):

{
  "displayName": "CA-Require-Compliant-O365-Pilot",
  "state": "enabledForReportingButNotEnforced",
  "conditions": {
    "applications": { "includeApplications": ["Office365"] },
    "users": {
      "includeGroups": ["<pilot-group-id>"],
      "excludeUsers": ["<breakglass-1-id>", "<breakglass-2-id>"]
    },
    "platforms": { "includePlatforms": ["windows"] }
  },
  "grantControls": {
    "operator": "OR",
    "builtInControls": ["compliantDevice", "domainJoinedDevice"]
  }
}

compliantDevice is the marked-compliant grant; domainJoinedDevice is the hybrid-joined grant (the API name is historical). operator: "OR" is Require one of the selected controls; "AND" is Require all. The policy ships as enabledForReportingButNotEnforced — report-only — by design.

Step 4 — Device filters: scope policies by device attributes

A filter for devices matches (or excludes) on properties of the device object rather than the user — ownership, OS, model, join type, or extension attributes — for scopes like “compliance for corporate Windows only” or “exclude this kiosk fleet.” The filter is a rule expression over device attributes:

Filter for devices
  Configure : Yes
  Mode      : Include   (or Exclude)
  Rule      : device.trustType -eq "ServerAD" -and device.isCompliant -eq True

Useful building blocks (combine with -and / -or):

Need Rule
Corporate-owned only device.deviceOwnership -eq "Company"
Specific OS device.operatingSystem -eq "Windows"
Hybrid-joined only device.trustType -eq "ServerAD"
Tag a fleet for exclusion device.extensionAttribute1 -eq "kiosk"
Model-based device.model -startsWith "Surface"

Extension attributes are the most powerful and most over-claimed part of this. The 15 device extension attributes usable in CA filters (extensionAttribute115) are written via Microsoft Graph on the device objectnot the AD user msDS-cloudExtensionAttribute fields, not the on-prem AD computer object. Treat them as a tagging system you own and document.

Set one via Graph so a filter can key off it:

Connect-MgGraph -Scopes "Device.ReadWrite.All"

$deviceObjectId = (Get-MgDevice -Filter "displayName eq 'KV-KIOSK-07'").Id
Update-MgDevice -DeviceId $deviceObjectId -BodyParameter @{
  extensionAttributes = @{ extensionAttribute1 = "kiosk" }
}

A practical pattern: an Exclude filter of device.extensionAttribute1 -eq "kiosk" carves shared/kiosk devices out of the require-compliant policy without weakening it for anyone else; an Include filter of device.deviceOwnership -eq "Company" keeps the grant on corporate hardware only, leaving BYOD to the app-protection path below.

Step 5 — Unmanaged and platform-specific devices: app protection as an alternative grant

The require-compliant grant is right for managed endpoints and wrong for personal phones — demanding Intune compliance there forces enrollment users will refuse. The clean answer is a second, parallel CA policy offering app protection as the mobile grant, while the compliant grant governs desktops:

Note for 2026: the legacy Require approved client app grant has been retired (it stopped functioning in early March 2026). New mobile policies must use Require app protection policy — do not author against the approved-client-app control.

For unmanaged-but-low-risk web access, “Use Conditional Access App Control” (session control via Defender for Cloud Apps) is a third option — a monitored/restricted browser session instead of an outright block, when “block entirely” is too blunt for an app.

Step 6 — Stage the rollout: report-only, What If, sign-in logs

Never flip a device grant straight to On. The safe sequence:

  1. Report-only. Set the policy state to Report-only: it evaluates every sign-in and records what it would have done, enforcing nothing. Leave it for at least a full business cycle (a week) so daily, weekly, and edge-case sign-ins all get sampled.

  2. What If. Use Conditional Access -> What If to simulate a user + app + device-state + platform. Run it for a compliant pilot user (passes), a noncompliant device (blocks), a break-glass account (not applied), and a hybrid-only legacy PC (passes via the hybrid control).

  3. Sign-in log analysis. Where you find surprises before users do. In Entra -> Monitoring -> Sign-in logs, the Report-only tab shows, per sign-in, whether each report-only policy would grant, block, or require. Pivot there, or query Log Analytics if sign-ins are exported:

SigninLogs
| where TimeGenerated > ago(7d)
| mv-expand policy = todynamic(ConditionalAccessPolicies)
| where policy.displayName == "CA-Require-Compliant-O365-Pilot"
| extend reportOnlyResult = tostring(policy.result)
| where reportOnlyResult in ("reportOnlyFailure", "reportOnlyInterrupted")
| summarize impacted = dcount(UserPrincipalName), sample = make_set(UserPrincipalName, 20)
    by reportOnlyResult, tostring(DeviceDetail.operatingSystem)

reportOnlyFailure = the policy would have blocked this sign-in — your list of users/devices that break the moment you enforce. Drive it to effectively zero (enroll/remediate devices, fix scope, or add a filter exclusion), then set state to enabled. Expand the ring (pilot -> department -> all) one step at a time, re-checking the report-only data at each step.

Enterprise scenario

A 9,000-seat manufacturing org flipped Require compliant to On for Office 365 after a clean report-only week — and within the hour the helpdesk lit up with Outlook and Teams failures from autopilot-provisioned Windows laptops that were demonstrably Compliant in Intune. The report-only data hadn’t predicted it because the breakage wasn’t at interactive sign-in; it was at silent token refresh on devices whose Entra isCompliant bit had aged out.

Root cause: their Compliance status validity period was set to 30 days, but a large autopilot batch had a deviceManagement check-in gap (a misconfigured proxy bypass on the corporate VLAN blocked the Intune check-in endpoints). Devices stopped reporting, the 30-day validity window expired, and isCompliant reverted to false on the device object even though the local Intune client still showed green. CA read the stale-aged object and blocked.

We confirmed the pattern with a single Graph query against the affected ring before touching the policy:

Connect-MgGraph -Scopes "Device.Read.All"
Get-MgDevice -All -Filter "operatingSystem eq 'Windows'" |
  Where-Object { -not $_.IsCompliant -and $_.ApproximateLastSignInDateTime -gt (Get-Date).AddDays(-2) } |
  Select-Object DisplayName, IsCompliant, RegistrationDateTime |
  Measure-Object

The fix was two-pronged: unblock *.manage.microsoft.com and *.dm.microsoft.com on the proxy so check-ins resumed, and re-scope the CA policy with a transient grace filter — an Exclude filter on extensionAttribute2 -eq "checkin-remediation" tagged onto the affected device IDs via Graph — so those machines fell out of enforcement until they re-evaluated, then removed the tag. Lesson: report-only samples sign-ins, not the aging of the compliance bit. Always confirm devices are actively checking in before you enforce, not just nominally compliant.

Verify

Run these against a real enrolled device and a deliberately noncompliant one.

  1. Compliance stamped: Intune shows the device Compliant and Get-MgDevice ... | Select IsCompliant returns True. The two must agree; if not, you’re in sync lag.
  2. Compliant device passes: sign into Outlook on the web from the compliant device. Access granted, and the sign-in log shows the CA policy Success with the compliant grant satisfied.
  3. Noncompliant device blocks: from a device that fails the policy (BitLocker off, or unenrolled), the same sign-in is blocked with a “device must be compliant” message.
  4. Filter scope holds: tag a test device extensionAttribute1 = "kiosk", confirm via Graph, then verify (What If + a real sign-in) the require-compliant policy does not apply.
  5. Break-glass is exempt: sign in with a break-glass account from an unmanaged device — it must be allowed and the device policy must show Not applied. If blocked, stop and fix the exclusion.
  6. Hybrid path (mixed estates): from a hybrid-joined, non-Intune PC, confirm the sign-in passes via the hybrid joined control, not compliance.

Checklist

Pitfalls

The failures that bite in production are almost always one of four. Self-lockout: a grant on “All cloud apps” with no break-glass exclusion locks admins out of the portal needed to undo it — scope to Office 365 and exclude break-glass. The toothless grant: “no policy assigned = Compliant” lets unmanaged devices sail through; flip it to Not compliant once policies are live. Grace-period whiplash: “mark noncompliant” at 0 days during rollout blocks every briefly-remediating device; use a grace period and tighten later. Compliant-vs-hybrid confusion: Require compliant on a hybrid-joined-but-not-Intune-enrolled fleet blocks all of it — use compliant OR hybrid until the migration completes. Get those four right, stage through report-only with the sign-in-log data in hand, and the compliant-device grant becomes the highest-signal, lowest-noise control in your CA estate.

Conditional AccessIntune ComplianceDevice FiltersEntra IDEndpoint

Comments

Keep Reading