Showing posts with label PnP. Show all posts
Showing posts with label PnP. Show all posts

May 29, 2025

Streamline SPFx Builds with Azure DevOps CI/CD Pipeline – Part 2: Automating Deployment (CD)

Introduction

In the First Part, we built a Continuous Integration (CI) pipeline to automatically package our SharePoint Framework (SPFx) solution. Now it’s time to automate deployment across multiple SharePoint sites using Continuous Deployment (CD).

This blog covers setting up a CD pipeline in Azure DevOps, triggered automatically after your CI pipeline finishes, to deploy your .sppkg file to all the required sites. 


Step 1: Prepare Your PowerShell Deployment Script

We’ll use PowerShell with PnP.PowerShell to handle the deployment. This script connects to SharePoint Online, uploads the .sppkg package, and publishes it on each target site.

Here's the script:

  Param (  
   [string] $RootPath = $(Throw "Root Path is required."),  
   [string] $ClientId = $(Throw "ClientId is required."),  
   [string] $TenantId = $(Throw "TenantId is required."),  
   [string] $TenantURL = $(Throw "Tenant URL is required."),  
   [string] $ClientSecret = $(Throw "Client Secret is required."),  
   [string] $packagename = $(Throw "Package name is required.")  
 )  
 $SiteURL = "$($TenantURL)/sites/TestSite"  
 $ModernPOPAppPath = "$RootPath\drop\$packagename"  
 $currentPOPSite = ""  
 function ReconnectPNPConnection {  
   Write-Host "Reconnecting to site $($currentPOPSite)"  
   Disconnect-PnPOnline  
   Connect-PnPOnline -Url $currentPOPSite -ClientId $ClientId -ClientSecret $ClientSecret -Tenant $TenantId  
 }  
 function addCustomSPFxApps {  
   $appCatalog = Get-PnPSiteCollectionAppCatalog -CurrentSite -ErrorAction SilentlyContinue  
   while ($null -eq $appCatalog) {  
     Write-Host "Waiting for app catalog creation..."  
     Start-Sleep -Seconds 30  
     $appCatalog = Get-PnPSiteCollectionAppCatalog -CurrentSite -ErrorAction SilentlyContinue  
   }  
   $uploadApp = $false  
   while ($uploadApp -eq $false) {  
     try {  
       Write-Host "Uploading SPFx app..."  
       $App = Add-PnPApp -Path $ModernPOPAppPath -Scope Site -Overwrite -Timeout 900  
       Write-Host "Publishing SPFx app..."  
       Publish-PnPApp -Identity $App.ID -Scope Site -SkipFeatureDeployment -ErrorAction SilentlyContinue  
       Write-Host "SPFx app deployed successfully."  
       Start-Sleep -Seconds 60  
       $uploadApp = $true  
     } catch {  
       Write-Host $_.Exception.Message  
       Write-Host "Retrying after 30 seconds..."  
       Start-Sleep -Seconds 30  
       ReconnectPNPConnection  
     }  
   }  
 }  
 Write-Host -f Cyan "Installing PnP.PowerShell module..."  
 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12  
 Install-Module PnP.PowerShell -RequiredVersion 2.4.0 -Force -Scope CurrentUser  
 Import-Module -Name "PnP.PowerShell"  
 Write-Host "Connecting to $($SiteURL)..."  
 Connect-PnPOnline -Url $SiteURL -ClientId $ClientId -ClientSecret $ClientSecret -Tenant $TenantId  
 $currentPOPSite = $SiteURL  
 addCustomSPFxApps  
 Write-Host "Disconnecting from $($SiteURL)..."  
 Disconnect-PnPOnline  

We need to upload this PowerShell script to a document folder as shown below and add a step in the CI (Continuous Integration) pipeline to copy this file into the artifacts.


Step 2: Set Up the CD Pipeline in Azure DevOps

1. Create the Release Pipeline:

  • Navigate to the Release section in Azure DevOps.
  • Click New Pipeline.

