Different approach to SharePoint Migration using Powershell

Many of my SharePoint requirements recently involve moving content around or retro-fitting metadata from legacy systems. Mini-migrations might be the term I'm looking for there.

I have a migration tool which is good for bulk movement of content e.g. from File Shares into SharePoint. The issue is that it's not flexible enough to place content from the same source into different libraries or folders based on metadata, or fill in metadata from legacy systems.

This is where I turned to Powershell. Unfortunately the community scripts I found tended to be unreliable. For example I found one promising script that recurses through libraries to copy the content from one location to another. However it was very slow, and inexplicably missed some items. (Tried to debug it, but never found out why the files were missed). To cut a long story short I created my own entirely different approach which I've not used for many months. It's reliable and performant. I tend to bulk move content into a staging library, then 'Export to Excel' the IDs and other metadata from that staging lib, fill in extra properties and then use the script to put content and metadata into its final home.

The key to it is that rather than use recursion I get items by ID; $sList.GetItemById This performs really well, and dispenses with looping through a library structure.

The other key command in the script is Import-CSV. This allows me to tee up my metadata in a spreadsheet which my script will be able to use when setting item properties.

To get item ID I make sure the ID column is in a given view then export to excel. This base spreadsheet is then embellished with extra metadata and perhaps target folder ID (GetItemById works for folders or Docsets as well as items!).

The example script is below. I'm not holding it up as a masterpiece of Powershell, I'm no powershell maven. However it could prove valuable in your SharePoint toolkit.

To use it, you need to provide a CSV file with a source item ID, and whatever other metadata is needed. My file might look like this (though thousands of rows are fine);

1,31,15/01/2015,4353
2,31,05/06/2015,3421
4,31,15/03/2015,4355
7,31,07/07/2015,5921

Here's the script;

 

$ErrorActionPreference = "Stop"
$ver = $host | select version 
if($Ver.version.major -gt 1) {$Host.Runspace.ThreadOptions = "ReuseThread"} 
if(!(Get-PSSnapin Microsoft.SharePoint.PowerShell -ea 0)) 
{ 
	Write-Progress -Activity "Loading Modules" -Status "Loading Microsoft.SharePoint.PowerShell" 
	Add-PSSnapin Microsoft.SharePoint.PowerShell 
} 

$SourceWebURL = "http://SomeSiteCollection/subweb"
$SourceLibraryTitle = "Migration Source Library"
$DestinationWebURL = "http://SomeOtherSiteCollection/subweb"
$DestinationLibraryTitle = "My Target Library"
$updateItems = Import-CSV C:\Users\Public\Documents\MigrationItems.csv -Header SourceItemID,TargetFolderID,InvoiceNumber,Amount

$sWeb = Get-SPWeb $SourceWebURL
$sList = $sWeb.Lists | ? {$.Title -eq $SourceLibraryTitle}
$dWeb = Get-SPWeb $DestinationWebURL
$dList = $dWeb.Lists | ? {$
.Title -like $DestinationLibraryTitle}

$CurrentFolder = $updateItems[0].TargetFolderID
$DestFolder = $dList.Folders | ? {$_.ID -eq $CurrentFolder}

foreach ($updateItem in $updateItems)
{
Write-Host $updateItem.SourceItemID #Give me some idea what's happening!
#Get new folder if necessary
if (!$updateItem.TargetFolderID.CompareTo($CurrentFolder.ToString()) -eq 0)
{
$CurrentFolder = $updateItem.TargetFolderID
$DestFolder = $dList.Folders | ? {$_.ID -eq $CurrentFolder}
}

#Get source binary
$TargetItem = $sList.GetItemById($updateItem.SourceItemID)

#Copy to Dest and set properties
$sBytes = $TargetItem.File.OpenBinary()

$dFile = $DestFolder.Folder.Files.Add($TargetItem.Name, $sBytes, $true)

$AllFields = $TargetItem.Fields | ? {!($_.sealed)} 
			 
foreach($Field in $AllFields) 
{ 
	if($TargetItem.Properties[$Field.Title]) 
	{ 
		if(!($dFile.Properties[$Field.Title])) 
		{ 
			$dFile.AddProperty($Field.Title, $TargetItem.Properties[$Field.Title]) 
		} 
		else 
		{ 
			$dFile.Properties[$Field.Title] = $TargetItem.Properties[$Field.Title] 
		} 
	} 
} 

if((!($dFile.Properties['Amount'])) -and ($updateItem.Amount -gt 0)) 
{ 
	$dFile.AddProperty('Amount', $updateItem.Amount) 
} 
else 
{ 
	if ($updateItem.Amount -gt 0)
	{	  
		$dFile.Properties['Amount'] = $updateItem.Amount
	}
} 

if((!($dFile.Properties['InvoiceNo'])) -and ($updateItem.InvoiceNumber -gt 0)) 
{ 
	$dFile.AddProperty('InvoiceNo', $updateItem.InvoiceNumber) 
} 
else 
{ 
	if ($updateItem.Amount -gt 0)
	{	  
		$dFile.Properties['InvoiceNo'] = $updateItem.InvoiceNumber
	}
} 

$dFile.Update()

}