An admin builds a Windows compliance policy, assigns it to All Devices, and adds an include assignment filter meant to scope it to Windows 11 23H2 machines. They type the rule as (device.osVersion -eq "10.0.22631"), save, and move on. The assignment page looks exactly right: target All Devices, filter mode Include, filter name present, no red text anywhere. Three weeks later a security review asks why those devices have no compliance state for that policy at all. Not "non-compliant" - no state. The policy never applied to a single device, because that filter matched exactly zero of them, and nothing in the portal ever said so.
This is the quietest failure mode in Intune targeting. An include filter that matches nothing turns a live assignment into a no-op, and unlike a broken sync job or a failed install there is no error, no event, and no counter ticking upward to catch your eye. This post walks through why a filter silently matches zero devices, the one manual check that proves it in under a minute, and a read-only PowerShell script that enumerates every filter in the tenant and flags the rules most likely to be doing this to you right now.
The problem: an assignment that is present but lands on nobody
Assignment filters let you narrow an assignment without building a dynamic group. You target a policy at a group (or at All Devices / All Users), then attach a filter in Include or Exclude mode. At evaluation time, Intune takes the targeted set and applies the filter rule against each device's properties to decide who actually receives the policy. The mechanics are well documented and, when the rule is right, they work beautifully.
The trouble is the failure is asymmetric, and the dangerous half is invisible. Consider what happens when the rule inside the filter matches zero devices:
An include filter that matches zero devices means zero devices get the policy - the assignment is technically present and functionally empty. An exclude filter that matches zero devices is harmless by comparison: nobody is excluded, so everyone still receives the policy. That asymmetry is exactly why include filters are where this bites. A compliance policy that silently applies to nobody is arguably worse than one that fails loudly, because Conditional Access that keys off "is this device compliant?" will happily wave through devices that were never actually evaluated against your baseline.
Why it happens: the rule is a string match, and strings are unforgiving
A filter rule is a small expression in the form ([entity].[property] [operator] [value]), where the entity is device for managed devices or app for managed apps. Properties, operators, and values are case-insensitive, and you can combine expressions with and / or. The rule builder validates syntax - it will not let you save malformed brackets - but it cannot know whether your perfectly-formed rule describes any device that actually exists. That gap between "syntactically valid" and "matches something real" is where the zero-match bug lives.
The single most common trap is an exact-match operator on a property whose real value is longer or differently formatted than the admin assumes. Here are the ones worth committing to memory:
| Rule the admin wrote | Why it matches zero | What to write instead |
|---|---|---|
(device.osVersion -eq "10.0.22631") | Windows reports osVersion as a full four-part build string such as 10.0.22631.4317. An exact -eq against a three-part value equals none of them. | (device.osVersion -startsWith "10.0.22631") for partial, or the version-aware operatingSystemVersion with -ge/-le. |
(device.enrollmentProfileName -eq "Autopilot - Standard") | Profile names are spelling- and spacing-sensitive. If the profile was renamed to Autopilot Standard, the exact match resolves to nobody. | Confirm the exact profile name under Devices > Enrol devices, or use -startsWith "Autopilot". |
(device.operatingSystemSKU -eq "Windows Enterprise") | The SKU property expects a specific token such as Enterprise or EnterpriseS, not a friendly name. The friendly string matches nothing. | Use the documented SKU value: (device.operatingSystemSKU -eq "Enterprise"). |
osVersion property is being deprecated. Microsoft is steering admins toward the newer operatingSystemVersion property, which understands version comparisons (-gt, -ge, -lt, -le) instead of treating the build as an opaque string. Existing osVersion filters keep working, but if you are writing a build-number rule today, prefer operatingSystemVersion - it removes an entire class of "I compared a string and got zero" mistakes. The osVersion string operators -eq, -ne, -in and -notIn are still the ones to be suspicious of.There is a second, subtler variant: a mistyped or non-existent property. The rule builder pulls properties from a fixed list, but the rule syntax editor is a free-text box, and rules can also be pushed through Microsoft Graph where nothing stops you writing device.enrolmentProfileName (British spelling, one "l" too few) or some other token that resolves to nothing. A property Intune does not recognise contributes nothing to the match, and depending on how the rest of the expression is composed, that can collapse the whole result to zero.
Compound expressions make this worse, because a single bad clause quietly poisons the whole rule. A filter written as (device.manufacturer -eq "Microsoft") and (device.osVersion -eq "10.0.22631") looks like it targets Microsoft-made Windows 11 23H2 devices, and the first clause is perfectly fine on its own - it matches thousands of Surface devices. But because the two clauses are joined with and, and the second clause matches nobody, the intersection is empty and the whole filter matches zero. The admin who eyeballs the rule sees a correct-looking manufacturer clause and assumes the filter is basically right, when the impossible second clause has silently zeroed everything out. When you review a rule, sanity-check every clause independently, not just the one that catches your eye first.
devices (managed devices enrolled in Intune) or apps (managed apps in MAM scenarios) - a device filter cannot be used on an app-protection assignment and vice versa. The filter type on an assignment is none, include, or exclude. And every assignment that references a filter is recorded against that filter as a payload - the same data the portal shows on the filter's Associated Assignments tab. Those three concepts - management type, filter type, and payloads - are exactly what the script later keys off.How to verify: one filter, one click, before you trust anything
Before running any script across the tenant, prove the mechanism on a single filter by hand. This takes under a minute and it is the one check that gives you ground truth, because it asks Intune itself "how many enrolled devices does this rule match right now?" - which is the exact question no Graph field answers.
- Sign in to the Microsoft Intune admin center as an Intune administrator.
- Go to Tenant administration > Assignment filters and open the filter you are suspicious of.
- Open Rules (select Edit if needed) and select Preview devices. Intune lists the enrolled devices that currently match the rule.
- If that list is empty, the rule matches zero devices - any include assignment using this filter is currently landing on nobody.
- Switch to the Associated Assignments tab to see every app and policy that uses this filter, the target group, and whether each one uses it in Include or Exclude mode.
The combination in that panel is the whole bug in one view: a syntactically valid rule, a Preview devices result of zero, and an include assignment on a compliance baseline targeting All Devices. Syntax passed; reality is empty.
The fix: Get-AssignmentFilterCoverageReport.ps1
Checking one filter by hand is fine. Checking forty of them after you have inherited a tenant, or confirming that a change last week did not quietly zero out a rule, does not scale. The companion script enumerates every assignment filter in the tenant through Microsoft Graph, reports which policies and apps reference each one (and in Include or Exclude mode), and flags the rules that match the known zero-match traps so you know exactly which filters to put through Preview devices.
It authenticates with the beta Graph endpoint - assignment filters and their payloads collection live under /deviceManagement/assignmentFilters, and the PowerShell surface for that is Get-MgBetaDeviceManagementAssignmentFilter in the Microsoft.Graph.Beta.DeviceManagement module. The only permission it asks for is DeviceManagementConfiguration.Read.All - the least-privilege read scope for listing filters.
It supports two authentication modes so it fits both interactive triage and unattended scheduling. For a quick investigation, run it with -UseDeviceCode and sign in as an Intune administrator - Graph brokers the read scope for you. For a scheduled job on a server or in Azure Automation, register an app, grant it DeviceManagementConfiguration.Read.All as an application permission, and pass -TenantId, -ClientId and -CertificateThumbprint for app-only certificate auth. Either way the permission ceiling is the same read-only scope, so the worst-case blast radius of a misconfigured run is nothing more than a report you did not want.
Download Get-AssignmentFilterCoverageReport.ps1 from the Imran76Awan/Daily-Tasks repository on GitHub — no sign-in required. It is read-only and cannot change anything in your tenant — but always validate any script in your own environment before you rely on it.
Here is a first run against a test tenant, before anyone has touched the broken compliance filter from earlier in this post:
The classification logic is deliberately conservative. It reads each filter's rule and its payloads, then raises an advisory when the rule uses an exact-match operator on a fragile property, references a property name it does not recognise, or is empty while still being referenced by an assignment:
getState function on assignmentFilters only reports whether the filter feature is enabled - it does not evaluate a rule against your fleet. That means this script surfaces red flags, not proof. A flagged filter is one to go and confirm with Preview devices; an unflagged filter is not a guarantee of a healthy match. The only tool that definitively counts matching devices is the portal's Preview devices feature, and there is no Graph equivalent to script around it.Proof it worked: re-run after fixing the rule
The fix for the compliance filter is a one-character-class change: swap the exact-match operator for a partial match, so -eq "10.0.22631" becomes -startsWith "10.0.22631" (or, better, move to operatingSystemVersion -ge for a version-aware comparison). Do that in the portal, use Preview devices to confirm the list is now populated, and re-run the script:
The compliance filter no longer trips the exact-match heuristic, and Preview devices in the portal now returns a healthy list of Windows 11 23H2 machines. The remaining flagged filter is the enrollmentProfileName one - which, on inspection, turned out to be a genuinely renamed profile, fixed the same way. Note the exit codes if you are wiring this into a scheduled job: the script returns 0 when nothing is flagged, 2 when it flags filters for review, and 1 if any Graph query fails - so a query failure can never be mistaken for a clean report.
References
- Create assignment filters in Microsoft Intune (includes Preview devices and Associated Assignments) — Microsoft Learn
- Assignment filter properties and operators reference — Microsoft Learn
- deviceAndAppManagementAssignmentFilter resource type (rule, platform, payloads, assignmentFilterManagementType) — Microsoft Graph beta
- List deviceAndAppManagementAssignmentFilters (DeviceManagementConfiguration.Read.All) — Microsoft Graph beta
- deviceAndAppManagementAssignmentTarget resource type (deviceAndAppManagementAssignmentFilterId / FilterType: none, include, exclude) — Microsoft Graph
- Get-MgBetaDeviceManagementAssignmentFilter (Microsoft.Graph.Beta.DeviceManagement) — Microsoft Learn
- Andrew Taylor (MVP) — Intune: User vs Device Targeting (supplementary community context on filter-based targeting)
How many of your include filters have you actually put through Preview devices, versus assuming the assignment landed because the portal showed it as configured? Run the audit, take the flagged ones to Preview devices, and see whether any of your compliance baselines have been quietly applying to nobody. I would genuinely like to hear how many turn up.