0 comments on “Tips & Tricks for Automated SharePoint Site Provisioning with PnP (Part 2)”

Tips & Tricks for Automated SharePoint Site Provisioning with PnP (Part 2)


Welcome to Part 2 of this series on Automated SharePoint Site Provisioning with PnP.  This blog series is focused on lessons learned with using PnP Powershell along with Site Actions, Site Scripts, Azure Automation and other components in order to automate creation of new SharePoint sites in a repeatable and well-governed manner. 

If you haven’t reviewed part 1, you can check it out here:

Tl;DR; when using Document Sets in your PnP Provisioning solution, be sure to:

  1. Enable site scripting (by setting DenyAddAndCustomizePages to false)
  2. Include feature activation for Document Sets in your provisioning template

This article shall is focused on something I just came across tonight, and figured I would post it while its fresh in my head.  The situation I stumbled on was related to deploying Document Sets as part of a provisioning site template.  In this case, I had a base PnP provisioning template with existing sections for SiteFields and ContentTypes containing a few basic site columns and content types. 

But now I added a custom document set content type (named “Project Packet”) to my test site which included columns for values like Project Manager, Project Name, Status, Start Date & End Date.  Since I needed to replicate the functionality of my test site in a new site, I added the related elements for my new SiteFields and ContentTypes to my existing provisioning template.

Using the “Features” handler in PnP

When you want to use Document Sets in a new site, you have to activate the Document Sets Site Collection feature (until you do this the base Document Set content type is not deployed into the site).  Rather than doing this through the normal Site Settings, I decided to update my provisioning template to handle this for me.  Since this was my first time using PnP Provisioning with Document Sets, I ran the following Powershell cmdlet on my test site which already had the Document Set feature activated:

Get-PnPProvisioningTemplate out “MyTemplate-Features.xml”
                            –Handlers “Features”

Note the use of the –Handlers parameter with a value of “Features”.  This tells the cmdlet to only export the custom site (site collection) and web features from the site, so we’ll get a small template from this, such as shown below:

<?xml version=“1.0”?>
     Generator=“OfficeDevPnP.Core, Version=3.2.1810.0,
                 Culture=neutral, PublicKeyToken=5e633289e95c321a” />
  <pnp:Templates ID=“CONTAINER-TEMPLATE-D9976DB23F1040BAB0A41BE5BC677F83”>
   <pnp:ProvisioningTemplate ID=“TEMPLATE-D9976DB23F1040BAB0A41BE5BC677F83”
                              Version=“1” BaseSiteTemplate=“GROUP#0”
      <pnp:Feature ID=“3bae86a2-776d-499d-9db8-fa4cdc7884f8” />
      <pnp:Feature ID=“f151bb39-7c3b-414f-bb36-6bf18872052f” />

In this case, the Site Feature of “3bae86a2-776d-499d-9db8-fa4cdc7884f8” is the Document Set feature.  The Web Feature of “f151bb39-7c3b-414f-bb36-6bf18872052f” refers to the Site Notebook feature.  Quick side note: It’s a mystery to me why the Site Notebook feature was included here.  I did not explicitly activate the Site Notebook feature as it is activated by default in modern team sites.  If I run the same powershell cmdlets on a brand new modern team site that does not have the Document Set feature activated, the generated PnP template does not contain either of these features.  But for some reason, the PnP engine feels that it needs to include the Site Notebook feature along with the Document Set feature.

I now copied the <pnp:Features> </pnp:Features> section from this Xml file and merged into my main pnp template (above the section for <pnp:SiteFields>).  At this point I now have two PnP provisioning templates:

  1. FieldCTs.xml – with sections for Features, SiteFields and ContentTypes (as described above)
  2. Lists.xml – with list definitions containing references to my Project Packet content type and related columns

Now, I figured, I’m good to go and I applied my updated pnp template containing the Document Set feature activation along with my custom document set content type (Project Packet) and associated columns, using these PowerShell cmdlets:

ApplyPnPProvisioningTemplate –Path “FieldCTs.xml”
ApplyPnPProvisioningTemplate –Path “Lists.xml”

These cmdlets completed without error.  So I went in to use my updated site.  From the library containing my Project Packet content type I clicked on new Project Packet and filled out the metadata fields and clicked Ok. But instead of seeing my new Project Packet document set, I was greeted with a 404 error!  I tested this a few times and replicated the error on additional sites.

