How to Copy User Permissions in SharePoint Online using PowerShell?

Permission management in SharePoint Online is always a complex task, especially in large environments. Granting permissions in SharePoint becomes cumbersome when you are in a situation to clone an existing user’s access rights. Consider this scenario: You have an existing user in a department granted access to various SharePoint Sites, lists, folders, and files, etc. and when a new user joins this department, You – SharePoint Administrator get the requirement of adding a new user to all the places with the same access rights as the existing team member!

So, How to clone user permissions in SharePoint Online? Well, there are no ways to copy permissions from one user to another user out of the box! You have to iterate through the below permission hierarchy and check if the source user has permissions to it, and then add the new user manually. Sounds simple? Hold-on! This will be nearly impossible when you have a larger site with lots of data! You can’t scan through 1000s of files in a document library, isn’t it?

SharePoint Online: Copy Permissions from One user to Another using PowerShell

Creating an AD group, SharePoint group, or Microsoft 365 group to manage permissions would be one option! So, you can simply add the new user to the respective group. Using 3rd party tools such as ShareGate, ControlPoint, Boost Solutions Permission manager would be another option that involves cost. Well, there is another free option. PowerShell!

SharePoint Online Permission Hierarchy

Any user in SharePoint Online gets access through these ways:

  • Added as a site collection administrator (or primary owner)
  • Permissions granted at site level, either as part of SharePoint group or with direct permissions
  • Permissions granted to list or libraries by breaking inheritance 
  • Access rights may be via List item (File) or folder level permissions.
sharepoint online clone user permissions

So, if you want to copy an existing user’s permissions, You have to look through all the above objects and then grant permission to the new user, if the existing user you want to copy has permissions on it.

You must run this script with Site collection Administrator permissions!

PowerShell Script to Clone User Permissions in SharePoint Online

Let’s use PowerShell to clone users’ permissions. This script iterates through each level of the permission hierarchy such as Site Collection, Subsite, List and libraries, Folder, List Item, and copies the given source user’s permissions to the target user if the source user has permissions on it. Just set the parameters for variables $SourceUser, $TargetUser and $SiteURL accordingly and run the script. You’ll find the script outputs log text on the screen, wherever it copies permissions.

