Microsoft Graph: How To Filter Users by Phone Number

This blog post contains PowerShell examples on how to filter Entra ID users by a matching mobile or business phone number.

Sometimes, something simple as writing a filter query to search for Entra ID users which have a specific phone number can take way longer than we would like. Instead of making you search through numerous Stack Overflow posts or ask ChatGPT on how to do it, I decided to write this short blog post which will give you everything you need to quickly find Entra ID users which have a specific phone number.

Number Types

The most common numbers in Entra ID are Mobile phone and Business Phone. While the Entra ID admin portal displays Business phone in singular, the property is actually called businessPhones and is a string collection.

Phone numbers in Entra ID

Filter Queries

This page tells us which conditions are supported for which user properties. I wasn’t able to get this to work using Get-MgUser -Filter "Filter Query" since an API request using an advanced filter query will require a special header. Not long ago, I wrote about the Invoke-MgGraphRequest Cmdlet and how that can be used to call the Graph REST API directly using the session token from Connect-MgGraph.

To make it work, you’ll need to create a header with ConsistencyLevel: eventual which is then added to the Graph Request.

Since this is a web request, you’ll also need to url encode the + character. A simple replace with %2B will do the trick here. It’s important to note that this will only work if the phone number is stored on the Entra user exactly the same as what’s in the filter query. For example, it won’t work if the phone number is stored without spaces in Entra but you’re querying it with spaces. If your phone numbers in Entra are a mess, I recommend to take a look at this blog post. This will show you how you can format all phone numbers in the same, and most importantly in the correct way.

Here’s how to create the header.

1
2
3
$header = @{
    ConsistencyLevel = "eventual"
}

PowerShell Example

Here is an example script which contains all possible filter conditions for both mobile and business phone numbers. The important thing here is that the custom header is included and that the Uri includes the Count variable. Note that the businessPhones property does not support the in condition.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Connect-MgGraph

$mobileNumber = "+41 79 456 78 90"
$businessNumber = "+41 43 123 45 67"

$mobileNumberUrlSafe = $mobileNumber.Replace("+", "%2B")
$businessNumberUrlSafe = $businessNumber.Replace("+", "%2B")

$header = @{
    ConsistencyLevel = "eventual"
}

# Filter users by mobile phone number exact match (equals)
$mgUserMatchesMobilePhone = (Invoke-MgGraphRequest -Method Get -Uri "https://graph.microsoft.com/v1.0/users?`$filter=mobilePhone eq '$mobileNumberUrlSafe'&`$count=true" -ContentType "application/json" -Headers $header).value

# Filter users by business phone number exact match (equals)
$mgUserMatchesBusinessPhone = (Invoke-MgGraphRequest -Method Get -Uri "https://graph.microsoft.com/v1.0/users?`$filter=businessPhones/any(p:p eq '$businessNumberUrlSafe')&`$count=true" -ContentType "application/json" -Headers $header).value

# Filter users by mobile phone number not equals
$mgUserMatchesMobilePhone = (Invoke-MgGraphRequest -Method Get -Uri "https://graph.microsoft.com/v1.0/users?`$filter=mobilePhone ne '$mobileNumberUrlSafe'&`$count=true" -ContentType "application/json" -Headers $header).value

# Filter users by business phone number not equals
$mgUserMatchesBusinessPhone = (Invoke-MgGraphRequest -Method Get -Uri "https://graph.microsoft.com/v1.0/users?`$filter=not businessPhones/any(p:p eq '$businessNumberUrlSafe')&`$count=true" -ContentType "application/json" -Headers $header).value

# Filter users by mobile phone number starts with
$mgUserMatchesMobilePhone = (Invoke-MgGraphRequest -Method Get -Uri "https://graph.microsoft.com/v1.0/users?`$filter=startsWith(mobilePhone, '$mobileNumberUrlSafe')&`$count=true" -ContentType "application/json" -Headers $header).value  

# Filter users by business phone number starts with
$mgUserMatchesBusinessPhone = (Invoke-MgGraphRequest -Method Get -Uri "https://graph.microsoft.com/v1.0/users?`$filter=businessPhones/any(p:startswith(p,'$businessNumberUrlSafe'))&`$count=true" -ContentType "application/json" -Headers $header).value

