powershellsharepointoffice365sharepoint-list

PowerShell script to clone a SharePoint list


This PS script should clone a SharePoint list, under the Sharepoint Online Management Shell, it should:

param(
    [Parameter(Mandatory=$true)] [string] $SourceListName,
    [Parameter(Mandatory=$true)] [string] $DestinationListName,
    [Parameter(Mandatory=$true)] [string] $User,
    [Parameter(Mandatory=$true)] [string] $Password,
    [Parameter(Mandatory=$false)] [string] $SiteURL = "https://mygroupgroup.sharepoint.com/sites/my-app",
    [Parameter(Mandatory=$false)] [bool] $ForceExisting = $false,
    [Parameter(Mandatory=$false)] [string] $CSOMPath = "C:\proj\myproj.import\Microsoft.SharePointOnline.CSOM.16.1.23912.12000\lib\net45"
)

# Convert the password to a SecureString
$SecurePassword = ConvertTo-SecureString $Password -AsPlainText -Force

# Load SharePoint CSOM Assemblies
Add-Type -Path "$CSOMPath\Microsoft.SharePoint.Client.dll"
Add-Type -Path "$CSOMPath\Microsoft.SharePoint.Client.Runtime.dll"

# Setup Credentials to connect
$Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($User, $SecurePassword)

# Setup the context
$Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
$Ctx.Credentials = $Credentials

Write-Host "Debug: Context set up."

# Declare the list of fields to ignore
$AllFieldsToIgnore = @("ID", "Attachments", "GUID", "Created", "Modified", "Author", "Editor", "_Level", "_IsCurrentVersion", "_UIVersion", "_UIVersionString", "WorkflowVersion", "owshiddenversion", "_ModerationStatus", "InstanceID", "Order", "WorkflowInstanceID", "FileRef", "FileDirRef", "Last_x0020_Modified", "Created_x0020_Date", "FSObjType", "SortBehavior", "FileLeafRef", "UniqueId", "ParentUniqueId", "SyncClientId", "ProgId", "ScopeId", "MetaInfo")

# Check if destination list already exists
$Lists = $Ctx.Web.Lists
$Ctx.Load($Lists)
$Ctx.ExecuteQuery()

Write-Host "Debug: Loaded existing lists."

$ExistingList = $Lists | Where-Object { $_.Title -eq $DestinationListName }

if ($ExistingList) {
    Write-Host "Debug: Existing list found."
    if ($ForceExisting) {
        $ExistingList.DeleteObject()
        $Ctx.ExecuteQuery()
        Write-Host "Debug: Existing list deleted."
    } else {
        $UserConfirmation = Read-Host "The list '$DestinationListName' already exists. Do you want to overwrite it? (y/n)"
        if ($UserConfirmation -eq 'y') {
            $ExistingList.DeleteObject()
            $Ctx.ExecuteQuery()
        } else {
            Write-Host "Operation cancelled."
            return
        }
    }
}

# Get the source list
try {
    $SourceList = $Ctx.Web.Lists.GetByTitle($SourceListName)
    $Ctx.Load($SourceList)
    $Ctx.ExecuteQuery()
    Write-Host "Debug: Source list loaded."
} catch {
    Write-Host "Error: Could not load source list."
    return
}

# Create the destination list
$ListInfo = New-Object Microsoft.SharePoint.Client.ListCreationInformation
$ListInfo.Title = $DestinationListName
$ListInfo.TemplateType = $SourceList.BaseTemplate
$DestinationList = $Ctx.Web.Lists.Add($ListInfo)
$Ctx.ExecuteQuery()

# Get items from the source list
$Query = New-Object Microsoft.SharePoint.Client.CamlQuery
$SourceItems = $SourceList.GetItems($Query)
$Ctx.Load($SourceItems)
$Ctx.ExecuteQuery()