#Function to copy user permissions 
Function Copy-PnPUserPermission($SourceUser, $TargetUser, [Microsoft.SharePoint.Client.SecurableObject]$Object)
{
    #Determine the type of the object
    Switch($Object.TypedObject.ToString())
    {
        "Microsoft.SharePoint.Client.Web"  
        { 
            $ObjectType = "Site" ; $ObjectURL = $Object.URL; $ObjectTitle = $Object.Title 
        }
        "Microsoft.SharePoint.Client.ListItem"
        {
            If($Object.FileSystemObjectType -eq "Folder")
            {
                $ObjectType = "Folder" ; $ObjectURL = $Object.FieldValues.FileRef; $ObjectTitle = $Object.FieldValues.FileLeafRef
            }
            ElseIf($Object.FileSystemObjectType -eq "File")
            {
                $ObjectType = "File" ; $ObjectURL = $Object.FieldValues.FileRef; $ObjectTitle = $Object.FieldValues.FileLeafRef
            }
            Else
            {
                $ObjectType = "List Item"; $ObjectURL = $Object.FieldValues.FileRef ;$ObjectTitle = $Object.FieldValues.Title
            }
        }
        Default
        { 
            $ObjectType = $Object.BaseType #List, DocumentLibrary, etc
            $ObjectTitle = $Object.Title
            #Get the URL of the List or Library
            $RootFolder = Get-PnPProperty -ClientObject $Object -Property RootFolder     
            $ObjectURL = $Object.RootFolder.ServerRelativeUrl
        }
    }
    
    #Get all users and group permissions assigned to the object
    $RoleAssignments = Get-PnPProperty -ClientObject $Object -Property RoleAssignments

    #Get Direct Permissions applied to the Target User on the Object
    [email protected]()
    ForEach($RoleAssignment in $RoleAssignments)
    {
        Get-PnPProperty -ClientObject $RoleAssignment -Property RoleDefinitionBindings, Member
        $TargetUserRA = $RoleAssignment | Where {$_.Member.PrincipalType -eq "User" -and $_.Member.LoginName -eq $TargetUser.LoginName}
        If($TargetUserRA)
        {    
            $TargetUserPermissions = $RoleAssignment.RoleDefinitionBindings | Select -ExpandProperty Name
        }
    }

    #Loop through each user or group associated with the object
    $TotalRoleAssignments = $Object.RoleAssignments.Count    
    For($i=0; $i -lt $TotalRoleAssignments; $i++)
    {
        $SourceRoleAssignment = $Object.RoleAssignments[$i]
        #Get all permission levels assigned to User account directly or via SharePoint Group
        [email protected]()
        Get-PnPProperty -ClientObject $SourceRoleAssignment -Property RoleDefinitionBindings, Member

        #Leave the Hidden Permissions
        If($SourceRoleAssignment.Member.IsHiddenInUI -eq $False)
        {        
            #Get the Permission Levels assigned
            $SourceUserPermissions = $SourceRoleAssignment.RoleDefinitionBindings | Select -ExpandProperty Name
            #Leave Principals with no Permissions assigned
            If($SourceUserPermissions.Length -eq 0) {Continue}
  
            #Check Source Permissions granted directly or through SharePoint Group
            If($SourceUserPermissions)
            {
                #Check if the Principal is SharePoint group
                If($SourceRoleAssignment.Member.PrincipalType -eq "SharePointGroup")
                { 
                    #Get the Group Name
                    $GroupName = $SourceRoleAssignment.Member.LoginName

                    #Leave Limited Access Groups
                    If($GroupName -notlike "*limited access*" -and $GroupName -notlike "*SharingLinks*" -and $GroupName -notlike "*tenant*")
                    {
                        #Check if the Source user is member of the Group
                        $SourceUserIsGroupMember = Get-PnPGroupMember -Identity $GroupName | Where {$_.LoginName -eq $SourceUser.LoginName}
                        If($SourceUserIsGroupMember -ne $null)
                        {
                            #Check if user is already member of the group - If not, Add to group
                            $TargetUserIsGroupMember = Get-PnPGroupMember -Identity $GroupName | Where {$_.LoginName -eq $TargetUser.LoginName}

                            If($TargetUserIsGroupMember -eq $null)
                            {
                                ##Add Target User to the Source User's Group
                                Add-PnPGroupMember -LoginName $TargetUser.LoginName -Identity $GroupName
                                Write-Host -f Green "`tAdded user to the Group '$GroupName' on $ObjectType '$ObjectTitle' at '$ObjectURL'"
                            }
                        }
                    }
                }
                ElseIf ($SourceRoleAssignment.Member.PrincipalType -eq "User" -and $SourceRoleAssignment.Member.LoginName -eq $SourceUser.LoginName)
                {     
                    #Add Each Direct permission (such as "Full Control", "Contribute") to the Target User
                    ForEach($SourceRoleDefinition in $SourceUserPermissions)
                    {
                        #Get the Permission Level  
                        $RoleDefinition = Get-PnPRoleDefinition -Identity $SourceRoleDefinition
                        If($RoleDefinition.Hidden -eq $false)
                        {
                            #Check if the Target User does not have the Permission Level already
                            If($TargetUserPermissions -notcontains $RoleDefinition.Name)
                            {
                                #Grant Source User's Permission Level to the Target User
                                $RoleDefBinding = New-Object Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($Object.Context)
                                $RoleDefBinding.Add($RoleDefinition)
                                $Permissions = $Object.RoleAssignments.Add($TargetUser,$RoleDefBinding)
                                $Object.Update()
                                Invoke-PnPQuery
                                Write-Host  -f Green "`tGranted $($RoleDefinition.Name) Permission to the User on $ObjectType '$ObjectTitle' at '$ObjectURL'"
                            }
                        }                   
                    }
                } 
            }
        }
    }
}

