PnP Provisioning: The Good, the Bad and the Ugly

PnP Provisioning: The Good, the Bad and the Ugly

You know that feeling when a Microsoft technology seems too good to be true? PnP Provisioning promised us the dream: extract a SharePoint site template, store it as XML, and deploy it anywhere with a single PowerShell command. After diving deep into production implementations, GitHub issues, and real-world failures, here's the truth about what actually happens when you bet your infrastructure on PnP provisioning.

The PnP Provisioning Engine is simultaneously one of the most powerful and most fragile tools in the SharePoint ecosystem.

It's brilliantly designed for simple scenarios, but the moment you need it to do real work—permissions, subsites, content migration—you'll discover why experienced developers have a love-hate relationship with it.


The Good: When PnP Provisioning Shines

1. Structural Provisioning Excellence

When you limit PnP to what it does best, it's genuinely impressive:

  • Lists and Libraries: Creating document libraries, lists, and their configurations is rock-solid
  • Content Types: Custom content types provision reliably (if you avoid syndication)
  • Site Columns: Field definitions translate perfectly across sites
  • Pages: Modern pages can be templated and deployed (on supported platforms)

2. Developer-Friendly Workflow

The basic workflow is elegant:

# Extract from template site
Get-PnPSiteTemplate -Out template.xml -Handlers Lists,ContentTypes,Fields

# Apply to new site
Invoke-PnPSiteTemplate -Path template.xml

3. Parameterization Power

The token system allows dynamic templates:

<List Title="{parameter:ProjectName}-Documents" />
Invoke-PnPSiteTemplate -Path template.xml `
  -Parameters @{"ProjectName"="Momentum"}

4. Active Community

The PnP community is vibrant, with:

  • 350+ organizations contributing to Gaia-X integration
  • Active GitHub issue tracking and resolution
  • Regular schema updates (current: V202209)
  • Extensive documentation and examples

The Bad: Where It Gets Messy

1. Permission Provisioning is Broken

The Subsite Problem: Hard-coded in ObjectSiteSecurity.cs is a check that prevents any security settings from applying to subsites, even those with broken inheritance. This is intentional but undocumented.

// From source code analysis
if (web.IsSubSite()) {
    // Skip all security provisioning!
    return;
}

Permissions Are Additive: If a group already has Edit permissions and you apply a template with Contribute, it keeps Edit. Your template silently fails.

No Permission Removal: There's no XML schema for removing permissions. You must use PowerShell scripts as workarounds.

2. Performance Nightmares

Handlers That Hang:

  • ApplicationLifecycleManagement - Can hang indefinitely
  • ImageRenditions - Causes multi-minute delays
  • Navigation - Slow and breaks on site URL changes

Solution: Always exclude these handlers:

Get-PnPSiteTemplate -Out template.xml `
  -ExcludeHandlers ApplicationLifecycleManagement,ImageRenditions,Navigation

Large Site Timeouts: TEAMCHANNEL and SRCHCENTERLITE sites can take ~1 hour to extract, even when nearly empty.

VSCode Incompatibility: Scripts hang when run via VSCode PowerShell extension - use native PowerShell terminal instead.

3. Content Migration Doesn't Work As Advertised

Files Don't Copy: The provisioning engine intentionally doesn't export files from the source site. You must manually add them to the template XML.

List Items Require Cleanup: Exported list items include internal columns like ContentType that must be manually removed before import works.

Document Set Duplication Bug: Default documents in Document Set subfolders duplicate on every template run - 2 copies, 4 copies, 8 copies...

4. Schema Version Hell

Multiple active schemas create compatibility nightmares:

  • V202209 (latest)
  • V202103
  • V202002 (required by some tools like Plumsail)
  • Earlier versions still in use

Breaking changes in August 2020 invalidated thousands of existing templates.


The Ugly: The Real Pitfalls

1. Modern vs Classic Landmines

Security Model Conflict: Modern sites use Microsoft 365 Groups; classic sites use AD/SharePoint groups. Templates often fail silently when crossing this boundary.