2. Add Artifacts:

  • Link your CI pipeline’s artifacts (the .sppkg file and related files).test

3. Enable Continuous Deployment Trigger:

  • Click the lightning bolt icon next to the artifact and enable the trigger to deploy automatically after CI.

4. Add a Stage:

  • Click Add a stage and choose Empty Job.

5. Add PowerShell Task:

  • In the stage, click to add a task.
  • Choose PowerShell.
  • Set the script path to point to your deployment script (e.g., Documents/CDScript.ps1).
  • Set the necessary parameters (RootPath, ClientId, TenantId, etc.).

6. Save and Deploy:

  • Save the pipeline and test it by running the full CI/CD process.

Conclusion

By integrating both CI and CD pipelines in Azure DevOps, you’ve established a fully automated and reliable deployment process for your SPFx solutions:

  • CI Pipeline: Automatically builds and packages your SPFx solution on every code push.
  • CD Pipeline: Seamlessly deploys the package to all required SharePoint sites immediately after a successful build.

This setup not only saves time but also ensures consistency and reduces the risk of manual deployment errors across environments.

If you have any questions you can reach out our SharePoint Consulting team here.

March 21, 2025

SharePoint Site Provisioning: A Guide to Site Scripts & Site Designs with PowerShell

Introduction: Site Scripts and Site Designs

Automating site provisioning in contemporary SharePoint Online environments is essential for ensuring consistency, upholding governance, and enhancing collaboration within an organization. Site Scripts and Site Designs are key tools that facilitate this process, helping to standardize site creation and maintain organizational standards.


What is Site Design?

Site Design is a predefined set of actions that allows users to create new sites with a Modern UI in SharePoint Online (Office 365). It helps SharePoint consultants enhance consistency across multiple site collections within the same tenant. For example, with SharePoint Site Design, users can create new lists and libraries, define the site and list columns, establish content types, set themes, and choose a site logo, among other features.

Site Design is often associated with templates, but it is important to understand that it is not the same as a traditional template tied to a specific site. Many users mistakenly believe that changes made in Site Design will automatically apply to their existing sites. However, this is not how SharePoint Site Design functions. To implement the modifications from Site Design on existing sites, you must actively apply the Site Design to those sites.

SharePoint consultants have the option to choose Site Design when creating a new site through the user interface. Additionally, Site Designs can be applied to existing sites using various methods such as REST, CSOM, or PowerShell scripts. Essentially, Site Designs consist of a collection of scripts that operate in the background during the selection of a design for the creation of a new site. This functionality streamlines the process of site setup and ensures consistency across different SharePoint sites.

What are Site Scripts?

Site Scripts are JSON files that outline a specific sequence of actions to be executed when a Site Design is applied to an existing site or when a new site is created. These scripts can be run multiple times on the same site. They have a non-destructive nature, meaning they will only add missing elements to SharePoint sites.

As of now, you can add the below actions to your Site Script:
  1. Create a new list or library (or alter the default one created with the site).
  2. Create the site columns, and content types, and configure other list settings.
  3. Set site branding properties such as navigation layout, header layout, and header background.
  4. Apply a theme and also Set a site logo.
  5. Triggering a Microsoft Flow.
  6. Include principals (users and groups) in SharePoint roles.

NOTE: For constructing a JSON schema and its valid verbs, you can check the following resources.

Why Use Site Scripts and Site Designs?

  1. Standardization & Consistency
    • Ensures all sites follow the same structure, branding, and compliance requirements.
    • Reduces manual errors by applying predefined settings.
  2. Automation & Efficiency
    • Eliminates repetitive tasks such as manually creating lists, libraries, columns, or permissions.
    • Reduces setup time for new SharePoint sites.
  3. Customization without Custom Code
    • Site Scripts enable customization using JSON, eliminating the need for complex development efforts.
    • It can be enhanced using PnP PowerShellPower Automate, and various other SharePoint automation tools.
  4. Easy Updates & Scalability
    • Site Designs can be updated centrally and re-applied to existing sites.
    • Ideal for large organizations managing multiple SharePoint sites.

