Sharepoint Online - copy folder tree and keep permissions?

Jeffrey Cuiper 16 Reputation points

Hello all,

I'm working on a little proof of concept to hopefully pull a client into working with Sharepoint for their cloud filemanagement.
For this, I'd like to create a folder tree in a Sharepoint library with a specific set of access rules. For example:

ProjectTemplate (root folder)

  • Documents Folder (edit rights for everyone, except freelancers who have read-only)
  • Sales Folder (edit rights only for management & sales, no access for everyone else)
  • Client Intel Folder (edit rights for everyone, except freelancers who have no access)

In the Sharepoint Online interface I've managed to create this template folder and the rights all work as expected. Now I want to use this folder as a template, so that one of the office workers can copy the folder, paste it and then change the name for each new project, while access permissions remain the same throughout. However, when I copy this template folder, it simply ignores the custom permissions and takes the default permissions from the Sharepoint Library.

I've done some googling already, but so far I haven't found a solution that's usable for low-tech office-type users. Does anyone know if there's a way to copy folders and preserve their custom permissions in Sharepoint Online?

And if it's not possible... Is there any other best-practise way to facilitate a solution like this?

A group of Microsoft Products and technologies used for sharing and managing content, knowledge, and applications.
10,274 questions
{count} vote

2 answers

Sort by: Most helpful
  1. Jinwei Li-MSFT 4,726 Reputation points Microsoft Vendor

    Hi @Jeffrey Cuiper ,

    There is no SharePoint out of the box functionality using which you can copy the folder and its permissions. You could solution for this using PnP-PowerShell.

    Please try to use this code:

    #PowerShell Function to copy permissions between Folders in SharePoint Online  
    Function Copy-PnPFolderPermissions  
             [Parameter(Mandatory=$True)] [string] $WebURL,  
             [Parameter(Mandatory=$True)] [string] $SourceFolderURL,  
             [Parameter(Mandatory=$True)] [string] $TargetFolderURL,  
             [Parameter(Mandatory=$False)] [Bool] $AppendToExisting = $True  
        Try {  
            #Connect to PnP Online  
            Connect-PnPOnline -Url $WebURL -Interactive  
            #Get the Web  
            $Web = Get-PnPweb  
            $Ctx = Get-PnPContext  
            #Get Source and Target Folders  
            $SourceFolderItem = Get-PnPFolder -Url $SourceFolderURL -Includes ListItemAllFields.HasUniqueRoleAssignments  
            $SourceFolder = $SourceFolderItem.ListItemAllFields  
            $TargetFolderItem = Get-PnPFolder -Url $TargetFolderURL -Includes ListItemAllFields.HasUniqueRoleAssignments  
            $TargetFolder = $TargetFolderItem.ListItemAllFields  
            #if permissions are Inherited in Target Folder, Break the Inheritance  
                If($AppendToExisting -eq $True)  
                    #Break Folder permissions - keep all existing permissions & Clear Item level permissions  
            Else #If the Folder has unique Permissions already  
                If($AppendToExisting -eq $False)  
            #Get all permissions assigned to the source folder  
            $SourceRoleAssignments = Get-PnPProperty -ClientObject $SourceFolder -Property RoleAssignments  
            #Copy Source Folder permissions to Destination Folder  
            ForEach($RoleAssignment in $SourceRoleAssignments)  
                #Get RoleDefinitions of the Role Assignment  
                Get-PnPProperty -ClientObject $RoleAssignment -Property RoleDefinitionBindings, Member  
                #Leave the Hidden permissions  
                If($RoleAssignment.Member.IsHiddenInUI -eq $False)  
                    $SourcePermissions = $RoleAssignment.RoleDefinitionBindings | Where {$_.Name -notin("Limited Access")}  
                    $PermissionLevels = ($SourcePermissions | Select -ExpandProperty Name) -join "; "  
                    If($SourcePermissions -ne $null)  
                        #Add Source Folder's Permission Level to the Target Folder  
                        $RoleDefBindings = New-Object Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($Ctx)  
                        ForEach($RoleDefinition in $SourcePermissions)  
                        $Permissions = $TargetFolder.RoleAssignments.Add($RoleAssignment.Member,$RoleDefBindings)  
                        Write-host "Copied '$($RoleAssignment.Member.Title)' with Permissions '$PermissionLevels'"  
        Catch {  
            write-host -f Red "Error Copying Folder Permissions!" $_.Exception.Message  
    #Set Parameters  
    $WebURL = ""  
    #Server Relative URLs of Source and Target Folders  
    $SourceFolderURL = "/sites/Marketing/Shared Documents/Old"  
    $TargetFolderURL = "/sites/Marketing/Shared Documents/New"  
    #Call the function to copy Folder permissions   
    Copy-PnPFolderPermissions -WebURL $WebURL -SourceFolderURL $SourceFolderURL -TargetFolderURL $TargetFolderURL  

    This script appends to existing permissions of the target folder by default. You can pass an optional parameter for -AppendToExisting with “$False” if you want to clear all existing permissions of the target folder and copy permissions from the source folder.

    If the answer is helpful, please click "Accept Answer" and kindly upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    2 people found this answer helpful.

  2. Jeffrey Cuiper 16 Reputation points

    In case anyone comes by this later: I managed to get this result via a slightly different way in Power Automate.

    Rather than copying an existing folder, I created a flow that creates a new folder with subfolders. Each subfolder is a parallel branch in the flow that first creates the subfolder, then strips that folder of the inherited permissions via a REST API call (a 'Send HTTP request to Sharepoint' cmdlet). After that it grants permission to either edit, or read-only to groups of my choosing.

    The API call code to strip the inherited permissions is:
    /_api/web/GetFolderByServerRelativeUrl('[LIBRARYNAME]/[TEXT INPUT USED IN MANUAL TRIGGER]/[FOLDERNAME]')/ListItemAllFields/breakroleinheritance(copyRoleAssignments=false, clearSubscopes=true)

    In this, LIBRARYNAME speaks for itself. The TEXT INPUT is the link to the text input used in the manual trigger of the flow. The FOLDERNAME is the name of the subfolder you just created. No doubt you can use dynamic content for this, but I just hard-coded this into the line.

    Whether or not this is the 'best' way to do things is no doubt up for debate, but with this method I can at least show a proof-of-concept to the client. Thanks for the input!

    1 person found this answer helpful.