Smart List Items Copying through PnP PowerShell - Part Two

Introduction 

 
Hi guys, If you have already tried my previous script (available here) it's time to develop scripts on a deeper level. Here is one more cool script to run so that we can copy all the list items from a Source List to Target List within the same collection. This is a smart modern way of copying list items without losing data from any column values. The amazing part of this PS script is it can handle all types of List Columns like look-ups, Metadata, other data types + attachments. I have added/updated a few more blocks of code on my PS script and present a better but more refined PnP script.
 
Script 
 
Here is the PnP Script,
  1. #Function to copy attachments between list items  
  2. Function Copy - SPOAttachments() {  
  3.         param(  
  4.             [Parameter(Mandatory = $true)][Microsoft.SharePoint.Client.ListItem] $SourceItem,  
  5.             [Parameter(Mandatory = $true)][Microsoft.SharePoint.Client.ListItem] $DestinationItem)  
  6.         Try {  
  7.             #Get All Attachments from Source list items  
  8.             $Attachments = Get - PnPProperty - ClientObject $SourceItem - Property "AttachmentFiles"  
  9.             $Attachments | ForEach - Object {  
  10.                 #Download the Attachment to Temp  
  11.                 $File = Get - PnPFile - Connection $SourceConn - Url $_.ServerRelativeUrl - FileName $_.FileName - Path $Env: TEMP - AsFile - force  
  12.                 #Add Attachment to Destination List Item  
  13.                 $FileStream = New - Object IO.FileStream(($Env: TEMP + "\"+$_.FileName),[System.IO.FileMode]::Open)  
  14.                         $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  
  15.                     }  
  16.                 }  
  17.                 Catch {  
  18.                     write - host - f Red "Error Copying Attachments:"  
  19.                     $_.Exception.Message  
  20.                 }  
  21.             }  
  22.             #Function to copy list items from one list to another  
  23.             Function Copy - SPOListItems() {  
  24.                 param(  
  25.                     [Parameter(Mandatory = $true)][Microsoft.SharePoint.Client.List] $SourceList,  
  26.                     [Parameter(Mandatory = $true)][Microsoft.SharePoint.Client.List] $DestinationList)  
  27.                 Try {  
  28.                     #Get All Items from the Source List in batches  
  29.                     Write - Progress - Activity "Reading Source..." - Status "Getting Items from Source List. Please wait..."  
  30.                     $SourceListItems = Get - PnPListItem - List $SourceList - PageSize 500 - Connection $SourceConn  
  31.                     $SourceListItemsCount = $SourceListItems.count  
  32.                     Write - host "Total Number of Items Found:"  
  33.                     $SourceListItemsCount  
  34.                     #Get fields to Update from the Source List - Skip Read only, hidden fields, content type and attachments  
  35.                     $SourceListFields = Get - PnPField - List $SourceListName | Where {  
  36.                         (-Not($_.ReadOnlyField)) - and(-Not($_.Hidden)) - and($_.InternalName - ne "ContentType") - and($_.InternalName - ne "Attachments")  
  37.                     }  
  38.                     #Loop through each item in the source and Get column values, add them to Destination[int] $Counter = 1  
  39.                     ForEach($SourceItem in $SourceListItems) {  
  40.                         $ItemValue = @ {}  
  41.                         #Map each field from source list to Destination list  
  42.                         Foreach($SourceField in $SourceListFields) {  
  43.                             #Check  
  44.                             if the Field value is not Null  
  45.                             If($SourceItem[$SourceField.InternalName] - ne $Null) {  
  46.                                 #Handle Special Fields  
  47.                                 $FieldType = $SourceField.TypeAsString  
  48.                                 If($FieldType - eq "User" - or $FieldType - eq "UserMulti" - or $FieldType - eq "Lookup" - or $FieldType - eq "LookupMulti") #People Picker or Lookup Field {  
  49.                                     $LookupIDs = $SourceItem[$SourceField.InternalName] | ForEach - Object {  
  50.                                         $_.LookupID.ToString()  
  51.                                     }  
  52.                                     $ItemValue.add($SourceField.InternalName, $LookupIDs)  
  53.                                 }  
  54.                                 ElseIf($FieldType - eq "URL") #Hyperlink {  
  55.                                     $URL = $SourceItem[$SourceField.InternalName].URL  
  56.                                     $Description = $SourceItem[$SourceField.InternalName].Description  
  57.                                     $ItemValue.add($SourceField.InternalName, "$URL, $Description")  
  58.                                 }  
  59.                                 ElseIf($FieldType - eq "TaxonomyFieldType" - or $FieldType - eq "TaxonomyFieldTypeMulti") #MMS {  
  60.                                     $TermGUIDs = $SourceItem[$SourceField.InternalName] | ForEach - Object {  
  61.                                         $_.TermGuid.ToString()  
  62.                                     }  
  63.                                     $ItemValue.add($SourceField.InternalName, $TermGUIDs)  
  64.                                 }  
  65.                                 Else {  
  66.                                     #Get Source Field Value and add to Hashtable  
  67.                                     $ItemValue.add($SourceField.InternalName, $SourceItem[$SourceField.InternalName])  
  68.                                 }  
  69.                             }  
  70.                         }  
  71.                         Write - Progress - Activity "Copying List Items:" - Status "Copying Item ID '$($SourceItem.Id)' from Source List ($($Counter) of $($SourceListItemsCount))" - PercentComplete(($Counter / $SourceListItemsCount) * 100)  
  72.                         #Copy column value from source to Destination  
  73.                         $NewItem = Add - PnPListItem - List $DestinationList - Values $ItemValue  
  74.                         #Copy Attachments  
  75.                         Copy - SPOAttachments - SourceItem $SourceItem - DestinationItem $NewItem  
  76.                         Write - Host "Copied Item ID from Source to Destination List:$($SourceItem.Id) ($($Counter) of $($SourceListItemsCount))"  
  77.                         $Counter++  
  78.                     }  
  79.                 }  
  80.                 Catch {  
  81.                     Write - host - f Red "Error:"  
  82.                     $_.Exception.Message  
  83.                 }  
  84.             }  
  85.             #Set Parameters  
  86.             $SourceSiteURL = "https://abcd.sharepoint.com/sites/ArchivalDevelopmentTest"  
  87.             $SourceListName = "CountriesArchive"  
  88.             $DestinationSiteURL = "https://abcd.sharepoint.com/sites/ArchivalDevelopmentTest"  
  89.             $DestinationListName = "CountriesArchive2"  
  90.             #Connect to Source and destination sites  
  91.             $SourceConn = Connect - PnPOnline - Url $SourceSiteURL - UseWebLogin - ReturnConnection  
  92.             $SourceList = Get - PnPList - Identity $SourceListName - Connection $SourceConn  
  93.             $DestinationConn = Connect - PnPOnline - Url $DestinationSiteURL - UseWebLogin - ReturnConnection  
  94.             $DestinationList = Get - PnPList - Identity $DestinationListName - Connection $DestinationConn  
  95.             #Call the Function to Copy List Items between Lists  
  96.             Copy - SPOListItems - SourceList $SourceList - DestinationList $DestinationList  
Just configure the above highlighted areas as per your requirement.
 
Steps 
 
Open Windows ISE >> Open the above script file >> Run Script or F5 
 
Advantages
 
You can use the above script for a cool, smooth archival process = Copy + Remove as you know how to remove List Items batchwise using PowerShell once above Copying is finished.
 
Applicable for both modern and classic experience.
 
Drawbacks
 
Copying Duplicate values if we run it multiple times
 
Can copy ListItems to another List within the same Site Collection only as SP Context can read one Site Coll at a time.
 
CrossSites not supported
 
Other Ideas:- Go with Migration Tools if you want to go with Site Collections from different Tenants on SP Online.
 
Cheers!