Last Updated on February 17, 2024
Are you looking for a way to copy list items from one SharePoint site to another?
Copying one list item or two by manually typing them out is not really a problem. Unfortunately, that is not the case if there are hundreds or even thousands of list items.
The good news is, it’s not really that hard to do. In this article, you will learn how to copy list items to another site in SharePoint.
Let’s get started.
Step 1: Create a List From an Existing Template
This step is important since you will be creating a copy of the list without the contents on another site.
To get started, simply navigate to the site where you want to copy the list items to.
On the home page, add a new list by clicking the “New” button on the menu and selecting the “List” option.

Then, select the option to create a list from an existing list.
Simply select the site and the specific list you want to copy and click the “Next” button.

You will then have to enter the name for the list. Feel free to add some description like mentioning where the list was copied from.
After that, you will have a copy of that list on your other site with the same columns and settings.
Sign up for exclusive updates, tips, and strategies
Step 2: Copy List Items Using PowerShell
This step is a little complicated since there’s code involved. However, all you need to do is copy the codes below and change the parameters for the web URL, the source list name, and the target list name.
If you didn’t follow the first step since you already created a separate list, make sure that both lists (source and target) have the same columns. Also, be sure to open PowerShell as an administrator.
Now, if all is well, copy the script below to copy all the list items from one list to another but make sure to modify the configuration variables (source):
#Option 1:
Install-Module -Name PnP.PowerShell
#Connect to the Source Site
Connect-PnPOnline -Url https://[tenantnamehere].sharepoint.com/sites/[source site name] -Interactive
#Create the Template
Get-PnPSiteTemplate -Out C:\Temp\Lists.xml -ListsToExtract "List 1", "List 2" -Handlers Lists
Get the List Data
Add-PnPDataRowsToSiteTemplate -Path C:\Temp\Lists.xml -List "List 1"
Add-PnPDataRowsToSiteTemplate -Path C:\Temp\Lists.xml -List "List 2"
#Connect to Target Site
Connect-PnPOnline -Url https://[tenantnamehere].sharepoint.com/sites/[destinationsite name] -Interactive
#Apply the Template
Invoke-PnPSiteTemplate -Path "C:\Temp\Lists.xml"
If the list items you want to copy have attachments, use this code (source):
#Option 2:
#Function to copy attachments between list items
Function Copy-SPOAttachments()
{
param
(
[Parameter(Mandatory=$true)] [Microsoft.SharePoint.Client.ListItem] $SourceItem,
[Parameter(Mandatory=$true)] [Microsoft.SharePoint.Client.ListItem] $DestinationItem
)
Try {
#Get All Attachments from Source list items
$Attachments = Get-PnPProperty -ClientObject $SourceItem -Property "AttachmentFiles"
$Attachments | ForEach-Object {
#Download the Attachment to Temp
$File = Get-PnPFile -Connection $SourceConn -Url $_.ServerRelativeUrl -FileName $_.FileName -Path $Env:TEMP -AsFile -force
#Add Attachment to Destination List Item
$FileStream = New-Object IO.FileStream(($Env:TEMP+"\"+$_.FileName),[System.IO.FileMode]::Open)
$AttachmentInfo = New-Object -TypeName Microsoft.SharePoint.Client.AttachmentCreationInformation
$AttachmentInfo.FileName = $_.FileName
$AttachmentInfo.ContentStream = $FileStream
$AttachFile = $DestinationItem.AttachmentFiles.Add($AttachmentInfo)
Invoke-PnPQuery -Connection $DestinationConn
#Delete the Temporary File
Remove-Item -Path $Env:TEMP\$($_.FileName) -Force
}
}
Catch {
write-host -f Red "Error Copying Attachments:" $_.Exception.Message
}
}
#Function to copy list items from one list to another
Function Copy-SPOListItems()
{
param
(
[Parameter(Mandatory=$true)] [Microsoft.SharePoint.Client.List] $SourceList,
[Parameter(Mandatory=$true)] [Microsoft.SharePoint.Client.List] $DestinationList
)
Try {
#Get All Items from the Source List in batches
Write-Progress -Activity "Reading Source..." -Status "Getting Items from Source List. Please wait..."
$SourceListItems = Get-PnPListItem -List $SourceList -PageSize 500 -Connection $SourceConn
$SourceListItemsCount= $SourceListItems.count
Write-host "Total Number of Items Found:"$SourceListItemsCount
#Get fields to Update from the Source List - Skip Read only, hidden fields, content type and attachments
$SourceListFields = Get-PnPField -List $SourceList -Connection $SourceConn | Where { (-Not ($_.ReadOnlyField)) -and (-Not ($_.Hidden)) -and ($_.InternalName -ne "ContentType") -and ($_.InternalName -ne "Attachments") }
#Loop through each item in the source and Get column values, add them to Destination
[int]$Counter = 1
ForEach($SourceItem in $SourceListItems)
{
$ItemValue = @{}
#Map each field from source list to Destination list
Foreach($SourceField in $SourceListFields)
{
#Check if the Field value is not Null
If($SourceItem[$SourceField.InternalName] -ne $Null)
{
#Handle Special Fields
$FieldType = $SourceField.TypeAsString
If($FieldType -eq "User" -or $FieldType -eq "UserMulti") #People Picker Field
{
$PeoplePickerValues = $SourceItem[$SourceField.InternalName] | ForEach-Object { $_.Email}
$ItemValue.add($SourceField.InternalName,$PeoplePickerValues)
}
ElseIf($FieldType -eq "Lookup" -or $FieldType -eq "LookupMulti") # Lookup Field
{
$LookupIDs = $SourceItem[$SourceField.InternalName] | ForEach-Object { $_.LookupID.ToString()}
$ItemValue.add($SourceField.InternalName,$LookupIDs)
}
ElseIf($FieldType -eq "URL") #Hyperlink
{
$URL = $SourceItem[$SourceField.InternalName].URL
$Description = $SourceItem[$SourceField.InternalName].Description
$ItemValue.add($SourceField.InternalName,"$URL, $Description")
}
ElseIf($FieldType -eq "TaxonomyFieldType" -or $FieldType -eq "TaxonomyFieldTypeMulti") #MMS
{
$TermGUIDs = $SourceItem[$SourceField.InternalName] | ForEach-Object { $_.TermGuid.ToString()}
$ItemValue.add($SourceField.InternalName,$TermGUIDs)
}
Else
{
#Get Source Field Value and add to Hashtable
$ItemValue.add($SourceField.InternalName,$SourceItem[$SourceField.InternalName])
}
}
}
#Copy Created by, Modified by, Created, Modified Metadata values
$ItemValue.add("Created", $SourceItem["Created"]);
$ItemValue.add("Modified", $SourceItem["Modified"]);
$ItemValue.add("Author", $SourceItem["Author"].Email);
$ItemValue.add("Editor", $SourceItem["Editor"].Email);
Write-Progress -Activity "Copying List Items:" -Status "Copying Item ID '$($SourceItem.Id)' from Source List ($($Counter) of $($SourceListItemsCount))" -PercentComplete (($Counter / $SourceListItemsCount) * 100)
#Copy column value from Source to Destination
$NewItem = Add-PnPListItem -List $DestinationList -Values $ItemValue
#Copy Attachments
Copy-SPOAttachments -SourceItem $SourceItem -DestinationItem $NewItem
Write-Host "Copied Item ID from Source to Destination List:$($SourceItem.Id) ($($Counter) of $($SourceListItemsCount))"
$Counter++
}
}
Catch {
Write-host -f Red "Error:" $_.Exception.Message
}
}
#Set Parameters
$SourceSiteURL = "https://[tenantnamehere].sharepoint.com/sites/[sitenamehere]"
$SourceListName = "[listnamehere]"
$DestinationSiteURL = "https://[tenantnamehere].sharepoint.com/sites/[sitenamehere]"
$DestinationListName = "[listnamehere]"
#Connect to Source and destination sites
$SourceConn = Connect-PnPOnline -Url $SourceSiteURL -Interactive -ReturnConnection
$SourceList = Get-PnPList -Identity $SourceListName -Connection $SourceConn
$DestinationConn = Connect-PnPOnline -Url $DestinationSiteURL -Interactive -ReturnConnection
$DestinationList = Get-PnPList -Identity $DestinationListName -Connection $DestinationConn
#Call the Function to Copy List Items between Lists
Copy-SPOListItems -SourceList $SourceList -DestinationList $DestinationList
That’s it! If you’re confused with something along the way, don’t hesitate to drop a comment with your questions. You can also reach me directly by sending me a message on my contact page.
I need a way to copy all list items from one list to another with list item comments.
I am looking into this to see if it’s possible. If it is, I’ll write a blog post on it.
Did you ever find a solution for copying all list items from one site to the other without having to export the list items into Excel and then import them into the newly created worklist. We want to keep comments, attachments, etc.
I would use a migration tool in the scenario you mentioned.
Thanks for the info. Seemed like a good solution but unfortunately, I ran into permission errors constantly along the way. When you connect to the PnpOnline module using Connect-PnPOnline make sure you add “-interactive” at the end or else the module will never authenticate to the O365 tenant. Then, even after that I kept getting 403 forbidden errors and others. I tried giving the app admin permissions but that did not help.
Hi Adel,
I do agree with adding “-interactive” to the Connect-PnPOnline command. I also just tested the options I have listed above and did not run into any issues.
I finally got this working and it worked well. Just got to make sure to have proper admin access and give the PnPmodule proper admin access as well.
Awesome! I added a note above to open PowerShell as an administrator. Thanks for your feedback.
Giving credit to the second script owner would be appreciated Ryan!
Sources are listed above. If I’m missing something let me know. Happy to make updates where needed and give credit where due!
My source and destination sites name are the same. I’m not sure why it just duplicated the list in source instead of copying into the target… How do I tell it to choose a list for applying the template?
If they are two different sites then they have to have unique URLs so check those.
Is it possible to add another list to an existing list? I created the first one using the import from excel option. This has info for January, but now I want to add items for February. Is there a way I can import from excel again or add multiple items.
I would need to do this frequently\monthly.
Is there a better option.
Yes, you could copy and paste or use Power Automate.
how do you copy the content of the List
Hey, one year ago script worked fine and got copied everything correctly.
Now it does not work, script is authenticating with my account, I will insert credentials and scripts finds the number of items, but after that it fails
Total Number of Items Found: 5209
Error: The current connection holds no SharePoint context. Please use one of the Connect-PnPOnline commands which uses the -Url argument to connect.
When I insert Connect-PnPOnline before activating the script it says:
Error: The object is used in the context different from the one associated with the object.