Enable Site Scripting

So with a little research, I found that in order to activate the Document Set feature properly, we first need to enable site scripting on the target site.  This article in the PnP-Sites-Core github repo got me started down the right path, but it referenced a solution using SharePoint Online powershell. I wanted to complete this using PnP powershell.  I also found this discussion from the MS Tech Community to be helpful: How do I set DenyAddAndCustomizePages using PnP? (thanks Alan Trafford and Pieter Veenstra).

So here is the final script with everything together:

Import-module SharePointPnPPowerShellOnline
Set-PnPTraceLog On LogFile “pnplog.txt” Level Debug
$DenyAddCustomizeEnum =
Connect-PnPOnlineUrl https://tenant-admin.sharepoint.com
$ctx = Get-PnPContext
$site = Get-PnPTenantSite Detailed Url $siteUrl
Write-Host “Site: $site
$site.DenyAddAndCustomizePages = $DenyAddCustomizeEnum::Disabled
Write-Host “Disabled DenyAddAndCustomizePages”
Get-PnPTenantSite Detailed Url $siteUrl |
Select-Object url,DenyAddAndCustomizePages
$status = $null
Write-Host “Waiting… $status
Start-Sleep Seconds 5
$Site=Get-PnPTenantSite url $siteUrl Detailed
$status = $Site.Status
} While ($status -ne ‘Active’)
ApplyPnPProvisioningTemplate –Path “FieldCTs.xml”
ApplyPnPProvisioningTemplate –Path “Lists.xml”
Write-Error “Exception Occured”
Write-Error “Exception Type: $($_.Exception.GetType().FullName)”
Write-Error “Exception Message: $($_.Exception.Message)”
Set-PnPTraceLog – –off


So when including document sets within your PnP Provisioning solution, remember two things:

  1. Enable site scripting (by setting DenyAddAndCustomizePages to false)
  2. Include feature activation in your provisioning template
2 comments on “Tips & Tricks for Automated SharePoint Site Provisioning with PnP (Part 1)”

Tips & Tricks for Automated SharePoint Site Provisioning with PnP (Part 1)


For the last few months, I’ve been working on a few new solutions where clients need to easily create new project sites in SharePoint.  These customers have found that by using Modern Team & Communication sites, Hub sites, Office 365 Groups and Microsoft Planner they can provide a compelling platform for a wide range of project management requirements. 

Each client has unique requirements for the specific components that they need for each project site.  But in each case, there have been common requirements that each of my customers share…

  1. Need to customize a Modern Team site with a unique set of content types, columns, home page layout and other features to support their own project management approach.
  2. Ability for users or IT to easily spin up new project sites, and have each site contain the appropriate configuration (columns, content types, pages, web parts, permissions, etc.)
  3. Enable users across projects to easily access and share common project resources through use of a Hub site.

To implement these solutions, I’ve leveraged a combination of Office & SharePoint technologies including:

  • PnP Powershell
  • Site Designs / Site Scripts
  • Microsoft Flow
  • Azure Storage Queues
  • Azure Functions / Azure Automation

The end result is a solution that behaves like this:


Although this type of solution has a lot of moving parts, the end result is a solution that offers incredible flexibility to support changing client requirements in the future. 

If you’re not already familiar with these components, Microsoft provides great documentation for PnP Powershell and the overall PnP Provisioning Engine, so you may wish to start with these articles first.  Since the major setup steps are well documented elsewhere, this blog series will cover various tips and tricks (and lessons learned) that I encountered while implementing my customers’ provisioning solutions.

Bakground on PnP Powershell

But before we jump into the first “tip”, lets review some basics of PnP Powershell…

With PnP Powershell, the easiest way to generate a PnP provisioning template from a site is with this cmdlet:

Connect-PnPOnline “https://[yourtenant].sharepoint.com/teams/[YourSite]

Get-PnPProvisioningTemplate -out “MyTemplate.xml”

This command will generate a site provisioning template that contains all aspects of your site.  When you generate a provisioning template like this, your MyTemplate.xml will include any customizations that you created in the site (lists, libraries, content types, columns, web parts, navigation, security, etc.) along with definitions for all of the Out of the Box components in a modern team site.

If you execute this Powershell cmdlet on a brand new (uncustomized) modern team site, the resulting Xml file shall contain 890 lines of XML including :

  • 233 lines for OOTB site columns
  • 180 lines for OOTB site content types
  • 306 lines for OOTB libraries

