Group membership and ownership remains an important concept in Microsoft 365, as it relates to both ensuring effective collaboration between your users and controlling access to documents and features. Thus, finding an answer to the “give me a list of all groups user X is a member of” is a common task, and in fact one we’ve explored at length, in numerous articles on this blog. Recently, we provided an updated version of a PowerShell script to generate a report of each user’s group membership, as well as an updated version of the script to remove a given user from all groups.
In some cases however, you don’t need a full blown script, and the task can be done via a quick one-liner instead. Which is what we will cover in the current article, presenting you an updated list of a series of PowerShell cmdlets you can use to quickly list all groups a given user (or object in general) is a member of. The examples will reflect the latest changes in the service and address incoming deprecations, and will hopefully continue to be useful in the years to come.
Using the Exchange Online cmdlets to get group membership
Exchange Online PowerShell continues to offer an easy method to report on group membership, thanks to the support for server-side filtering and the Members property. The example below can be used to give you a list of all groups a given user is a member of:
Get-Recipient -Filter "Members -eq 'CN=user,OU=tenant.onmicrosoft.com,OU=Microsoft Exchange Hosted Organizations,DC=EURPR03A001,DC=prod,DC=outlook,DC=com'"
Compared to the last time we covered the same topic, the output of the cmdlet above will also include any Microsoft 365 Groups the user is a member of, thus you no longer need to do separate queries to cover them. On the downside, the server-side filter query still requires us to use the rather long DistinguishedName property’s value, which is certainly not as convenient as a simple alias or email address value. Then again, we don’t actually have to type it, but use the following instead:
$dn = Get-User vasil | select -ExpandProperty DistinguishedName Get-Recipient -Filter "Members -eq '$dn'"
Another caveat is that being an Exchange Online cmdlet, Get-Recipient will only return groups that Exchange Online is aware of. That is to say, “pure” Azure AD security groups will not be listed in the output above, even if the user is a member. Although Microsoft seems to be slowly ramping up support for such groups, we still do not have any means of reporting on them via “pure” Exchange Online cmdlets. And this goes both ways – Azure AD in turn does not cover all Exchange Online recipient types, so if you want to get the full picture, you need to query both.
With the above in mind, you can actually get more out of Exchange Online, by leveraging the Get-Group cmdlet instead of Get-Recipient. Said cmdlet also supports server-side filters based on the Members property, so the syntax remains the same. The output however will also include group objects that are not recognized as valid Exchange Online recipients, such as Role Groups. And since Role Groups are commonly used to grant access to certain admin functionalities, you might as well include them in the report. Here’s how:
$dn = Get-User vasil | select -ExpandProperty DistinguishedName Get-Group -Filter "Members -eq '$dn'"
Another scenario to cover in Exchange Online is membership of Dynamic Distribution Groups. Unfortunately, server-side filters do not support the Members property in this scenario, so in order to include Dynamic DGs in the output, we have to expand the membership of each DDG individually. The recently introduced Get-DynamicDistributionGroupMember cmdlet can help here, but the experience will be nowhere near that fast as using a server-side filter. For the sake of completeness, here’s how to return all Dynamic Distribution Groups a given user is a member of:
Get-DynamicDistributionGroup | ? { Get-DynamicDistributionGroupMember $_.ExchangeObjectId -ResultSize Unlimited | ? {$_.EmailAddresses -match 'user@domain.com'} }
Lastly, you can also leverage the REST-based Exchange Online cmdlets, introduced in the V2 module. Here’s an example on how to get all groups a given user is a member of via the Get-ExOMailbox and Get-ExORecipient cmdlets:
$dn = Get-EXOMailbox vasil | select -ExpandProperty DistinguishedName Get-ExORecipient -Filter "Members -eq '$dn'"
Using the Graph SDK for PowerShell cmdlets to get group membership
As mentioned above, while the Exchange Online cmdlets offer a quick way to cover group membership, they do not account for all possible group types. Most importantly, Azure AD security groups will be missing from the output, and to cover those, we need to use Azure AD cmdlets. Since both the MSOnline and Azure AD modules are getting deprecated soon, the examples below will use the Graph SDK for PowerShell cmdlets.
The good news is that we can also leverage server-side queries with the Graph cmdlets. For example the Get-MgUserMemberOf cmdlet will give us a list of all objects (not just groups!) a given user is a member of. Similarly, the Get-MgUserTransitiveMemberOf cmdlet will return objects for which the user is an indirect member (for example, nested groups). While both these methods are great and quite useful, the cmdlets themselves leaves a lot to be desired. Not only we have zero support for the pipeline, but the default formatting is useless and requires some (or a lot of) tinkering in order to surface “human-readable” values. You might also need to clear some unwanted entries, such as administrative units. Or maybe not, if you would like the full picture.
Anyway, without further complaints, here are some examples you can leverage.
#Get a list of all AAD objects a given user is a member of Get-MgUserMemberOf -UserId user@domain.com #Get a human-readable list of all AAD objects a given user is a member of Get-MgUserMemberOf -UserId user@domain.com -Property id,displayName | select @{n="Name";e={$_.AdditionalProperties.displayName}},id #Get a human-readable list of Azure AD groups a given user is a member of $res = Get-MgUserMemberOf -UserId user@domain.com -DirectoryObjectId 'microsoft.graph.group' -Property id,displayName $out = @(); $res.AdditionalProperties["value"] | % {$out += [pscustomobject]@{"Id" = $_["id"];"DisplayName" = $_["displayName"]}} $out #Get a human-readable TRANSITIVE list of Azure AD groups a given user is a member of $res = Get-MgUserMemberOf -UserId user@domain.com -DirectoryObjectId 'microsoft.graph.group' -Property id,displayName $out = @(); $res.AdditionalProperties["value"] | % {$out += [pscustomobject]@{"Id" = $_["id"];"DisplayName" = $_["displayName"]}} $out
Since using a server-side filter for Groups in the above examples unnecessary complicates the output, here are also some examples on how to do the filter client-side. Or you can ignore this part altogether and go over the full object list, as you might need to address removals from Azure AD roles or AUs as well.
#Get a human-readable list of Azure AD groups a given user is a member of Get-MgUserMemberOf -UserId user@domain.com -Property id,displayName | ? {$_.AdditionalProperties.'@odata.type' -eq "#microsoft.graph.group"} | select @{n="Name";e={$_.AdditionalProperties.displayName}},id Get a human-readable list of Azure AD groups a given user is a member of Get-MgUserTransitiveMemberOf -UserId user@domain.com -Property id,displayName | ? {$_.AdditionalProperties.'@odata.type' -eq "#microsoft.graph.group"} | select @{n="Name";e={$_.AdditionalProperties.displayName}},id
When it comes to covering Azure AD groups with Dynamic membership rules, no special treatment is needed and you can use the above examples. The reason being, dynamic membership works a bit different in Azure AD compared to Exchange Online, although with the latest changes the two are getting closer I suppose. And while we are on the subject of dynamic groups, it’s worth reiterating that the Graph SDK cmdlets we used in the examples from this section will not cover membership of Exchange-specific objects, such as Dynamic Distribution Groups.
What about Microsoft Teams?
While technically the answer to the “which Teams a given user is a member of” question is contained within the examples we explored above (as each Microsoft Team has an underlying Microsoft 365 Group to power its membership), in some cases you might be interested in getting this answer without any additional “noise”. So here’s how to get a list of all user’s Teams, the simple way:
#Use the Graph SDK for PowerShell to list all user's Teams: Get-MgUserJoinedTeam -UserId user@domain.com #Use Exchange Online PowerShell to list all user's Teams: $dn = Get-user vasil | select -ExpandProperty DistinguishedName Get-UnifiedGroup -Filter "Members -eq '$dn' -and ResourceProvisioningOptions -eq 'Team'"
Getting a list of object the user is an owner for
Apart from reporting on membership, there are scenarios where you want to know which objects are “owned” by a given user. In Exchange Online, that translates to covering the ManagedBy property, although technically this is not the same thing as the Owner property. Regardless, here are some examples you can use on Exchange side:
#List all objects a given user is an owner for Get-Recipient -Filter "ManagedBy -eq 'CN=user,OU=tenant.onmicrosoft.com,OU=Microsoft Exchange Hosted Organizations,DC=EURPR03A001,DC=prod,DC=outlook,DC=com'" #As above, but without having to type in the DistinguishedName, and using the REST cmdlets $dn = Get-EXOMailbox vasil | select -ExpandProperty DistinguishedName Get-ExORecipient -Filter "ManagedBy -eq '$dn'"
To achieve the same via the Graph SDK for PowerShell, we can leverage the Get-MgUserOwnedObject cmdlet. As usual, the output will be a total mess, so you might want to transform it a bit.
#Get a human-readable list of all AAD objects a given user is an owner of Get-MgUserOwnedObject -UserId user@domain.com -Property id,displayName | select @{n="Name";e={$_.AdditionalProperties.displayName}},id
Some device-related scenarios
One additional set of scenarios you might be interested revolves around devices. In particular, we are interested in devices in Azure AD, not the Exchange mobile devices, as the former are the ones relevant to services such as Intune. Here, we can distinguish between Azure AD registered devices and Azure AD joined ones, and report on each type. The example below leverages the Get-MgUserRegisteredDevice cmdlet to return a human-readable list of devices registered to a user:
#List all devices registered to a given user Get-MgUserRegisteredDevice -UserId user@domain.com -Property id,displayName | select @{n="Name";e={$_.AdditionalProperties.displayName}},id
If you want to report on Azure AD joined devices owned by a given user, use the cmdlet Get-MgUserOwnedDevice instead:
#List all devices owned by a given user Get-MgUserOwnedDevice -UserId user@domain.com -Property id,displayName | select @{n="Name";e={$_.AdditionalProperties.displayName}},id
Lastly, you might want to obtain a list of all groups a given device is a member of. Yes, devices can also be members of Azure AD groups now, and there are legitimate scenarios that involve them. We can use the Get-MgDeviceMemberOf and/or the Get-MgDeviceTransitiveMemberOf cmdlet to obtain a list of groups a given device is a member of:
Get-MgDeviceMemberOf -DeviceId 12345678-1234-1234-1234-6748e45b2c49 | select @{n="Name";e={$_.AdditionalProperties.displayName}},id
As an added bonus, here’s how to list all devices NOT a member of any Azure AD group:
Get-MgDevice | ? {!(Get-MgDeviceMemberOf -DeviceId $_.Id)} | select Id,DisplayName
Let me know if you are interested in any other scenarios with regards to Group membership!
Great article as always! I learned a lot from your site. One quick question about reporting user devices. Do you know the difference between the cmdlets “ Get-MgUserRegisteredDevice” and “ Get-MgUserManagedDevice”? They seems return similar results but the latter provides more information. Is it better?
One lists Azure AD registered devices, the other devices managed by Intune.