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 "https://graph.microsoft.com/beta/users/vasil@michev.info/transitiveMemberOf"
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 '#microsoft.graph.group'} | select DisplayName,Mail,GroupTypes,MailEnabled,SecurityEnabled displayName mail groupTypes mailEnabled securityEnabled ----------- ---- ---------- ----------- --------------- domaintest testdomain@michev.onmicrosoft.com {Unified} True False DG DG@michev.onmicrosoft.com {} True False Enabled Users {DynamicMembership} False True Default default2@michev.info {Unified} True False USG USG@michev.info {} True True Unified Unified@michev.info {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 '#microsoft.graph.group'} | 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:
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 "https://graph.microsoft.com/beta/groups/c91cd116-a8a5-443b-9ae1-e1f0bade4a23/transitiveMembers" $result = ($Members.Content | ConvertFrom-Json).Value $result | group '@odata.type' | select Name,Count Name Count ---- ----- #microsoft.graph.user 5 #microsoft.graph.group 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 '#microsoft.graph.group'} | select DisplayName,userPrincipalName,Mail,AccountEnabled displayName userPrincipalName mail accountEnabled ----------- ----------------- ---- -------------- HuKu HuKu@michev.onmicrosoft.com HuKu@michev.onmicrosoft.com True Marian Marian marian_ski_outlook.com#EXT#@michev.info True Vasil Michev vasil@michev.info vasil@michev.info True Vasil Michev vasil.michev_quadrotech-it.com#EXT#@michev.info True pesh0 SfBUser SfBUserSatya@michev.onmicrosoft.com 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:
This functionality appears to be available and functioning via https://graph.microsoft.com/v1.0/me/transitiveMemberOf. 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:
https://graph.microsoft.com/v1.0/me/transitiveMemberOf?$filter=startswith(displayName,’D’)
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… :/
Excellent!