#Function to Clone Permissions on the Web and its underlying objects such as Lists and Libraries, Folders and List Items
Function Clone-PnPPermission
{
    [cmdletbinding()]
 
    Param 
    (    
        [Parameter(Mandatory=$True)] [Microsoft.SharePoint.Client.Web] $Web,
        [Parameter(Mandatory=$True)] [Microsoft.SharePoint.Client.User] $SourceUser,
        [Parameter(Mandatory=$True)] [Microsoft.SharePoint.Client.User] $TargetUser,
        [Parameter(Mandatory=$false)] [bool] $ScanSubsites,
        [Parameter(Mandatory=$false)] [bool] $ScanFolders,
        [Parameter(Mandatory=$false)] [bool] $ScanFiles
    )

    #Call the function to clone permissions on the web
    Write-host -f Yellow "Scanning Permissions of the Web: $($Web.URL)"
    Copy-PnPUserPermission -SourceUser $SourceUser -TargetUser $TargetUser -Object $Web
    
    #Clone Permissions on Lists
    Write-host "Scanning Permissions on Lists at $($web.url)" -f Yellow
    #Exclude system lists
    $ExcludedLists = @("Site Assets","Preservation Hold Library","Style Library", "Site Collection Images","Site Pages", "Content and Structure Reports",
                        "Form Templates", "Home Page Links", "Forms", "Workflow Tasks", "MicroFeed")
    $Lists = Get-PnPProperty -ClientObject $Web -Property Lists
    $Lists = $Lists | Where {($_.Hidden -eq $false) -and $ExcludedLists -notcontains $_.Title}
    
    Foreach($List in $Lists)
    {
        $ListHasUniquePermissions = Get-PnPProperty -ClientObject $List -Property HasUniqueRoleAssignments
        If($List.HasUniqueRoleAssignments)
        {
            #Call the function to Copy Permissions to TargetUser on lists
            Copy-PnPUserPermission -SourceUser $SourceUser -TargetUser $TargetUser -Object $List
        }

        #Get List Items (and folders)
        If($ScanFolders -or $ScanFiles)
        {
             #Get Items from List
             If($List.ItemCount -gt 0)
             {
                $global:counter = 0;
                $AllListItems = Get-PnPListItem -List $List -PageSize 500 -Fields ID, FileSystemObjectType, FileLeafRef -ScriptBlock `
                 { Param($items) $global:counter += $items.Count; Write-Progress -PercentComplete ($global:Counter / ($List.ItemCount) * 100) `
                          -Activity "Getting Items from List '$($List.Title)'" -Status "Processing Items $global:Counter to $($List.ItemCount)";}
                    Write-Progress -Activity "Completed Retrieving Items from List $($List.Title)" -Completed
             }
        }

        If($ScanFolders)
        {
            #Clone Permissions on List folders
            Write-host "Scanning Permissions on Folders on List '$($List.Title)'" -f Yellow

            #Get Folders from List Items
            $Folders = $AllListItems | Where { ($_.FileSystemObjectType -eq "Folder") -and ($_.FieldValues.FileLeafRef -ne "Forms") -and (-Not($_.FieldValues.FileLeafRef.StartsWith("_")))}

            If($Folders.count -gt 0)
            {
                $ItemCounter = 1 
                #Get Folder permissions
                Foreach($Folder in $Folders)
                {
                    Write-Progress -PercentComplete ($ItemCounter / ($Folders.Count) * 100) -Activity "Processing Folders $ItemCounter of $($Folders.Count)" -Status "Searching Unique Permissions in Folders of List '$($List.Title)'"
                
                    $FolderHasUniquePermissions = Get-PnPProperty -ClientObject $Folder -Property HasUniqueRoleAssignments
                    If($FolderHasUniquePermissions -eq $True)
                    {
                        #Call the function to Copy Permissions to TargetUser
                        Copy-PnPUserPermission -SourceUser $SourceUser -TargetUser $TargetUser -Object $folder
                    }
                    $ItemCounter++ 
                }
                Write-Progress -Activity "Completed Cloning Folder Permissions on List $($List.Title)" -Completed
            }
        }         
       
        If($ScanFiles)
        {
            Write-host "Scanning Permissions on Files/Items on List '$($List.Title)'" -f Yellow

            #Get Files / List Items with Unique Permissions
            $ListItems =  $AllListItems | Where { ($_.FileSystemObjectType -ne "Folder") }
            
            If($ListItems.count -gt 0)
            {                
                $ItemCounter = 1              
                #Get Item level permissions
                Foreach($Item in $ListItems)
                {
                    Write-Progress -PercentComplete ($ItemCounter / ($ListItems.Count) * 100) -Activity "Processing Items $ItemCounter of $($ListItems.Count)" -Status "Searching Unique Permissions in Files/List Items of '$($List.Title)'"
                
                    $ItemHasUniquePermissions = Get-PnPProperty -ClientObject $Item -Property HasUniqueRoleAssignments
                    If($ItemHasUniquePermissions -eq $True)
                    {                    
                        #Call the function to Copy Permissions to TargetUser
                        Copy-PnPUserPermission -SourceUser $SourceUser -TargetUser $TargetUser -Object $Item
                    }
                    $ItemCounter++
                }
                Write-Progress -Activity "Completed Cloning Item Permissions on List $($List.Title)" -Completed
            }
        }
    }
}