Creating a New Site Design and Site Script

Let us develop a new site script and design to enhance our understanding and generate additional ideas regarding the site script and design.

Create a JSON file named "Site-Script.json" and include the following code within it. The code defines five actions: 
  1. The first two actions create site columns.
  2. The third action creates a new content type in the site, named "User," and adds the two previously created site columns to this content type.
  3. The fourth action creates a new list in the SharePoint site, named "User Information," utilizing the newly created content type.
  4. The fifth action creates a new document library in the SharePoint site, named "User Documents", utilizing the created content type.

{
  "$schema": "https://developer.microsoft.com/json-schemas/sp/site-design.json#",
  "actions": [
    {
      "verb": "createSiteColumn", //verb to create SiteColumn
      "fieldType": "Text", //column type you want to create
      "displayName": "First Name", //Column Display Name
      "internalName": "FirstName",//Column Internal Name
      "isRequired": false, 
      "id": "2d738560-6a73-4d1b-854e-8b195f9021eb", //Unique gui-id for your site column you can genrate it manuualy.
      "group": "Test Site Columns" //give group name to identify
    },
    {
      "verb": "createSiteColumn",
      "fieldType": "DateTime",
      "displayName": "Date of Birthday",
      "internalName": "DateOfBirthday",
      "isRequired": false,
      "id": "da840479-d27e-490d-8624-cd1144ee07cc",
      "group": "Test Site Columns"
    },
    {
      "verb": "createContentType", //verb to create content type in site
      "name": "User", //ContentType Name
      "id": "0x0101009D1CB255DA76424F860D91F20E6C411800B609FEFDEFAA484299C6DE254182E666", //Unique content typeID
      "description": "User content type containing personal information",
      "parentId": "0x0101009D1CB255DA76424F860D91F20E6C4118", //Parent content type id
      "hidden": false,
      "subactions": [
        // you can add your site columns as many as you can..
        {
          "verb": "addSiteColumn",
          "internalName": "FirstName"
        },
        {
          "verb": "addSiteColumn",
          "internalName": "DateOfBirthday"
        }
      ]
    },
    {
      "verb": "createList", //Verb for Create a List
      "listType": "GenericList", //List type you want to create Ex:GenericList(CustomList),Document Library,Task List etc...
      "title": "User Information", //Title of your list
      "description": "List to store user personal information.", //Desciption of your list
      "templateType": 100, //Gave templtype for custom list it's 100, for document library it's 101
      "contentTypesEnabled": true, 
      "subactions": [
        {
          "verb": "addContentType",
          "name": "User"
        }
      ]
    },
    {
      //Creates a new Document Library
      "verb": "createList",
      "listType": "DocumentLibrary", //List type Document Library 
      "title": "User Documents", 
      "description": "Library for storing user-related documents.",
      "templateType": 101,
      "contentTypesEnabled": true,
      "subactions": [
        {
          "verb": "addContentType",
          "name": "User"
        }
      ]
    }
  ],
  "version": 1
}
If you want to create a list directly with its columns, you need to add the below JSON schema structure in the site script to accomplish this.

{
  "verb": "createList",
  "listType": "GenericList",
  "title": "User Information",
  "description": "List to store user personal information.",
  "templateType": 100,
  "subactions": [
    {
      "verb": "addField",
      "fieldType": "Text",
      "displayName": "First Name",
      "internalName": "FirstName",
      "isRequired": false
    },
    {
      "verb": "addField",
      "fieldType": "DateTime",
      "displayName": "Date of Birthday",
      "internalName": "DateOfBirthday",
      "isRequired": false
    }
  ]
}

How we can add Taxonomy Fields (Managed Metadata Fields) as Site Columns in Site-Script

In SharePoint, taxonomy fields, also known as managed metadata fields, are used to connect content types and lists with the Term Store. When creating site scripts, you can incorporate taxonomy fields as site columns and attach them to content types. Below is an example of how to construct the JSON schema to create a taxonomy field as a site column and add it to a content type using SharePoint site scripts.