Now let’s say you have a modern SharePoint team site and  you have customized the site by adding some custom site columns, content types, lists and document libraries.  You’ve also added some web parts to the home page and customized the left navigation.

If you generate a provisioning template for your customized site, the Xml file created shall be larger as it will contain the OOTB components and your custom components.  Now that you have a customized “MyTemplate.xml” you could use that template to apply your customizations to a new modern template site using this PnP Powershell cmdlet:

Connect-PnPOnline “https://[yourtenant].sharepoint.com/teams/TestSite001

Apply-PnPProvisioningTemplate –path “MyTemplate.xml”

One of the best parts about PnP Provisioning is that you can re-apply a template to the same site multiple times.  The provisioning engine is very smart about applying changes from the template to the site.  For elements that have not been changed, the provisioning engine simply skips those items.  So even though our template contains definitions for existing libraries like Site Assets, Style Library etc, if we have not customized those things there is no problem to re-apply the template that contains those definitions.

This also means that if you apply a template to a site, then make a small tweak to your template, you can apply the revised template to the existing site and it will simply apply the changes.

Tip # 1 – Customize your PnP site templates

Although the PnP Provisioning engine does an excellent job about ignoring definitions that already exist, you may still wish to customize your templates to make them smaller and more modular.  Personally, I tend to delete the the XML definitions for the out of the box Fields, ContentTypes and Lists from my template XML file, to make the template easier to review in the future.

Removing the OOTB lists is pretty easy, but removing the OOTB Fields and ContentTypes is much more tedious.  If you open your provisioning template XML file in your favorite XML editor, and collapse all sections you can see exactly where the OOTB ListInstances are stored.  In the example below, my template has one custom list (a library named “Sales”) and four OOTB libraries: Documents, Form Templates, Site Pages and Style Library. 

Assuming we have not customized the OOTB libraries, then we don’t need to keep them in the template, as we’ll have a copy of these libraries created by SharePoint in each new team site.  Therefore, I’ll delete the pnp:ListInstance elements for the OOTB libraries:

Before Delete

This Provisioning template XML only contains definitions for the site’s lists and it is 326 lines.

After Delete

Note that the provisioning template XML is now down to 59 lines


It’s been my experience that there are numerous scenarios where you’ll need or want to manually edit the provisioning template XML file.  By starting with a vastly smaller Xml file, our future tweaking to the template file will be easier.

Tip # 2 – Modularize your PnP site templates

In addition to removing redundant or unneeded components from the template, you may also find value in refactoring templates to make them more re-usable.

Consider a scenario where you’re defining a site template to manage projects.  Your company has various divisions that all manage projects, so we want a site template that is customized to our company’s project management standards.  Each project site needs a consistent set of custom columns, content types, libraries, lists, web parts, etc.  However, within each department, they may wish to customize the homepage and left navigation for their project sites differently from the standard company project sites.

It is easier to manage this type of requirement by creating PnP site templates that are modular and re-usable.  In this case, we may want to generate two provisioning templates for each project site:

  • A template for the “information architecture” (e.g. ProjectIA-Template.xml)
    • Contains definitions for site fields, content types, lists and libraries
    • One version of this template shall be used across all departments
  • A template for the layout and web parts on the home page along with left navigation (e.g. ProjectUI-DeptX-Template.xml)
    • Contains definitions for home page and left navigation
    • A unique version of this template may be created for each department

To generate modular templates, we use the Handlers parameter within the Get-PnPProvisioningTemplate cmdlet.  For example:

  • Get-PnPProvisioningTemplate -out “ProjectIA-Template.xml” –Handlers “Fields,ContentTypes,Lists”
  • Get-PnPProvisioningTemplate -out “ProjectUI-DeptX-Template.xml” –Handlers “Pages,PageContents,Navigation”

A few common examples of PnP Provisioning Handlers include:

Handler Name Description Represented in template XML as


Site columns



Site’s ContentTypes



Site’s lists and libraries



Members of SharePoint groups in the site



Layout and webparts on the home page




Left and Top navigation links


Note: For a full list of all PnP Provisioning Handler values, check out OfficeDevPnP.Core in Github


That concludes the Part 1 of this series on PnP Provisioning tips and tricks.  I’ll follow up this article soon with Part 2.