Graph API adds support for transitive membership queries

In other words, you no longer need to run recursive queries and enumerate the membership of all groups in order to display a list of groups for which a particular user is a member of. Similarly, you can use a simple query to get a “flattened” list of members for a particular group, with the membership of all nested groups expanded. Let’s check some examples.

First of all, in order to use the examples below, you must have an Azure AD application registered with the Directory.Read.All, User.Read.All permissions at minimum. If you don’t know how to do that, I suggest you start by reviewing the documentation on how to get an access token for the Graph, or use the Graph explorer tool instead to run the examples from this article.

List all Groups a given user is a member of

In this example, we will be using the transitiveMemberOf method to query the Graph for all group objects a given user is a member of. The request is very simple, all you need to provide is the user identifier (UPN will do just fine) and the Access Token you’ve obtained via the application we mentioned above.

$MemberOf = Invoke-WebRequest -Headers $AuthHeader -Uri ""

The query will return a JSON object, which you can convert to an array via the following:

$result = ($MemberOf.Content | ConvertFrom-Json).Value

Then, you can simply review the results, filter them out, export them to CSV or whatever. For example, I can list only the Admin groups I’m a member of by filtering on the “#microsoft.graph.directoryRole” entity:

$result | ? {$_.'@odata.type' -eq '#microsoft.graph.directoryRole'} | select DisplayName,id

displayName                   id                                  
-----------                   --                                  
Privileged Role Administrator 81705206-0da5-4ae7-a615-277fad156754
Company Administrator         c25d133f-4944-481a-84d2-6e41d6a101f4
Security Administrator        9eae26d0-29b0-49e8-834d-5025c8542618

To list all Security, Office 365, and Mail Enabled Security and Distribution groups, you can use the following filter:

$result | ? {$_.'@odata.type' -eq ''} | select DisplayName,Mail,GroupTypes,MailEnabled,SecurityEnabled

displayName   mail                                 groupTypes          mailEnabled securityEnabled
-----------   ----                                 ----------          ----------- ---------------
domaintest    {Unified}                  True           False
DG              {}                         True           False
Enabled Users                                      {DynamicMembership}       False            True
Default                 {Unified}                  True           False
USG                       {}                         True            True
Unified                  {Unified}                  True           False

Since there might be a lot of groups returned, you can just export the results to CSV file and review/filter them in Excel:

$result | ? {$_.'@odata.type' -eq ''} | Export-CSV -nti MemberOf.csv

The list will also include any groups with Dynamic membership rules, however it will *not* include Exchange Dynamic Distribution Groups and other object types not recognized by Azure AD. So, here’s the place to remind you about the PowerShell script I released recently to generate a report of given users’ group membership via the Exchange cmdlets, which you can download from the GitHub repo.

And that’s pretty much all there is to it. For additional details and code samples, review the official documentation articles on users, groups, devices and service principals (once they become available that is).

In case you are having a hard time with the process of registering an application, obtaining a token and so on, you can simply open the Graph explorer tool instead and paste the URI I user in the first example above. Here’s how it will look like:

Graph explorer example of transitive Membership

List all members of a given group recursively

The other new method allows us to perform the “opposite” operation: given a group, expand all its membership, including the membership of any nested groups, and return a “flattened” list of members. The method in question is transitiveMembers, and the usage is very similar to the transitiveMemberOf method described above. This time we simply need to use the /groups endpoint and provide a valid identifier for the group in question, say its objectID.

$Members = Invoke-WebRequest -Headers $AuthHeader -Uri ""
$result = ($Members.Content | ConvertFrom-Json).Value

$result | group '@odata.type' | select Name,Count

Name                        Count
----                        -----
#microsoft.graph.user           5          2
#microsoft.graph.orgContact     1

So, the group we run this query for has a total of 8 members, 5 of which are Users, 1 is a Contact, and there are 2 nested groups. Personally, when I run such a query I don’t care at all about any group entries, I simply want to know how many users a given change or email will affect. Thus, we can filter out any groups from the results obtained above, which also has the added benefit of making the output a bit easier to handle.

$result | ? {$_.'@odata.type' -ne ''} | select DisplayName,userPrincipalName,Mail,AccountEnabled

displayName   userPrincipalName                               mail                        accountEnabled
-----------   -----------------                               ----                        --------------
HuKu                      True
Marian Marian                                               True
Vasil Michev                                          True
Vasil Michev                                       True
SfBUser                                                   True

As with the transitiveMemberOf method, we cannot use this against Dynamic distribution groups or any other type of group not recognized by Azure Ad. For additional details and examples, refer to the official documentation.

And in case you want to take the easy road and run this in the Graph explorer, here’s how the query should look like there:

Graph explorer example transitiveMembers

3 thoughts on “Graph API adds support for transitive membership queries

  1. carbo-will says:

    This functionality appears to be available and functioning via Does this mean that is no long a beta feature and is now officially supported through the non-beta URL?

    As a side note, it would be great to have support for the $filter parameter because the unfiltered results can get rather large. For example, it would be great if this worked:$filter=startswith(displayName,’D’)

    1. Vasil Michev says:

      The general rule is if it’s moved to /1.0, it’s GA. And don’t get me started on filtering with Graph, there’s sooooo much room for improvement… :/


Leave a 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.