PnP Provisioning

Extract and apply templates with Office Dev PnP Provisioning PowerShell

Earlier I introduced the Office Dev PnP Program and Office Dev PnP Provisioning. I mentioned the great functionality to define a template of a site or site collection in XML. In this blog post I will explain how to extract and apply XML templates with Office Dev PnP Provisioning using the Office 365 Developer Patterns and Practices PowerShell Cmdlets, from now on referred to as PnP Provisioning cmdlets.

PnP Provisioning cmdlets?

Basically the PnP Provisioning cmdlets are an extension to the standard CSOM functionality Microsoft offers. PnP Provisioning cmdlets are specifically designed to provision and modify artifacts in SharePoint Online using CSOM.

Layers of CSOM and PnP

Preparing you environment

Of course, you first need to prepare your environment before you are able to use the PnP Provisioning cmdlets. Although the cmdlets are designed for SharePoint Online, it is also possible to use the cmdlets on an on-premises environment. The on-premises variant of the cmdlets is another build, because SharePoint 2013 uses an older version (15) of CSOM whereas SharePoint Online uses the newest version (16) of CSOM. Therefore you first need to determine the right build of the PnP Provisioning cmdlets:

  • msi for SharePoint 2013
  • msi for SharePoint Online

Next download the latest release of the right build from the release page and run the install. Erwin van Hunen created a blog post about running the install and connecting to the SharePoint environment with Connect-SPOnline. Complete the installation as described in his blog post.

Connect and get the context

As mentioned earlier you can use Connect-SPOnline to connect to SharePoint. Connect-SPOnline creates the context you need to acquire or apply the XML template.

To ease your life, use the Windows Credential Manager to store your credentials. This way you don’t have to fill in your credentials each time you connect to SharePoint with Connect-SPOnline without having to hardcode your credentials in the script.

Almost ready to extract

Once you have the necessary context you are almost ready to extract a template. One thing is missing: the source to extract from. The source is a SharePoint site which is designed as the blueprint of to be provisioned sites. Create this source site exactly as you want this blueprint to be. Do this in a root site (of a separate site collection) for best results. In a subsite it is possible that some artifacts are not extracted properly. Things like site columns are only obtained correctly when the source is a root site. Once you created the source site, you are ready to extract.

Extract a XML template with Get-SPOProvisioningTemplate

Now you have a source site as blueprint, you need the context of the source site. So if /sites/MySourceSite is the source site your code should look like something like this:

# Define variables
$tenant = "tenantName";
$sourceSite = "/sites/MySourceSite";
$path = "C:\MyPnPTemplate.xml";

# Get context
$webUrl = "https://{0}.sharepoint.com{1}/" -f $tenant, $sourceSite;
Write-Output $("Connecting to {0}..." -f $webUrl);
Connect-SPOnline -Url $webUrl -Credentials WCMStoredCredentials;
Write-Output "Context obtained";

To make the script easier to use with different environments, I used a $tenant and a $sourceSite variable to define the name of the SharePoint tenant and the source site respectively. Later these variables are used in $webUrl to define the complete URL of the source site. The $path variable is to define the location where the XML template should be saved. Last thing to do is the actual extraction with Get-SPOProvisioningTemplate and save the XML template to the given path:

# Extract template
Write-Output "Creating PnP template...";
Get-SPOProvisioningTemplate -Out $path;
Write-Output $("Template saved to {0}" –f $path);
Combined the code looks like this:

# Define variables
$tenant = "tenantName";
$sourceSite = "/sites/MySourceSite";
$path = "C:\MyPnPTemplate.xml";

# Get context
$webUrl = "https://{0}.sharepoint.com{1}/" -f $tenant, $sourceSite;
Write-Output $("Connecting to {0}..." -f $webUrl);
Connect-SPOnline -Url $webUrl -Credentials WCMStoredCredentials;
Write-Output "Context obtained";

# Extract template
Write-Output "Creating PnP template...";
Get-SPOProvisioningTemplate -Out $path;
Write-Output $("Template saved to {0}" –f $path);

Apply a XML template with Apply-SPOProvisioningTemplate

Applying a XML template is as easy as extracting a XML template. Obtaining the context works the same, but instead of a defining source site, you define a target site. With Get-SPOWeb you get the context of the subsite. Now it’s time to apply the XML template with Apply-SPOProvisioningTemplate:

# Define variables
$tenant = "tenantName";
$targetSite = "/sites/MyTargetSite";
$path = "C:\MyPnPTemplate.xml";

# Get context
$webUrl = "https://{0}.sharepoint.com{1}" -f $tenant, $ targetSite;
Write-Output $("Connecting to {0}..." -f $webUrl);
Connect-SPOnline -Url $webUrl -Credentials WCMStoredCredentials;
Write-Output "Context obtained";