# Filter users by mobile phone number greater or equal
$mgUserMatchesMobilePhone = (Invoke-MgGraphRequest -Method Get -Uri "https://graph.microsoft.com/v1.0/users?`$filter=mobilePhone ge '$mobileNumberUrlSafe'&`$count=true" -ContentType "application/json" -Headers $header).value

# Filter users by business phone number greater or equal
$mgUserMatchesBusinessPhone = (Invoke-MgGraphRequest -Method Get -Uri "https://graph.microsoft.com/v1.0/users?`$filter=businessPhones/any(p:p ge '$businessNumberUrlSafe')&`$count=true" -ContentType "application/json" -Headers $header).value

# Filter users by mobile phone number less or equal
$mgUserMatchesMobilePhone = (Invoke-MgGraphRequest -Method Get -Uri "https://graph.microsoft.com/v1.0/users?`$filter=mobilePhone le '$mobileNumberUrlSafe'&`$count=true" -ContentType "application/json" -Headers $header).value

# Filter users by business phone number less or equal
$mgUserMatchesBusinessPhone = (Invoke-MgGraphRequest -Method Get -Uri "https://graph.microsoft.com/v1.0/users?`$filter=businessPhones/any(p:p le '$businessNumberUrlSafe')&`$count=true" -ContentType "application/json" -Headers $header).value

# Filter users by mobile phone number in
$mobileNumberUrlSafe2 = "+41 79 456 78 91".Replace("+", "%2B")
$mgUserMatchesMobilePhone = (Invoke-MgGraphRequest -Method Get -Uri "https://graph.microsoft.com/v1.0/users?`$filter=mobilePhone in ['$mobileNumberUrlSafe','$mobileNumberUrlSafe2']&`$count=true" -ContentType "application/json" -Headers $header).value

Example Output

What’s a little weird is that PowerShell outputs an array with name/value pairs instead of a PS custom object.

Output from Invoke-MgGraphRequest

While it’s possible to access properties directly, it doesn’t work in Format-Table.

Format-Table does not work with specific properties

A simple trick to convert the array returned by Graph into a PS custom object I often use is to convert it to Json and than convert it from Json straight away again.

1
$mgUserMatchesMobilePhone = $mgUserMatchesMobilePhone | ConvertTo-Json | ConvertFrom-Json

Obviously, you can do the same for the $mgUserMatchesBusinessPhone variable. That will store and display the values as a proper PowerShell object.

There are no name/value pairs anymore.

Now you can work with the $mgUserMatchesMobilePhoneand $mgUserMatchesBusinessPhone as expected and do anything you want with them.

Bonus Tip (Power Automate)

The standard action Search for users (V2) from the Office 365 Users connector doesn’t support to filter for phone numbers of any type.

Supported filters for Office 365 Users

Luckily, there’s also a an action called Send an HTTP request within the Office 365 Users connector. This will allow you to make the same kinds of advanced requests to the Graph API as I’ve shown above with PowerShell.

Below is an example of a simple Power Automate Flow that can also filter for Entra ID users by phone number. What’s important is that you also need to add the ConsistencyLevel header and that you must remove all occurrences of the escape character ` from the Uri. Since Power Automate does not use $ to define variables, the escape character is not needed/supported here. The &$count=true also needs to be included at the end of the Uri.

Power Automate Flow to filter for users by phone number

Since this is just an example, the phone number used as the filter is simply stored in Compose Phone Number action. The next action, Compose Phone Number URL Safe will replace the + with %2B using an expression.

1
replace(outputs('Compose_Phone_Number'), '+', '%2B')

To extract the Display Name of the result, I’m using another compose action with the following expression.

1
outputs('Send_an_HTTP_request')?['body']?['value'][0]['displayName']

Example Output

If there is a user match for the phone number, Graph will return a nice Json response to Power Automate.

Output from Microsoft Graph within Power Automate

Summary & Download

I hope that these examples help you with whatever you’re trying to achieve. I’m certainly going to use this blog post as a cheat sheet for myself in the future. You can also download the entire script from my GitHub profile.

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Hosted on GitHub Pages
Built with Hugo
Theme Stack designed by Jimmy