How do you use powershell to copy a sharepoint online site to another sharepoint online site?

SteveH 0 Reputation points
2023-09-13T12:59:42.1366667+00:00

Hi,

How do you use powershell to copy a sharepoint online site to another sharepoint online site?

I have a site already created and a 2nd site using the same base template created - I want to copy the original site - that has been customised completely to the 2nd site...

How do you do this please?

Thanks!

Microsoft 365 and Office Install, redeem, activate For business Windows
Microsoft 365 and Office SharePoint For business Windows
Windows for business Windows Server User experience PowerShell
{count} votes

2 answers

Sort by: Most helpful
  1. Ling Zhou_MSFT 23,620 Reputation points Microsoft External Staff
    2023-09-14T02:59:54.9966667+00:00

    Hi @SteveH,

    Thank you for posting in this community.

    Here I am using PnP PowerShell to copy the structure of a website to another website. This PnP PowerShell only copies the structure of the source website and not the content. Also, you can’t extract the site template of a root site and apply it on subsites.

    Prerequisites: Install PnP PowerShell

    Check if the classic PnP PowerShell module is installed with the below command:

    Get-Module SharePointPnPPowerShellOnline -ListAvailable | Select-Object Name,Version
    

    This returns the Name and version of legacy PnP PowerShell installed on the machine (If any). Uninstall Any previous PnP PowerShell Modules for SharePoint Online installed:

    Uninstall-Module SharePointPnPPowerShellOnline -Force -AllVersions
    

    Install the New PnP PowerShell Module:

    Install-Module PnP.PowerShell
    

    If you need install PnP PowerShell offline, you can also use setup files Download the PnP PowerShell Offline Installation package and install it with:

    Install-Package C:\Path\File.nupkg
    

    Step1: Get the Source Site Schema XML

    This cmdlet gets all artifacts from the source site, such as content types, site columns, term store, list and libraries, theme, pages, etc., into the given template XML file. We also have a switch -includesitegroups to include site security so that your target site will have unique permissions.

    #Set variables
    $SiteURL = "Your source site url" 
    $SchmaXMLPath = "YourPath\SiteSchema.xml"
     
    #Connect to PnP Online
    Connect-PnPOnline -Url $siteUrl -Interactive
     
    #Get Site Schema
    Get-PnPSiteTemplate -Out ($SchmaXMLPath) -PersistBrandingFiles -PersistPublishingFiles 
    

    Step 2: Create a New Site Collection or Subsite

    I notice you have already done this step, so you can skip it then. I added this step here to ensure the integrity of the process of copying the site.

    Step 3: Apply the Site Schema into Target Site

    #Set variables
    $SiteURL = "Your target site url" 
    $SchmaXMLPath = "Your Path\SiteSchema.xml"  
    #Connect to PnP Online
    Connect-PnPOnline -Url $SiteURL -Interactive
     
    #Apply Pnp Provisioning Template
    Invoke-PnPSiteTemplate -Path $SchmaXMLPath -ClearNavigation
    

    If you need to copy the content of the website, then you can save the original website as a template while including his content and then use this template to create the target website. Refer to this thread for the exact steps.


    If the answer is helpful, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    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. Ling Zhou_MSFT 23,620 Reputation points Microsoft External Staff
    2023-09-15T08:26:49.8266667+00:00

    Hi @SteveH,

    Thank you for your reply.

    We regret to inform you that we have not been able to replicate the structure and content of a modern SharePoint Online site together. The only way to fulfill this requirement is to save the site as a template with content and create a new site with this template. However, this method is not available for SharePoint Modern sites.

    Image

    Reference: Save, download, and upload a SharePoint site as a template

    Here I provide you with the method to copy List and Library using PnP PowerShell:

    1.Copy List from one site to another using PowerShell

    #SharePoint Online: Copy List from one site to another using PowerShell
    #Parameters
    $SourceSiteURL = "https://crescent.sharepoint.com/sites/Source"
    $TargetSiteURL = "https://crescent.sharepoint.com/sites/Target"
    $ListName= "Projects"
    $TemplateFile ="$env:TEMP\Template.xml"
     
    #Connect to the Source Site
    Connect-PnPOnline -Url $SourceSiteURL -Interactive
     
    #Create the Template
    Get-PnPSiteTemplate -Out $TemplateFile -ListsToExtract $ListName -Handlers Lists
     
    #Get Data from source List
    Add-PnPDataRowsToSiteTemplate -Path $TemplateFile -List $ListName
     
    #Connect to Target Site
    Connect-PnPOnline -Url $TargetSiteURL -Interactive
     
    #Apply the Template
    Invoke-PnPSiteTemplate -Path $TemplateFile
    
    

    2.Copy Library from one site to another using PowerShell

    Custom script must be enabled both in source and the destination sites, before running this script.

    #Parameters
    $TenantAdminURL= "https://crescent-admin.sharepoint.com"
    $SiteURL= "https://crescent.sharepoint.com/sites/Procurement"
     
    #Connect to Tenant Admin
    Connect-PnPOnline $TenantAdminURL -Interactive
     
    #Enable Custom Scripting by turning OFF Deny Flag
    Set-PnPTenantSite -Identity $SiteURL -DenyAddAndCustomizePages:$false
    
    #Function to Copy library to Another site
    Function Copy-PnPLibrary
    {
        param (
        [parameter(Mandatory=$true, ValueFromPipeline=$true)][string]$SourceSiteURL,
        [parameter(Mandatory=$true, ValueFromPipeline=$true)][string]$DestinationSiteURL,
        [parameter(Mandatory=$true, ValueFromPipeline=$true)][string]$SourceLibraryName,
        [parameter(Mandatory=$true, ValueFromPipeline=$true)][string]$DestinationLibraryName
        )
       
        Try {
        #Connect to the Source Site
        $SourceConn = Connect-PnPOnline -URL $SourceSiteURL -Interactive -ReturnConnection
        $SourceCtx = $SourceConn.Context
       
        #Get the Source library
        $SourceLibrary =  Get-PnPList -Identity $SourceLibraryName -Includes RootFolder -Connection $SourceConn
       
        #Get the List Template
        $SourceRootWeb = $SourceCtx.Site.RootWeb
        $SourceListTemplates = $SourceCtx.Site.GetCustomListTemplates($SourceRootWeb)
        $SourceCtx.Load($SourceRootWeb)
        $SourceCtx.Load($SourceListTemplates)
        $SourceCtx.ExecuteQuery()
        $SourceListTemplate = $SourceListTemplates | Where {$_.Name -eq $SourceLibrary.id.Guid}
        $SourceListTemplateURL = $SourceRootWeb.ServerRelativeUrl+"/_catalogs/lt/"+$SourceLibrary.id.Guid+".stp"
       
        #Remove the List template if exists   
        If($SourceListTemplate)
        {
            #Remove-PnPFile -ServerRelativeUrl $SourceListTemplateURL -Recycle -Force -Connection $SourceConn
            $SourceListTemplate = Get-PnPFile -Url $SourceListTemplateURL -Connection $SourceConn
            $SourceListTemplate.DeleteObject()
            $SourceCtx.ExecuteQuery()
        }
        Write-host "Creating List Template from Source Library..." -f Yellow -NoNewline
        $SourceLibrary.SaveAsTemplate($SourceLibrary.id.Guid, $SourceLibrary.id.Guid, [string]::Empty, $False)
        $SourceCtx.ExecuteQuery()
        Write-host "Done!" -f Green
       
        #Reload List Templates to Get Newly created List Template
        $SourceListTemplates = $SourceCtx.Site.GetCustomListTemplates($SourceRootWeb)
        $SourceCtx.Load($SourceListTemplates)
        $SourceCtx.ExecuteQuery()
        $SourceListTemplate = $SourceListTemplates | Where {$_.Name -eq $SourceLibrary.id.Guid}    
       
        #Connect to the Destination Site
        $DestinationConn = Connect-PnPOnline -URL $DestinationSiteURL -Interactive -ReturnConnection
        $DestinationCtx = $DestinationConn.Context
        $DestinationRootWeb = $DestinationCtx.Site.RootWeb
        $DestinationListTemplates = $DestinationCtx.Site.GetCustomListTemplates($DestinationRootWeb)
        $DestinationCtx.Load($DestinationRootWeb)
        $DestinationCtx.Load($DestinationListTemplates)
        $DestinationCtx.ExecuteQuery()   
        $DestinationListTemplate = $DestinationListTemplates | Where {$_.Name -eq $SourceLibrary.id.Guid}
        $DestinationListTemplateURL = $DestinationRootWeb.ServerRelativeUrl+"/_catalogs/lt/"+$SourceLibrary.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()       
        }
       
        #Copy List Template from source to the destination site
        Write-host "Copying List Template from Source to Destination Site..." -f Yellow -NoNewline
        Copy-PnPFile -SourceUrl $SourceListTemplateURL -TargetUrl ($DestinationRootWeb.ServerRelativeUrl+"/_catalogs/lt") -Force -OverwriteIfAlreadyExists -Connection $SourceConn
        Write-host "Done!" -f Green
       
        #Reload List Templates to Get Newly created List Template
        $DestinationListTemplates = $DestinationCtx.Site.GetCustomListTemplates($DestinationRootWeb)
        $DestinationCtx.Load($DestinationListTemplates)
        $DestinationCtx.ExecuteQuery()
        $DestinationListTemplate = $DestinationListTemplates | Where {$_.Name -eq $SourceLibrary.id.Guid}
       
        #Create the destination library from the list template
        Write-host "Creating New Library in the Destination Site..." -f Yellow -NoNewline
        If(!(Get-PnPList -Identity $DestinationLibraryName -Connection $DestinationConn))
        {
            #Create the destination library
            $ListCreation = New-Object Microsoft.SharePoint.Client.ListCreationInformation
            $ListCreation.Title = $DestinationLibraryName
            $ListCreation.ListTemplate = $DestinationListTemplate
            $DestinationList = $DestinationCtx.Web.Lists.Add($ListCreation)
            $DestinationCtx.ExecuteQuery()
            Write-host "Library '$DestinationLibraryName' created successfully!" -f Green
        }
        Else
        {
            Write-host "Library '$DestinationLibraryName' already exists!" -f Yellow
        }
      
        Write-host "Copying Files and Folders from the Source to Destination Site..." -f Yellow   
        $DestinationLibrary = Get-PnPList $DestinationLibraryName -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 $SourceLibraryName -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 -Connection $SourceConn
                Write-host "`tCopied $($_.FileSystemObjectType) '$($_.FieldValues.FileRef)' Successfully!" -f Green    
            }
        }
       
        #Cleanup List Templates in source and destination sites
        $SourceListTemplate = Get-PnPFile -Url $SourceListTemplateURL -Connection $SourceConn
        $DestinationListTemplate = Get-PnPFile -Url $DestinationListTemplateURL -Connection $DestinationConn
        $SourceListTemplate.DeleteObject()
        $DestinationListTemplate.DeleteObject()
        $SourceCtx.ExecuteQuery()
        $DestinationCtx.ExecuteQuery()
        #Remove-PnPFile -ServerRelativeUrl $SourceListTemplateURL -Recycle -Force -Connection $SourceConn
        #Remove-PnPFile -ServerRelativeUrl $DestinationListTemplateURL -Recycle -Force -Connection $DestinationConn
        }
        Catch {
            write-host -f Red "Error:" $_.Exception.Message
        }
    }
       
    #Parameters
    $SourceSiteURL = "https://crescent.sharepoint.com/sites/Retail"
    $DestinationSiteURL = "https://crescent.sharepoint.com/sites/warehouse"
    $SourceLibraryName = "Invoices"
    $DestinationLibraryName = "Invoices V2"
       
    #Call the function to copy document library to another site
    Copy-PnPLibrary -SourceSiteURL $SourceSiteURL -DestinationSiteURL $DestinationSiteURL -SourceLibraryName $SourceLibraryName -DestinationLibraryName $DestinationLibraryName
    
    
    #Read more: https://www.sharepointdiary.com/2020/03/sharepoint-online-copy-document-library-to-another-site.html#ixzz8DMhqNNUr
    

    You can apply the structure of the site to the target site first as I provided at the beginning and then copy the content.

    I recognize that this operation is complex, but it is the only way to replicate the structure and content of the site. I hope my answer helps you and thank you for your kind understanding.


    If the answer is helpful, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    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.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.