Best Export Group Memberships in Microsoft Entra ID with PowerShell

By Ahmet Tolga KAYA 6 min read
Best Export Group Memberships in Microsoft Entra ID with PowerShell

Managing group memberships through the Microsoft Entra ID portal comes with a frustrating limitation: you are forced to check users individually. When auditing or reporting requires a complete list of every user and their assigned groups, the web interface simply cannot handle the request. This article will show you how to bypass this restriction and leverage PowerShell to easily extract and export all Microsoft Entra ID group memberships in bulk.

Install Microsoft Graph PowerShell

Before you start, you must Install the Microsoft Graph PowerShell module. Start Windows PowerShell as administrator and run the command below.

Install-Module Microsoft.Graph -ForceCode language: CSS (css)

Important: Always update to the latest Microsoft Graph PowerShell module version before you run a cmdlet or script to prevent errors and incorrect results.

Connect to Microsoft Graph PowerShell

Connect-MgGraph -Scopes "Directory.Read.All", "Sites.Read.All"Code language: CSS (css)

Download Export-GroupMemberships PowerShell script

To download the Export-GroupMemberships.ps1 PowerShell script, follow these steps:

  1. Create the folders temp and scripts in the (C:) drive if you don’t have them already
  2. Download the Export-GroupMemberships.ps1 PowerShell script
  3. Or copy the script below into Notepad and save it as Export-GroupMemberships.ps1 file
# Define parameters for the script
param (
    [Parameter(Mandatory = $true, HelpMessage = "Enter the User ID (e.g., email or object ID) of the Entra ID user", ParameterSetName = "SingleUser")]
    [string]$UserId,

    [Parameter(Mandatory = $false, HelpMessage = "Specify the path for the CSV output file")]
    [string]$CsvFilePath,

    [Parameter(Mandatory = $false, HelpMessage = "Enable to display results in Out-GridView")]
    [switch]$OutGridView,

    [Parameter(Mandatory = $false, HelpMessage = "Process all users in the tenant", ParameterSetName = "AllUsers")]
    [switch]$All
)

# Connect to Microsoft Graph with the required scopes
Connect-MgGraph -Scopes "User.Read.All", "Group.Read.All" -NoWelcome

# Initialize a List[Object] to store group details
$Report = [System.Collections.Generic.List[Object]]::new()

try {
    # Determine whether to process a single user or all users
    $UsersToProcess = if ($All) {
        Write-Host "Fetching all users from the tenant..." -ForegroundColor Cyan
        Get-MgUser -All -ErrorAction Stop
    }
    else {
        Write-Host "Fetching user: $UserId" -ForegroundColor Cyan
        Get-MgUser -UserId $UserId -ErrorAction Stop | Select-Object -First 1
    }

    # Check if any users were found
    if (-not $UsersToProcess) {
        Write-Host "No users found." -ForegroundColor Cyan
        return
    }

    # Iterate through each user
    foreach ($User in $UsersToProcess) {
        Write-Host "Processing group memberships for user: $($User.UserPrincipalName)" -ForegroundColor Cyan

        # Fetch the groups the user is a member of
        $EntraGroupMembers = Get-MgUserMemberOf -UserId $User.Id -All -ErrorAction SilentlyContinue

        # Check if the user is a member of any groups
        if (-not $EntraGroupMembers) {
            Write-Host "No group memberships found for user: $($User.UserPrincipalName)" -ForegroundColor Yellow
            # Add a record for users with no group memberships
            $GroupDetails = [PSCustomObject]@{
                UserId            = $User.Id
                UserPrincipalName = $User.UserPrincipalName
                DisplayName       = $User.DisplayName
                GroupId           = "N/A"
                GroupDisplayName  = "N/A"
                GroupEmail        = "N/A"
                SecurityEnabled   = "N/A"
                MailEnabled       = "N/A"
                GroupType         = "N/A"
                Source            = "N/A"
            }
            # Add the group details to the report list
            $Report.Add($GroupDetails)
            continue
        }

        # Iterate through each group membership
        foreach ($EntraGroup in $EntraGroupMembers) {
            # Extract group details from AdditionalProperties
            $AdditionalProperties = $EntraGroup.AdditionalProperties

            # Determine group type based on groupTypes, securityEnabled, and mailEnabled
            $GroupType = if ($AdditionalProperties.groupTypes -contains "Unified" -and $AdditionalProperties.securityEnabled) {
                "Microsoft 365 (security-enabled)"
            }
            elseif ($AdditionalProperties.groupTypes -contains "Unified" -and -not $AdditionalProperties.securityEnabled) {
                "Microsoft 365"
            }
            elseif (-not ($AdditionalProperties.groupTypes -contains "Unified") -and $AdditionalProperties.securityEnabled -and $AdditionalProperties.mailEnabled) {
                "Mail-enabled security"
            }
            elseif (-not ($AdditionalProperties.groupTypes -contains "Unified") -and $AdditionalProperties.securityEnabled) {
                "Security"
            }
            elseif (-not ($AdditionalProperties.groupTypes -contains "Unified") -and $AdditionalProperties.mailEnabled) {
                "Distribution"
            }
            else {
                "N/A"
            }

            # Create a custom object for the group details, including user information
            $GroupDetails = [PSCustomObject]@{
                UserId            = $User.Id
                UserPrincipalName = $User.UserPrincipalName
                DisplayName       = $User.DisplayName
                GroupId           = $EntraGroup.Id
                GroupDisplayName  = if ($AdditionalProperties.displayName) { $AdditionalProperties.displayName } else { "N/A" }
                GroupEmail        = if ($AdditionalProperties.mail) { $AdditionalProperties.mail } else { "N/A" }
                SecurityEnabled   = if ($AdditionalProperties.securityEnabled) { $AdditionalProperties.securityEnabled } else { "N/A" }
                MailEnabled       = if ($AdditionalProperties.mailEnabled) { $AdditionalProperties.mailEnabled } else { "N/A" }
                GroupType         = $GroupType
                Source            = if ($AdditionalProperties.onPremisesSyncEnabled) { "On-Premises" } else { "Cloud" }
            }
            # Add the group details to the report list
            $Report.Add($GroupDetails)
        }
    }

    # Output the results to the console (sorted by UserPrincipalName, then GroupDisplayName)
    $Report | Sort-Object UserPrincipalName, GroupDisplayName | Format-Table -AutoSize

    # Display results in Out-GridView if the switch is enabled
    if ($OutGridView) {
        $Report | Sort-Object UserPrincipalName, GroupDisplayName | Out-GridView -Title "Group Memberships Report"
    }

    # Export to CSV only if CsvFilePath is provided
    if ($CsvFilePath) {
        $Report | Sort-Object UserPrincipalName, GroupDisplayName | Export-Csv -Path $CsvFilePath -NoTypeInformation -Force
        Write-Host "Group memberships exported to $CsvFilePath" -ForegroundColor Cyan
    }
}
catch {
    # Handle errors (e.g., invalid UserId, insufficient permissions, or Graph API issues)
    Write-Host "An error occurred: $($_.Exception.Message)" -ForegroundColor Red
}Code language: PHP (php)

