PnP PowerShell and more...

Here I occasionally post about Microsoft 365 Patterns and Practices in general and PnP PowerShell more specifically.

Getting started with the PnP Provisioning Engine and PnP PowerShell

2018-03-19 6 min read PowerShell SharePoint

Two of the most popular cmdlets in the PnP PowerShell module are Get-PnPProvisioningTemplate and its sibling Apply-PnPProvisioningTemplate.

Follow me in this post where I dive into using them and show how make the most of them in your provisioning flow.

Creating a Template

Okay, so while it’s totally possible to start creating a template by hand from scratch, it’s a daunting task for sure. The PnP Provisioning Engine supports so many artifacts and each artifact has so many properties, you’ll end up browsing the schema documentation for hours to come.

Lets do it easier: create a site collection, modern, classic, doesn’t matter. Configure it to what you would like to have in your template as much as you can. E.g. create lists, add content types, add columns to lists, add webparts to the homepage, etc.

Now you extract a template from that site as follows:

Connect-PnPOnline -Url https://yourtenant.sharepoint.com/sites/yoursite
Get-PnPProvisioningTemplate -Out <filenameofyourtemplate.xml>

The cmdlet will connect to the site and will work its way through all the supported artifacts. In the end it will write down a file to the filesystem with the name you specified in the -Out parameter.

Open the template with your favorite text editor and modify things to your likings.

Validation

The PnP Provisioning Engine uses a XSD schema internally to validate a template before applying it. You can download this schema. Then open your template XML file in Visual Studio and go to the properties (F4) of your file. PnPProvisioning2 Now click the button as highlighted in the image and click ‘Add’. Point to the schema file you just downloaded. Now you have intellisense and parameter completion. As a nice side effect, you will immediately see where there are any issues in the template.

Tokens

When you have a look at the template, you’ll notice at certain locations text alike {site}. These are tokens. At provisioning time we will replace those tokens with actual values. Check out the documentation for the tokens we support.

What about content?

We do not extract content, with the exception of the homepage. The reason for this is that, while it might sound simple, it is actually not. Take the scenario where a library has 1000 documents. We will have to download those documents, add references to them in the template, and store there somewhere (see the section about the PnP File format on how we solve that). Simple enough. However… what if you have broken security inheritance on those items. And what if the users in the original site are not present in the target site? Or what if you are applying that template to a completely different tenant? We end up in user-mapping chaos, and as result, it would make the whole process of applying a template so much more complex. So we made a strategic decision to not support content. For that you will have to fall back to other products. Having said that: there is nothing stopping you from adding content to a provisioning template. You can add items to lists, you can upload documents, pages, set metadata, change the security, etc. It’s all supported by the engine. We just don’t extract it.

Advanced extraction options

Limit what to include in the template

If you want for instance only your lists in a template, but nothing else, you can tell the engine to do exactly only that:

Get-PnPProvisioningTemplate -Out <yourfile.xml> -Handlers Lists

PowerShell supports completion on the Handlers parameter, so make sure to loop through all the options to understand what you can do. You can combine handlers, so for instance you can do

Get-PnPProvisioningTemplate -Out <yourfile.xml> -Handlers Lists,ContentTypes

Instead of telling the engine what to do, you can also tell the engine what not to do:

Get-PnPProvisioningTemplate -Out <yourfile.xml> -ExcludeHandlers Lists

The above command will include everything but the lists in the template.

Export certain files

If you extract a template from a publishing site, you might want to include the masterpages and/or pagelayouts. By default the engine will not to do, and you have to excplicitly specify a switch to do so.

Get-PnPProvisioningTemplate -Out <yourfile.xml> -PersistPublishingFiles

These files will be exported to the folder you are currently, and if they are located in subfolders, a folder structure will be made in the current folder you’re in.

This brings a challenge: if you want to distribute the template, and make sure the template can be provisioned correctly by someone else, you will also have to distribute those additional files. The template refers to those files and expects them to be present. If not present at application time, you will receive an error.

To make this easier, check out the next topic:

The PnP File Format

The PnP Provisioning Engine has its own file format: .PNP. Effectively it’s an Office Open XML file, e.g a zip file, with metadata in there. The huge benefit of the PnP file format is that when you include switches to extract the publishing files, or any other file (like pages, etc.) we will store those files in the PnP file. You will end up with a single file that contains it all: the template, and the artifacts it refers to in the template.

If you extract a template and you name it ‘yourfile.pnp’ we will automatically save it in that format. Just rename the file to .zip and have a look in inside.You’ll notice a few folders and in one of those folders is a file with an XML extension. That’s the template. Notice that we also add all the extract files to the folder, but we rename them to a unique filename (GUID based). The reason we do that is that there might be files with the same filename in your site but in different folders. In the PnP file we stored them in one flat folder structure, and we have to rename them as a result to keep them unique. There is a mapping table in the PnP File which tells the engine which file goes where.

In PnP PowerShell there are several cmdlets available to manipulate or create a Provisioning Template: Add-PnPDataRowsToProvisioningTemplate, Add-PnPFileToProvisioningTemplate, Add-PnPListFoldersToProvisioningTemplate, Convert-PnPFolderToProvisioningTemplate, Convert-PnPProvisioningTemplate, New-PnPProvisioningTemplate, New-PnPProvisioningTemplateFromFolder, Read-PnPProvisioningTemplate, Remove-PnPFileFromProvisioningTemplate, Save-PnPProvisioningTemplate, Set-PnPProvisioningTemplateMetadata

Applying a template

So now that you have a template, there is basically one thing to do: apply it to a site. One thing is important to understand: we do not create a site. That is up to you to do. You can write code, use the New-PnPSite cmdlet, it does not matter. But the site has to be present.

So: Create Site, Apply Template.

Applying a template to a site is pretty straightforward:

Connect-PnPOnline -Url https://yourtenant.sharepoint.com/sites/yoursite
Apply-PnPProvisioningTemplate -Path <yourtemplate>.<xml|pnp>

Just like with Get-PnPProvisioningTemplate, the Apply-PnPProvisioningTemplate cmdlet supports the -Handlers and -ExcludeHandlers parameter. This means that given a large template, you can decide to only apply a part of that template, by making use of those parameters

Important : Navigation

If you extract the navigation (quicklaunch, topnavigation) from a site, and want to apply that to another site, it’s important to realize that the provisioning engine will never delete things, unless you explicitely tell it do so. So if a site has a base navigation in place (and every site has that if it’s just been created), the provisioning engine will add the navigation nodes found in a template to a site. If you want to replace the existing navigation, make sure to specify the -ClearNavigation switch when you apply the template.

Concluding

I only touched the most important cmdlets and parameters in this post. I urge you to check out the documentation for both cmdlets, and see what possible switches there are: