scloud by Florian Salzmann
ende

Advanced Dynamic Autopilot Group Queries for Production (Autopilot v1)

Table of Contents

Open Table of Contents

Why the Single-Tag Rule Stops 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. 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.

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.

Match 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.

Exclusions and 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.

A Note on 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.

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

Microsoft Intune - Unlock the full potential of Microsoft Intune with our comprehensive Starter Series. Explore in-depth guides and tips for seamless device management and security.

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.