SharePoint Online: Copy All Document Libraries to Another Site using PowerShell

Requirement: Copy All Document Libraries to another Site collection in SharePoint Online.

sharepoint online copy document library to another site powershell

PowerShell to Copy Document Libraries from One Site to Another Site Collection:

Are you looking to migrate all of your SharePoint Online document libraries from one site to another? If so, you can use PowerShell to copy them all over easily. In a migration project, I have a requirement to copy all document libraries from one site collection to another. Here is the PowerShell script I’ve used to copy all libraries (just the library without its actual content) between site collections:

#Parameters
$SourceSiteURL = "https://crescent.sharepoint.com/sites/Neoma"
$DestinationSiteURL = "https://crescent.sharepoint.com/sites/Neoma-Copy"

#Connect to the source Site
Connect-PnPOnline -URL $SourceSiteURL -Interactive 

#Get all document libraries
$SourceLibraries =  Get-PnPList | Where {$_.BaseType -eq "DocumentLibrary" -and $_.Hidden -eq $False}

#Connect to the destination site
Connect-PnPOnline -URL $DestinationSiteURL -Interactive

#Get All Lists in the Destination
$Libraries = Get-PnPList

ForEach($Library in $SourceLibraries)
{
    #Check if the library already exists in target
    If(!($Libraries.Title -contains $Library.Title))
    { 
        #Create a document library
        New-PnPList -Title $Library.Title -Template DocumentLibrary
        Write-host "Document Library '$($Library.Title)' created successfully!" -f Green
    }
    else
    {
        Write-host "Document Library '$($Library.Title)' exists already!" -f Yellow
    }
}

SharePoint Online: PowerShell to Copy Libraries with its Content Between Sites

This time, let’s copy all document libraries along with their contents (Files, Folders, Sub-folders, etc.) of the document libraries to another site collection:

#Parameters
$SourceSiteURL = "https://crescent.sharepoint.com/sites/Marketing"
$DestinationSiteURL = "https://crescent.sharepoint.com/sites/Branding"

#Connect to the source Site
$SourceConn = Connect-PnPOnline -URL $SourceSiteURL -Interactive -ReturnConnection
$Web = Get-PnPWeb -Connection $SourceConn

#Get all document libraries
$SourceLibraries =  Get-PnPList -Includes RootFolder -Connection $SourceConn | Where {$_.BaseType -eq "DocumentLibrary" -and $_.Hidden -eq $False}

#Connect to the destination site
$DestinationConn = Connect-PnPOnline -URL $DestinationSiteURL -Interactive -ReturnConnection

#Get All Lists in the Destination site
$DestinationLibraries = Get-PnPList -Connection $DestinationConn

ForEach($SourceLibrary in $SourceLibraries)
{
    #Check if the library already exists in target
    If(!($DestinationLibraries.Title -contains $SourceLibrary.Title))
    { 
        #Create a document library
        $NewLibrary  = New-PnPList -Title $SourceLibrary.Title -Template DocumentLibrary -Connection $DestinationConn
        Write-host "Document Library '$($SourceLibrary.Title)' created successfully!" -f Green
    }
    else
    {
        Write-host "Document Library '$($SourceLibrary.Title)' already exists!" -f Yellow
    }

    #Get the Destination Library
    $DestinationLibrary = Get-PnPList $SourceLibrary.Title -Includes RootFolder -Connection $DestinationConn
    $SourceLibraryURL = $SourceLibrary.RootFolder.ServerRelativeUrl
    $DestinationLibraryURL = $DestinationLibrary.RootFolder.ServerRelativeUrl
    
    #Calculate Site Relative URL of the Folder
    If($Web.ServerRelativeURL -eq "/")
    {
	    $FolderSiteRelativeUrl = $SourceLibrary.RootFolder.ServerRelativeUrl
    }
    Else
    {      
	    $FolderSiteRelativeUrl = $SourceLibrary.RootFolder.ServerRelativeUrl.Replace($Web.ServerRelativeURL,[string]::Empty)
    }

    #Get All Content from Source Library's Root Folder
    $RootFolderItems = Get-PnPFolderItem -FolderSiteRelativeUrl $FolderSiteRelativeUrl -Connection $SourceConn | Where {($_.Name -ne "Forms") -and (-Not($_.Name.StartsWith("_")))}
        
    #Copy Items to the Destination
    $RootFolderItems | ForEach-Object {
        $DestinationURL = $DestinationLibrary.RootFolder.ServerRelativeUrl
        Copy-PnPFile -SourceUrl $_.ServerRelativeUrl -TargetUrl $DestinationLibraryURL -Force -OverwriteIfAlreadyExists
        Write-host "`tCopied '$($_.ServerRelativeUrl)'" -f Green    
    }    
    Write-host "`tContent Copied from $SourceLibraryURL to  $DestinationLibraryURL Successfully!" -f Cyan
}

The above script works perfectly fine with simple document libraries. However, What if your document library has custom metadata columns added to it? What if you want to copy document libraries along with their custom settings? Well, here is the PowerShell script that uses creating list template and list instances method to copy the document library in SharePoint Online!

