Using PowerShell to Save or Clone an Existing SharePoint Online Site to Publish a the Global Template to the "From Your Organization" Menu 

January 14, 2023 05:56 PM Comment(s) By Ryley Bauer

SharePoint Online is a powerful tool for creating intranet sites, and it's even more powerful when you can create and use custom templates to streamline the process for multiple projects or clients. Unfortunately, creating a template from an existing SharePoint Online site can be a difficult task, especially for non-programmers.


In the past, creating a template was as simple as a few clicks in SharePoint classic, but with the new SharePoint Online, it's not so easy anymore. I've tried various methods, such as using flows and http requests, but I've found that the best way to clone a site multiple times is by using PowerShell to export the existing site and create a template from it.


I created a simple script based on Microsoft's documentation, and even corrected a typo in the documentation regarding spaces in list and library names. I even corrected a typo on the docs! The internal names, such as "Shared%20Documents" had to be used instead of "Shared Documents".


In the upcoming Part 2 of this post, I will review the entire workflow, from a SharePoint list for projects, to a Power Automate flow that creates the site, provides permissions, applies the template, copies over some template files, and even applies customizations to the home page.


In summary, publishing an existing site as a template using PowerShell is the best way to clone a site multiple times. It's a little technical, but with the right script and a little bit of tweaking, you can make this process a lot more manageable.


Before proceeding, make sure to install the SharePoint Online Management Shell. The script I've provided includes a check and installation for the module, but I recommend installing it beforehand. 


Open PowerShell as administrator and try my sample code below. Adapting the sample script with some parameters and handling the module installation, we can use the following:

# install the module if you don't already have it
 if(
-not (
Get-Module Microsoft.Online.SharePoint.PowerShell -ListAvailable)){
 Install-Module Microsoft.Online.SharePoint.PowerShell -Scope CurrentUser -Force
 }

 # Choices for operations, set to either $true or $false
 $exportFromWeb = $true
 $exportBackupFile = $true
 $createGlobalTemplate = $true

 # SharePoint base URL
 $baseURL = "ryleybauer"
 $companyDisplayName = "Bauer Automate"

 # Edit these parameters to define your template
 $newSiteTitle = "Quick Project Template"
 $shortName = "Projects"

 # These variables are generated from the parameters above
 $adminURL = ' https://' + $baseURL + '-admin.sharepoint.com'
 $siteURL = 'https://' + $baseURL + '.sharepoint.com/sites/' + $shortName
 $outFolder = $env:USERPROFILE + '\Downloads\' + $newSiteTitle + ' \ '
 $outFile = $outFolder + $newSiteTitle + '.txt '

# Select Source Data
 $lists = "Shared%20Documents", "SitePages", "Lists/Losses", "Lists/Projects%20List"

# Connect to SharePoint Online
Connect-SPOService -Url $adminURL

# Generate Script from existing site
if ($exportFromWeb){
   $sourceSiteScript = Get-SPOSiteScriptFromWeb -WebUrl $siteURL -IncludeLinksToExportedItems -IncludedLists ($lists)
}

# Optionally create a backup file in the user' s documents folder
if ($exportBackupFile) {
   New-Item -Path $outFolder -ItemType Directory
   $sourceSiteScript | Out-File -filepath $outFile
}

# Optionally publish as global template and site design
if ($createGlobalTemplate) {
   $SiteScript = Add-SPOSiteScript -Title $newSiteTitle -Content $sourceSiteScript
   $SiteDesign = Add-SPOSiteDesign -Title $newSiteTitle -WebTemplate 64 -SiteScripts $SiteScript.Id
}

# Get site template list to show the ID of the newly created template and confirm it was successful
   Get-SPOSiteDesign

When I first started working with SharePoint Site Designs and Site Scripts, I hadn't used them before and wasn't familiar with their structure. However, I found a great resource in DevFacto's blog post on Getting Started with SharePoint Site Designs and Site Scripts. It helped me understand the anatomy of a site script, which was especially helpful when I needed to split my template file...

...
I didn't want to split the template file

...

I initially didn't want to split the template file, but I learned that site scripts are limited to 100,000 characters. My site had multiple lists and libraries with lots of columns and formatting, so I realized that I had more work ahead of me. Excited for the challenge, I decided to split my file by list and library. This way, it would be more modular for future use and each list/library file could be used individually in a new template if needed. If your template is simple and the file is less than 100,000 characters, then you can skip the next section where I discuss splitting the file.


Reading DevFacto's post was particularly helpful as it gave me a better understanding of the structure of the site script. The post does a great job of outlining the basic structure of the script that I had to use for each file:

 The structure of the script helped me to split my file by list and library which made it more modular for future use. My template site had 3 libraries and 7 lists, so I created 10 files. Each file had the same structure and looked something like this:


