SOLVED

See groups menbership of a list of users

Copper Contributor

Hello,

I'm trying to get a list from all groups that a user is part of, on a single file (DL, Teams groups, ...)

 

This is what I'm doing:

 

Connect-AzureAD

$Users=Get-Content -Path "C:\Temp\Test\Test.txt" #List the upn of the users i need to see what groups they are member of
 
$GroupsMembers=forEach($user in ($Users))
{Get-AzureADUser -SearchString $user | Get-AzureADUserMembership | % {Get-AzureADObjectByObjectId -ObjectId $_.ObjectId | select DisplayName,ObjectType,MailEnabled,SecurityEnabled,ObjectId,UserPrincipalName}}

$GroupsMembers | Export-CSV "C:\Temp\Test\Groupstest.CSV" -NoTypeInformation -Encoding Unicode
 
This way i can get a list of groups but i can´t see the username of the users i imported ant their menbership.
Catarinagm_0-1661164227355.png

 

What am i doing wrong?

Thank you.

8 Replies

@Catarinagm 

 

Basically, you need to capture the user's details such as id, userPrincipalName, etc. in the outer loop, before proceeding to the inner loop to fetch the group/object details.

 

I started earlier with one answer but decided to switch Azure modules (to a more "recent" one) and have a play. The example below is the result, where it pulls any kind of membership - not just groups. I've also added a flag called IsDynamicGroup since that's usually something you want to know when figuring out how someone ended up in (or out) of scope for a given group.

 

The key additional inclusion from testing in my tenant is that it pulls Azure AD role memberships - which is quite nifty to know when working with PIM-style auditing. You can always filter this out client-side (or just take it out of the script altogether if you like.)

 

For context, this script uses the following first-party Microsoft Azure modules:

 

  • Microsoft.Graph.Authentication
  • Microsoft.Graph.DirectoryObjects
  • Microsoft.Graph.Users

 

It uses the Graph beta endpoint, which you can select using the following command:

 

Select-MgProfile -Name "beta";

 

Example script

Get-Content -Path "D:\Data\Temp\Test.txt" |
    ForEach-Object { Get-MgUser -UserId "$_" -ExpandProperty TransitiveMemberOf -Select Id, UserPrincipalName, TransitiveMemberOf } |
        ForEach-Object {
            # We're now in your per user "outer" loop.
            $User = $_;

            $User.TransitiveMemberOf.Id |
                ForEach-Object {
                    # Whereas now, we're in your per user membership inner loop. Here, we're iterating through each one of their memberships and pulling the Azure AD object details below.

                    Get-MgDirectoryObject -DirectoryObjectId "$_" |
                        ForEach-Object {
                            # This is yet another nested level we reach after having pulled the object for which the user is a member.
                            
                            [PSCustomObject] @{
                                UserId = $User.Id;
                                UserPrincipalName = $User.UserPrincipalName;
                                ObjectId =  $_.id;
                                ObjectType = $_.AdditionalProperties["@odata.type"].Split('.')[-1];
                                ObjectName = $_.AdditionalProperties["displayName"];
                                MailEnabled = $_.AdditionalProperties["mailEnabled"];
                                SecurityEnabled = $_.AdditionalProperties["securityEnabled"];
                                IsDynamicGroup = if ($null -ne $_.AdditionalProperties["groupTypes"]) { $_.AdditionalProperties["groupTypes"].Contains("DynamicMembership") } else { $null };
                            }
                        }
                }
        } |
        Export-Csv -Path "D:\Data\Temp\test.csv" -NoTypeInformation -Encoding Unicode;

 

Example output (having removed the Export-Csv in order to see this)

LainRobertson_0-1661171481418.png

 

Cheers,

Lain

@Catarinagm 

 

I also meant to add that you mentioned Exchange groups, however, if you use dynamic Exchange groups, you can't pull that from Azure AD. You'd need to use the Exchange Online module to fetch those and their memberships.

 

 

Cheers,