ClientSidePages Ignored on SP2019: Modern pages simply don't provision on-premises - your template succeeds but pages vanish.

No Modern Subsites: Any subsite under a modern site must use classic templates. Modern subsite templates don't exist.

2. The Unique Permissions Trap

⚠️
Here's a nasty surprise: If you don't specify ObjectSecurity for items, PnP breaks inheritance anyway and creates unique permissions with no role assignments. Your carefully planned permission model? Destroyed.

From GitHub issue #216:

"No check is done that there are actually any roleassignments specified before breaking the inheritance"

3. Default Group Names Are Variable

SharePoint's default groups (Owners, Members, Visitors) don't have fixed names. A template expecting "Site Owners" will fail on a site that has "ProjectX Owners".

Workaround: Use tokens:

<Group>{associatedownergroup}</Group>

4. Search Configuration Goes Stale

The Search handler frequently fails with:

"Bad request: Please export a new search configuration firstly"

Search configurations expire. Templates that worked yesterday fail today.

5. Navigation Breaks on Deployment

Navigation nodes with hard-coded URLs:

<NavigationNode Url="https://contoso.sharepoint.com/sites/template" />

These don't update when applied to a different site collection. Your navigation points to the template site.

Battle-Tested Best Practices

Template Extraction

# Enable detailed logging
Set-PnPTraceLog -On -Level Debug

# Selective extraction (faster + cleaner)
Get-PnPSiteTemplate -Out template.xml `
  -Handlers Lists,ContentTypes,Fields,Files,WebSettings `
  -ExcludeHandlers ApplicationLifecycleManagement,ImageRenditions,Navigation,SiteSecurity `
  -ExcludeContentTypesFromSyndication

# Use configuration file for complex scenarios
Get-PnPSiteTemplate -Out template.xml -Configuration config.json

Template Application

# Test with specific handlers first
Invoke-PnPSiteTemplate -Path template.xml `
  -Handlers Lists `
  -Parameters @{
    "SiteTitle"="Production Site"
    "Owner"="admin@domain.com"
  }

# Gradually add more handlers
Invoke-PnPSiteTemplate -Path template.xml `
  -Handlers Lists,ContentTypes,Fields `
  -ExcludeHandlers SiteSecurity `
  -IgnoreDuplicateDataRowErrors

XML Template Editing

Always edit in Visual Studio with XSD validation:

<!-- Add proper namespace for IntelliSense -->
<pnp:Provisioning
  xmlns:pnp="http://schemas.dev.office.com/PnP/2022/09/ProvisioningSchema"
  Version="1.0">

  <!-- Remove default attributes -->
  <pnp:SiteColumn Name="CustomField"
                  DisplayName="Custom Field"
                  Type="Text"
                  Group="MyFields">
    <!-- DON'T include: Required='FALSE' -->
    <!-- DON'T include: FillInChoice='FALSE' -->
    <!-- DON'T include: Version='X' -->
  </pnp:SiteColumn>
</pnp:Provisioning>

Consecutive Template Strategy

For complex dependencies, use multiple templates:

# Template 1: Base structure (no dependencies)
Invoke-PnPSiteTemplate -Path template-base.xml

# Template 2: Dependent artifacts
Invoke-PnPSiteTemplate -Path template-advanced.xml

Handlers to ALWAYS Exclude

Never include these unless you absolutely need them:

$excludedHandlers = @(
    'ApplicationLifecycleManagement',  # Hangs
    'ImageRenditions',                  # Slow
    'Navigation',                       # Breaks on URL changes
    'SiteSecurity',                     # Better handled manually
    'SiteGroups',                       # Conflicts with custom security
    'WebApiPermissions'                 # Rarely needed
)

