EASYENTRA Blog

News & Updates

How to Copy Group Membership Between Users in Microsoft Entra ID 

Copy paste group memberships, MS Graph, EXO, AD

If you’ve ever needed to set up a new employee with the same access as an existing team member, or transition responsibilities between users, you know the pain of manually adding someone to dozens of groups. It’s tedious, error-prone, and frankly, there’s got to be a better way. 

Well, there is, but it’s not quite as straightforward as you might hope. This blog walks you through how group membership copying actually works in Microsoft Entra ID, including the quirks you need to know about!

And, as an alternative, we’ll show you how to simply copy/paste AD, MS Graph, and Exchange Online group memberships between users! 

Let’s break it down. 

Not All Group Memberships in Microsoft 355 Can Be Copied With MS Graph 

Before attempting to copy group memberships, it’s important to understand that not all group types in Microsoft 365 behave the same way. Each group type has its own management model and technical limitations. 

Group Type  Can Copy? 
Microsoft 365 Groups  Yes 
 Security Groups  Yes 
Distribution Lists  Limited* 
Mail-Enabled Security  Limited* 
Dynamic Groups  No 
 Synchronized Groups  No 

Important Points to Note:  

    • Distribution Lists and Mail-Enabled Security groups require Exchange Online PowerShell cmdlets rather than Microsoft Graph. 

    • Dynamic groups pose a unique challenge. Unlike static groups where administrators manually add or remove members, dynamic group membership is determined by Azure AD rules based on user attributes such as department, location, or job title. Since membership is rule-based, you cannot manually add users to dynamic groups. 

    • Synchronized groups (groups synced from on-premises Active Directory via Azure AD Connect/Entra Connect) cannot be managed through Microsoft Graph. These groups must be modified at the source using the ActiveDirectory PowerShell module in your on-premises environment. Changes made on-premises will then sync to Entra ID. 

How To Copy Group Memberships Between Users in Microsoft Entra ID 

Group memberships in Microsoft 365 can be copied either manually or through a scripted approach. 

    1. While the manual method may seem straightforward, it is practical only for small, one-time scenarios. Manually reviewing each group and adding users through the admin portals quickly becomes time-consuming, increases the risk of human error, and offers no reliable way to validate the results.  

    1. Because of these limitations, a script-based approach is strongly recommended. Using PowerShell scripts allows administrators to automatically identify supported group types, exclude unsupported ones, and copy memberships consistently.  

PowerShell Script To Copy Group Memberships From One User To Another 

The following PowerShell script copies static Microsoft 365 and security group memberships from one user to another. Dynamic groups are automatically detected and skipped. 

⚠️ Note: Distribution Lists and Mail-Enabled Security groups are not supported in this script. Synchronized groups from on-premises Active Directory are also not supported and must be managed using the Active Directory PowerShell module.

Prerequisites 

Before running the script, ensure the Microsoft Graph PowerShell SDK is installed. The required permissions User.Read.AllGroup.Read.All, and GroupMember.ReadWrite.All must be granted. You must also have an appropriate admin role, such as Global AdministratorPrivileged Role Administrator, Groups Administrator, or Users Administrator

Script 



[CmdletBinding(SupportsShouldProcess)]
param (
    [Parameter(Mandatory = $true, HelpMessage = "Enter the source user's email or object ID")]
    [string]$SourceUserId,

    [Parameter(Mandatory = $true, HelpMessage = "Enter the target user's email or object ID")]
    [string]$TargetUserId,

    [Parameter(Mandatory = $false, HelpMessage = "Path to export CSV report")]
    [string]$ExportReport
)

# Function to connect to Microsoft Graph
function Connect-ToMicrosoftGraph {
    Write-Host "`n[INFO] Connecting to Microsoft Graph..." -ForegroundColor Cyan
    try {
        Connect-MgGraph -Scopes "User.Read.All", "Group.Read.All", "GroupMember.ReadWrite.All" -NoWelcome -ErrorAction Stop
        Write-Host "[SUCCESS] Connected to Microsoft Graph" -ForegroundColor Green
    }
    catch {
        Write-Host "[ERROR] Failed to connect to Microsoft Graph: $($_.Exception.Message)" -ForegroundColor Red
        exit
    }
}

# Function to verify user exists
function Test-UserExists {
    param([string]$UserId, [string]$UserType)
    
    try {
        $User = Get-MgUser -UserId $UserId -ErrorAction Stop
        Write-Host "[SUCCESS] $UserType user found: $($User.DisplayName) ($($User.UserPrincipalName))" -ForegroundColor Green
        return $User
    }
    catch {
        Write-Host "[ERROR] $UserType user not found: $UserId" -ForegroundColor Red
        Write-Host "[ERROR] $($_.Exception.Message)" -ForegroundColor Red
        exit
    }
}

