Sitecore Content Migration Part 3 Sitecore PowerShell Extensions

Created: 15 Aug 2026, last update: 15 Aug 2026

Sitecore Content and Layout Migration Part 3: Sitecore PowerShell Extensions

In Part 1 of this series, we looked at the Marketplace application Layout Manager Pro, which is very useful for finding and fixing layout errors. In Part 2, we covered Sitecore Commander, a .NET C# automation toolkit for Sitecore designed to help build a high-quality migrator quickly. In this post, we’ll explore Sitecore PowerShell Extensions (SPE), which allow us to bypass some of the limitations of API functionality.

There are three key migration scenarios where the Sitecore Authoring API falls short, but PowerShell fills the gap:
1. Changing an item's template.
Useful for automatically fixing incorrectly created items or assigning the correct template to items imported via serialization. Transferring items via serialization is a reliable method to retain the original Item ID, but you still need a way to change the template afterward.
2. Setting custom Item IDs upon creation.
The Sitecore Authoring API doesn't allow you to specify an Item ID when creating a new item. During migrations, having control over Item IDs is extremely helpful. Re-creating or importing items via serialization to preserve Item IDs makes it significantly easier to maintain relationships, such as data source references.
3. Querying the Sitecore Links Database.
Sitecore PowerShell provides easy access to the Sitecore Links database. This allows you to quickly retrieve all items that refer to a specific item—ideal for mapping relationships or generating analysis reports.

Tip: Creating Toolbox Scripts for Clean Execution
To keep your PowerShell scripts manageable and prevent execution errors, you often need default settings such as a starting path. Creating a Toolbox script with an Input Dialog helps streamline this process.
Another major benefit is that you can transfer these scripts across different environments using item serialization without requiring PowerShell elevation rights. In SitecoreAI, elevation rights are disabled by default (and enabling them is generally discouraged). This workflow ensures that only tested, safe scripts are promoted to production environments.
How to create a Toolbox script:
1. Open the Content Editor.
2. Navigate to /sitecore/system/Modules/PowerShell/Script Library.
3. Right-click and select Create a new module.
4. Check the Toolbox option.

You can now start building your scripts! AI tools like Google Gemini understand the specific requirements for SPE Toolbox scripts well, just craft a clear prompt, and you're good to go.

Below is an example script I used myself to perform template changes:

<#
    .SYNOPSIS
    Changes the template of a selected root node and all of its descendants.
#>

# 1. Input Dialog: Select root node, old template, and new template
$dialogProps = @{
    Title = "Change Template Toolbox"
    Description = "Select the start node and the templates you want to swap."
    Width = 650
    Height = 450
    OkButtonName = "Next"
    CancelButtonName = "Cancel"
    Parameters = @(
        @{ Name = "rootItem"; Title = "Root Node"; Editor = "droptree"; Source = "datasource=/sitecore/content"; Tooltip = "Select the folder/item where you want to search" },
        @{ Name = "oldTemplate"; Title = "Old Template"; Editor = "droptree"; Source = "datasource=/sitecore/templates"; Tooltip = "The template that needs to be replaced" },
        @{ Name = "newTemplate"; Title = "New Template"; Editor = "droptree"; Source = "datasource=/sitecore/templates"; Tooltip = "The new template" }
    )
}

$result = Read-Variable @dialogProps

# Stop if the user clicks Cancel
if ($result -ne "ok") {
    Write-Host "Action cancelled."
    exit
}

# Validation: check if all fields are filled
if (-not $rootItem -or -not $oldTemplate -or -not $newTemplate) {
    Show-Alert "All fields are required. Script has stopped."
    exit
}

# 2. Format the paths (removes '/sitecore/templates/' for a cleaner view)
$oldTemplatePath = $oldTemplate.Paths.FullPath -replace "(?i)^/sitecore/templates/", ""
$newTemplatePath = $newTemplate.Paths.FullPath -replace "(?i)^/sitecore/templates/", ""

# 3. Build the warning and summary message for the confirmation dialog
$confirmationProps = @{
    Title = "Confirm Template Change"
    Description = "Please review the template changes carefully before proceeding."
    OkButtonName = "Change Template"
    CancelButtonName = "Cancel"
    Width = 600
    Height = 400
    Parameters = @(
        @{ 
            Name = "infoText"; 
            Editor = "info"; 
            Value = "<#/pre>
<div style="color: #c00; font-weight: bold; margin-bottom: 15px;">⚠️ WARNING: Data loss may occur if the new template does not contain the exact same fields as the old template!</div>
<pre class="brush: csharp;">" +
                    "<strong>Are you sure you want to change the template? Click 'Change Template' to proceed.</strong><br /><br />" +
                    "The template will be changed from:<br />" +
                    "$oldTemplatePath - {$($oldTemplate.ID)}<br /><br />" +
                    "to:<br />" +
                    "$newTemplatePath - {$($newTemplate.ID)}"
        }
    )
}

# Show the confirmation dialog using Read-Variable for better layout control
$confirmResult = Read-Variable @confirmationProps

# Stop if the user does not click the confirmation button
if ($confirmResult -ne "ok") {
    Write-Host "Action cancelled in the confirmation screen."
    exit
}

# 4. Search for items
Write-Host "Searching for items with template '$($oldTemplate.Name)' under '$($rootItem.Paths.FullPath)'..."

$itemsToChange = @()

# Check if the selected root node itself uses the old template
if ($rootItem.TemplateID -eq $oldTemplate.ID) {
    $itemsToChange += $rootItem
}

# Get all descendants that use the old template
$itemsToChange += Get-ChildItem -Path $rootItem.ProviderPath -Recurse | Where-Object { $_.TemplateID -eq $oldTemplate.ID }

# Stop if no items were found
if ($itemsToChange.Count -eq 0) {
    Show-Alert "No items found with the old template under the selected root node."
    exit
}

# 5. Change templates
Write-Host "Found $($itemsToChange.Count) items. Processing changes..."

foreach ($item in $itemsToChange) {
    Write-Host "Changing template for: $($item.Paths.FullPath)"
    Set-ItemTemplate -Item $item -TemplateItem $newTemplate
}

# 6. Success message
Show-Alert "Done! The template of $($itemsToChange.Count) items has been successfully changed."