First, you need to create a hidden site column associated with your actual taxonomy field. This column is already created with your managed property when you registered your managed property in the term store. Enter the below JSON schema to add the site column and change the value as per your managed property.

These fields are essential for backend processing, calculations, or metadata purposes but should remain hidden from end-users, especially when utilized for taxonomy management.

{
  "verb": "createSiteColumnXml",
  "schemaXml": "<Field ID=\"{"Unique Field GUID of your Managed Property"}\" Type=\"Note\" Name=\"DegreeTaxnomyField\" StaticName=\"DegreeTaxnomyField\" Group=\"Test Site Columns\" DisplayName=\"DegreeTaxnomyField\" ShowInViewForms=\"FALSE\" Required=\"FALSE\" Hidden=\"TRUE\" CanToggleHidden=\"TRUE\" />"
}
NOTE: Ensure that the Hidden attribute is set to true, and the Field ID matches exactly as it was created in the term store.

As mentioned before, these fields are hidden from the end-user and cannot be seen in the term store anywhere. To find the term's unique field ID, you can acquire it using the below PnP commands.

$adminSite = "https://<<Your-TenantName>>-admin.sharepoint.com"

Connect-PnPOnline -Url $adminSite -UseWebLogin

$TermField = Get-PnPField -Identity "<<Your Term-Name>>"
$fieldID = $TermField.TextField

write-Host "Field Id: $fieldID"
These commands return the Term's Field ID like: "Field Id: 6b9b1fd5-474c-4f08-820c-f1fc4352c1ba"

Now we create the Taxonomy Field Site Column, which we can add as site columns in our content type or anywhere else we choose to use it.
  • To create a taxonomy field as a site column in a site script, you need to define the field’s properties such as the Term Store, Term Set, and whether the field allows multiple selections.
  • Below is an example of creating a TaxonomyFieldType (managed metadata field) as a site column:
{
    "$schema": "https://developer.microsoft.com/json-schemas/sp/site-design.json#",
    "actions": [
        {
            "verb": "createSiteColumnXml",
            "schemaXml": "<Field ID=\"{Random unique Field-ID}\" Type=\"TaxonomyFieldType\" Name=\"Degree\" SourceID=\"http://schemas.microsoft.com/sharepoint/v3\" StaticName=\"Degree\" DisplayName=\"Degree\" Group=\"Test Site Columns\" ShowField=\"Term1033\" Required=\"FALSE\" EnforceUniqueValues=\"FALSE\" Mult=\"TRUE\"> \
      <Default></Default> \
      <Customization> \
          <ArrayOfProperty> \
              <Property> \
                  <Name>SspId</Name> \
                  <Value><<Your TaxonomyStoreSSPID>></Value> \
              </Property> \
              <Property> \
                  <Name>GroupId</Name> \
                  <Value><<Your TermStoreGroupID>></Value> \
              </Property> \
              <Property> \
                  <Name>TermSetId</Name> \
                  <Value><<DegreeTermSetID>></Value> \
              </Property> \
              <Property> \
                  <Name>TextField</Name> \
                  <Value>{<<Your Taxonomy Field ID>>  // same as per the Your Hidden column's FieldID
            }</Value>      
              </Property> \
              <Property> \
                  <Name>AnchorId</Name> \
                  <Value>00000000-0000-0000-0000-000000000000</Value> \
              </Property> \
              <Property> \
                  <Name>IsPathRendered</Name> \
                  <Value>false</Value> \
              </Property> \
              <Property> \
                  <Name>IsKeyword</Name> \
                  <Value>false</Value> \
              </Property> \
              <Property> \
                  <Name>Open</Name> \
                  <Value>false</Value> \
              </Property> \
          </ArrayOfProperty> \
      </Customization> \
  </Field>"
        }
    ]
}
Explanation of the JSON Schema:
  • Type: TaxonomyFieldType – Defines that this field will be used as a managed metadata field.
  • SourceID: Points to the SharePoint schema.
  • ShowField: Defines the language to be used to show the term (e.g., Term1033 for English).
  • Required: If the field is mandatory (FALSE means optional).
  • Mult: Allows multiple terms to be selected from the Term Set (TRUE).
  • Customization: Contains additional configuration for the taxonomy field, such as:
  • SspId: The ID for the taxonomy store.
  • GroupId: The ID of the Term Set group.
  • TermSetId: The ID of the Term Set from the Term Store.
  • TextField: The GUID of the field that will render the term text.
  • IsPathRendered: Whether to show the term path (hierarchy).
  • AnchorId: A default value (usually 00000000-0000-0000-0000-000000000000 for root terms).
  • IsKeyword: Defines whether the term is a keyword.
  • Open: Specifies whether the term set is open.
