Share and Enjoy !


A customer recently migrated from on-premise SharePoint 2013 to SharePoint Online. Unfortunately, the migration tool they used did not migrate their custom search pages correctly. With four custom pages for 40+ subsites, that’s 160+ pages that needed to be fixed. Fortunately, many of these subsite search pages were identical across the subsites, so scripting a solution to this problem was the approach I took.

Create the search page prototypes

The first step was to create an example of each search page to make certain the search refinement web part and content rollup/content search web part were configured correctly. The search pages are stored in the Site Pages document library and use the Wiki Page template, with a two-column layout, search refiner on the left and content search on the right.

The page prototypes I created included:

  • two corporate-level search pages that were identical across all subsites
  • two division-level search pages that were slightly different based on the four divisions

This resulted in ten search page prototypes (2 corporate-level plus 8 division-level.)

Export the web parts

Now that I had working examples of search pages it was time to prepare for scripting by exporting the web parts to use for all the other search pages. The steps were:

  • edit the search page
  • click the drop-down context menu for each web part
  • choose the “Export…” option

This results in two web part files, “Refinement.webpart” and “Content Search.webpart”.

This pair of “.webpart” files contain the search configuration for each of the ten prototype search pages. I placed each of these pairs of web part files into an appropriately named folder so I wouldn’t get them mixed up.

Identify the scripting technology to use

Here is a recap of what I needed to automate:

  • For each of the 40+ subsites…
  • For each of the four search pages…
  • Create a Wiki Page in Site Pages document library
  • Set the Text Layout for the Wiki Page to “two columns”
  • Add the appropriate Web Parts to the Wiki Page, based on whether it is one of the two Corporate search pages or one of the eight Division search pages
    • Left column, add the Search Refinement web part
    • Right column, add the Content Search web part

There are several options to use for scripting a SharePoint Online solution. This blog article, PowerShell Modules for Managing SharePoint Online ( does a great job of outlining the options available:

  1. Microsoft Online Services Sign-in Assistant
  2. SharePoint Online Management Shell
  3. Office Dev PnP PowerShell Module
  4. SharePoint Online Client-Side Object Model (CSOM)
  5. Azure Azure AD Management Shell
  6. Security and Compliance Center

I’ve used the SharePoint Online Client-Side Object Model (CSOM) option before in solutions I’ve created for on-premise customers. This option exposes much of the SharePoint API and would’ve worked great.

However, I wanted a solution that was a bit more administrator-friendly and not so developer-friendly. The Office Dev PnP PowerShell Module fit that criterion perfectly. These commands use CSOM behind-the-scenes to perform provisioning and artifact management actions, and many of these commands can work against both SharePoint Online and SharePoint Server.

Using the Office Dev PnP PowerShell Module

You’ll need to install the following modules to prepare a machine to use PnP PowerShell:

PS> Install-Module -Name PowerShellGet -Force
PS> Install-Module SharePointPnpPowerShellOnline

More details about PnP PowerShell can be found here,

Scripting the solution

Where am I now?

  • I have my search pages prototyped
  • The web part XML is saved to the local file system
  • I have the PnP PowerShell module installed for SharePoint Online

Now, it’s time to script the solution.

  • Connect to SharePoint Online
  • Get a handle to the SharePoint web (subsite) for division / plant
  • Create the wiki page in the Site Pages library
  • Add the web parts to the page

Below is the list of commands needed to create the search pages:

# connect to SharePoint Online
Connect-PnPOnline -Url "" -Credentials (Get-Credential)

# get the subweb for the division / plant
$web = Get-PnPWeb -Identity "/sites/corporate/division/plant" -ErrorVariable err -ErrorAction Continue
if ($err.count -gt 0 -or $web -eq $null) { throw "Web not found: $($subsite)" }

# Wiki Page to create
$wikiPageUrl = "/sites/corporate/division/plant/SitePages/SearchPageOne.aspx"

# check to see if page exists
$query = "SearchPageOne"
$sitePage = Get-PnPListItem -Web $web -List "Site Pages" -Query $query
if ($sitePage -ne $null) {
Write-Host "Page already exists:" $wikiPageUrl
else {
# create the Wiki Page with "Two columns" text layout
Add-PnPWikiPage -Web $web -ServerRelativePageUrl $wikiPageUrl -Layout TwoColumns
Write-Host "Page created:" $wikiPageUrl

# from the local filesystem, get the web parts to add to the wiki page
$pathRefinement = Join-Path $PSScriptRoot "webparts\Refinement.webpart"
if ((Test-Path $pathRefinement) -eq $false) { throw "File not found: $($pathRefinement)"}
$pathContentSearch = Join-Path $PSScriptRoot "webparts\Content Search.webpart"
if ((Test-Path $pathContentSearch) -eq $false) { throw "File not found: $($pathContentSearch)"}

# the Site Pages library has check out required enabled, so, check out file
Set-PnPFileCheckedOut -Web $web -Url $wikiPageUrl

# remove the web parts if they already exist; if they don't exist, no error is thrown
Remove-PnPWebPart -Web $web -ServerRelativePageUrl $wikiPageUrl -Title "Refinement"
Remove-PnPWebPart -Web $web -ServerRelativePageUrl $wikiPageUrl -Title "Content Search"

# add the Refinement web part to the "left column", row 1 column 1
Add-PnPWebPartToWikiPage -Web $web -ServerRelativePageUrl $wikiPageUrl -Path $pathRefinement -Row 1 -Column 1

# add the Content Search web part to the "right column", row 1 column 2
Add-PnPWebPartToWikiPage -Web $web -ServerRelativePageUrl $wikiPageUrl -Path $pathContentSearch -Row 1 -Column 2

# Now, check in page with comments
Set-PnPFileCheckedIn -Web $web -Url $wikiPageUrl -CheckinType MajorCheckin -Comment "Modified by $($PSCommandPath)"

To keep the example script as concise as possible I intentionally did not include much error checking. In the “production version” of the script I stored the subsite information (division, plant, etc.) in a comma-delimited (.csv) file. I executed the “create search page” commands in a loop for each of the entries in the file.


What I like about the PnP PowerShell module is the relative simplicity of the commands and scripts. Once the connection is established to SharePoint Online, the “PnP” commands work just like server-side PowerShell. There is no need to pass around a context variable like we need to do when using SharePoint Online CSOM. I think this makes this technology much more suitable for SharePoint administrators.

This script accomplished in minutes what would’ve taken me hours to do by hand. And, if changes in the refiners or content search were needed, no worries…just edit the web part XML as needed and re-run the script!

Share and Enjoy !

Related Content: