Get-AzureADUser -Filter Options Drama

Recently I found myself testing/researching filtering capabilities of Get-AzureADUser cmdlet.

This interest has been provoked by pretty simple and generic use case:
Get users from AzureAD with specific domain in UPN(User Principal Name).

Kudos to Stephen James (@Acidrs) for asking this question on MS Tech Community Forum and triggering me to writing this blog post.

First thoughts

The one might suppose that intuitively solution should be something like this:

Get-AzureADUser -Filter "userPrincipalName like '*@someemail.com'"

Well it is not working…

The next kind of subconscious/muscle memory approach would be Where-Object cmdlet:

Get-AzureADUser | Where-Object {$_.UserPrincipalName -like "*@someemail.com"}

This solution works BUT it is straightforward, cruel and not so elegant as it could be with -Filter. As it making a call to get all user object from AzureAD and then filters them locally.

Let’s take a step back and start from the beginning – The Documentation.

Documentation

Get-AzureADUser – cmdlet to get user object info from Azure Active Directory and is part of AzureAD PowerShell module.

Logically (and even intuitively) -Filter parameter was my first potential solution for our task.

It appears that -Filter is using an oData v3.0 filter statement.

When it comes to “filtering” oData v3.0 provides us with 2 options:

  1. Filter Operations
  2. Query Functions

Filter Operations consist of logical, arithmetic and grouping operators.

Because we are not trying to group or multiply our User objects – arithmetic and grouping operators are out of the picture. Logical operators are our next bet but unfortunately they do not contain like operator, so they will not help us in our case…

Our next option are Query Functions, which contain 4 categories: string, date, math and type functions. We can easily discard 3 out of 4 categories, as UPNs are not date, math or types. So let’s take a closer look to String functions.

We have 12 functions in total which can be categorized in 3 group by return type bool, int and string.

Trial and Error

Now let’s just start from the top of the list and try to filter User Principal Name using string functions.

#1 bool substringof(string searchString, string searchInString)

Sounds promising, substringof returns true if searchString contains in searchInString and return false otherwise. Let’s try:

Get-AzureADUser -Filter "substringof('someemail.com',UserPrincipalName)"

Well it is not working… No problem let’s move to the next one.

#2 bool endswith(string string, string suffixString) 

Looks OK, as it returns true if end of string contains specific value.

Get-AzureADUser -Filter "endswith(UserPrincipalName,'someemail.com')"

Unsupported Query. Neeeeext!

#3 bool startswith(string string, string prefixString)

Well it not exactly what we are looking for but let’s try it just for the sake of sanity.

Get-AzureADUser -Filter "startswith(UserPrincipalName,'jo')"

It works but doesn’t fit to our use case.

#4 int length(string string)

Does not help us at all, however it might be used in future as part of the possible solution, so let’s test if it is implemented:

#34 is the length of my UPN
Get-AzureADUser -Filter "length(UserPrincipalName) eq 34"

Well it was worth a shot…

#5 indexof(string searchInString, string searchString)
Get-AzureADUser -Filter "indexof(UserPrincipalName,'m365x886499.onmicrosoft.com') ne 0"

Unfortunately Unsupported Query again. Really bad that it is not supported as indexof is very powerful while working with string. It might looks like that it is not that important but believe me when you are chaining string filters together in a complex query indexof can be very helpful.

#6 string replace(string searchInString, string searchString, string replaceString)

On a first hand it doesn’t sound too useful for our use case. But we can be creative and come up with some creative solution here:

Get-AzureADUser -Filter "replace(UserPrincipalName,'someemail.com','Oct2020') eq adminOct2020"

OK, this type of query is not supported. Anyway it is not that trivial to use replace for filtering.

#7 string substring(string string, int pos)

Well I don’t think it will be useful for us as we previously identified and indexof is not supported, so we could not dynamically calculate starting position of our searchString. Let’s if it is implemented anyway.

Get-AzureADUser -Filter "substring(UserPrincipalName,10) eq a"

Hmm, not supported… Let’s try another one.

#8 string substring(string string, int pos, int length)

This is a overridden version of the substring function which has more parameters. With very little hope that it is implemented lets test it.

Get-AzureADUser -Filter "substring(UserPrincipalName,10,1) eq a"

Well this was expected. Let’s see what other options we have,

#9 string tolower(string string)
#10 string toupper(string string)
#11 string trim(string string)
#12 string concat(string string1, string string2)

All of the functions 9-12 will not help us with our use case.

Summary

Out of the all 12 filtering string functions available in oData Get-AzureADUser supports only 1 startswith.

And the solution is to get all users and then filter by specific domain.

Get-AzureADUser | Where-Object {$_.UserPrincipalName -like "*@someemail.com"}

Bonus

Shout out to Vasil Michev for providing different prospective and presenting alternate solution using Get-AzureADDomainNameReference.

Get-AzureADDomainNameReference -Name somedomain.com | where {$_.ObjectType -eq "User"}

Thanks a lot for reading.

Icons made by Freepik from www.flaticon.com

6 thoughts on “Get-AzureADUser -Filter Options Drama”

  1. Wow, that has to be one of the dumbest things I’ve seen from Microsoft. Thanks for explaining that only startswith works. I was banging my head against the wall for a couple of hours trying to get -Filter to work.

  2. when I run Get-AzureADDomainNameReference -name domain.com I can only retrieve 300 objects. Is there a way to bypass that limit ?

Leave a Reply to Jude Chen Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.