Create a modern SharePoint site template with multiple pages using PnP provisioning engine

The PnP provisioning engine is a framework that allows you to create templates based on pre-built SharePoint sites. The framework was not built to be a migration tool and when a template is generated it only saves the home page, although the schema supports multiple pages.

In this article you can find a PowerShell script that saves a modern SharePoint site as a template with all the included pages.

The algorithm to save all the pages is quite simple and all the commands used are available in the PnP PowerShell.

It starts by saving the site as a template with all properties and definitions included, then it loops through all the pages in the Site Pages library, set each page as home page of the site and saves a new template with the name of the page.

Since all the site properties and definitions were saved before each subsequent save only gets the page using the -Handlers PageContents option, when the last page is saved the original home page is applied to the site.

The script ends by copying the first template saved and by importing all the page nodes from the smaller templates, generating then a full template with all the pages.

The full script is available below.

pushd (Split-Path -Path $MyInvocation.MyCommand.Definition -Parent)
$saveDir = (Resolve-path ".\")
$siteURL = Read-Host "Please enter your site URL"

Write-Host "Connecting to: $siteURL"
Connect-PnPOnline -Url $siteURL; 
Write-Host "Connected!"

$web = Get-PnPWeb
$sourceSite = $web.ServerRelativeUrl
$library = "Site Pages"

#get all pages in the site pages library
$pages = Get-PnPListItem -List $library

#save current homepage
$currentHomePage = Get-PnPHomePage

$pagesList = New-Object System.Collections.Generic.List[System.Object]

$pageNumber = 1

foreach($page in $pages){
	if($page.FileSystemObjectType -eq "File"){
		$pagePath = $page.FieldValues["FileRef"]
	    $pageFile = $pagePath -replace $sourceSite, ""
		$pageTemplate = $pageFile -replace "/SitePages/","" -replace ".aspx",".xml"
	    $pagesList.Add($($pageTemplate -replace "./TemplateWithPages",""))	
		
		#set current page as home page
		Set-PnPHomePage -RootFolderRelativeUrl ($pagePath -replace ($sourceSite+"/"), "")
		
		Write-Host ("Saving page #" + $pageNumber + " - " + $pageTemplate)
		
		if($pageNumber -eq 1){
			Get-PnPProvisioningTemplate -Out $($saveDir.Path + "\" + $pageTemplate)
		}else{
			Get-PnPProvisioningTemplate -Out $($saveDir.Path + "\" + $pageTemplate) -Handlers PageContents
		}
		
		$pageNumber++
	}
}

$pagesList.ToArray()

#apply default default homepage
Set-PnPHomePage -RootFolderRelativeUrl $currentHomePage

#copy base template 
Copy-item -path ($saveDir.Path + "\" + $pagesList[0]) -destination ($saveDir.Path + "\Template.xml")

#open main xml
$mainFile = [xml][io.File]::ReadAllText($($saveDir.Path + $("\Template.xml")))
$clientSidePages = $mainFile.Provisioning.Templates.ProvisioningTemplate.ClientSidePages

#remove pages to avoid duplicates 
$clientSidePages.RemoveChild($mainFile.Provisioning.Templates.ProvisioningTemplate.ClientSidePages.ClientSidePage)

foreach( $page in $pagesList ){
	#open page template xml
	$xmlContents = [xml][io.File]::ReadAllText($saveDir.Path + "\" + $page)
	foreach($node in $xmlContents.Provisioning.Templates.ProvisioningTemplate.ClientSidePages.ClientSidePage)
	{
			
		#copy nodes from page xml
		$importNode = $clientSidePages.OwnerDocument.ImportNode($node, $true);
		$clientSidePages.AppendChild($importNode) | Out-Null
		
		#save final tempate
		$mainFile.Save($($saveDir.Path + $("\Template.xml")))
	
	}
}

popd

Designed by Freepik


No comments yet

Leave a Reply


Web developer focused on SharePoint branding, blogger, tech enthusiast. Travelling and sports are my addictions, knowledge and success are my daily motivations.