PowerShell to Copy Document Libraries Between Site Collections in SharePoint Online

This PowerShell script copies lists and their settings and metadata columns to the destination site.

Make sure you have enabled custom script in SharePoint Online prior running this script! Otherwise, you’ll end up in error “Access denied. You do not have permission to perform this action or access this resource.” How to Enable Custom Script in SharePoint Online?
#Function to Copy All libraries from One Site to another
Function Copy-PnPAllLibraries
{
    param (
    [parameter(Mandatory=$true, ValueFromPipeline=$true)][string]$SourceSiteURL,
    [parameter(Mandatory=$true, ValueFromPipeline=$true)][string]$DestinationSiteURL
    )
 
    Try {
        #Connect to the Source Site
        $SourceConn = Connect-PnPOnline -URL $SourceSiteURL -Interactive -ReturnConnection
        $SourceCtx = $SourceConn.Context
        $SourceRootWeb = $SourceCtx.Site.RootWeb
        $SourceCtx.Load($SourceRootWeb)
        $SourceCtx.ExecuteQuery()

        #Connect to the Destination Site
        $DestinationConn = Connect-PnPOnline -URL $DestinationSiteURL -Interactive -ReturnConnection
        $DestinationCtx = $DestinationConn.Context
        $DestinationRootWeb = $DestinationCtx.Site.RootWeb
        $DestinationCtx.Load($DestinationRootWeb)
        $DestinationCtx.ExecuteQuery()    
 
        #Exclude certain libraries
        $ExcludedLibraries =  @("Style Library","Preservation Hold Library", "Site Pages", "Site Assets","Form Templates", "Site Collection Images","Site Collection Documents")
    
        #Get Libraries from Source site - Skip hidden and certain libraries
        $SourceLibraries =  Get-PnPList -Includes RootFolder -Connection $SourceConn | Where {$_.BaseType -eq "DocumentLibrary" -and $_.Hidden -eq $False -and $_.Title -notin $ExcludedLibraries}
        
        #region PrepareTemplates
        $SourceListTemplates = $SourceCtx.Site.GetCustomListTemplates($SourceRootWeb)
        $SourceCtx.Load($SourceListTemplates)
        $SourceCtx.ExecuteQuery()
        $DestinationListTemplates = $DestinationCtx.Site.GetCustomListTemplates($DestinationRootWeb)
        $DestinationCtx.Load($DestinationListTemplates)
        $DestinationCtx.ExecuteQuery()

        #Remove Document Library Templates from source and destination sites
        ForEach($Library in $SourceLibraries)
        {
            $SourceListTemplate = $SourceListTemplates | Where {$_.Name -eq $Library.id.Guid}
            $SourceListTemplateURL = $SourceRootWeb.ServerRelativeUrl+"/_catalogs/lt/"+$Library.id.Guid+".stp"   
 
            #Remove the List template if exists in source   
            If($SourceListTemplate)
            {
                $SourceListTemplateFile = Get-PnPFile -Url $SourceListTemplateURL -Connection $SourceConn
                $SourceListTemplateFile.DeleteObject()
                $SourceCtx.ExecuteQuery()
            }
        }

        Write-host "Creating List Templates..." -f Yellow -NoNewline
        #Create Templates
        $SourceLibraries | ForEach-Object {
            $_.SaveAsTemplate($_.id.Guid, $_.id.Guid, [string]::Empty, $False)
            $SourceCtx.ExecuteQuery()
  
            #Copy List Template from source to the destination site
            $SourceListTemplateURL = $SourceRootWeb.ServerRelativeUrl+"/_catalogs/lt/"+$_.id.Guid+".stp"   
            Copy-PnPFile -SourceUrl $SourceListTemplateURL -TargetUrl ($DestinationRootWeb.ServerRelativeUrl+"/_catalogs/lt") -Force -OverwriteIfAlreadyExists
        }
        Write-host "Done!" -f Green
        Start-Sleep 5

        #Reload the List Templates in the Destination Site
        $DestinationListTemplates = $DestinationCtx.Site.GetCustomListTemplates($DestinationRootWeb)
        $DestinationCtx.Load($DestinationListTemplates)
        $DestinationCtx.ExecuteQuery()
        #endregion

        #Iterate through each library in the source
        ForEach($SourceLibrary in $SourceLibraries)
        {
            Write-host "Copying library:"$SourceLibrary.Title -f Magenta

            #Get the Template
            $DestinationListTemplate = $DestinationListTemplates | Where {$_.Name -eq $SourceLibrary.id.Guid}
 
            #Create the destination library from the list template, if it doesn't exist
            Write-host "Creating New Library in the Destination Site..." -f Yellow -NoNewline
            If(!(Get-PnPList -Identity $SourceLibrary.Title -Connection $DestinationConn))
            {
                #Create the destination library
                $ListCreation = New-Object Microsoft.SharePoint.Client.ListCreationInformation
                $ListCreation.Title = $SourceLibrary.Title
                $ListCreation.ListTemplate = $DestinationListTemplate
                $DestinationList = $DestinationCtx.Web.Lists.Add($ListCreation)
                $DestinationCtx.ExecuteQuery()
                Write-host "Library '$($SourceLibrary.Title)' created successfully!" -f Green
            }
            Else
            {
                Write-host "Library '$($SourceLibrary.Title)' already exists!" -f Yellow
            }

            Write-host "Copying Files and Folders from the Source to Destination Site..." -f Yellow    
            $DestinationLibrary = Get-PnPList $SourceLibrary.Title -Includes RootFolder -Connection $DestinationConn
            #Copy All Content from Source Library's Root Folder to the Destination Library
            If($SourceLibrary.ItemCount -gt 0)
            {
                #Get All Items from the Root Folder of the Library
                $global:counter = 0
                $ListItems = Get-PnPListItem -List $SourceLibrary.Title -Connection $SourceConn -PageSize 500 -Fields ID -ScriptBlock {Param($items) $global:counter += $items.Count; Write-Progress -PercentComplete `
                    (($global:Counter / $SourceLibrary.ItemCount) * 100) -Activity "Getting Items from List" -Status "Getting Items $global:Counter of $($SourceLibrary.ItemCount)"}
                $RootFolderItems = $ListItems | Where { ($_.FieldValues.FileRef.Substring(0,$_.FieldValues.FileRef.LastIndexOf($_.FieldValues.FileLeafRef)-1)) -eq $SourceLibrary.RootFolder.ServerRelativeUrl}
                Write-Progress -Activity "Completed Getting Items from Library $($SourceLibrary.Title)" -Completed
        
                #Copy Items to the Destination
                $RootFolderItems | ForEach-Object {
                    $DestinationURL = $DestinationLibrary.RootFolder.ServerRelativeUrl
                    Copy-PnPFile -SourceUrl $_.FieldValues.FileRef -TargetUrl $DestinationLibrary.RootFolder.ServerRelativeUrl -Force -OverwriteIfAlreadyExists
                    Write-host "`tCopied $($_.FileSystemObjectType) '$($_.FieldValues.FileRef)' Successfully!" -f Green     
                }
            }
        }

        #Cleanup List Templates in source and destination sites
        ForEach($Library in $SourceLibraries)
        {
            $SourceListTemplate = $SourceListTemplates | Where {$_.Name -eq $Library.id.Guid}
            $SourceListTemplateURL = $SourceRootWeb.ServerRelativeUrl+"/_catalogs/lt/"+$Library.id.Guid+".stp"   
 
            #Remove the List template if exists in source   
            If($SourceListTemplate)
            {
                #Remove-PnPFile -ServerRelativeUrl $SourceListTemplateURL -Recycle -Force -Connection $SourceConn
                $SourceListTemplateFile = Get-PnPFile -Url $SourceListTemplateURL -Connection $SourceConn
                $SourceListTemplateFile.DeleteObject()
                $SourceCtx.ExecuteQuery()
            }
            #Remove the List template if exists in target 
            $DestinationListTemplate = $DestinationListTemplates | Where {$_.Name -eq $Library.id.Guid}
            $DestinationListTemplateURL = $DestinationRootWeb.ServerRelativeUrl+"/_catalogs/lt/"+$Library.id.Guid+".stp" 
            #Remove the List template if exists    
            If($DestinationListTemplate)
            {
                #Remove-PnPFile -ServerRelativeUrl $DestinationListTemplateURL -Recycle -Force -Connection $DestinationConn
                $DestinationListTemplate = Get-PnPFile -Url $DestinationListTemplateURL -Connection $DestinationConn
                $DestinationListTemplate.DeleteObject()
                $DestinationCtx.ExecuteQuery()        
            }
        }
    }
    Catch {
        write-host -f Red "Error:" $_.Exception.Message
    }
}

