Site Collection Migration Guide¶
Status: Authored 2026-04-30 Audience: SharePoint administrators and migration engineers executing site-level migrations from SharePoint Server to SharePoint Online. Scope: Site collection migration using SPMT and Migration Manager, including batching strategies, large list handling, site assessment, and versioning considerations.
1. Site migration overview¶
Site collection migration is the core activity of any SharePoint on-premises to SPO migration. Every site collection contains document libraries, lists, pages, web parts, permissions, content types, and potentially workflows and customizations. The migration strategy must account for site size, complexity, last activity date, and business criticality.
Migration tool selection by scenario¶
| Scenario | Recommended tool | Rationale |
|---|---|---|
| Single site < 100 GB | SPMT (desktop) | Simple, fast, admin-controlled |
| 5-50 sites, < 500 GB total | SPMT (PowerShell batch) | Scriptable, sequential or parallel |
| 50+ sites, multi-farm | Migration Manager | Centralized dashboard, multi-agent |
| Complex permissions, metadata | Third-party (Sharegate) | Granular control, pre-migration reporting |
| 10,000+ sites, enterprise scale | Migration Manager + FastTrack | Microsoft-led engagement with tooling |
2. Pre-migration site assessment¶
Run SMAT for site-level inventory¶
# Run SMAT against the entire farm
.\SMAT.exe -SiteURL https://sharepoint.contoso.com -OutputFolder C:\SMAT-Results
# SMAT generates reports for:
# - Checked-out files
# - Customized pages (unghosted)
# - Large lists (> 5,000 items)
# - External BCS connections
# - InfoPath forms
# - Sandbox solutions
# - Custom web parts
# - Locked sites
# - Workflow associations
Site collection inventory with PowerShell¶
# On-premises: Get all site collections with key metrics
Add-PSSnapin Microsoft.SharePoint.PowerShell
Get-SPSite -Limit All | ForEach-Object {
[PSCustomObject]@{
Url = $_.Url
ContentDatabase = $_.ContentDatabase.Name
StorageMB = [math]::Round($_.Usage.Storage / 1MB, 2)
SiteCount = $_.AllWebs.Count
LastModified = $_.LastContentModifiedDate
Owner = $_.Owner.LoginName
Template = $_.RootWeb.WebTemplate + "#" + $_.RootWeb.Configuration
LockState = $_.WriteLocked
CompatibilityLevel = $_.CompatibilityLevel
CustomSolutions = ($_.Solutions | Measure-Object).Count
}
} | Export-Csv -Path "C:\Migration\site-inventory.csv" -NoTypeInformation
Site classification for migration waves¶
Classify each site collection into migration waves based on complexity:
| Wave | Criteria | Examples |
|---|---|---|
| Wave 0 -- Pilot | Small, low-risk, representative of common patterns | Team site < 10 GB, standard document libraries |
| Wave 1 -- Simple | Standard team sites, no customizations, no workflows | Project sites, department sites, archive sites |
| Wave 2 -- Medium | Sites with managed metadata, content types, moderate size | Document centers, records repositories |
| Wave 3 -- Complex | Publishing sites, custom solutions, workflows | Intranet, department portals, process sites |
| Wave 4 -- High-risk | Large sites (> 100 GB), farm solutions, InfoPath, BCS | Enterprise search center, LOB integration sites |
Site assessment checklist¶
For each site collection in scope, document:
- Total size (GB) including all subsites and versions
- Number of subsites (webs)
- Number of lists and libraries
- Largest list/library (item count and size)
- Content types in use (site and list-level)
- Managed metadata columns (term set references)
- Custom solutions deployed (sandbox or farm)
- Workflows (SP 2010, SP 2013, Nintex, K2)
- InfoPath forms
- Custom master pages or page layouts
- Broken permission inheritance (count and depth)
- External data connections (BCS, web services)
- Last content modification date
- Active users (from audit logs or usage analytics)
3. Target site architecture in SPO¶
Flat architecture vs subsites¶
SharePoint Online best practice is a flat site architecture using hub sites rather than deep subsite hierarchies:
| On-premises pattern | SPO target pattern |
|---|---|
| Site collection with 5+ levels of subsites | Multiple site collections associated to a hub site |
| Department site collection /sites/HR/Benefits/Enrollment | /sites/hr-benefits, /sites/hr-enrollment (hub-associated) |
| Publishing site collection with subsite hierarchy | Communication site with hub navigation |
| Project site /sites/projects/project-alpha | /sites/project-alpha (associated to projects hub) |
Create target sites with PnP PowerShell¶
# Connect to SPO tenant
Connect-PnPOnline -Url https://contoso-admin.sharepoint.com -Interactive
# Create a hub site
$hubSite = New-PnPSite -Type CommunicationSite `
-Title "Finance Department" `
-Url "https://contoso.sharepoint.com/sites/finance" `
-Description "Finance department hub"
Register-PnPHubSite -Site "https://contoso.sharepoint.com/sites/finance"
# Create team sites and associate to hub
$teamSites = @(
@{ Title = "Finance - Accounts Payable"; Url = "finance-ap" },
@{ Title = "Finance - Accounts Receivable"; Url = "finance-ar" },
@{ Title = "Finance - Budget Planning"; Url = "finance-budget" }
)
foreach ($site in $teamSites) {
New-PnPSite -Type TeamSite `
-Title $site.Title `
-Alias $site.Url
Add-PnPHubSiteAssociation `
-Site "https://contoso.sharepoint.com/sites/$($site.Url)" `
-HubSite "https://contoso.sharepoint.com/sites/finance"
}
4. Site migration with SPMT¶
Single site migration¶
Import-Module Microsoft.SharePoint.MigrationTool.PowerShell
# Initialize SPMT session
Register-SPMTMigration -SPOUrl "https://contoso.sharepoint.com" `
-SPOCredential (Get-Credential) `
-Force
# Add migration task
Add-SPMTTask `
-SharePointSourceSiteUrl "https://sp2016.contoso.com/sites/finance" `
-TargetSiteUrl "https://contoso.sharepoint.com/sites/finance" `
-MigrateAll
# Start migration
Start-SPMTMigration
Batch migration with CSV¶
Create a CSV file defining multiple migration tasks:
Source,SourceDocLib,SourceSubFolder,TargetWeb,TargetDocLib,TargetSubFolder
https://sp2016.contoso.com/sites/finance,,/,https://contoso.sharepoint.com/sites/finance,,/
https://sp2016.contoso.com/sites/hr,,/,https://contoso.sharepoint.com/sites/hr,,/
https://sp2016.contoso.com/sites/legal,,/,https://contoso.sharepoint.com/sites/legal,,/
https://sp2016.contoso.com/sites/marketing,,/,https://contoso.sharepoint.com/sites/marketing,,/
# Register and run batch migration
Register-SPMTMigration -SPOUrl "https://contoso.sharepoint.com" `
-SPOCredential (Get-Credential) -Force
Add-SPMTTask -SharePointMigrationSourceCSV "C:\Migration\batch-sites.csv"
Start-SPMTMigration
SPMT migration settings¶
# Configure SPMT settings before adding tasks
$settings = @{
MigrateFileVersionHistory = $true
KeepAllVersions = $false
NumberOfVersionToMigrate = 10 # Last 10 versions per file
EnableIncremental = $true # Support incremental runs
PreserveUserPermissionsForDocumentLibrary = $true
PreserveUserPermissionsForLists = $true
MigrateHiddenItems = $true
MigrateItemsCreatedAfter = "2020-01-01" # Optional: filter by date
MigrateItemsModifiedAfter = "2020-01-01" # Optional: filter by date
SkipListWithAudienceEnabled = $false
FilterOutPathSpecialCharacters = $true
AzureActiveDirectoryLookup = $true # Map on-prem users to Entra
UserMappingCSVFile = "C:\Migration\user-mapping.csv"
}
5. Large list handling¶
SharePoint Online has a list view threshold of 5,000 items for classic views (modern views handle large lists better through automatic indexing). Lists with more than 20,000 items require special consideration during migration.
Pre-migration list assessment¶
# Find large lists across all site collections
Get-SPSite -Limit All | ForEach-Object {
$site = $_
$_.AllWebs | ForEach-Object {
$web = $_
$_.Lists | Where-Object { $_.ItemCount -gt 5000 } | ForEach-Object {
[PSCustomObject]@{
SiteUrl = $site.Url
WebUrl = $web.Url
ListTitle = $_.Title
ItemCount = $_.ItemCount
SizeMB = [math]::Round(($_.Items | Measure-Object -Property File.Length -Sum).Sum / 1MB, 2)
Template = $_.BaseTemplate
}
}
}
} | Export-Csv -Path "C:\Migration\large-lists.csv" -NoTypeInformation
Large list migration strategies¶
| List size | Strategy | Notes |
|---|---|---|
| 5,000 - 20,000 items | Migrate as-is | SPO handles well with modern views and automatic indexing |
| 20,000 - 100,000 items | Migrate as-is with indexed columns | Ensure critical filter/sort columns are indexed in SPO |
| 100,000 - 1,000,000 items | Evaluate alternatives | Consider Dataverse, Azure SQL, or splitting into multiple lists |
| > 1,000,000 items | Do not migrate to SPO list | Move to Dataverse, Azure SQL, or Power BI dataset |
SPO list limit
SharePoint Online supports up to 30 million items per list, but performance degrades significantly above 100,000 items without proper indexing. Lists with complex views, calculated columns, or lookup columns perform worse at scale.
6. Version history management¶
Version history is frequently the largest contributor to migration size and duration. A 1 GB document library with 500 versions per file can consume 50+ GB when fully versioned.
Version migration strategies¶
| Strategy | SPMT setting | Use when |
|---|---|---|
| Migrate all versions | KeepAllVersions = $true | Regulatory requirement, legal hold |
| Migrate last N versions | NumberOfVersionToMigrate = 10 | Balance between history and speed |
| Migrate current version only | NumberOfVersionToMigrate = 1 | Maximum speed, acceptable for archive sites |
| Migrate versions after date | MigrateItemsModifiedAfter | Only recent activity matters |
Calculate version impact¶
# Estimate version storage impact for a document library
$web = Get-SPWeb "https://sp2016.contoso.com/sites/finance"
$list = $web.Lists["Shared Documents"]
$versionStats = $list.Items | ForEach-Object {
$item = $_
[PSCustomObject]@{
FileName = $item.File.Name
CurrentSizeMB = [math]::Round($item.File.Length / 1MB, 2)
VersionCount = $item.File.Versions.Count
TotalSizeMB = [math]::Round(
($item.File.Length + ($item.File.Versions | Measure-Object -Property Size -Sum).Sum) / 1MB, 2
)
}
}
$totalCurrentMB = ($versionStats | Measure-Object -Property CurrentSizeMB -Sum).Sum
$totalWithVersionsMB = ($versionStats | Measure-Object -Property TotalSizeMB -Sum).Sum
$versionOverhead = [math]::Round(($totalWithVersionsMB - $totalCurrentMB) / $totalCurrentMB * 100, 1)
Write-Host "Current size: $totalCurrentMB MB"
Write-Host "With versions: $totalWithVersionsMB MB"
Write-Host "Version overhead: $versionOverhead%"
7. Subsite to site collection conversion¶
For sites with deep subsite hierarchies, convert subsites to standalone site collections before or during migration:
Option 1: Migrate subsites as-is, then restructure¶
- Migrate the entire site collection including subsites to SPO
- Use the SharePoint Admin Center or PnP PowerShell to create new site collections
- Move content from subsites to new site collections using Move-PnPFile and Copy-PnPFile
- Associate new site collections to a hub site
Option 2: Selective migration (subsites to individual site collections)¶
# Migrate each subsite to a separate SPO site collection
$subsites = @(
@{
Source = "https://sp2016.contoso.com/sites/finance/ap"
Target = "https://contoso.sharepoint.com/sites/finance-ap"
},
@{
Source = "https://sp2016.contoso.com/sites/finance/ar"
Target = "https://contoso.sharepoint.com/sites/finance-ar"
}
)
foreach ($subsite in $subsites) {
Add-SPMTTask `
-SharePointSourceSiteUrl $subsite.Source `
-TargetSiteUrl $subsite.Target `
-MigrateAll
}
8. Incremental migration¶
SPMT and Migration Manager support incremental migration, allowing you to run the initial migration and then re-run to capture changes made during the migration window.
Incremental migration workflow¶
- Initial migration (T-14 days): Run full migration during off-hours
- Incremental sync (T-7 days): Re-run migration to capture changes since initial run
- Final sync (T-0, cutover): Run final incremental sync, then switch users to SPO
- Set source to read-only after final sync to prevent further changes
# Enable incremental migration in SPMT
Register-SPMTMigration -SPOUrl "https://contoso.sharepoint.com" `
-SPOCredential (Get-Credential) -Force
# Same task definition as initial run -- SPMT detects incremental
Add-SPMTTask `
-SharePointSourceSiteUrl "https://sp2016.contoso.com/sites/finance" `
-TargetSiteUrl "https://contoso.sharepoint.com/sites/finance" `
-MigrateAll
Start-SPMTMigration
# SPMT will only migrate items changed since the last successful run
9. Post-migration validation¶
Content validation checklist¶
- Total item count matches source (within tolerance for filtered/excluded items)
- Document library file counts match
- List item counts match
- Managed metadata columns display correct terms
- Content types are associated correctly
- Version history is present (per migration settings)
- Check-in/check-out status is correct
- Created/modified dates and users are preserved
- Folder structure is intact
Permission validation¶
# Validate permissions on target site
Connect-PnPOnline -Url "https://contoso.sharepoint.com/sites/finance" -Interactive
# Get site permissions
Get-PnPSiteGroup | ForEach-Object {
[PSCustomObject]@{
GroupName = $_.Title
Users = ($_.Users | Select-Object -ExpandProperty Email) -join "; "
Roles = ($_.Roles | Select-Object -ExpandProperty Name) -join "; "
}
} | Format-Table -AutoSize
# Check for broken inheritance
Get-PnPList | ForEach-Object {
[PSCustomObject]@{
ListTitle = $_.Title
HasUniquePerms = $_.HasUniqueRoleAssignments
ItemCount = $_.ItemCount
}
} | Where-Object { $_.HasUniquePerms } | Format-Table -AutoSize
Search validation¶
After migration, SPO search indexes content automatically. Allow 24-48 hours for full indexing, then validate:
- Search returns results from migrated content
- Managed properties are populated correctly
- Content types appear in search refiners
- People search returns correct profile information
10. Troubleshooting common issues¶
| Issue | Cause | Resolution |
|---|---|---|
| Files skipped -- invalid characters | On-prem allows characters that SPO does not (#, %, &) | Rename files pre-migration or enable FilterOutPathSpecialCharacters |
| Files skipped -- path too long | SPO path limit is 400 characters | Shorten folder names or restructure hierarchy |
| Files skipped -- file size > 250 GB | SPO file size limit is 250 GB | Split files or use Azure Blob Storage |
| Slow migration speed | Network bandwidth, throttling, large versions | Reduce version count, increase agents, use off-hours |
| Permission mapping failures | AD users not synced to Entra ID | Verify Entra Connect sync; use user mapping CSV |
| Managed metadata not mapping | Term store not migrated | Migrate term store before content; map term set IDs |
| Modern pages not rendering | Classic web parts not supported in modern | Convert pages post-migration; use PnP page transformation |
References¶
- SPMT documentation
- Migration Manager documentation
- SharePoint Online limits
- PnP PowerShell
- Site collection to hub site planning
Maintainers: csa-inabox core team Last updated: 2026-04-30