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?

Are you looking for a way to copy files between site collections in SharePoint Online? Depending on the situation, this can be done using a few different methods. This blog post will provide you with step-by-step instructions to copy files from one site to another manually. We’ll also show you how to copy files between site collections using PowerShell to automate the process.

Here is how to copy files between document libraries in SharePoint Online:

  1. Navigate to your SharePoint Online document library >>  Select the file(s) 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
  2. 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
  3. 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 on the same site or between different site collections.

PowerShell to Copy Files between Site Collections in SharePoint Online:

PowerShell is an efficient way to copy SharePoint Online files if you want to quickly transfer files between different site collections. To copy files using PowerShell, you will first need to connect to both the source and destination sites. Once you have connected to both sites, you can use the CSOM methods OpenBinaryDirect and SaveBinaryDirect to copy files from the source site to the destination site.

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 the source site/site collection to the target site/site collection.

Copy All Files and Folder from One Library to Another Library between different Sites:

PowerShell is a powerful scripting language that can be used to automate many tasks, including copying files between SharePoint sites. Here is how to copy all files and folders from one library to another in SharePoint Online:

#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 

It is worth noting that 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 library folders from one site collection to another using the PnP PowerShell cmdlet Copy-PnPFile. Make sure you replace the $SiteURL with the URL of the source site, $TargetFolderURL with the server-relative URL of the folder destination site, and $SourceFolderURL with the server-relative URL of the folder you want to copy.

#Parameters
$SiteURL = "https://crescent.sharepoint.com/sites/Marketing"
$SourceFolderURL = "/sites/marketing/Branding/Logos"
$TargetFolderURL = "/sites/Purchase/Shared Documents"
 
#Connect to PnP Online
Connect-PnPOnline -Url $SiteURL -Interactive
 
#Copy All Files and Folders between source and target folders
Copy-PnPFile -SourceUrl $SourceFolderURL -TargetUrl $TargetFolderURL -Force -OverwriteIfAlreadyExists

This script copies the folder “Logos” from the “Branding” library of the Marketing site to the “Shared Documents” library of the Purchases site. You can specify any existing folder in the destination site as well.

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!

21 thoughts on “SharePoint Online: Copy Files Between Site Collections using PowerShell

  • I am trying to copy a document from “DocA” that exists in multiple subsites (same library name), to another site collection under DocB with “subsite name” as a column that it is being copied from.
    please share your thoughts.

    Reply
  • Hi there,
    Great script, thank you! I’m trying to use ‘Copy All Files and Folder from One Library to Another Library between different Site Collections’ script to copy all files and folders to a subfolder on another site collection. It works for files after I modified $TargetFileURL = $TargetFolder.ServerRelativeUrl+”/”+$SourceLibraryName+”/”+$SourceFile.Name but struggling with subfolders from the source. They copy to the root without files in them. How can I make them copy to the same subfolder in the target library?

    Reply
  • Hi,

    I wanted to move a file from One List to another list (This list is inside a subsite of this site collection). tried with File.Move it didn’t work. Can you please suggest?

    Reply
  • Thank you Salaudeen this is very helpful. I am trying to copy just the files from a site library sub folder to another site library and not finding it possible. Any guidance with this I would be grateful. Thanks Dave.

    Reply
  • Hi,
    Thanks for your work because the script works like a charm, but, can works with personalized metadata or document set?
    Thanks in advance anyone have a similar necesity?
    Regards

    Reply
  • I have several documents that I need to copy from one library in a site collection to another library in a different site collection based on Modified date. Is there a way to use a CSV for source and target?

    Reply
  • Hi,
    Thanks for your work because the script works.
    However, I would like to ask if you have any suggestion on how to keep the Author and Created values for the folders.
    It’s working great for the files but not to the folders.
    I would really appreciate if you could provide some ideas on that.

    Thanks in advance

    Reply
  • So, I was able to make it works and it’s working but there is just one detail that is not working: when the script copies a folder or sub-folder it’s not keeping the author and the Created By and Modified by original dates.
    Any suggestion to keep the original metadata for folders?
    Thanks

    Reply
  • Thanks for the post and the script. It’s really good but I am having some issues to making it work.
    The only problem is that is not keeping the Author and the Editor.
    When I use the script as is I am getting an error message:

    Error Copying File! Exception calling “ExecuteQuery” with “0” argument(s): “The specified user could not be found.”

    If I change these lines of your code:
    $Author =$TargetFolder.Context.web.EnsureUser($SourceListItem[“Author”].Email)
    $TargetListItem[“Author”] = $Author
    $Editor =$TargetFolder.Context.web.EnsureUser($SourceListItem[“Editor”].Email)
    $TargetListItem[“Editor”] = $Editor
    By this code:
    $TargetListItem[“Author”] = $SourceListItem[“Author”].Email
    $TargetListItem[“Editor”] = $SourceListItem[“Editor”].Email

    The error message does not show and the items are copied but the metadata is not being kept for the Author and the Editor.
    Do you have any suggestions?
    Thanks in advance

    Reply
    • Just to provide you with some more information:
      I am connecting to 2 SharePoint Online sites, not on premises.
      Also, I found that the web member is not part of the Context.
      When I type in this: $TargetFolder.Context. (I cannot get the “web” option but only “WebRequestExecutorFactory”)
      Perhaps that’s the issue and it’s preventing the below instruction to work as expected:
      $Author =$TargetFolder.Context.web.EnsureUser($SourceListItem[“Author”].Email)

      It’s just a thought, I am not sure.
      Any help is very appreciated.

      Reply
  • For some reason, I keep getting “The sign-in name or password does not match one in the Microsoft account system.”, even though I am an admin on the site and I know I’m typing the right credentials. Anything I’m doing wrong?

    Reply
  • Getting an error “Error copying file! Exception calling “Execute Query” with “0” arguments: The specified user xxx@abc.com could not be found”, where the folder and file creator no longer exists.

    Can you pls help how to handle this?

    Reply
  • 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?

    Reply
  • 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

    Reply
    • 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.

      Reply
  • 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!

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *