top of page
Writer's pictureKasper Larsen

Dealing with deep Content type hierarchies in PnP Provisioning Templates


When designing the Information Architecture for a solution, Content Types are one of the important tools in our toolbelt.


In most cases the Content Type design will result in a Content Type hierarchy like the one you see here:




When setting up the Master site before extracting the PnP Provisioning Template, the top-level Content Type, in this case the "NorthWindProjectDocType" is added to the document libraries as required.


However, when extracting a PnP Provisioning Template from the Master site you will notice that only the top-level is extracted, and when deploying the Template on the target site you will get this error:


Until now I have handled this issue by running an Azure Function before deploying the Template. The Function will call

Add-PnPContentTypesFromContentTypeHub -ContentTypes $ContentTypesList

on a list of Content Type IDs that I have looked up in the Content Type Gallery. This will ensure that the required Content Types will be available in the site.


This approach does work but is error prone as it is a manual process, and typos and fat fingering are a fact.


So, what are the alternatives?

I guess you could add each of the parent Content Type in one of the Lists or Document Libraries, and then delete them again, before extracting the Template. However, this is also error prone and we should be able to do better




So PnP.PowerShell to the rescue:


Basically, before extrating the Template I iterate all Lists, excluding the default lists. In the remaining Lists I grab each Content Type, locating the Content Type in the Content Type Gallery and finding the parents recursively.

Once all the parent Content Types have been located, I add them to the Master Site using Add-PnPContentTypesFromContentTypeHub (lightning fast).

When extracting the PnP Provisioning Template now it will contain ALL the required Content Types, and you will not get any errors when deploying the Template to a target site.


$sourceUrl = "https://contoso.sharepoint.com/sites/MasterSite"
$sourceConn = Connect-PnPOnline -Url $sourceUrl -Interactive -ReturnConnection
$ctHubConn = Connect-PnPOnline -Url "https://contoso.sharepoint.com/sites/contenttypehub" -Interactive -ReturnConnection
$allcontenttypesInCTHub = Get-PnPContentType -Connection $ctHubConn

function getParentContenttype($contentType) 
{
    $contenttypes = @()
    if($contentType.Id.StringValue -eq "0x01" -or $contentType.Id.StringValue -eq "0x0101" -or $contentType.Id.StringValue -eq "0x0120") #root content type
    {
        Write-Host "Root content type" -ForegroundColor Green
    }
    else 
    {
        write-host "$($contentType.Name) with id $($contentType.Id)" -ForegroundColor Blue
        $contenttypes += $contentType.Id
        $ctInHub = $allcontenttypesInCTHub | Where-Object { $_.Id.StringValue -eq $contentType.Id.StringValue }    
        $ctInHubParent = Get-PnPProperty -ClientObject $ctInHub -Property Parent -Connection $ctHubConn
        
        getParentContenttype($ctInHubParent)
        
    }
    return $contenttypes
}

#iterate through all non default lists and libraries on the site
$lists = Get-PnPList -Connection $sourceConn 
foreach($list in $lists)
{
    if($list.Hidden -eq $true -or $list.Title -eq "Site Assets" -or $list.Title -eq "Site Pages" -or $list.Title -eq "Style Library" -or $list.Title -eq "Form Templates" )
    {
        Write-Host "Skipping List $($list.Title) as it is a default list" -ForegroundColor Yellow
    }
    else 
    {
        Write-Host "Processing List $($list.Title)" -ForegroundColor Green
        $listContentTypes = Get-PnPContentType -List $list.Title -Connection $sourceConn
        foreach($ct in $listContentTypes)
        {
            #get site content type
            $onsiteCT = Get-PnPContentType -Identity $ct.Name -Connection $sourceConn
            $result = getParentContenttype($onsiteCT)
            #add the parent content type to the site
            foreach($ctId in $result)
            {
                Add-PnPContentTypesFromContentTypeHub -ContentTypes $ctId.StringValue -Connection $sourceConn -ErrorAction Stop | Out-Null
            }
        }
    }
}
 

The script is also available in the Script Samples | PnP Samples

27 views0 comments

Comments


bottom of page