This is how we can create our site script with desired Content Types, Ste Columns, Predefined List Configurations, Document Libraries, and much more.

Now, we'll deploy our JSON Schema as a site script for our SharePoint tenant. Please refer to the PnP commands below for more information.

$adminSite = "https://<<Your-TenantName>>-admin.sharepoint.com" #Your Sharepoint admin site URL
$siteScriptFile ="c:\scripts\site-script.json" #File path of JSON file.

Connect-PnPOnline -Url $adminSite -UseWebLogin

$content = [IO.File]::ReadAllText($siteScriptFile)

$SPSiteScript = Add-PnPSiteScript -Content $content -Title "Test Site-Script"

$SiteScriptId = $SPSiteScript.Id

Write-Host "Site-Script ID: $SiteScriptId"
NOTE: Please save the Script ID that we used to create the Site Design.

Now, We create a new site design from the newly created site script. Please refer to the PnP commands below to create the site design.

$adminSite = "https://<<Your-TenantName>>-admin.sharepoint.com" #Your Sharepoint admin site URL

Connect-PnPOnline -Url $adminSite -UseWebLogin

$SiteDesignDetails = Add-PnPSiteDesign -Title $SiteDesignTitle -SiteScriptIds $SPSiteScript.Id -Description $SiteDesignTitle -WebTemplate TeamSite

Write-Host "Site Design is deployed successfully the GUID is:$($SiteDesignDetails.Id)"
NOTE: Please save the Site Design ID that we can use anywhere else.

After creating the site design, we can apply it to any site within our tenant. To do this, we need to run the following command.

$adminSite = "https://<<Your-TenantName>>-admin.sharepoint.com" #Your Sharepoint admin site URL

Connect-PnPOnline -Url $adminSite -UseWebLogin

# Run this Command If Your Site-Script has 30 Or less than 30 Actions
Invoke-PnPSiteDesign -Identity "<<Site-DesigneeID>>" -WebUrl "<<Site url>>"

# Run this Command If Your Site-Script has More than 30 actions
Add-PnPSiteDesignTask -SiteDesignId "<<Site-DesigneeID>>" -WebUrl "<<Site url>>"
NOTE: Execute the command based on the number of actions defined in your site script.

Summary 

This blog post discusses how Site Scripts and Site Designs in SharePoint Online simplify the site provisioning process by automating the creation of lists, libraries, content types, and branding. These tools help maintain consistency and governance across sites. 

 

Site Designs specify the configurations for a site, while Site Scripts—created using JSON—execute specific actions. These actions can include adding site columns, applying themes, and integrating Power Automate. By using these tools, organizations can reduce manual effort, enforce standardization, and achieve customization without the need for complex coding.


If you have any questions you can reach out our SharePoint Consulting team here.

August 14, 2024

Harnessing SharePoint OOTB Events with PnP Commands for Automated Permission Updates & Efficient Operations

Introduction

In SharePoint Online, automating operations in response to internal events — such as the creation of new site groups or granting permissions — can significantly streamline administrative tasks. This is particularly vital in environments with multiple associated sites where permissions need to be synchronized regularly. Here's how we can use SharePoint's OOTB events to improve operational efficiency and ensure timely updates across sites.