#Parameters
$SourceSiteURL = "https://crescent.sharepoint.com/sites/Marketing"
$DestinationSiteURL = "https://crescent.sharepoint.com/sites/HR"

#Call the function to copy libraries to another site
Copy-PnPAllLibraries -SourceSiteURL $SourceSiteURL -DestinationSiteURL $DestinationSiteURL

To copy a single document library to another site collection, use: How to copy a document library to another site in SharePoint Online using PowerShell?

Salaudeen Rajack

Salaudeen Rajack - SharePoint Expert with Two decades of SharePoint Experience. Love to Share my knowledge and experience with the SharePoint community, through real-time articles!

5 thoughts on “SharePoint Online: Copy All Document Libraries to Another Site using PowerShell

  • is it possible to set up a csv file with source folders and destination folders to copy multiple folders from one site to other sites?
    I have to accommodate a full data restructure.

    Reply
  • 1. I need to copy libraries from one tenant to another as well as part of a migration. Currently looking at Sharegate but would like to do it using powershell for free if possible…

    Reply
  • Is there any way to do this but among different tenants? I need to copy a specific set of files from different libraries from one tenant to another one, unfortunately copy-pnpfile cmdlet doesn’t support this 🙁 I hope you can shed some ideas here. Thanks in advance!

    Reply

Leave a Reply

Your email address will not be published.