SharePoint Online: Copy Files Between Site Collections using PowerShell

Requirement: Copy Files between document libraries in SharePoint Online Site Collections.

How to Copy a File Between SharePoint Online Site Collections?
Here is how to copy files between document libraries in SharePoint Online:
  • Navigate to your SharePoint Online document library>>  Select the file to copy >> Click on "Copy To" in the toolbar. (or you can right-click on a document and choose "Copy To" from the context menu)
    sharepoint online powershell copy documents
  • This opens the information panel on the right. Select the target library to which your file needs to be copied. You can select any folder in the current library, any other library in the current site or even a library in any different site collections.
    powershell sharepoint online copy files
  • Pick the target location to copy the document and click on the "Copy Here" button to start copying the file.
You'll see the "Copying" message in the toolbar and your file will be copied momentarily. You can use this method to copy files between libraries in the same site or between different site collections.

PowerShell to Copy Files between Site Collections in SharePoint Online:
Let's copy a single file from source to destination.
#Load SharePoint CSOM Assemblies
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"

#Set parameter values
$SourceSiteURL="https://crescent.sharePoint.com/sites/sales"
$TargetSiteURL="https://crescent.sharepoint.com/sites/Ops/"

#Set Source and Destination File URLs - Relative path
$SourceFileURL="/Sites/Sales/Project Documents/Active Users.xlsx"
$TargetFileURL="/Sites/Ops/Shared Documents/Active Users.xlsx"

#Setup Credentials to connect
$Cred= Get-Credential
$Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.Username, $Cred.Password)

#Setup the contexts
$SourceCtx = New-Object Microsoft.SharePoint.Client.ClientContext($SourceSiteURL)
$SourceCtx.Credentials = $Credentials
$TargetCtx = New-Object Microsoft.SharePoint.Client.ClientContext($TargetSiteURL)
$TargetCtx.Credentials = $Credentials

#Get the Source File
$FileInfo = [Microsoft.SharePoint.Client.File]::OpenBinaryDirect($SourceCtx, $SourceFileURL)

#Copy File to the Target location
[Microsoft.SharePoint.Client.File]::SaveBinaryDirect($TargetCtx, $TargetFileURL, $FileInfo.Stream,$True)
This script simply copies a particular file from source site/site collection to the target site/site collection.

Copy All Files and Folder from One Library to Another Library between different Site Collections:
#Load SharePoint CSOM Assemblies
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"

Function Copy-Files
{
  param
    (
        [Parameter(Mandatory=$true)] [Microsoft.SharePoint.Client.Folder] $SourceFolder,
        [Parameter(Mandatory=$true)] [Microsoft.SharePoint.Client.Folder] $TargetFolder
    )
    Try {
        #Get all Files from the source folder
        $SourceFilesColl = $SourceFolder.Files
        $SourceFolder.Context.Load($SourceFilesColl)
        $SourceFolder.Context.ExecuteQuery()

        #Iterate through each file and copy
        Foreach($SourceFile in $SourceFilesColl)
        {
            #Get the source file
            $FileInfo = [Microsoft.SharePoint.Client.File]::OpenBinaryDirect($SourceFolder.Context, $SourceFile.ServerRelativeUrl)
            
            #Copy File to the Target location
            $TargetFileURL = $TargetFolder.ServerRelativeUrl+"/"+$SourceFile.Name
            [Microsoft.SharePoint.Client.File]::SaveBinaryDirect($TargetFolder.Context, $TargetFileURL, $FileInfo.Stream,$True)

            Write-host -f Green "Copied File '$($SourceFile.ServerRelativeUrl)' to '$TargetFileURL'"
        }

        #Process Sub Folders
        $SubFolders = $SourceFolder.Folders
        $SourceFolder.Context.Load($SubFolders)
        $SourceFolder.Context.ExecuteQuery()
        Foreach($SubFolder in $SubFolders)
        {
            If($SubFolder.Name -ne "Forms")
            {
                #Prepare Target Folder
                $TargetFolderURL = $SubFolder.ServerRelativeUrl -replace $SourceLibrary.RootFolder.ServerRelativeUrl, $TargetLibrary.RootFolder.ServerRelativeUrl
                Try {
                        $Folder=$TargetFolder.Context.web.GetFolderByServerRelativeUrl($TargetFolderURL)
                        $TargetFolder.Context.load($Folder)
                        $TargetFolder.Context.ExecuteQuery()
                    }
                catch {
                        #Create Folder
                        if(!$Folder.Exists)
                        {
                            $TargetFolderURL
                            $Folder=$TargetFolder.Context.web.Folders.Add($TargetFolderURL)
                            $TargetFolder.Context.Load($Folder)
                            $TargetFolder.Context.ExecuteQuery()
                            Write-host "Folder Added:"$SubFolder.Name -f Yellow
                        }
                    }
                #Call the function recursively
                Copy-Files -SourceFolder $SubFolder -TargetFolder $Folder
            }
        } 
    }
    Catch {
        write-host -f Red "Error Copying File!" $_.Exception.Message
    } 
}