Challenges in Synchronizing Permissions

Consider a typical scenario in a SharePoint environment with one hub site and several associate sites. When a new user is added to a group in the hub site, the same user must be added across all associated sites. 

If we use the Logic App or Power Automate to sync the permission across all these sites it would require daily SharePoint group checks, leading to permissions synchronization delays.

Innovative Solution Using PnP Commands

To address this delay, we leveraged SharePoint PnP (Patterns and Practices) commands to trigger Flows or Azure Functions directly via HTTP when specific OOTB events occur. This method provides immediate event details such as usernames and group names, which can then be used to synchronize user permissions across sites instantly.

Setup and Configuration

We set up event receivers in SharePoint that respond to specific user group changes. Here are the PnP PowerShell commands used to configure these event receivers:

1. When a User is Added:

 Add-PnPEventReceiver -Name "GroupUserAdded" -Url $functionUrlToAddUser -EventReceiverType GroupUserAdded -Synchronization Asynchronous  

2. When a User is Removed:

 Add-PnPEventReceiver -Name "GroupUserDeleted" -Url $functionUrlToRemoveUser -EventReceiverType GroupUserDeleted -Synchronization Asynchronous  

These commands integrate two Azure Functions: the first triggers when a user is added to any SharePoint group on the Hub site, and the second when a user is removed. This setup ensures that any modifications to user groups are immediately reflected across all associated sites without the need for manual intervention.

Benefits and Capabilities

By leveraging OOTB events, we can perform a variety of operations that respond dynamically to changes within the SharePoint environment. These include but are not limited to:

  • Adding, updating, or deleting items, lists, and sites
  • Managing group and role definitions and assignments
  • Handling file movements and attachment operations

Conclusion

Utilizing SharePoint's OOTB events with PnP commands to trigger automated actions eliminates delays inherent in daily synchronization tasks. This approach not only enhances operational efficiency but also improves user experience by ensuring that permissions and other configurations are consistently up-to-date. This strategy ensures more efficient management of SharePoint systems, minimizing delays and boosting overall operational effectiveness.

 If you have any questions you can reach out our SharePoint Consulting team here.

June 23, 2022

SharePoint Framework (SPFx) - How to add Request Header while using PnP for SharePoint List CRUD Operations?

Introduction:

In this blog we will learn how can we add the Request Header while using PnP in SharePoint Framework (SPFx) web parts.

Problem Statement:

When we use the Fetch or Ajax requests in SPFx, we have option to add the Request Header to the REST API requests we are using, but when using PnP, there is no option to add the Request Header while performing CRUD operations in SharePoint.

For example, while using Fetch Request to get the list items, we can add the header as give in below code snippet:
 return fetch(APIUrl, {  
    credentials: 'same-origin',  
    headers: {   
         'Accept': 'application/json;odata=verbose',  
         'odata-version': ''   
    }  
   })  
    .then((res) => res.json())  
    .then((result) => {      
     return result.d.results;  
    },  
     (error) => {  
      console.error('Error:', error);  
     }  
    );  

But, to get the list items using PnP, we will use below code and we will not have option to add the headers:
 await web.lists.getByTitle('List Title').items.select("Title").then((response) => {  
    this.setState({ SiteTimeZoneOnly: response[0].TimeZone });  
 });  

Resolution:

How to add headers:

Step 1) Go to the .ts file of your web part.

Step 2) Now, go to the onInit method.

Step 3) Now, we can add the header in onInit method as given in below code snippet:
 protected onInit(): Promise<void> {  
   return super.onInit().then(_ => {  
    // other init code may be present  
    sp.setup({  
     spfxContext: this.context,  
     sp: {  
      headers: {  
        "X-ClientTag": "********"  
      }  
     }  
    });  
   });  
  }  