# Get web object
$web = Get-Web;

# Apply template
Write-Output $("Applying PnP template [{0}] to site [{1} ({2})]..." -f $path, $web.Title, $web.Url);
Apply-SPOProvisioningTemplate -Web $web -Path $path;
Write-Output "Template created";

After some time (seconds or minutes, depending on the size of the template and the connection speed) your template will be successfully applied to the site.

In the sample a $web context is used as input for Apply-SPOProvisioningTemplate, but when no web context is given, Apply-SPOProvisioningTemplate will use the context of Connect-SPOnline (which in this sample is indeed the same context as $web). The code will then look like:

Apply-SPOProvisioningTemplate -Path $path;

The XML template is now applied on an existing site. It could also be applied to a new site by creating the new site first with New-SPOWeb using the standard blank (STS#1) template.

# Define variables
$tenant = "tenantName";
$targetSite = "/sites/MyTargetSite";
$path = "C:\MyPnPTemplate.xml";
$siteTitle = "My Target Site";
$siteDescription = "My Target Site Description";
$siteUrl = "MyTargetSite";

# Get context
$webUrl = "https://{0}.sharepoint.com/{1}" -f $tenant, $targetSite;
Write-Output $("Connecting to {0}..." -f $webUrl);
Connect-SPOnline -Url $webUrl -Credentials WCMStoredCredentials;
Write-Output "Done";

# Create new subsite
$web = New-SPOWeb -Title "$siteTitle" -Url "$siteUrl" -Description "$siteDescription" -Locale 1043 -Template "STS#1"

# Apply template
Write-Output $("Applying PnP template [{0}] to site [{1} ({2})]..." -f $path, $web.Title, $web.Url);
Apply-SPOProvisioningTemplate -Web $web -Path $path;
Write-Output "Done";

This is also a good example of a situation when you actually need the $web context. In the coming blog posts I will explain how to create a XML template yourself.

Further information

5 thoughts on “Extract and apply templates with Office Dev PnP Provisioning PowerShell

  1. If xml file has files such as javascript files which has to be includes then how to create them as package before extracting the template.

  2. Best is to place those files in the same folder as the XML version of the PnP template and reference them with the relative path, or else with an absolute path. You can reference the files with the Files element. In the Files element you can specify the desired location in the SPWeb. This extract from the sample template [ProvisioningSchema-2016-05-FullSample-02.xml] shows you how to do this:

    <pnp:Files>
      <pnp:File Src="Logo.png" Folder="SiteAssets" Overwrite="true" />
      <pnp:File Src="CustomPage.aspx" Folder="SitePages" Overwrite="true">
        <pnp:Security>
          <pnp:BreakRoleInheritance CopyRoleAssignments="true" ClearSubscopes="true">
            <pnp:RoleAssignment Principal="Power Users" RoleDefinition="Full Control"/>
          </pnp:BreakRoleInheritance>
        </pnp:Security>
      </pnp:File>
      <pnp:File Src="CustomMaster.master" Folder="_catalogs/MasterPage" Overwrite="true" Level="Published" />
      <pnp:File Src="Custom.spcolor" Folder="_catalogs/Theme/15" Overwrite="true" />
      <pnp:File Src="Custom.spfont" Folder="_catalogs/Theme/15" Overwrite="true" />
    
      <!-- This is the custom WelcomePage for the ProjectDocumentSet ContentType -->
      <pnp:File Src="ProjectHomePage.aspx" Folder="_cts/ProjectDocumentSet/" Overwrite="true" />
    
      <pnp:Directory Src="c:\LocalPath\StyleLibrary" Folder="Style%20Library" Overwrite="true"
          Recursive="true" IncludedExtensions="*.css,*.js,*.woff" ExcludedExtensions="*.xml,*.json" />
      <pnp:Directory Src=".\Invoices" Folder="Invoices" Overwrite="true"
                  Recursive="false" MetadataMappingFile=".\InvoicesMetadata.json" />
      <pnp:Directory Src="c:\LocalPath\Pages" Folder="{PagesLibrary}"
                  Overwrite="true" Recursive="true" Level="Published"
                  MetadataMappingFile="c:\LocalPath\PagesMetadata.json" />
    
    </pnp:Files>
    

    With Convert-PnPFolderToProvisioningTemplate you can create a PNP file of the template and its referenced files.

  3. I’m getting the error “Get-SPOProvisioningTemplate The file is not checked out. You must first check out this document before making changes”. I have write access, I’ve tried with an existing file there and not, and running PowerShell as administrator.

    https://github.com/SharePoint/PnP/issues/921 sounds similar. Any advice on solving this?

Leave a Reply

Your email address will not be published. Required fields are marked *