#Set Parameter values
$SourceSiteURL="https://crescent.sharePoint.com/sites/sales"
$TargetSiteURL="https://crescent.sharepoint.com/sites/ops"

$SourceLibraryName="Project Documents"
$TargetLibraryName="Documents"

#Setup Credentials to connect
$Cred= Get-Credential
$Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.Username, $Cred.Password)

#Setup the contexts
$SourceCtx = New-Object Microsoft.SharePoint.Client.ClientContext($SourceSiteURL)
$SourceCtx.Credentials = $Credentials
$TargetCtx = New-Object Microsoft.SharePoint.Client.ClientContext($TargetSiteURL)
$TargetCtx.Credentials = $Credentials
     
#Get the source library and Target Libraries
$SourceLibrary = $SourceCtx.Web.Lists.GetByTitle($SourceLibraryName)
$SourceCtx.Load($SourceLibrary)
$SourceCtx.Load($SourceLibrary.RootFolder)

$TargetLibrary = $TargetCtx.Web.Lists.GetByTitle($TargetLibraryName)
$TargetCtx.Load($TargetLibrary)
$TargetCtx.Load($TargetLibrary.RootFolder)
$TargetCtx.ExecuteQuery()

#Call the function 
Copy-Files -SourceFolder $SourceLibrary.RootFolder -TargetFolder $TargetLibrary.RootFolder 
Please note, copying files will not preserve version history! You need to use "Move" instead of copy if you want to preserve versioning.

Copy All Files from One Library to Another Library along with Metadata values:
This script copies all files and folders between document libraries in different site collections, preserving metadata field values such as: Created by, Modified By, Created At, Modified At, etc.
#Load SharePoint CSOM Assemblies
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"

Function Copy-AllFilesWithMetadata
{
  param
    (
        [Parameter(Mandatory=$true)] [Microsoft.SharePoint.Client.Folder] $SourceFolder,
        [Parameter(Mandatory=$true)] [Microsoft.SharePoint.Client.Folder] $TargetFolder
    )
    Try {
        #Get all Files from the source folder
        $SourceFilesColl = $SourceFolder.Files
        $SourceFolder.Context.Load($SourceFilesColl)
        $SourceFolder.Context.ExecuteQuery()

        #Iterate through each file and copy
        Foreach($SourceFile in $SourceFilesColl)
        {
            #Get the source file
            $FileInfo = [Microsoft.SharePoint.Client.File]::OpenBinaryDirect($SourceFolder.Context, $SourceFile.ServerRelativeUrl)
            
            #Copy File to the Target location
            $TargetFileURL = $TargetFolder.ServerRelativeUrl+"/"+$SourceFile.Name
            [Microsoft.SharePoint.Client.File]::SaveBinaryDirect($TargetFolder.Context, $TargetFileURL, $FileInfo.Stream,$True)

            #Copy Metadata field values
            $SourceListItem = $SourceFile.ListItemAllFields
            $SourceFolder.Context.Load($SourceListItem)
            $SourceFolder.Context.ExecuteQuery()
            
            #Get the new file created
            $TargetFile = $TargetFolder.Context.Web.GetFileByServerRelativeUrl($TargetFileURL)
            $TargetListItem = $TargetFile.ListItemAllFields
            
            #Set Metadata values from the source
            $Author =$TargetFolder.Context.web.EnsureUser($SourceListItem["Author"].Email)
            $TargetListItem["Author"] = $Author
            $Editor =$TargetFolder.Context.web.EnsureUser($SourceListItem["Editor"].Email)
            $TargetListItem["Editor"] = $Editor
            $TargetListItem["Created"] = $SourceListItem["Created"]
            $TargetListItem["Modified"] = $SourceListItem["Modified"]
            $TargetListItem.Update()
            $TargetFolder.Context.ExecuteQuery()

            Write-host -f Green "Copied File '$($SourceFile.ServerRelativeUrl)' to '$TargetFileURL'"
        }

        #Process Sub Folders
        $SubFolders = $SourceFolder.Folders
        $SourceFolder.Context.Load($SubFolders)
        $SourceFolder.Context.ExecuteQuery()
        Foreach($SubFolder in $SubFolders)
        {
            If($SubFolder.Name -ne "Forms")
            {
                #Prepare Target Folder
                $TargetFolderURL = $SubFolder.ServerRelativeUrl -replace $SourceLibrary.RootFolder.ServerRelativeUrl, $TargetLibrary.RootFolder.ServerRelativeUrl
                Try {
                        $Folder=$TargetFolder.Context.web.GetFolderByServerRelativeUrl($TargetFolderURL)
                        $TargetFolder.Context.load($Folder)
                        $TargetFolder.Context.ExecuteQuery()
                    }
                catch {
                        #Create Folder
                        if(!$Folder.Exists)
                        {
                            $TargetFolderURL
                            $Folder=$TargetFolder.Context.web.Folders.Add($TargetFolderURL)
                            $TargetFolder.Context.Load($Folder)
                            $TargetFolder.Context.ExecuteQuery()
                            Write-host "Folder Added:"$SubFolder.Name -f Yellow
                        }
                    }
                #Call the function recursively
                Copy-AllFilesWithMetadata -SourceFolder $SubFolder -TargetFolder $Folder
            }
        } 
    }
    Catch {
        write-host -f Red "Error Copying File!" $_.Exception.Message
    } 
}