Lain

Thank you so much for your answer and help.
I used:
Connect-Graph -Scopes User.ReadWrite.All, Organization.Read.All
Select-MgProfile -Name "beta"

but still getting this privileges error:

Get-MgDirectoryObject : Insufficient privileges to complete the operation.
At line:13 char:21
+ Get-MgDirectoryObject -DirectoryObjectId "$_" |
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: ({ DirectoryObje...ndProperty = }:<>f__AnonymousType2`3) [Get-MgDirectoryObject_Get1], RestException`1
+ FullyQualifiedErrorId : Authorization_RequestDenied,Microsoft.Graph.PowerShell.Cmdlets.GetMgDirectoryObject_Get1
Get-MgDirectoryObject : Insufficient privileges to complete the operation.
At line:13 char:21

It never asked me for any tenant credentials.
PS C:\WINDOWS\system32> Get-MgDirectoryObject -DirectoryObjectId xxxx

Id DeletedDateTime
-- ---------------
xxx


I have the permissions to run it individually

@Catarinagm 

 

If your account has the necessary rights, you can just leave -Scopes out.

 

That said, you can run Connect-MgGraph as many times in a row as you like to change the -Scopes specification (i.e. you don't have to keep disconnecting and logging back on.)

 

If leaving -Scopes out doesn't work for you, then put it back in with the following scope:

 

  • Directory.Read.All

 

You don't really need write access for this script, and neither of the two scopes you provided are broad enough for running Get-MgDirectoryObject.

 

Cheers,

Lain

best response confirmed by Catarinagm (Copper Contributor)
Solution

@Catarinagm

 

Hi,

 

The easy answer is that you're trying to get the UserPrincipalName of the Group rather than the user, which returns null because the group doesn't have a UserPrincipalName.

As the variable $_. has already been overwritten by Get-AzureAdUsermembership you can't really reference back.

 

I have written a Powershell script that would do the job and shared it on Github:

https://github.com/Raindrops-dev/RAIN-TechCommunityContributions/blob/main/Get-AzureAdGroupMembershi...

 

Check it out and let me know if it fits your requirements.

 

EDIT: Apparently I was too slow and @LainRobertson already came with a more modern solution!

@raindropsdev Thank you so much it worked perfectly since i'm not used to use graph yet.
Lifesaver. Thank you!

@Catarinagm 

 

I'm happy my code was helpful!

 

That said, be aware that the AzureAd module will reach end of life at the end of this year, so it's strongly discouraged to put anything new in production using it.

 

To assist I've also prepared a Graph API version of that same script and updated the Azure AD with parameters to customize input and output paths and filename:

https://github.com/Raindrops-dev/RAIN-TechCommunityContributions/blob/main/Get-GraphGroupMembership....

https://github.com/Raindrops-dev/RAIN-TechCommunityContributions/blob/main/Get-AzureAdGroupMembershi...

 

Strong recommendation NOT to just use it as is though. Read it and understand why everything was one as is and learn it so you can do it yourself next time for the next task you need to do with MgGraph.

 

P.S: to find equivalent cmdlets you can use this table: https://docs.microsoft.com/en-us/powershell/microsoftgraph/azuread-msoline-cmdlet-map?view=graph-pow...

1 best response

Accepted Solutions
best response confirmed by Catarinagm (Copper Contributor)
Solution

@Catarinagm

 

Hi,

 

The easy answer is that you're trying to get the UserPrincipalName of the Group rather than the user, which returns null because the group doesn't have a UserPrincipalName.

As the variable $_. has already been overwritten by Get-AzureAdUsermembership you can't really reference back.

 

I have written a Powershell script that would do the job and shared it on Github:

https://github.com/Raindrops-dev/RAIN-TechCommunityContributions/blob/main/Get-AzureAdGroupMembershi...

 

Check it out and let me know if it fits your requirements.

 

EDIT: Apparently I was too slow and @LainRobertson already came with a more modern solution!

View solution in original post