4. Save the Export-GroupMemberships.ps1 PowerShell script in the C:\scripts folder

Microsoft Entra ID

Export group memberships for single user

Export a list of group memberships for a single user with PowerShell.

C:\scripts.\Export-GroupMemberships.ps1 -UserId "<a href="mailto:[email protected]">[email protected]</a>"Code language: HTML, XML (xml)

The PowerShell output shows these results.

Microsoft Entra ID

Export a list of group memberships for a single user ([email protected]) to an Out-GridView.

C:\scripts\.\Export-GroupMemberships.ps1 -UserId "amanda.hansen@m365info.com" -OutGridViewCode language: CSS (css)
Microsoft Entra ID

Export a list of group memberships for a single user ([email protected]) to a CSV file.

C:\scripts\.\Export-GroupMemberships.ps1 -UserId "amanda.hansen@m365info.com" -CsvFilePath "C:\temp\GroupMemberships.csv"Code language: CSS (css)
Microsoft Entra ID

Run the PowerShell command below to export a list of group memberships for a single user ([email protected]) to an Out-GridView and CSV file.

C:\scripts\.\Export-GroupMemberships.ps1 -UserId "amanda.hansen@m365info.com" -OutGridView -CsvFilePath "C:\temp\GroupMemberships.csv"Code language: CSS (css)

Export group memberships for all users

To export a list of group memberships for all users in Microsoft 365, specify the CSV path in the PowerShell command below.

C:\scripts\.\Export-GroupMemberships.ps1 -AllCode language: CSS (css)

The PowerShell output shows these results.

Microsoft Entra ID

Export a list of group memberships for all users to an Out-GridView.

C:\scripts\.\Export-GroupMemberships.ps1 -All -OutGridViewCode language: CSS (css)

The Out-GridView appears.

Microsoft Entra ID

Export a list of group memberships for all users to a CSV file.

C:\scripts\.\Export-GroupMemberships.ps1 -All -CsvFilePath "C:\temp\GroupMemberships.csv"Code language: CSS (css)

Open the CSV file with an application like Microsoft Excel to see the results below.

Microsoft Entra ID

Export a list of group memberships for all users to an Out-GridView and CSV file.

C:\scripts\.\Export-GroupMemberships.ps1 -All -OutGridView -CsvFilePath "C:\temp\GroupMemberships.csv"Code language: CSS (css)

Read More: How To Install Microsoft Graph PowerShell Module

FAQ

Why can’t I export group memberships for all users at once directly from the Microsoft Entra ID portal?

The Microsoft Entra ID web interface is intentionally designed to display group memberships on a strict per-user basis for performance and data privacy reasons. Extracting a bulk report of all users and their respective groups inherently requires using Microsoft Graph PowerShell.

Which API permissions (Scopes) do I need to approve when connecting via Graph PowerShell?

To successfully read user objects and their associated group memberships, you must grant specific read permissions during your initial connection. You need to run Connect-MgGraph -Scopes "User.Read.All", "GroupMember.Read.All" to provide the necessary admin consent on the tenant.

Which specific PowerShell cmdlets drive this bulk export process in the background?

A standard export script relies on two primary cmdlets. First, it uses Get-MgUser to pull the complete list of users (fetching properties like UPN and DisplayName). Then, it processes each user through a loop using Get-MgUserMemberOf (or Get-MgGroup to resolve the objects) to gather their assigned memberships.

How is the data formatted in the CSV output for a user who belongs to dozens of groups?

Instead of creating a new row for every single group, the PowerShell script typically concatenates all group names into a single text string separated by semicolons (;) or commas (,). When you open the resulting CSV in Excel, each user occupies exactly one row, with all their memberships neatly compiled in a single “Groups” column.

Is it possible to filter the exported report to only show specific group types, such as Security Groups?

Yes, you can easily achieve this by adding a brief logical condition inside the script’s loop. By checking the GroupTypes or SecurityEnabled properties returned by the Get-MgUserMemberOf cmdlet, you can instruct the script to only write Security groups or Microsoft 365 groups to the final CSV file.

Ahmet Tolga KAYA

Systems Engineer and Technical Writer focused on Windows, Microsoft technologies, infrastructure, cybersecurity, automation, and platform reliability.