scloud by Florian Salzmann
ende

Advanced Dynamic Autopilot Group Queries for Production (Autopilot v1)

Why Does the Single-Tag Rule Stop Being Enough?

I have written dozens of dynamic Autopilot group queries over the years, and almost every one starts the same way:

(device.devicePhysicalIds -any (_ -eq "[OrderID]:scloud"))

One group tag. One dynamic group. One Autopilot profile. That works well for a single site or a single customer. It breaks down fast once you manage more than a handful of locations, business units, or hardware batches. At that point you end up with dozens of near-identical groups. Or you start bending the single-tag rule into something it was never meant to do.

I ran into this exact problem while rolling out Autopilot for a client with twelve retail branches. A single tag per branch would have meant twelve dynamic groups and twelve profiles to keep in sync. That is when I started building a proper dynamic Autopilot group query instead of one rule per tag.

This post walks through the query patterns I actually use in production. First, I will show you how to match multiple tags. Then I will cover how to exclude tags correctly, since there is a real gotcha here. Finally, I will show you how to match whole families of tags by prefix. Everything below applies to Windows Autopilot v1, the classic deployment profile model built on Azure AD and Entra ID dynamic device groups (see Microsoft’s Windows Autopilot overview and the dynamic membership rule syntax). You set group tags at hash upload or at registration time. If you are rolling out Autopilot v2 (device preparation), jump to the note at the end. The grouping model works differently there.

How Do You Match Multiple Tags in a Dynamic Autopilot Group Query?

The most common request I get is simple: put every device with any of these tags into one group. You can chain conditions with -or:

(device.devicePhysicalIds -any (_ -eq "[OrderID]:HQ")) -or
(device.devicePhysicalIds -any (_ -eq "[OrderID]:Branch01")) -or
(device.devicePhysicalIds -any (_ -eq "[OrderID]:Branch02"))

Or match against a list with -in. This is more compact:

(device.devicePhysicalIds -any (_ -in ["[OrderID]:HQ","[OrderID]:Branch01","[OrderID]:Branch02"]))

My tip: switch to the -in form once you pass two or three tags. It is shorter to write. More importantly, it is easier to scan and extend six months later, when someone asks you to add a fourth site.

Narrow the Query with a Second Condition (AND)

Sometimes a tag alone is not specific enough. You want devices with a given tag, but only if they also match a second signal, like a specific OS SKU or device category. Combine both with -and:

(device.devicePhysicalIds -any (_ -eq "[OrderID]:Kiosk")) -and
(device.deviceOSType -eq "Windows")

I reach for this pattern when a tag gets reused for more than one purpose. Then I need a second condition to tell devices apart. It is not a substitute for choosing a more specific tag in the first place. More on that below.

How Do You Match Tag Prefix Families with -startsWith?

If your naming convention already encodes a hierarchy in the tag, you rarely need to enumerate every value. Say every managed-service customer gets a tag like Cust-Contoso or Cust-Fabrikam. Match the whole family with -startsWith:

(device.devicePhysicalIds -any (_ -startsWith "[OrderID]:Cust-"))

I use this pattern for a “master” group. It applies a baseline profile or compliance policy to every managed customer at once. Each individual Cust-<Name> group still gets its own dynamic group and its own Autopilot profile.

How Do You Exclude a Tag? The Mistake Almost Everyone Makes

Here is the one that catches almost everyone, myself included, the first time. The instinct is to exclude a tag with -any (_ -ne "…"):

(device.devicePhysicalIds -any (_ -eq "[OrderID]:HQ")) -and
(device.devicePhysicalIds -any (_ -ne "[OrderID]:Decommissioned"))     ❌ almost always true

This looks correct, and it passes validation. It does not do what you expect, though. devicePhysicalIds is a multi-valued property. The clause -any (_ -ne "X") asks whether at least one value in the array is not X. That is true for almost every device. A device carries many physical IDs: serial number, hardware hash reference, ZTDID, and the order or group tag itself. So the -ne clause silently matches everyone, exclusion tag or not.

I only caught this myself after auditing an “exclusion” rule that turned out to exclude nothing. The correct way to exclude a value from a multi-valued property is -all with -ne, not -any:

(device.devicePhysicalIds -any (_ -eq "[OrderID]:HQ")) -and
(device.devicePhysicalIds -all (_ -ne "[OrderID]:Decommissioned"))     ✅ correct

-all (_ -ne "X") means every value in the array is not X, so none of them is X. That is the actual exclusion you want. It is worth double-checking any exclusion rule you already run in production. A rule that “validates fine” in the portal is not the same as a rule that does what you meant.

My advice: if you find yourself excluding by tag at all, stop and ask a question first. Would a separate static group, like “decommissioned” or “do-not-deploy”, excluded at the assignment level, be simpler? It is usually easier to audit than baking the exclusion into the dynamic rule. Excluding dynamic groups from assignments has its own caveats. I cover those in Mastering Assignments in Intune.

Match Tag Patterns with -match

For naming conventions that are not simple prefixes, -match with a regular expression can help. Say your tags embed a region code in the middle, like EU-Retail-HQ versus US-Retail-HQ:

(device.devicePhysicalIds -any (_ -match "^\[OrderID\]:EU-.*-HQ$"))

