Windows Autopilot

Fun with Windows Autopilot Group Tags

This one falls squarely in the “I can’t believe I’m doing another blob about group tags” category.  Past examples:

And of course there is some documentation around this too, e.g. in the Intune docs.  So what more can possibly be said?  Well, here’s one area to address tied to questions like this:

When will you add support for more than one group tag?

To this I would say “never” and “we already do” at the same time.  We have no plans to ever add another group tag field, but at the same time, it’s already possible for you to do what you want with just the one field, because you can put anything you want in it.

So let’s look at an example.  I have five devices with group tags assigned:


Let’s say then that each of those values are meaningful:

  • S001 and S002 are school IDs
  • C001 and C002 are classroom IDs within each of those schools
  • Admin indicates that the user should get admin rights (e.g. a teacher or administrator)

As you can see, I’ve encoded multiple pieces of information into one group tag.  If I look in Azure AD, using Graph Explorer to query for “” (since every Autopilot device has an associated Azure AD device object), I can find these in the resulting list:


See where that group tag value is stashed?  It’s in a multi-valued string property called “physicalIds” where each string is prefixed with an identifier, “[OrderId]” in this case.  (You can find more details about that in my previous blog post.)  Alright, but why does this matter?  Because Azure AD dynamic groups will let you do matching based on that string.  Notice from a previous example that you can do equality matches:

(device.devicePhysicalIds -any _ -eq “[OrderID]:Whatever you want”)

But what about partial matching?  That’s where the Azure AD documentation comes in handy, talking about the “-match” and “-startsWith” operators.  Here are some examples.

All devices for School 1:

(device.devicePhysicalIds -any _ –startsWith “[OrderID]:S001”)

All devices for School 2:

(device.devicePhysicalIds -any _ -startsWith “[OrderID]:S002”)

All devices for School 1 Class 1:

(device.devicePhysicalIds -any _ -startsWith “[OrderID]:S001-C001”)

All devices for School 1 Class 2:

(device.devicePhysicalIds -any _ -startsWith “[OrderID]:S001-C002”)

All devices for School 2 Class 1:

(device.devicePhysicalIds -any _ -startsWith “[OrderID]:S002-C001”)

All classes for Class 1 (in either school):

(device.devicePhysicalIds -any (_ -match “^\[OrderID\]:S.*-C001.*$”))

All admin devices (any school or class):

(device.devicePhysicalIds -any (_ -match “^\[OrderID\]:.*-Admin$”))

Not surprisingly, I created a dynamic group for each one of these.


The basic process for each is the same:

  1. From the Azure portal, Azure AD tenant, All groups list, click “+ New Group.”
  2. Specify “Security” for the group type, an appropriate name, and “Dynamic Device” as the membership type.
  3. Click the link to “Add dynamic query.”
  4. Toward the right of the gray “Rule syntax” box, click “Edit.”
  5. Paste in one of the queries above, or construct your own.
  6. Click OK, then Save.

It can take Azure AD a little while to calculate the group membership, so be a little patient.  Then you can check each group to see how many members (see the icon toward the bottom right with a number next to it, click on each image to enlarge if needed):


Now if you were really paying attention above, you would have noticed that I slipped in two nasty examples using -match.  If you’re a fan of regular expressions, you’ll recognize those strings, e.g.:


Let me try to decipher that:

  • The line should start with the literal string “[OrderID]:S” (the backslashes escape the brackets, which are otherwise treated as special characters).
  • It should then have any number of characters.
  • Then it should have the literal string “-C001”.
  • That can be followed by any number of characters (remember the “-Admin” example above?).
  • And that should be the end of the string.

Perfectly obvious, right?  (And if you’re a fan of regular expressions, there’s something wrong with you.)  But it does go to show that you can put in any random string you want (e.g. “Every good fox does fine”) and build groups off of any part of the string.

Now, whether any of this is really a good idea is a separate debate.  Keeping it simple is best.  You should have a very small number of Autopilot profiles (e.g. one or two).  And at some point, it will be easier to manually put devices into Azure AD groups than it is to create dynamic groups after manually edited devices to put the appropriate group tag on them.  So don’t get carried away. (There are certainly valid reasons to have more than one or two – there’s no technical limitation. But don’t think you need one Autopilot profile per dynamic group. There’s never a reason to have multiple identical profiles.)

5 replies »

  1. Hi Michael – your work here is very useful and thanks for sharing – however is this query formatted correctly above?

    (device.devicePhysicalIds -any (_ -match “^\[OrderID\]:S.*-C001.*$”))”)

    seem to be an extra ” and )

    thanks for posting


  2. Hi Michael, I few quick questions.
    I notice that there are a lot of walkthroughs using operator “any” followed by additional operator items in the value section.
    At least that’s how the gui places it in the top part above the editor in the portal.
    At first I assumed it was a workaround but seeing that you are also using it this way makes me think that this should be the correct method. Is there anyway you can give a quick explanation on this?

    Also would you be able to give more info on using the -match command? I have been trying to use it as you have it shown and it doesn’t not allow me to save it.

    I am wanting to use partial searches form our order ids but so far it is only working using contains which is not a good idea.

    (device.devicePhysicalIds -any “_ -contains “[ZTDId] “) and (device.devicePhysicalIds -any _ -contains “[OrderId]:America”) or (device.devicePhysicalIds -any _ -contains “[OrderId]:notebooks”) or (device.devicePhysicalIds -any _ -notContains “[OrderId]:Desktops”)

    any assistance with the above would be greatly appreciated.


    • The GUI does a very poor job of showing “any” queries – you will always need to use the edit button to edit the rule text directly. The “match” queries are working for me (although I did have a typo in one of them that I fixed yesterday – I had an extra quote and parenthesis at the end). The key points with those: The parenthesis after -any are important (and watch out for smart quotes, WordPress makes in nearly impossible to copy and paste without turning quotes into smart quotes): (device.devicePhysicalIds -any (_ -match “^\[OrderID\]:.*-Admin$”))

      Liked by 1 person