Migrating large corporate SharePoint intranets

Verification

For most of my migration projects I have used ShareGate very successfully as the primary migration tool. ShareGate has got quite excessive logging to get insights about the quality of the migration. But what if ShareGate logs that everything is fine, but when in fact it is not? To get better confidence about the quality of ShareGate (which is just fine in my opinion) I also developed a PowerShell script to check if all documents have been migrated.

Script

The following screenshot shows how the script should be used. Replace the source and destination URLs to your situation. Also change the Documents list name if needed.

shadow

Next the username and password should be supplied to connect to the SharePoint online tenant. The current user credentials are being used to connect to the source on-premise tenant.

Screen output:

shadow

and csv output:

shadow

listCompare.ps1

  1# Copyright (C) www.jurjan.info - All Rights Reserved (MIT License)
  2
  3param(
  4    [Parameter(Mandatory = $False, Position = 1)] [string]$siteUrlOnPrem,
  5    [Parameter(Mandatory = $False, Position = 2)] [string]$siteUrlOnline,
  6    [Parameter(Mandatory = $False, Position = 3)] [string]$listTitle
  7)
  8
  9Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
 10
 11Add-Type -Path "$PSScriptRoot\Microsoft.SharePoint.Client.dll" 
 12Add-Type -Path "$PSScriptRoot\Microsoft.SharePoint.Client.Runtime.dll"
 13
 14function getAllListItems($_ctx, $_listName, $_rowLimit) {
 15    $list = $_ctx.Web.Lists.GetByTitle($_listName)
 16    $_ctx.Load($list)
 17
 18    $query = New-Object Microsoft.SharePoint.Client.CamlQuery
 19    $query.ViewXml = "<View Scope='RecursiveAll'>
 20        <RowLimit>$_rowLimit</RowLimit>
 21    </View>"
 22
 23    $items = @()
 24    do {
 25        $listItems = $list.getItems($query)
 26        $_ctx.Load($listItems)
 27        $_ctx.ExecuteQuery()
 28        $query.ListItemCollectionPosition = $listItems.ListItemCollectionPosition
 29
 30        foreach ($item in $listItems) {
 31            $items += $item
 32        }
 33        Write-Host "Getting next batch of $_rowLimit"
 34    }
 35    While ($null -ne $query.ListItemCollectionPosition)
 36
 37    return $items
 38}
 39
 40# SharePoint Online (destination)
 41$clientContext = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrlOnline)
 42$userCredentials = Get-Credential
 43$userName = $userCredentials.UserName
 44$securePassword =  $userCredentials.Password
 45$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($userName, $securePassword)  
 46$clientContext.Credentials = $credentials  
 47
 48if (!$clientContext.ServerObjectIsNull.Value) {  
 49    Write-Host "Connected to SharePoint Online site: '$siteUrlOnline'" -ForegroundColor Green  
 50}
 51
 52$theitemsonline = getAllListItems $clientContext $listTitle 2000  # get listitems in batches of 2000 (needed for large lists). Adjust 2000 to your own needs.
 53Write-Host $theitemsonline.Count "items found"
 54$onlineFiles = @()
 55foreach ($onlineitem in $theitemsonline) {
 56    $onlineFiles += $onlineitem["FileLeafRef"]
 57}
 58
 59Write-Host "Connected to on-prem site: $siteUrlOnPrem" -ForegroundColor Green
 60
 61$clientContext = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrlOnPrem)
 62#$clientContext.Credentials = Get-Credential # optional use other credentials than current user
 63$currentWeb = $clientContext.Web
 64
 65$lists = $currentWeb.Lists
 66$list = $lists.GetByTitle($listTitle) 
 67$theitemsonprem = getAllListItems $clientContext $listTitle 2000  # get listitems in batches of 2000 (needed for large lists). Adjust 2000 to your own needs.
 68Write-Host $theitemsonprem.Count "items found"
 69
 70$onPremFiles = @()
 71foreach ($onpremitem in $theitemsonprem) {
 72    $onPremFiles += $onpremitem["FileLeafRef"] 
 73}
 74
 75$a = Get-Date
 76$fileName = $a.Ticks 
 77$tasks = @()
 78$i = 0
 79
 80$differences = Compare-Object $onPremFiles $onlineFiles
 81Write-Host $differences.Count files are missing, starting export to csv file "'ListCompare_Export_$fileName.csv'" -ForegroundColor Red
 82
 83$differences | ForEach-Object { 
 84	$completed = ($i*100)/$differences.Count
 85    Write-Progress -Activity "Export " -percentComplete $completed; 
 86    Write-Host $_.SideIndicator $_.InputObject -ForegroundColor DarkYellow
 87
 88    $inputObjectAsString = $_.InputObject
 89
 90    if ($_.SideIndicator -eq "<=") {
 91        Write-Host " missing online: " -ForegroundColor DarkYellow -NoNewLine
 92        Write-Host $_.SideIndicator $_.InputObject -ForegroundColor DarkYellow
 93        $theitem = $theitemsonprem | Where-Object {$_["FileLeafRef"] -eq $inputObjectAsString} 
 94    }
 95
 96    if ($_.SideIndicator -eq "=>") {
 97        Write-Host " missing on-prem: " -ForegroundColor DarkYellow -NoNewLine
 98        Write-Host $_.SideIndicator $_.InputObject -ForegroundColor DarkYellow
 99        $theitem = $theitemsonline | Where-Object {$_["FileLeafRef"] -eq $inputObjectAsString} 
100    }
101
102    if ($theitem -ne $null) {
103        $o = new-object psobject
104        $o | Add-Member -MemberType noteproperty -Name SideIndicator -value $_.SideIndicator;
105        $o | Add-Member -MemberType noteproperty -Name inputObjectAsString -value $inputObjectAsString;
106        $o | Add-Member -MemberType noteproperty -Name FileRef -value $theitem["FileRef"];
107        $tasks += $o;
108    }
109    $i++
110}
111
112$tasks | export-csv ".\ListCompare_Export_$fileName.csv" -noTypeInformation;

open raw file

Final words

This concludes my blog post about SharePoint migrations. For questions, remarks or suggestions feel free to drop me a note in the contact form on the left.

If this project help you reduce time to develop, you can give me a cup of coffee 😄

Donate

qr