Table of contents
When to use what
General disclaimer: Both tools are meant for the initial deployment of a site collection or its elements. Both cannot handle updates, at least I haven’t tried so far.
PNP - PowerShell | REST API | |
Description | A module you can install to your PowerShell locally.
https://pnp.github.io/powershell/ | API endpoints that are already available and can be executed using Power Automate.
https://learn.microsoft.com/en-us/sharepoint/dev/declarative-customization/site-design-rest-api |
Supported objects | ApplicationLifecycleManagement, AuditSettings, ComposedLook, ContentTypes, CustomActions, ExtensibilityProviders, Features, Fields, Files, ImageRenditions, Lists, Navigation, PageContents, Pages, PropertyBagEntries, Publishing, RegionalSettings, SearchSettings, SiteFooter, SiteHeader, SitePolicy, SiteSecurity, SiteSettings, SupportedUILanguages, SyntexModels, Tenant, TermGroups, Theme, WebApiPermissions, WebSettings, Workflows
Supports multi language site collections. | Lists, Libraries (more possible but I only use it for that), with their Columns, Views, Custom Formatting (some basic list settings without permissions).
Only takes one language. |
When to use | When you want to copy complete site collections or single objects like lists or libraries that users interact with.
Copying from one to another site collection | When you only want to copy basic lists or libraries, that users do not interact with. These lists or libraries are only “databases” for your Power Platform solution.
Copying lists or libraries from one to another site collection |
Caveats | Way more effort necessary than using rest.
Requires an enterprise application registered in Entra ID
Only for initial creation of objects, no updates possible | Only for initial creation of objects, no updates possible
Copies lists, libraries and columns with their displaynames and not their internal names. This might break your flows or apps depending on your naming convention.
Explanation: I usually build my lists and libraries that I use with Power Apps and Power Automate only using internal column names like qmsResubmissionDate instead of readable display names. If you were to copy the lists with this method using the displayname, you new list will have the previous displayname as the columns internal name. This might break your flows or apps. |
PNP - PowerShell
When-to-use-example You are building a solution on SharePoint, where the SharePoint interface is the primary system for your enduser. You might have Power Apps or Automate helping you out, but users interact with SharePoint directly. Therefore you have lists, libraries, terms, site columns, a structured navigation as well as a few default pages.
Check out the installation of the PNP-PowerShell here: https://pnp.github.io/powershell/articles/installation.html
Create a site template
This is not a complete script, it only supposed to show a rough guideline. For all possible parameters for the copy command, check out the CMDlet here: https://pnp.github.io/powershell/cmdlets/Get-PnPSiteTemplate.html
$exportsiteurl = Read-Host -Prompt "Export Site URL"
$ClientId = Read-Host -Prompt "Please enter the Client-Id"
$exportpath = "C:\temp\"
#Connect to the site collection you want to export from with the URL and the Client ID you got from the registration process
Connect-PnPOnline -Url $exportsiteurl -Interactive -ClientId $ClientId
#Depending on what you want to copy the command might vary. I always like to select only the lists I need, as the script doesnt need to extract hidden or default objects.
#This command will export only the two lists mentioned here with their IDs. It will create a .xml file with the lists information.
Get-PnPSiteTemplate -ListsToExtract "LIST 1 ID","LIST 2 ID" -Out $ExportFilePath -Verbose
#If you build a multi language enabled site collection, use the PersistMultiLanguageResources parameters. This will create a .resx file where language information is stored
Get-PnPSiteTemplate -PersistMultiLanguageResources -ListsToExtract "LIST 1 ID","LIST 2 ID" -IncludeAllPages -Out $ExportFilePath -Verbose
Pasting a site template
This is not a complete script, it only supposed to show a rough guideline. For all possible parameters for the paste command, check out the CMDlet here: https://pnp.github.io/powershell/cmdlets/Invoke-PnPSiteTemplate.html
$ImportSiteUrl = Read-Host -Prompt "Import Site URL"
$ImportFolder = "C:\temp\Solution\"
$ImportFile = $importFolder + "SolutionMain.xml"
#Connect to the site collection you want to import to with the URL and the Client ID you got from the registration process
Connect-PnPOnline -Url $ImportSiteUrl -Interactive -ClientId $ClientId
#If you want to completely apply the template to your new site collection, you can use this command
Invoke-PnPSiteTemplate -Path $ImportFile
#Usually I split up my template file into smaller pieces, as this help sometimes with performance. For example I will have multiple of those command importing different objects. You can specify the objects using the parameter for Handlers.
Invoke-PnPSiteTemplate -Path $ImportFile -Handlers TermGroups, Fields, ContentTypes
#If you have a multi language enabled site collection, you should use the parameter ResourceFolder to specify where you language files are stored. The command will automatically pick the correct ones to apply
Invoke-PnPSiteTemplate -Path $ImportFile -Handlers TermGroups, Fields, ContentTypes -ResourceFolder $ImportFolder
Copy with REST API
I use Power Automate to achieve this easily.
When-to-use-example You are building a solution in the Power Platform and you are using SharePoint lists or libraries as a database. User are never accessing the SharePoint site collection directly. They will always you the app or flows will process data in your lists and libraries. You are only using lists or libraries with list columns.
Feel free to check out the site design rest api here for some more information: https://learn.microsoft.com/en-us/sharepoint/dev/declarative-customization/site-design-rest-api
Copy a list
Looking at what the site design rest api is, you are basically creating a instructions on how to create the list with all its parameters. You can save those instruction into a JSON file. With the API call you request those instruction for one list at a time. If you have multiple lists, you will have to create a loop that appends each instruction JSON to an array. You can then save the array to a file and store it in a library somewhere.
Method: POST
Uri: _api/Microsoft.SharePoint.Utilities.WebTemplateExtensions.SiteScriptUtility.GetSiteScriptFromList
Headers:
{
"accept": "application/json;odata=verbose",
"content-type ": "application/json;odata=verbose"
}
Body:
{
//for lists
"listUrl":"SITEURL/Lists/ListNameAsDisplayedInUrl"
//for libraries
"listUrl":"SITEURL/LibraryNameAsDisplayedInUrl"
}
Result:
//this is the result that you either store directly into the JSON file, or if you have multiple lists append to an array variable. Later you will then save that array variable into the JSON file
body('ACTIONNAME')?['d']?['GetSiteScriptFromList']
Pasting a list
Pasting is only possible on another site collection, if a list with the same name already exists. The pasting process will be skipped. You could probably make a few adjustments to the instructions JSON to paste even into the same site collection, but I haven’t tried that so far.
Since you saved one instruction manual per list to your JSON file, you will also have to execute those instruction per list while pasting it into another site collection. The array you save into the JSON file can be used in an Apply to each loop, where you execute each instruction manual one by one.
Method: POST
Uri: _api/Microsoft.Sharepoint.Utilities.WebTemplateExtensions.SiteScriptUtility.ExecuteTemplateScript()
Headers:
{
"accept": "application/json;odata=verbose",
"content-type ": "application/json;odata=verbose"
}
Body:
{
"script":"ResultsFromCopyAction"
}
Results:
//An array of the past log, everything the past has done with error codes. If successful, the error code is 0.
body('ACTIONNAME')?['d']?['ExecuteTemplateScript']?['results']
I hope this helps as it helped me save a lot of time when developing Power Platform solution on SharePoint.
Thanks for reading! 💕