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
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"
}