My template site had 3 libraries and 7 lists, so I had created 10 files. If you opened the files, each of them looked basically like this: 
{
    "$schema": "schema.json",
    "actions": [{
            "verb": "createSPList",
            "listName": "Shared Documents",
            "templateType": 101,
            "subactions": [{
                    "verb": "setDescription",
                    "description": "Project team can use this Documents Library to store deliverable documents."
                }
//EXTRA STEPS HERE
            ]
        }
    ],
    "bindata": {},
    "version": 1
};

Once we put all of that together, and understand the anatomy of a site script, we can clone very complex site templates.

If you take the original script from above and add a few more parameters to check if the script should import one or more files instead, you get the following:

# install the module if you don 't already have it
if (
  -not (
    Get-Module Microsoft.Online.SharePoint.PowerShell -ListAvailable
  )
) {
  Install-Module Microsoft.Online.SharePoint.PowerShell -Scope CurrentUser -Force
}

# Choices for operations, set to either $true or $false
$exportFromWeb = $true
$importFromFile = $false
$importFromMultipleFiles = $false
$exportBackupFile = $false
$createGlobalTemplate = $false

# SharePoint base URL
$baseURL = "ryleybauer"
$companyDisplayName = "Bauer Automate"

# Edit these parameters to define your template
$newSiteTitle = "Project Template"

# Select your existing site. The short name is the URL section after "https://bauerautomate.sharepoint.com/sites/SHORTNAME" 
$existingSiteShortName = "Projects"

# These variables are generated from the parameters above
$adminURL = ' https://' + $baseURL + '-admin.sharepoint.com'
$siteURL = 'https://' + $baseURL + '.sharepoint.com/sites/' + $existingSiteShortName
$outFolder = $env:USERPROFILE + '\Downloads\' + $newSiteTitle + ' \ '
$outFile = $outFolder + $newSiteTitle + '.txt '

# Select Source Data
$lists = "Shared%20Documents","SitePages","Lists/Losses","Lists/Projects%20List"

# Generate a name for each script - same as above - add as many as needed to match above
$inFile1Name = $newSiteTitle + " - Projects"
$inFile2Name = $newSiteTitle + " - Documents"
$inFile3Name = $newSiteTitle + " - SitePages"

# Input files from existing template that was split into multiple files - add as many as needed, and I recommend 1 per list/library to keep it modular
$inFile1 = $outFolder + ' \' + $inFile1Name + '.txt '
$inFile2 = $outFolder + ' \' + $inFile2Name + '.txt '
$inFile3 = $outFolder + ' \' + $inFile3Name + '.txt '

# Connect to SharePoint Online
Connect-SPOService -Url $adminURL

# Generate Script from existing site
if ($exportFromWeb) {
  $sourceSiteScript = Get-SPOSiteScriptFromWeb -WebUrl $siteURL -IncludeLinksToExportedItems -IncludedLists ($lists)
}

# Apply Script from existing single file
if ($importFromFile) {
  [string]$sourceSiteScript = Get-Content $inFile1
}

# Optionally create a backup file in the user' s documents folder
if ($exportBackupFile) {
  New-Item -Path $outFolder -ItemType Directory
  $sourceSiteScript | Out- File - filepath $outFile
}

# Optionally publish as global template and site design from single file
if ($createGlobalTemplate -and -not $importFromMultipleFiles) {
  $SiteScript = Add -SPOSiteScript -Title $newSiteTitle -Content $sourceSiteScript
  $SiteDesign = Add -SPOSiteDesign -Title $newSiteTitle -WebTemplate 64 -SiteScripts $SiteScript.Id
}

# Apply Script from existing multiple files - add as many as needed
if ($importFromMultipleFiles) {
  [string]$sourceSiteScript1 = Get -Content $inFile1
  [string]$sourceSiteScript2 = Get -Content $inFile2
  [string]$sourceSiteScript3 = Get -Content $inFile3
}

# Optionally publish as global template and site design from multiple files - add as many as needed
if ($createGlobalTemplate -and $importFromMultipleFiles) {
  $SiteScript1 = Add -SPOSiteScript -Title $inFile1Name -Content $sourceSiteScript1
  $SiteScript2 = Add -SPOSiteScript -Title $inFile2Name -Content $sourceSiteScript2
  $SiteScript3 = Add -SPOSiteScript -Title $inFile3Name -Content $sourceSiteScript3

  # add as many as needed to the end of this line to add all of the scripts to the site design
  $SiteDesign = Add -SPOSiteDesign -Title $newSiteTitle -WebTemplate 64 -SiteScripts ($SiteScript1.Id,$SiteScript2.Id,$SiteScript3.Id)
}

# Get site template list to show the ID of the newly created template and confirm it was successful
Get-SPOSiteDesign

Recent Blogs

Loading...

Ryley Bauer

Share -
Added to cart
- There was an error adding to cart. Please try again.
Quantity updated
- An error occurred. Please try again later.
Deleted from cart
- Can't delete this product from the cart at the moment. Please try again later.