Get-PnPSiteTemplate -Out template.xml `
  -ExcludeHandlers ($excludedHandlers -join ',')

Real-World Production Strategy

Based on production implementation research:

What PnP Should Handle:

  • ✅ Lists and libraries structure
  • ✅ Content types (non-syndicated)
  • ✅ Site columns
  • ✅ Web settings
  • ✅ Files (if manually added to XML)

What You Should Handle Manually:

  • ❌ Permissions (use Azure AD groups via Microsoft Graph)
  • ❌ Site security (custom PowerShell modules)
  • ❌ Navigation (configure separately)
  • ❌ Search (configure after provisioning)

Implementation Flow

# 1. Extract and cache template (one-time or triggered)
Get-PnPSiteTemplate -Out ./Configs/transactions_template.xml `
  -Handlers Lists,ContentTypes,Fields,WebSettings `
  -ExcludeHandlers SiteSecurity,Navigation,ApplicationLifecycleManagement

# 2. In provisioning workflow: Apply structure FIRST
if (Test-Path $templatePath) {
    try {
        Invoke-PnPSiteTemplate -Path $templatePath `
          -ExcludeHandlers SiteSecurity `
          -IgnoreDuplicateDataRowErrors `
          -ErrorAction Stop
    } catch {
        Write-Warning "Template failed, continuing with permission config only"
        # Don't let template failure block security provisioning
    }
}

# 3. THEN apply custom permissions via Azure AD
Invoke-AzureAdGroupConfiguration -SiteUrl $siteUrl -Config $config
Invoke-SharePointPermissionsConfiguration -SiteUrl $siteUrl -Config $config

The Kill Switch

Critical: Never let PnP template failures block your core provisioning:

$SkipPnPProvisioning = $false  # Config flag

if (-not $SkipPnPProvisioning -and (Test-Path $templatePath)) {
    # Attempt PnP provisioning
} else {
    Write-Host "Skipping PnP provisioning (disabled or template missing)"
}

# Always continue to permissions, regardless of PnP result

Descision Matrix

Scenario Verdict Why
Repeatable site structures YES PnP excels at deploying identical lists/libraries multiple times
Dev to Production deployment YES Promotes configurations across environments reliably
Content type deployment YES Organization-wide content types work well
Simple modern sites (root only) YES No subsites = no permission headaches
Cached templates in source control YES Store once, deploy on demand
Complex permissions NO Broken inheritance, subsites, custom groups fail
Content migration NO Files don't copy - use dedicated migration tools
Large sites (1000+ items) NO Performance degrades, timeouts common
Cross-tenant scenarios NO Templates don't translate between tenants
Dynamic Azure AD security NO Use Microsoft Graph instead
Frequent permission changes NO Manual updates always faster
Hybrid: PnP structure + custom security MAYBE Works if you handle permissions separately
Classic sites MAYBE More stable than modern, but legacy concerns
Partial provisioning (specific handlers) MAYBE Use only Lists, Fields, ContentTypes
Template chains (sequential) MAYBE Requires careful dependency management

Quick Reference Card

Your Need Solution
Site structure PnP
Permissions Microsoft Graph
Content migration ShareGate/Migration Tool
Subsites Custom PowerShell
Large sites Custom code
Cross-tenant Rebuild templates

Use it for:

  • Structural provisioning (lists, libraries, content types)
  • Repeatable site templates
  • Development workflow automation

Don't use it for:

  • Permission management (use Microsoft Graph + Azure AD)
  • Content migration (use migration tools)
  • Complex subsites (manual configuration or custom code)

The Golden Rule:

"If your provisioning requirements include the word 'complex,' 'dynamic,' or 'permissions,' build custom PowerShell modules instead."

Action Items

For Developers Starting Out:

  1. Start small: Extract a simple template with only Lists and Fields
  2. Enable logging: Always use Set-PnPTraceLog -On -Level Debug
  3. Test in dev: Never run untested templates in production
  4. Build incrementally: Add one handler at a time

For Production Systems:

  1. Audit your handlers: Review what you're actually using
  2. Cache your templates: Store in source control, version them
  3. Build fallbacks: Don't let template failures block critical workflows
  4. Consider alternatives: Evaluate if custom PowerShell modules suit you better

For Decision Makers:

  1. Calculate TCO: Include template maintenance, debugging time, workarounds
  2. Assess complexity: If >3 handlers fail, consider custom development
  3. Plan exit strategy: Know how to provision without PnP if needed

Stay updated

Get new posts delivered to your inbox.