# Main Script Execution
Write-Host "`n========================================" -ForegroundColor Yellow
Write-Host "  M365 Group Membership Copy Tool" -ForegroundColor Yellow
Write-Host "========================================`n" -ForegroundColor Yellow

# Connect to Microsoft Graph
Connect-ToMicrosoftGraph

# Verify both users exist
Write-Host "`n[INFO] Verifying users..." -ForegroundColor Cyan
$SourceUser = Test-UserExists -UserId $SourceUserId -UserType "Source"
$TargetUser = Test-UserExists -UserId $TargetUserId -UserType "Target"

# Get source user's group memberships
Write-Host "`n[INFO] Retrieving group memberships for source user..." -ForegroundColor Cyan
try {
    $SourceGroups = Get-MgUserMemberOf -UserId $SourceUser.Id -All -ErrorAction Stop
}
catch {
    Write-Host "[ERROR] Failed to retrieve group memberships: $($_.Exception.Message)" -ForegroundColor Red
    exit
}

# Filter out non-group objects and get full group details
$GroupsToProcess = @()
$SkippedDynamic = @()
$Report = [System.Collections.Generic.List[Object]]::new()

Write-Host "[INFO] Analyzing groups..." -ForegroundColor Cyan

foreach ($Membership in $SourceGroups) {
    # Only process if it's a group
    if ($Membership.AdditionalProperties.'@odata.type' -eq '#microsoft.graph.group') {
        try {
            # Get full group details to check if it's dynamic
            $GroupDetails = Get-MgGroup -GroupId $Membership.Id -ErrorAction Stop
            
            # Check if group is dynamic
            if ($GroupDetails.GroupTypes -contains "DynamicMembership") {
                $SkippedDynamic += [PSCustomObject]@{
                    GroupName = $GroupDetails.DisplayName
                    GroupId   = $GroupDetails.Id
                    Reason    = "Dynamic group - membership is rule-based"
                }
                Write-Host "  [SKIP] Dynamic group: $($GroupDetails.DisplayName)" -ForegroundColor Yellow
            }
            else {
                $GroupsToProcess += $GroupDetails
            }
        }
        catch {
            Write-Host "  [WARNING] Could not retrieve details for group $($Membership.Id)" -ForegroundColor Yellow
        }
    }
}

# Display summary
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host "  Summary" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Total groups found: $($SourceGroups.Count)" -ForegroundColor White
Write-Host "Static groups to copy: $($GroupsToProcess.Count)" -ForegroundColor Green
Write-Host "Dynamic groups (skipped): $($SkippedDynamic.Count)" -ForegroundColor Yellow
Write-Host "========================================`n" -ForegroundColor Cyan

if ($GroupsToProcess.Count -eq 0) {
    Write-Host "[INFO] No static groups to copy. Exiting." -ForegroundColor Cyan
    exit
}

# Process each group
$SuccessCount = 0
$AlreadyMemberCount = 0
$FailedCount = 0

Write-Host "[INFO] Processing group memberships...`n" -ForegroundColor Cyan

foreach ($Group in $GroupsToProcess) {
    $Status = ""
    $Action = ""
    
    try {
        # Check if target user is already a member
        $ExistingMembers = Get-MgGroupMember -GroupId $Group.Id -All -ErrorAction Stop
        $IsAlreadyMember = $ExistingMembers | Where-Object { $_.Id -eq $TargetUser.Id }
        
        if ($IsAlreadyMember) {
            $Status = "Already Member"
            $Action = "Skipped"
            $AlreadyMemberCount++
            Write-Host "  [SKIP] Already member: $($Group.DisplayName)" -ForegroundColor Yellow
        }
        else {
            if ($PSCmdlet.ShouldProcess("$($Group.DisplayName)", "Add $($TargetUser.DisplayName) to group")) {
                New-MgGroupMember -GroupId $Group.Id -DirectoryObjectId $TargetUser.Id -ErrorAction Stop
                $Status = "Success"
                $Action = "Added"
                $SuccessCount++
                Write-Host "  [SUCCESS] Added to: $($Group.DisplayName)" -ForegroundColor Green
            }
            else {
                $Status = "WhatIf"
                $Action = "Would Add"
                Write-Host "  [WHATIF] Would add to: $($Group.DisplayName)" -ForegroundColor Cyan
            }
        }
    }
    catch {
        $Status = "Failed"
        $Action = "Error"
        $FailedCount++
        Write-Host "  [ERROR] Failed: $($Group.DisplayName) - $($_.Exception.Message)" -ForegroundColor Red
    }
    
    # Add to report
    $Report.Add([PSCustomObject]@{
        GroupName       = $Group.DisplayName
        GroupId         = $Group.Id
        GroupType       = if ($Group.GroupTypes -contains "Unified") { "Microsoft 365" } 
                          elseif ($Group.SecurityEnabled -and $Group.MailEnabled) { "Mail-Enabled Security" }
                          elseif ($Group.SecurityEnabled) { "Security" }
                          else { "Distribution" }
        Status          = $Status
        Action          = $Action
        SecurityEnabled = $Group.SecurityEnabled
        MailEnabled     = $Group.MailEnabled
    })
}