Here we have added 'X-ClientTag' header. You can also add comma separated multiple headers as shown in the below code snippet.
 sp.setup({  
     spfxContext: this.context,  
     sp: {  
      headers: {  
        "X-ClientTag": "********",  
        "Accept": "application/json;odata=verbose"  
      }  
     }  
    });  

Now your header is added successfully. You can verify the headers from Network tab of browser developer tool as shown in the below screenshot:

Conclusion:

This is how we can add Request Header in SPFx while using PnP to perform SharePoint list/library operations.

If you have any questions you can reach out our SharePoint Consulting team here.

January 13, 2022

How to use the context of another site while using PnP in SharePoint Framework (SPFx) web part?

Introduction:

When we use PnP to work with SharePoint lists, PnP will use the context of the current site. We cannot use the context of another site.

In this blog, we will learn how can we get the context of another site.

Steps:

Step 1: First, we need to install npm package for sp-pnp-js. Execute below command to install it:

 npm I sp-pnp-js  

Step 2: Once it is installed, import the web from sp-pnp-js as below:

 import { Web } from "sp-pnp-js";  

Step 3: Now, we will get the web context of particular site collection as below:

  • var siteUrl = "https://***********.sharepoint.com/sites/*******";
  • let web: any = new Web(siteUrl);

Step 4: Now, you can use this web context to work with SharePoint lists as below:

  • var tickers = await web.lists.getByTitle("List Name").items.select("Title”).get();

Now, in a tickers variable, you will be able to fetch the list items from another site collection.

Conclusion:

This is how we can use the context of another site in the SPFx web part. Hope this helps!

If you have any questions you can reach out our SharePoint Consulting team here.

December 2, 2021

[Issue Resolved]: "Specified argument was out of the range of valid values" while executing Add-PnPSiteScript and Add-PnPSiteDesign PowerShell commands

Introduction

Recently, we implemented a SharePoint site design using a PowerShell script for a Postal and Parcel company based out of Melbourne, Victoria, Australia. We encountered an issue while applying the site script and site design during the execution of  Add-PnPSiteScript and Add-PnPSiteDesign commands.


In this blog, we will learn about the issue and its solution that we can implement while creating a new site design template using the PowerShell script. 

Issue

    • We normally use the Add-PnPSiteScript and Add-PnPSiteDesign commands to add a new Site Script and Site Design on a SharePoint tenant. 
    • When we execute these commands frequently on the same tenant, a new Site Script is being created every time. 
    • And after some time, we encounter these below issues during the execution of the PowerShell script. 

    Image 1


    • It says “Specified argument was out of the range of valid values”. 

    Solution

      This issue is occurring because there is a tenant level limitation in SharePoint that we can create max of 100 Site Scripts and 100 Site Designs templates.

      • To resolve these issues, first of all, we need to remove some of the unused Site Scripts and Site Designs so that we can add our new Site Scripts and Site Designs. 
      • To remove a Site Script and Site Design, we will be using the below commands. 

       Remove-PnPSiteScript -Identity $scriptID -Force  
       Remove-PnPSiteDesign -Identity $designID -Force  
      
      • Here the $scriptID & the $designID is the ID of the existing Site Script and Site Design that is been available at the SharePoint tenant level.
      • The –Force in the Remove command is used to confirm the execution of this command without asking for confirmation to remove for its execution.
      • We cannot view these IDs directly from the SharePoint tenant, so we will be using the below commands to retrieve the details of these IDs.

       Get-PnPSiteScript  
       Get-PnPSiteDesign   
      

      • The Get-PnPSiteScript & the Get-PnPSiteDesign commands will retrieve the available Site Scripts and Site Design details and we will extract these details, identify unused Site Script/Site Design and will be using them for removing the existing unused Site Design and Site Scripts.
      • And then, it should allow creating your new Site Design and Site Script by executing the Add-PnPSiteScript and Add-PnPSiteDesign commands.

      Conclusion

      This is how we can resolve the "Specified argument was out of the range of valid values" error in the PowerShell script. Hope this helps, good day! 

      If you have any questions you can reach out our SharePoint Consulting team here.