Function Clone-PnPUser
{
    [cmdletbinding()]
 
    Param 
    (    
        [Parameter(Mandatory=$True)] [String] $SiteURL, 
        [Parameter(Mandatory=$True)] [String] $SourceUserEmail,         
        [Parameter(Mandatory=$True)] [String] $TargetUserEmail,
        [Parameter(Mandatory=$false)] [bool] $ScanSubsites,
        [Parameter(Mandatory=$false)] [bool] $ScanFolders,
        [Parameter(Mandatory=$false)] [bool] $ScanFiles
    )   
        #Connect to PnP Online
        Connect-PnPOnline -Url $SiteURL -Interactive
        #Get the Context of the Root web
        $Ctx = Get-PnPContext

        #Prepare the source and target users
        $SourceUser = Get-PnPUser | Where-Object Email -eq $SourceUserEmail
        If($SourceUser -eq $null) {        
            $SourceUser = New-PnPUser -LoginName $SourceUserEmail
        }
        $TargetUser = Get-PnPUser | Where-Object Email -eq $TargetUserEmail
        If($TargetUser -eq $null) {        
            $TargetUser = New-PnPUser -LoginName $TargetUserEmail
        }

        #Check Whether the Source User is a Site Collection Administrator
        Write-host "Scanning Site Collection Administrators..." -f Yellow        
        $SourceUserIsSiteAdmin = Get-PnPSiteCollectionAdmin | Where {$_.LoginName -eq $SourceUser.LoginName}
        If($SourceUserIsSiteAdmin -ne $Null)
        {
            #Check if the target user is site collection admin already!
            $TargetUserIsSiteAdmin = Get-PnPSiteCollectionAdmin | Where {$_.LoginName -eq $TargetUser.LoginName}
            If($TargetUserIsSiteAdmin -eq $Null)
            {
                #Add the Target user as Site Collection Admin
                Add-PnPSiteCollectionAdmin -Owners $TargetUser
                Write-host "Added Site Collection Administrator!" -f Green
            }
        }

        #Clone Permissions on the site and its underlying objects
        $Web = Get-PnPWeb
        Clone-PnPPermission -Web $Web -SourceUser $SourceUser -TargetUser $TargetUser -ScanSubsites $ScanSubSites -ScanFolders $ScanFolders -ScanFiles $ScanFiles
        
        #Call the function for subsites
        If($ScanSubsites)
        {
            #Get all subsites
            $WebsCollection = Get-PnPSubWeb -Includes HasUniqueRoleAssignments
            #Loop throuh each SubSite (web)
            Foreach($SubWeb in $WebsCollection)
            {
                If($SubWeb.HasUniqueRoleAssignments -eq $True)
                {  
                    #Call the function to Copy Permissions to TargetUser
                    Clone-PnPPermission -Web $SubWeb -SourceUser $SourceUser -TargetUser $TargetUser -ScanSubsites $ScanSubSites -ScanFolders $ScanFolders -ScanFiles $ScanFiles
                }           
            }
        }
    Write-Host -f Green "*** Permission Cloned Successfully! *** "
}
 
#Define Parameters
$SiteURL = "https://crescentintranet.sharepoint.com/sites/Marketing"
$SourceUserEmail ="[email protected]"
$TargetUserEmail ="[email protected]"
 
#Call the function to clone user permissions
Clone-PnPUser -SiteURL $SiteURL -SourceUserEmail $SourceUserEmail -TargetUserEmail $TargetUserEmail
#Clone-PnPUser -SiteURL $SiteURL -SourceUserEmail $SourceUserEmail -TargetUserEmail $TargetUserEmail -ScanSubsites $true -ScanFolders $true -ScanFiles $True

Please note, this script doesn’t copy Active Directory Group or Microsoft 365 group permissions. And it’s scoped at the site collection level. So, If you want to clone permissions for multiple site collections, You have to call the function “Clone-PnPUser” with the relevant site URLs.

This would be extremely helpful when a new team member is replacing an old team member. With this script, all the permissions of the old team member can simply be transferred to the new team member. Not just direct permissions, but this script also adds the target user to all the SharePoint groups the source user is a part of.

Salaudeen Rajack

Salaudeen Rajack is a SharePoint Architect with Two decades of SharePoint Experience. He loves sharing his knowledge and experiences with the SharePoint community, through his real-world articles!

2 thoughts on “How to Copy User Permissions in SharePoint Online using PowerShell?

Leave a Reply