# Copy items to the destination list
foreach ($Item in $SourceItems) {
    $NewItemInfo = New-Object Microsoft.SharePoint.Client.ListItemCreationInformation
    $NewItem = $DestinationList.AddItem($NewItemInfo)
    $Ctx.Load($NewItem)
    $Ctx.ExecuteQuery()

    foreach ($Field in $Item.FieldValues.Keys) {
        # Skip system fields
        if ($AllFieldsToIgnore -notcontains $Field) {
            # Check for null values
            if ($null -ne $Item[$Field]) {
                # Detect field type and handle accordingly
                $FieldType = $Item[$Field].GetType().Name
                switch ($FieldType) {
                    "FieldLookupValue" {
                        $LookupValue = New-Object Microsoft.SharePoint.Client.FieldLookupValue
                        $LookupValue.LookupId = $Item[$Field].LookupId
                        $NewItem[$Field] = $LookupValue
                    }
                    "FieldUserValue" {
                        $UserValue = New-Object Microsoft.SharePoint.Client.FieldUserValue
                        $UserValue.LookupId = $Item[$Field].LookupId
                        $NewItem[$Field] = $UserValue
                    }
                    "TaxonomyFieldValue" {
                        $TaxonomyValue = New-Object Microsoft.SharePoint.Client.TaxonomyFieldValue
                        $TaxonomyValue.Label = $Item[$Field].Label
                        $TaxonomyValue.TermGuid = $Item[$Field].TermGuid
                        $NewItem[$Field] = $TaxonomyValue
                    }
                    default {
                        $NewItem[$Field] = $Item[$Field]
                    }
                }
            }
        }
    }

    $NewItem.Update()
    $Ctx.ExecuteQuery()
}

# Output success message
Write-Host "List and items cloned successfully!"

The output:

PS C:\proj\myproj.import\ps-scripts> .\clone-list.ps1 -SourceListName "source-contacts" -DestinationListName "ClonedDestContacts" -User "admin@mygroup.net" -ForceExisting $true -Password "mypassword"
Debug: Context set up.
Debug: Loaded existing lists.
Debug: Existing list found.
Debug: Existing list deleted.
Debug: Source list loaded.
Exception calling "ExecuteQuery" with "0" argument(s): "Item does not exist. It may have been deleted by another user."
At C:\proj\base-myproj.import\ps-scripts\clone-list.ps1:121 char:5
> +     $Ctx.ExecuteQuery()
> +     ~~~~~~~~~~~~~~~~~~~
CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
FullyQualifiedErrorId : ServerException

Exception calling "ExecuteQuery" with "0" argument(s): "Item does not exist. It may have been deleted by another user."
At C:\proj\base-myproj.import\ps-scripts\clone-list.ps1:121 char:5
> +     $Ctx.ExecuteQuery()
> +     ~~~~~~~~~~~~~~~~~~~
CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
FullyQualifiedErrorId : ServerException

Exception calling "ExecuteQuery" with "0" argument(s): "Item does not exist. It may have been deleted by another user."
At C:\proj\base-myproj.import\ps-scripts\clone-list.ps1:121 char:5
> +     $Ctx.ExecuteQuery()
> +     ~~~~~~~~~~~~~~~~~~~
CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
FullyQualifiedErrorId : ServerException

Solution

  • The procedure above is like to (hard)rename a list in SharePoint, By hard, I mean not only the title, but also the URL. So, here is a function that renames the URL, it uses the (third party) ShareGate PowerShell Scripting

    param(
        [Parameter(Mandatory=$true)][string]$oldListName,
        [Parameter(Mandatory=$true)][string]$newListName,
        [string]$siteUrl = "https://mygroup.sharepoint.com/sites/my-site"
    )
    
    $newListUrl = "Lists/$newListName"
    
    Import-Module PnP.PowerShell
    
    # Connect to SharePoint online site
    Connect-PnPOnline -Url $siteUrl -Interactive
    
    # Get the SharePoint list
    $list = Get-PnPList -Identity $oldListName -ErrorAction SilentlyContinue
    
    if ($null -eq $list) {
        Write-Warning "List $oldListName not found"
        Write-Host "Failure: Unable to find the list with the name $oldListName"
    } else {
        # Move SharePoint list to the new URL
        $list.Rootfolder.MoveTo($newListUrl)
        Invoke-PnPQuery
    
        # Rename List
        Set-PnPList -Identity $oldListName -Title $newListName
    
        Write-Host "Success: List $oldListName has been moved to $newListUrl and renamed to $newListName"
    }