#Set Parameter values
$SourceSiteURL="https://crescent.sharepoint.com/sites/sales"
$TargetSiteURL="https://crescent.sharepoint.com/sites/Ops"

$SourceLibraryName="Project Documents"
$TargetLibraryName="Documents"

#Setup Credentials to connect
$Cred= Get-Credential
$Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.Username, $Cred.Password)

#Setup the contexts
$SourceCtx = New-Object Microsoft.SharePoint.Client.ClientContext($SourceSiteURL)
$SourceCtx.Credentials = $Credentials
$TargetCtx = New-Object Microsoft.SharePoint.Client.ClientContext($TargetSiteURL)
$TargetCtx.Credentials = $Credentials
     
#Get the source library and Target Libraries
$SourceLibrary = $SourceCtx.Web.Lists.GetByTitle($SourceLibraryName)
$SourceCtx.Load($SourceLibrary)
$SourceCtx.Load($SourceLibrary.RootFolder)

$TargetLibrary = $TargetCtx.Web.Lists.GetByTitle($TargetLibraryName)
$TargetCtx.Load($TargetLibrary)
$TargetCtx.Load($TargetLibrary.RootFolder)
$TargetCtx.ExecuteQuery()

#Call the function 
Copy-AllFilesWithMetadata -SourceFolder $SourceLibrary.RootFolder -TargetFolder $TargetLibrary.RootFolder 

PnP PowerShell to Copy Files and Folders between Site Collections
This script copies files and folders between document libraries (or folders) from one site collection to another.
#Parameters
$SiteURL = "https://crescent.sharepoint.com/sites/Marketing/"
$SourceFolderURL = "/sites/marketing/Branding"
$TargetFolderURL = "/sites/Purchase/Shared Documents"
 
#Connect to PnP Online
Connect-PnPOnline -Url $SiteURL -UseWebLogin
 
#Copy All Files and Folders between source and target folders
Copy-PnPFile -SourceUrl $SourceFolderURL -TargetUrl $TargetFolderURL -SkipSourceFolderName -Force -OverwriteIfAlreadyExists

9 comments:

  1. Hi there, how does this work with file/folder modification? For example: We have the library on siteA and we copy to siteB. Once the copy has finished and we run it again, what happens? Ideally, I'd we'd like to run this on a regular basis like a 'sync'. Any help would be amazing!

    ReplyDelete
    Replies
    1. This script overwrites the target file if exists! So your requirement is met, I suppose!!

      Delete
    2. Hi this script is really great well i was just wondering how can we copy only specific items instead of all from one doc lib to another and also does this work for inside
      different folders too?Please suggest

      Delete
  2. This really works great. Is there any way to exclude files in copying from the source library which were not accessed in last 2 yrs? please suggest

    ReplyDelete
    Replies
    1. You can filter list items based on created/modified date. However, it appears there isn't any solution to get last accessed date of items in SharePoint Online.

      Delete
  3. Script works fine for other library, but when i copy "Site Pages" am getting this error" Error Copying File! Exception calling "SaveBinaryDirect" with "4" argument(s): "The remote server returned an error: (401) Unauthorized." Can you help?

    ReplyDelete
  4. Getting an error "Error copying file! Exception calling "Execute Query" with "0" arguments: The specified user [email protected] could not be found", where the folder and file creator no longer exists.

    Can you pls help how to handle this?

    ReplyDelete

Please Login and comment to get your questions answered!

Powered by Blogger.