# Final summary
Write-Host "`n========================================" -ForegroundColor Green
Write-Host "  Operation Complete" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host "Successfully added: $SuccessCount" -ForegroundColor Green
Write-Host "Already a member: $AlreadyMemberCount" -ForegroundColor Yellow
Write-Host "Failed: $FailedCount" -ForegroundColor $(if ($FailedCount -gt 0) { "Red" } else { "Green" })
Write-Host "Dynamic groups skipped: $($SkippedDynamic.Count)" -ForegroundColor Yellow
Write-Host "========================================`n" -ForegroundColor Green

# Export report if requested
if ($ExportReport) {
    try {
        $Report | Export-Csv -Path $ExportReport -NoTypeInformation -Force
        Write-Host "[SUCCESS] Report exported to: $ExportReport" -ForegroundColor Green
        
        # Also export skipped dynamic groups if any
        if ($SkippedDynamic.Count -gt 0) {
            $DynamicReportPath = $ExportReport -replace '\.csv$', '_DynamicGroupsSkipped.csv'
            $SkippedDynamic | Export-Csv -Path $DynamicReportPath -NoTypeInformation -Force
            Write-Host "[INFO] Skipped dynamic groups exported to: $DynamicReportPath" -ForegroundColor Cyan
        }
    }
    catch {
        Write-Host "[ERROR] Failed to export report: $($_.Exception.Message)" -ForegroundColor Red
    }
}

Write-Host "`n[INFO] Script execution completed.`n" -ForegroundColor Cyan

Why Scripts Still Aren’t Ideal for Everyone 

While PowerShell scripts are powerful, they’re not always practical: 

    • They require admin permissions and scripting knowledge. 
    • Every variation (DLs, mail-enabled security) often means another script. 

The Easy Way: Using EasyEntra 

While those scripts work great, there’s a much simpler approach if you’re managing this regularly. 

EasyEntra provides a straightforward interface that lets you copy group memberships in just a few clicks using standard Ctrl+C and Ctrl+V keyboard shortcuts. The tool automatically handles both Entra ID and Exchange Online groups, checks for existing memberships, and skips the problematic group types we discussed, all without writing a single line of code! 

For organizations managing hybrid environments, EasyEntra goes a step further by supporting on-premises Active Directory groups as well. This gives administrators a single, consistent way to manage user access across cloud and on-premises identities, without switching tools or maintaining multiple scripts.

Copy paste group memberships, MS Graph, EXO, AD
Copy/paste AD, Entra ID, and EXO group membership

Overall, EasyEntra is ideal for teams that want to save time, reduce manual effort, and safely delegate group management tasks, while still maintaining accuracy and control over user access! 

For detailed instructions on copying group memberships from one user to another in EasyEntra, visit: 
https://easyentra.com/knowledge-base/how-to/how-to-copy-group-membership-from-one-user-to-another/

Free 30-minute demo

try 30 days for free

GET EASYENTRA NEWS

Opt out at any time

“One of the best products I've used.”
Gary Shurland
Chief Information Officer, Mirick, United States
“This tool has been invaluable in streamlining our IT processes.”
Tyson Mckay
Chief Information Officer, Southwest Network, United States
“This product has been a miracle for our Help Desk. EasyEntra has completely transformed how we handle Microsoft 365 administration.”
Doug Sanders
Manager of Technical Customer Support, Junior Achievement USA, United States
“Your product is such a time saver. I love it!”
Scott Fehr
IT Infrastructure, MEC Aerial Work Platforms, United States
“It's a good product and saves us lots of time for these ongoing quick admin tasks.” 
Chris McFerran
Managing Director, CTech IT Solutions Ltd, United Kingdom
“EasyEntra has significantly streamlined our workflow, simplifying everything. It feels almost like a revolution.”
Johan Sadelius
IT-chef, Arjeplog Kommun, Sweden
I greatly appreciate your assistance and willingness to enhance the already outstanding product.”
Michael I. Wilson
Executive Director of Information Technology, Archdiocese Of Washington, United States
“It's great not having to switch back and forth between the O365 admin center and the Teams admin center to assign groups. I am sold!”
Thomas Madden
Director Information Technology, AutoPayPlus, United States
“I would highly recommend organizations use the solution as it greatly simplifies various tasks.”
S. Roger Singh
Chief Technology Officer, Prasad & Company LLP, Canada
“EasyEntra is time-saving. Love the copy/paste for user/computer groups and the copy to new user.”
Damian Nita
Associate Network Administrator, Shenandoah Valley Westminster-Canterbury, United States
“EasyEntra has transformed our daily IT operations by simplifying user management, reducing errors, and enhancing overall efficiency.”
Henrik Nefling
IT- and Digitalization Manager, Animal Protection Denmark, Denmark