Use this sparingly. Regex rules are powerful, but they are also the hardest pattern on this page to read at a glance. They are the easiest to get subtly wrong, too. The ^ and $ anchors matter: an unanchored pattern can match more than you intend. If a -startsWith or -in combination gets the job done, use that instead.

Quick Reference

ScenarioMembership rule patternWhen to use it
Single tag(device.devicePhysicalIds -any (_ -eq "[OrderID]:X"))One site, one customer, one profile: the default case
Multiple tags (OR)(device.devicePhysicalIds -any (_ -in ["[OrderID]:A","[OrderID]:B"]))Several sites or tags sharing one Autopilot profile or policy
Narrow with a second signal (AND)tag rule -and (device.deviceOSType -eq "Windows")A tag is reused for more than one purpose and needs disambiguation
Prefix family(device.devicePhysicalIds -any (_ -startsWith "[OrderID]:Cust-"))A naming convention already encodes the grouping you need
Exclude a tagtag rule -and (device.devicePhysicalIds -all (_ -ne "[OrderID]:X"))Rare: prefer a static exclusion group over baking this into the rule
Pattern or region match(device.devicePhysicalIds -any (_ -match "^\[OrderID\]:EU-.*-HQ$"))Naming convention is not a simple prefix, and -startsWith/-in cannot express it

Keep It Simple: Maintenance Is the Real Cost

Every clause you add to a dynamic membership rule is something a future admin has to understand first. That admin might be you, in eight months, with no memory of why the clause is there. They need to understand it before they can safely rename a tag, add a site, or debug why a device landed in the wrong group. A five-line rule with three -or branches and a -match regex is not hard to write once. It is hard to maintain forever.

A few habits pay for themselves over time:

  • Document intent next to the group. Do not rely on a wiki nobody opens. A one-line description on the group itself, like “all EU retail HQ devices, tag prefix EU-Retail-HQ”, saves the next person from reverse-engineering the regex.
  • Prefer several well-named, simple groups over one clever mega-rule. Dynamic group evaluation is fast enough at typical fleet sizes. So “more groups” rarely costs you anything real. Readability is worth more than cleverness here.
  • Re-validate exclusion rules periodically, especially for the -any versus -all mistake above. It is easy to copy an old rule forward without checking whether it still does what it says.

Do These Queries Work for Autopilot v2?

Everything above is specific to Autopilot v1. A device’s group tag lives on devicePhysicalIds, and dynamic membership rules route devices to profiles. Autopilot v2 (device preparation) skips this model entirely. Instead, the device preparation policy groups devices at enrollment time, through its own account or group assignment during OOBE. No group tag exists for a dynamic rule to pattern-match afterward. If you are building a new v2 rollout, do not replicate these group-tag patterns. Use enrollment-time grouping as designed instead. The dynamic-rule patterns in this post apply to v1 only.

Frequently asked questions

How do I match multiple Autopilot group tags in one dynamic group?

Use the -in operator inside -any on devicePhysicalIds, for example (device.devicePhysicalIds -any (_ -in [‘[OrderID]:HQ’,‘[OrderID]:Branch01’])). You can also chain separate conditions with -or, but -in is shorter and easier to extend once you have more than two or three tags.

How do I exclude an Autopilot group tag from a dynamic group?

Use -all with -ne, for example (device.devicePhysicalIds -all (_ -ne ‘[OrderID]:Decommissioned’)), combined with your inclusion rule via -and. Do not use -any with -ne: devicePhysicalIds holds many values per device, so at least one value is almost always not equal to the excluded tag and the rule silently matches everyone.

Why does -any (_ -ne ‘X’) match almost every device?

devicePhysicalIds is a multi-valued property that contains the serial number, hardware hash reference, ZTDID and the group tag. -any (_ -ne ‘X’) asks whether at least one value is not X, which is true for nearly every device. -all (_ -ne ‘X’) asks whether every value is not X, which is the actual exclusion.

How do I match all Autopilot tags that start with the same prefix?

Use -startsWith, for example (device.devicePhysicalIds -any (_ -startsWith ‘[OrderID]:Cust-’)). This is useful for a master group that applies a baseline profile or policy to every tag in a naming family while each tag keeps its own dynamic group and Autopilot profile.

Do these dynamic group queries work for Autopilot v2?

No. They apply to Autopilot v1 only, where the group tag is stored on devicePhysicalIds. Autopilot v2 (device preparation) groups devices at enrollment time through its own policy assignment, so no group tag exists for a dynamic rule to match afterwards.

Related posts

Autopilot Hash Upload without Admin Role
Microsoft Intune

Autopilot Hash Upload without Admin Role

Upload an Autopilot hash to Intune without administrator rights. This allows you to use RBAC and still let users upload new hashes.

Windows Autopilot Intune
Intune Starter Series

Windows Autopilot: Overview and Setup

Set up Windows Autopilot in Intune: register devices via Intune or PowerShell, choose Entra ID or hybrid join, create a profile and configure the ESP.

Autopilot Registration automated
Microsoft Intune

Windows Autopilot Registration with App registration

Windows Autopilot Hast collection / registration simplified and easy via PowerShell, user interaction and/or GPO.