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:
- Filter Operations
- 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
In your Summary for solution you’ve got the one that doesn’t work instead of the one with Where-Object
Good catch! Post has been updated.
Thank you.
Thanks for writing this up, the same thing is also happening to Azure CLI.
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.
when I run Get-AzureADDomainNameReference -name domain.com I can only retrieve 300 objects. Is there a way to bypass that limit ?
Based on documentation
Get-AzureADDomainNameReference
have only-Name
and CommonParameters (-Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable
).There is a MS Graph cmdlet called Get-MgDomainNameerenceByRef (which maps directly to Get-AzureADDomainNameReference.) and it has
-PageSize
parameter.Hope it helps.