How to Automate SharePoint Site Creation with Site Scripts & Site Designs (2025)

SharePoint Online site scripts and site designs are powerful tools that allow administrators and developers to automate the creation of SharePoint sites with predefined configurations. This comprehensive guide will walk you through creating site scripts, defining various field types, working with site columns, and setting up default templates.

In SharePoint Online, site scripts are JSON files that define actions like adding lists, applying themes, and configuring site columns. You can reference the official Microsoft Site Script JSON Schema to understand the full structure and supported actions. Once the script is ready, you can associate it with a site design. To learn how to apply site designs programmatically or through the UI, refer to this Microsoft guide on Site Designs.

What Are Site Scripts and Site Designs?

Site Scripts are JSON files that contain a series of actions to execute when creating a new SharePoint site. They define the structure, columns, lists, and other configurations.

Site Designs are templates that reference one or more site scripts and can be applied when creating new sites, making them available as custom templates in SharePoint.

Getting Started with Site Scripts

Basic Site Script Structure

Every site script follows a standard JSON structure with specific actions. Here's the fundamental template:

{
  "$schema": "https://developer.microsoft.com/json-schemas/sp/site-design-script-actions.schema.json",
  "actions": [
    {
      "verb": "createSiteColumn",
      "fieldType": "Text",
      "internalName": "ProjectStatus",
      "displayName": "Project Status",
      "isRequired": false,
      "group": "Custom Columns"
    }
  ],
  "bindata": {},
  "version": 1
}

Understanding Field Types and Their Definitions

SharePoint supports various field types, each with specific properties and use cases. Let's explore the most commonly used ones:

1. Text Fields

{
  "verb": "createSiteColumn",
  "fieldType": "Text",
  "internalName": "ProjectTitle",
  "displayName": "Project Title",
  "isRequired": true,
  "group": "Project Columns",
  "description": "Enter the project title",
  "maxLength": 255
}

2. Number Fields

{
  "verb": "createSiteColumn",
  "fieldType": "Number",
  "internalName": "ProjectBudget",
  "displayName": "Project Budget",
  "isRequired": false,
  "group": "Project Columns",
  "description": "Enter project budget in USD",
  "min": 0,
  "max": 1000000,
  "decimals": 2
}

3. Choice Fields

Choice fields are essential for standardizing data entry. In SharePoint site scripts, choice fields must be defined using schema XML format:

{
  "verb": "createSiteColumn",
  "fieldType": "Choice",
  "internalName": "ProjectPriority",
  "displayName": "Project Priority",
  "isRequired": true,
  "group": "Project Columns",
  "schemaXml": "<Field Type='Choice' DisplayName='Project Priority' Required='TRUE' Format='Dropdown' StaticName='ProjectPriority' Name='ProjectPriority' Group='Project Columns'><CHOICES><CHOICE>Critical</CHOICE><CHOICE>High</CHOICE><CHOICE>Medium</CHOICE><CHOICE>Low</CHOICE></CHOICES><Default>Medium</Default></Field>"
}

For multi-select choices with fill-in option:

{
  "verb": "createSiteColumn",
  "fieldType": "MultiChoice",
  "internalName": "ProjectSkills",
  "displayName": "Required Skills",
  "isRequired": false,
  "group": "Project Columns",
  "schemaXml": "<Field Type='MultiChoice' DisplayName='Required Skills' Required='FALSE' StaticName='ProjectSkills' Name='ProjectSkills' Group='Project Columns' FillInChoice='TRUE'><CHOICES><CHOICE>JavaScript</CHOICE><CHOICE>C#</CHOICE><CHOICE>Python</CHOICE><CHOICE>SharePoint</CHOICE><CHOICE>Power Platform</CHOICE></CHOICES></Field>"
}

For a radio button choice field:

{
  "verb": "createSiteColumn",
  "fieldType": "Choice",
  "internalName": "ProjectType",
  "displayName": "Project Type",
  "isRequired": true,
  "group": "Project Columns",
  "schemaXml": "<Field Type='Choice' DisplayName='Project Type' Required='TRUE' Format='RadioButtons' StaticName='ProjectType' Name='ProjectType' Group='Project Columns'><CHOICES><CHOICE>Internal</CHOICE><CHOICE>External</CHOICE><CHOICE>Research</CHOICE><CHOICE>Maintenance</CHOICE></CHOICES><Default>Internal</Default></Field>"
}

4. URL Fields

URL fields store hyperlinks with display text and must be defined using schema XML:

{
  "verb": "createSiteColumn",
  "fieldType": "URL",
  "internalName": "ProjectRepository",
  "displayName": "Project Repository",
  "isRequired": false,
  "group": "Project Columns",
  "schemaXml": "<Field Type='URL' DisplayName='Project Repository' Required='FALSE' StaticName='ProjectRepository' Name='ProjectRepository' Group='Project Columns' Format='Hyperlink'></Field>"
}

For URL fields that display as images:

{
  "verb": "createSiteColumn",
  "fieldType": "URL",
  "internalName": "ProjectLogo",
  "displayName": "Project Logo",
  "isRequired": false,
  "group": "Project Columns",
  "schemaXml": "<Field Type='URL' DisplayName='Project Logo' Required='FALSE' StaticName='ProjectLogo' Name='ProjectLogo' Group='Project Columns' Format='Image'></Field>"
}

5. Date and DateTime Fields

{
  "verb": "createSiteColumn",
  "fieldType": "DateTime",
  "internalName": "ProjectDeadline",
  "displayName": "Project Deadline",
  "isRequired": true,
  "group": "Project Columns",
  "format": "DateOnly",
  "defaultValue": "today"
}

For date and time:

{
  "verb": "createSiteColumn",
  "fieldType": "DateTime",
  "internalName": "ProjectStartTime",
  "displayName": "Project Start Time",
  "isRequired": false,
  "group": "Project Columns",
  "format": "DateTime"
}

6. Person or Group Fields

{
  "verb": "createSiteColumn",
  "fieldType": "User",
  "internalName": "ProjectManager",
  "displayName": "Project Manager",
  "isRequired": true,
  "group": "Project Columns",
  "selectionMode": "PeopleOnly",
  "selectionGroup": 0,
  "showField": "ImnName"
}

7. Boolean (Yes/No) Fields

{
  "verb": "createSiteColumn",
  "fieldType": "Boolean",
  "internalName": "IsActive",
  "displayName": "Is Active Project",
  "isRequired": false,
  "group": "Project Columns",
  "defaultValue": "true"
}

Schema XML Reference for Choice Fields

When working with choice fields in SharePoint site scripts, understanding the schema XML structure is crucial. Here are the key attributes and their purposes:

Basic Schema XML Structure

<Field Type='Choice' 
       DisplayName='Field Display Name' 
       Required='TRUE/FALSE' 
       Format='Dropdown/RadioButtons' 
       StaticName='InternalName' 
       Name='InternalName' 
       Group='Column Group Name'
       FillInChoice='TRUE/FALSE'>
    <CHOICES>
        <CHOICE>Option 1</CHOICE>
        <CHOICE>Option 2</CHOICE>
        <CHOICE>Option 3</CHOICE>
    </CHOICES>
    <Default>Default Option</Default>
</Field>

Key Attributes Explained

  • Type: 'Choice' for single selection, 'MultiChoice' for multiple selections
  • Format: 'Dropdown' (default), 'RadioButtons' for single choice display
  • FillInChoice: 'TRUE' allows users to enter custom values not in the list
  • Required: 'TRUE' makes the field mandatory
  • StaticName/Name: Should match the internalName in your JSON

Advanced Choice Field Examples

Choice field with custom values allowed:

{
  "verb": "createSiteColumn",
  "fieldType": "Choice",
  "internalName": "ProjectCategory",
  "displayName": "Project Category",
  "isRequired": false,
  "group": "Project Columns",
  "schemaXml": "<Field Type='Choice' DisplayName='Project Category' Required='FALSE' Format='Dropdown' StaticName='ProjectCategory' Name='ProjectCategory' Group='Project Columns' FillInChoice='TRUE'><CHOICES><CHOICE>Web Development</CHOICE><CHOICE>Mobile App</CHOICE><CHOICE>Data Analysis</CHOICE><CHOICE>Infrastructure</CHOICE><CHOICE>Security</CHOICE></CHOICES></Field>"
}

Multi-choice field for team skills:

{
  "verb": "createSiteColumn",
  "fieldType": "MultiChoice",
  "internalName": "TeamSkills",
  "displayName": "Team Skills Required",
  "isRequired": false,
  "group": "Project Columns",
  "schemaXml": "<Field Type='MultiChoice' DisplayName='Team Skills Required' Required='FALSE' StaticName='TeamSkills' Name='TeamSkills' Group='Project Columns'><CHOICES><CHOICE>Frontend Development</CHOICE><CHOICE>Backend Development</CHOICE><CHOICE>Database Design</CHOICE><CHOICE>UI/UX Design</CHOICE><CHOICE>Project Management</CHOICE><CHOICE>Quality Assurance</CHOICE></CHOICES></Field>"
}
{
  "verb": "createSiteColumn",
  "fieldType": "Currency",
  "internalName": "ProjectCost",
  "displayName": "Project Cost",
  "isRequired": false,
  "group": "Project Columns",
  "currencyLocaleId": 1033,
  "decimals": 2
}

Adding SharePoint Built-in Columns Using addSPField

In addition to creating custom site columns, you can also add existing SharePoint built-in columns to your lists using the addSPField verb. This is useful when you want to include standard SharePoint columns without recreating them.

Common SharePoint Built-in Columns

{
  "verb": "createList",
  "listName": "Project Documents",
  "templateType": 101,
  "subactions": [
    {
      "verb": "addSPField",
      "fieldType": "Text",
      "displayName": "Document Type",
      "internalName": "DocType",
      "isRequired": false,
      "addToDefaultView": true
    },
    {
      "verb": "addSPField",
      "fieldType": "User",
      "displayName": "Assigned To",
      "internalName": "AssignedTo",
      "isRequired": false,
      "addToDefaultView": true
    },
    {
      "verb": "addSPField",
      "fieldType": "DateTime",
      "displayName": "Due Date",
      "internalName": "DueDate",
      "isRequired": false,
      "addToDefaultView": true
    },
    {
      "verb": "addSPField",
      "fieldType": "Choice",
      "displayName": "Priority",
      "internalName": "Priority",
      "isRequired": false,
      "addToDefaultView": true,
      "choices": [
        "(1) High",
        "(2) Normal",
        "(3) Low"
      ]
    }
  ]
}

Adding Standard SharePoint Columns

You can also reference existing SharePoint columns by their internal names:

{
  "verb": "createList",
  "listName": "Task List",
  "templateType": 100,
  "subactions": [
    {
      "verb": "addSPField",
      "fieldType": "User",
      "displayName": "Assigned To",
      "internalName": "AssignedTo",
      "isRequired": false,
      "addToDefaultView": true
    },
    {
      "verb": "addSPField",
      "fieldType": "DateTime",
      "displayName": "Start Date",
      "internalName": "StartDate",
      "isRequired": false,
      "addToDefaultView": true
    },
    {
      "verb": "addSPField",
      "fieldType": "DateTime",
      "displayName": "Due Date",
      "internalName": "DueDate",
      "isRequired": false,
      "addToDefaultView": true
    },
    {
      "verb": "addSPField",
      "fieldType": "Choice",
      "displayName": "Status",
      "internalName": "Status",
      "isRequired": false,
      "addToDefaultView": true,
      "choices": [
        "Not Started",
        "In Progress",
        "Completed",
        "Deferred",
        "Waiting on someone else"
      ]
    },
    {
      "verb": "addSPField",
      "fieldType": "Number",
      "displayName": "% Complete",
      "internalName": "PercentComplete",
      "isRequired": false,
      "addToDefaultView": true,
      "min": 0,
      "max": 1,
      "showAsPercentage": true
    }
  ]
}

Adding Custom Site Columns vs Built-in Fields

Here's an example showing both approaches in a single list:

{
  "verb": "createList",
  "listName": "Project Tracker",
  "templateType": 100,
  "subactions": [
    {
      "verb": "addSiteColumn",
      "internalName": "ProjectTitle"
    },
    {
      "verb": "addSiteColumn",
      "internalName": "ProjectStatus"
    },
    {
      "verb": "addSiteColumn",
      "internalName": "ProjectManager"
    },
    {
      "verb": "addSPField",
      "fieldType": "User",
      "displayName": "Assigned To",
      "internalName": "AssignedTo",
      "isRequired": false,
      "addToDefaultView": true
    },
    {
      "verb": "addSPField",
      "fieldType": "DateTime",
      "displayName": "Due Date",
      "internalName": "DueDate",
      "isRequired": false,
      "addToDefaultView": true
    },
    {
      "verb": "addSPField",
      "fieldType": "Choice",
      "displayName": "Priority",
      "internalName": "Priority",
      "isRequired": false,
      "addToDefaultView": true,
      "choices": [
        "(1) High",
        "(2) Normal",
        "(3) Low"
      ]
    }
  ]
}

Complete Site Script Example

Here's a comprehensive site script that demonstrates multiple field types and creates a project management solution:

{
  "$schema": "https://developer.microsoft.com/json-schemas/sp/site-design-script-actions.schema.json",
  "actions": [
    {
      "verb": "createSiteColumn",
      "fieldType": "Text",
      "internalName": "ProjectTitle",
      "displayName": "Project Title",
      "isRequired": true,
      "group": "Project Management Columns",
      "description": "Enter the project title"
    },
    {
      "verb": "createSiteColumn",
      "fieldType": "Note",
      "internalName": "ProjectDescription",
      "displayName": "Project Description",
      "isRequired": false,
      "group": "Project Management Columns",
      "description": "Detailed project description"
    },
    {
      "verb": "createSiteColumn",
      "fieldType": "Choice",
      "internalName": "ProjectStatus",
      "displayName": "Project Status",
      "isRequired": true,
      "group": "Project Management Columns",
      "schemaXml": "<Field Type='Choice' DisplayName='Project Status' Required='TRUE' Format='Dropdown' StaticName='ProjectStatus' Name='ProjectStatus' Group='Project Management Columns'><CHOICES><CHOICE>Not Started</CHOICE><CHOICE>In Progress</CHOICE><CHOICE>On Hold</CHOICE><CHOICE>Completed</CHOICE><CHOICE>Cancelled</CHOICE></CHOICES><Default>Not Started</Default></Field>"
    },
    {
      "verb": "createSiteColumn",
      "fieldType": "Choice",
      "internalName": "ProjectPriority",
      "displayName": "Priority",
      "isRequired": true,
      "group": "Project Management Columns",
      "schemaXml": "<Field Type='Choice' DisplayName='Priority' Required='TRUE' Format='Dropdown' StaticName='ProjectPriority' Name='ProjectPriority' Group='Project Management Columns'><CHOICES><CHOICE>Critical</CHOICE><CHOICE>High</CHOICE><CHOICE>Medium</CHOICE><CHOICE>Low</CHOICE></CHOICES><Default>Medium</Default></Field>"
    },
    {
      "verb": "createSiteColumn",
      "fieldType": "User",
      "internalName": "ProjectManager",
      "displayName": "Project Manager",
      "isRequired": true,
      "group": "Project Management Columns",
      "selectionMode": "PeopleOnly"
    },
    {
      "verb": "createSiteColumn",
      "fieldType": "DateTime",
      "internalName": "ProjectStartDate",
      "displayName": "Start Date",
      "isRequired": true,
      "group": "Project Management Columns",
      "format": "DateOnly"
    },
    {
      "verb": "createSiteColumn",
      "fieldType": "DateTime",
      "internalName": "ProjectEndDate",
      "displayName": "End Date",
      "isRequired": false,
      "group": "Project Management Columns",
      "format": "DateOnly"
    },
    {
      "verb": "createSiteColumn",
      "fieldType": "Currency",
      "internalName": "ProjectBudget",
      "displayName": "Budget",
      "isRequired": false,
      "group": "Project Management Columns",
      "currencyLocaleId": 1033
    },
    {
      "verb": "createSiteColumn",
      "fieldType": "URL",
      "internalName": "ProjectDocuments",
      "displayName": "Project Documents",
      "isRequired": false,
      "group": "Project Management Columns",
      "format": "Hyperlink"
    },
    {
      "verb": "createSiteColumn",
      "fieldType": "Number",
      "internalName": "CompletionPercentage",
      "displayName": "Completion %",
      "isRequired": false,
      "group": "Project Management Columns",
      "min": 0,
      "max": 100
    },
    {
      "verb": "createList",
      "listName": "Project Tasks",
      "templateType": 100,
      "subactions": [
        {
          "verb": "addSiteColumn",
          "internalName": "ProjectTitle"
        },
        {
          "verb": "addSiteColumn",
          "internalName": "ProjectDescription"
        },
        {
          "verb": "addSiteColumn",
          "internalName": "ProjectStatus"
        },
        {
          "verb": "addSiteColumn",
          "internalName": "ProjectPriority"
        },
        {
          "verb": "addSiteColumn",
          "internalName": "ProjectManager"
        },
        {
          "verb": "addSiteColumn",
          "internalName": "ProjectStartDate"
        },
        {
          "verb": "addSiteColumn",
          "internalName": "ProjectEndDate"
        },
        {
          "verb": "addSiteColumn",
          "internalName": "ProjectBudget"
        },
        {
          "verb": "addSiteColumn",
          "internalName": "ProjectDocuments"
        },
        {
          "verb": "addSiteColumn",
          "internalName": "CompletionPercentage"
        }
      ]
    },
    {
      "verb": "createList",
      "listName": "Project Milestones",
      "templateType": 100,
      "subactions": [
        {
          "verb": "addSiteColumn",
          "internalName": "ProjectTitle"
        },
        {
          "verb": "addSiteColumn",
          "internalName": "ProjectStartDate"
        },
        {
          "verb": "addSiteColumn",
          "internalName": "ProjectStatus"
        },
        {
          "verb": "addSiteColumn",
          "internalName": "ProjectManager"
        }
      ]
    }
  ],
  "bindata": {},
  "version": 1
}

Creating and Managing Site Scripts with PowerShell

Installing Required Modules

Before working with site scripts, ensure you have the necessary PowerShell modules:

Install-Module -Name PnP.PowerShell -Force
Install-Module -Name Microsoft.Online.SharePoint.PowerShell -Force

Adding a Site Script

# Connect to SharePoint Online
Connect-PnPOnline -Url "https://yourtenant-admin.sharepoint.com" -Interactive

# Add the site script
$siteScript = Add-PnPSiteScript -Title "Project Management Site Script" -Content (Get-Content "C:\Scripts\ProjectManagement.json" -Raw) -Description "Creates project management columns and lists"

Write-Host "Site Script created with ID: $($siteScript.Id)"

Creating a Site Design

# Create site design that references the site script
$siteDesign = Add-PnPSiteDesign -Title "Project Management Template" -WebTemplate "TeamSite" -SiteScriptIds $siteScript.Id -Description "Project management site template with predefined columns and lists"

Write-Host "Site Design created with ID: $($siteDesign.Id)"

Making Site Design Available as Default

To make your site design available to all users:

# Grant rights to specific users or groups
Grant-PnPSiteDesignRights -Identity $siteDesign.Id -Principals "everyone@yourtenant.com" -Rights View

# Or make it available to everyone in the organization
Set-PnPSiteDesign -Identity $siteDesign.Id -IsDefault $true

Advanced Field Configuration Examples

Lookup Fields

{
  "verb": "createSiteColumn",
  "fieldType": "Lookup",
  "internalName": "ProjectCategory",
  "displayName": "Project Category",
  "isRequired": false,
  "group": "Project Management Columns",
  "lookupListName": "Categories",
  "lookupFieldName": "Title"
}

Calculated Fields

{
  "verb": "createSiteColumn",
  "fieldType": "Calculated",
  "internalName": "DaysRemaining",
  "displayName": "Days Remaining",
  "group": "Project Management Columns",
  "formula": "=ProjectEndDate-TODAY()",
  "outputType": "Number",
  "showAsPercentage": false
}

Best Practices and Tips

1. Naming Conventions

  • Use descriptive internal names without spaces
  • Keep display names user-friendly
  • Group related columns using consistent group names

2. Field Validation

Always include appropriate validation for your fields:

{
  "verb": "createSiteColumn",
  "fieldType": "Number",
  "internalName": "ProjectScore",
  "displayName": "Project Score",
  "isRequired": false,
  "group": "Project Management Columns",
  "min": 1,
  "max": 10,
  "defaultValue": "5"
}

3. Error Handling

When deploying site scripts, always test in a development environment first:

# Test site script syntax
Test-PnPSiteScript -Identity $siteScript.Id

4. Version Control

Maintain version control of your site scripts:

{
  "$schema": "https://developer.microsoft.com/json-schemas/sp/site-design-script-actions.schema.json",
  "actions": [...],
  "bindata": {},
  "version": 2
}

Updating and Managing Site Scripts

Updating an Existing Site Script

# Update site script content
Set-PnPSiteScript -Identity $siteScript.Id -Content (Get-Content "C:\Scripts\UpdatedProjectManagement.json" -Raw)

Removing Site Scripts and Designs

# Remove site design first
Remove-PnPSiteDesign -Identity $siteDesign.Id -Force

# Then remove site script
Remove-PnPSiteScript -Identity $siteScript.Id -Force

Working with Content Types in Site Scripts

Content types are essential for defining the structure and behavior of items in SharePoint. Site scripts provide several verbs for creating content types, adding columns to them, and associating them with lists.

Creating Custom Content Types

{
  "verb": "createContentType",
  "name": "Project Document",
  "description": "Content type for project-related documents",
  "parentName": "Document",
  "parentId": "0x0101",
  "id": "0x0101001234567890ABCDEF",
  "hidden": false,
  "subactions": [
    {
      "verb": "addSiteColumn",
      "internalName": "ProjectTitle"
    },
    {
      "verb": "addSiteColumn",
      "internalName": "ProjectStatus"
    },
    {
      "verb": "addSiteColumn",
      "internalName": "ProjectManager"
    }
  ]
}

Adding Columns to Existing Content Types

You can add site columns to existing SharePoint content types:

{
  "verb": "addContentTypeColumn",
  "contentTypeName": "Document",
  "internalName": "ProjectCategory",
  "required": false,
  "hidden": false
}

Or add to custom content types:

{
  "verb": "addContentTypeColumn",
  "contentTypeName": "Project Document",
  "internalName": "ProjectBudget",
  "required": false,
  "hidden": false
}

Associating Content Types with Lists

Method 1: Using setSiteExternalSharingCapability with Content Type Association

{
  "verb": "createList",
  "listName": "Project Documents",
  "templateType": 101,
  "subactions": [
    {
      "verb": "addContentType",
      "name": "Project Document"
    },
    {
      "verb": "removeContentType",
      "name": "Document"
    },
    {
      "verb": "setSiteExternalSharingCapability",
      "capability": "ExistingExternalUserSharingOnly"
    }
  ]
}

Method 2: Direct Content Type Association

{
  "verb": "createList",
  "listName": "Project Library",
  "templateType": 101,
  "subactions": [
    {
      "verb": "addContentType",
      "name": "Project Document"
    },
    {
      "verb": "addContentType", 
      "name": "Project Template"
    },
    {
      "verb": "removeContentType",
      "name": "Document"
    },
    {
      "verb": "setDefaultContentType",
      "contentTypeName": "Project Document"
    }
  ]
}

Complete Content Type Example

Here's a comprehensive example showing content type creation and association:

{
  "$schema": "https://developer.microsoft.com/json-schemas/sp/site-design-script-actions.schema.json",
  "actions": [
    {
      "verb": "createSiteColumn",
      "fieldType": "Text",
      "internalName": "ProjectCode",
      "displayName": "Project Code",
      "isRequired": true,
      "group": "Project Management Columns"
    },
    {
      "verb": "createSiteColumn",
      "fieldType": "Choice",
      "internalName": "DocumentType",
      "displayName": "Document Type",
      "isRequired": true,
      "group": "Project Management Columns",
      "schemaXml": "<Field Type='Choice' DisplayName='Document Type' Required='TRUE' Format='Dropdown' StaticName='DocumentType' Name='DocumentType' Group='Project Management Columns'><CHOICES><CHOICE>Specification</CHOICE><CHOICE>Design</CHOICE><CHOICE>Test Plan</CHOICE><CHOICE>User Manual</CHOICE><CHOICE>Technical Documentation</CHOICE></CHOICES><Default>Specification</Default></Field>"
    },
    {
      "verb": "createSiteColumn",
      "fieldType": "User",
      "internalName": "DocumentOwner",
      "displayName": "Document Owner",
      "isRequired": true,
      "group": "Project Management Columns"
    },
    {
      "verb": "createSiteColumn",
      "fieldType": "DateTime",
      "internalName": "ReviewDate",
      "displayName": "Next Review Date",
      "isRequired": false,
      "group": "Project Management Columns"
    },
    {
      "verb": "createContentType",
      "name": "Project Document",
      "description": "Document content type for project management",
      "parentName": "Document",
      "parentId": "0x0101",
      "id": "0x010100A1B2C3D4E5F6789012345678901234",
      "hidden": false,
      "subactions": [
        {
          "verb": "addSiteColumn",
          "internalName": "ProjectCode"
        },
        {
          "verb": "addSiteColumn",
          "internalName": "DocumentType"
        },
        {
          "verb": "addSiteColumn",
          "internalName": "DocumentOwner"
        },
        {
          "verb": "addSiteColumn",
          "internalName": "ReviewDate"
        }
      ]
    },
    {
      "verb": "createContentType",
      "name": "Project Template",
      "description": "Template content type for project documents",
      "parentName": "Document",
      "parentId": "0x0101",
      "id": "0x010100B2C3D4E5F6789012345678901234A1",
      "hidden": false,
      "subactions": [
        {
          "verb": "addSiteColumn",
          "internalName": "ProjectCode"
        },
        {
          "verb": "addSiteColumn",
          "internalName": "DocumentType"
        },
        {
          "verb": "addSiteColumn",
          "internalName": "DocumentOwner"
        }
      ]
    },
    {
      "verb": "createList",
      "listName": "Project Documents",
      "templateType": 101,
      "subactions": [
        {
          "verb": "addContentType",
          "name": "Project Document"
        },
        {
          "verb": "addContentType",
          "name": "Project Template"
        },
        {
          "verb": "removeContentType",
          "name": "Document"
        },
        {
          "verb": "setDefaultContentType",
          "contentTypeName": "Project Document"
        }
      ]
    },
    {
      "verb": "createList",
      "listName": "Project Tasks",
      "templateType": 100,
      "subactions": [
        {
          "verb": "addContentTypeColumn",
          "contentTypeName": "Task",
          "internalName": "ProjectCode",
          "required": true,
          "hidden": false
        }
      ]
    }
  ],
  "bindata": {},
  "version": 1
}

Advanced Content Type Scenarios

Creating Content Type Inheritance

{
  "verb": "createContentType",
  "name": "Confidential Project Document",
  "description": "Content type for confidential project documents",
  "parentName": "Project Document",
  "parentId": "0x010100A1B2C3D4E5F6789012345678901234",
  "id": "0x010100A1B2C3D4E5F6789012345678901234001",
  "hidden": false,
  "subactions": [
    {
      "verb": "addSPField",
      "fieldType": "Choice",
      "displayName": "Classification Level",
      "internalName": "ClassificationLevel",
      "isRequired": true,
      "addToDefaultView": true,
      "choices": [
        "Internal",
        "Confidential",
        "Restricted",
        "Top Secret"
      ]
    },
    {
      "verb": "addSPField",
      "fieldType": "User",
      "displayName": "Security Officer",
      "internalName": "SecurityOfficer",
      "isRequired": true,
      "addToDefaultView": false
    }
  ]
}

Adding Columns to Built-in Content Types

{
  "verb": "addContentTypeColumn",
  "contentTypeName": "Announcement",
  "internalName": "ProjectCode",
  "required": false,
  "hidden": false
},
{
  "verb": "addContentTypeColumn",
  "contentTypeName": "Event",
  "internalName": "ProjectCode", 
  "required": true,
  "hidden": false
}

Content Type Management Best Practices

1. Content Type ID Structure

Always use proper content type ID hierarchy:

  • Base Document: 0x0101
  • Custom Document: 0x0101 + your unique hex string
  • Child of Custom: 0x0101 + parent hex + additional hex
{
  "verb": "createContentType",
  "name": "Project Specification",
  "parentId": "0x010100A1B2C3D4E5F6789012345678901234",
  "id": "0x010100A1B2C3D4E5F6789012345678901234001"
}

2. Column Ordering in Content Types

{
  "verb": "createContentType",
  "name": "Project Report",
  "parentName": "Document",
  "parentId": "0x0101",
  "id": "0x010100C1D2E3F4567890123456789012345A",
  "subactions": [
    {
      "verb": "addSiteColumn",
      "internalName": "ProjectCode"
    },
    {
      "verb": "addSiteColumn", 
      "internalName": "DocumentType"
    },
    {
      "verb": "addSiteColumn",
      "internalName": "DocumentOwner"
    },
    {
      "verb": "addSiteColumn",
      "internalName": "ReviewDate"
    }
  ]
}

3. Content Type Association with Multiple Lists

{
  "verb": "createList",
  "listName": "Engineering Documents",
  "templateType": 101,
  "subactions": [
    {
      "verb": "addContentType",
      "name": "Project Document"
    },
    {
      "verb": "addContentType",
      "name": "Technical Specification"
    }
  ]
},
{
  "verb": "createList", 
  "listName": "Marketing Documents",
  "templateType": 101,
  "subactions": [
    {
      "verb": "addContentType",
      "name": "Project Document"
    },
    {
      "verb": "addContentType",
      "name": "Marketing Material"
    }
  ]
}

PowerShell Commands for Content Type Management

# Get content types in a site
Get-PnPContentType

# Get specific content type
Get-PnPContentType -Identity "Project Document"

# Add field to existing content type
Add-PnPFieldToContentType -Field "ProjectCode" -ContentType "Project Document"

# Remove content type from list
Remove-PnPContentTypeFromList -List "Documents" -ContentType "Document"

## Adding SharePoint Framework (SPFx) Extensions and Web Parts

SharePoint site scripts support adding SharePoint Framework (SPFx) solutions including web parts and extensions. This allows you to automatically deploy custom components as part of your site template.

### Installing SPFx Solutions

Before adding SPFx components to your site script, the SPFx solution must be deployed to the tenant app catalog. Here's how to reference them in site scripts:

### Adding SPFx Web Parts

```json
{
  "verb": "installSolution",
  "id": "12345678-1234-1234-1234-123456789012",
  "name": "custom-project-webpart"
}

Adding SPFx Extensions

Application Customizer Extension

{
  "verb": "associateExtension",
  "title": "Project Header Extension",
  "location": "ClientSideExtension.ApplicationCustomizer",
  "clientSideComponentId": "12345678-1234-1234-1234-123456789012",
  "clientSideComponentProperties": "{\"headerText\":\"Project Management Portal\",\"backgroundColor\":\"#0078d4\"}"
}

Field Customizer Extension

{
  "verb": "associateExtension",
  "title": "Project Status Field Customizer",
  "location": "ClientSideExtension.FieldCustomizer",
  "clientSideComponentId": "87654321-4321-4321-4321-210987654321",
  "clientSideComponentProperties": "{\"showIcon\":true,\"iconBaseUrl\":\"https://yourtenant.sharepoint.com/sites/assets/\"}",
  "scope": "Web",
  "registrationId": "ProjectStatus",
  "registrationType": "Field"
}

List View Command Set Extension

{
  "verb": "associateExtension",
  "title": "Project Actions Command Set",
  "location": "ClientSideExtension.ListViewCommandSet",
  "clientSideComponentId": "11111111-2222-3333-4444-555555555555",
  "clientSideComponentProperties": "{\"enableExport\":true,\"enableBulkEdit\":true}",
  "scope": "Web",
  "registrationId": "100",
  "registrationType": "List"
}

Complete SPFx Integration Example

Here's a comprehensive site script that includes SPFx components:

{
  "$schema": "https://developer.microsoft.com/json-schemas/sp/site-design-script-actions.schema.json",
  "actions": [
    {
      "verb": "installSolution",
      "id": "12345678-1234-1234-1234-123456789012",
      "name": "project-management-spfx"
    },
    {
      "verb": "createSiteColumn",
      "fieldType": "Choice",
      "internalName": "ProjectStatus",
      "displayName": "Project Status",
      "isRequired": true,
      "group": "Project Management Columns",
      "schemaXml": "<Field Type='Choice' DisplayName='Project Status' Required='TRUE' Format='Dropdown' StaticName='ProjectStatus' Name='ProjectStatus' Group='Project Management Columns'><CHOICES><CHOICE>Not Started</CHOICE><CHOICE>In Progress</CHOICE><CHOICE>On Hold</CHOICE><CHOICE>Completed</CHOICE><CHOICE>Cancelled</CHOICE></CHOICES><Default>Not Started</Default></Field>"
    },
    {
      "verb": "createList",
      "listName": "Project Tasks",
      "templateType": 100,
      "subactions": [
        {
          "verb": "addSiteColumn",
          "internalName": "ProjectStatus"
        },
        {
          "verb": "addSPField",
          "fieldType": "User",
          "displayName": "Assigned To",
          "internalName": "AssignedTo",
          "isRequired": false,
          "addToDefaultView": true
        }
      ]
    },
    {
      "verb": "associateExtension",
      "title": "Project Management Header",
      "location": "ClientSideExtension.ApplicationCustomizer",
      "clientSideComponentId": "12345678-1234-1234-1234-123456789012",
      "clientSideComponentProperties": "{\"headerText\":\"Project Management Portal\",\"showUserProfile\":true,\"backgroundColor\":\"#0078d4\",\"textColor\":\"#ffffff\"}"
    },
    {
      "verb": "associateExtension",
      "title": "Project Status Field Customizer",
      "location": "ClientSideExtension.FieldCustomizer",
      "clientSideComponentId": "87654321-4321-4321-4321-210987654321",
      "clientSideComponentProperties": "{\"showStatusIcon\":true,\"iconMapping\":{\"Not Started\":\"StatusCircleErrorX\",\"In Progress\":\"ProgressRingDots\",\"On Hold\":\"StatusCircleQuestionMark\",\"Completed\":\"StatusCircleCheckmark\",\"Cancelled\":\"StatusCircleBlock\"}}",
      "scope": "Web",
      "registrationId": "ProjectStatus",
      "registrationType": "Field"
    },
    {
      "verb": "associateExtension",
      "title": "Project Tools Command Set",
      "location": "ClientSideExtension.ListViewCommandSet",
      "clientSideComponentId": "11111111-2222-3333-4444-555555555555",
      "clientSideComponentProperties": "{\"enableExportToExcel\":true,\"enableBulkStatusUpdate\":true,\"enableProjectReports\":true}",
      "scope": "Web",
      "registrationId": "100",
      "registrationType": "List"
    },
    {
      "verb": "addNavLink",
      "url": "/sites/projectmanagement/SitePages/ProjectDashboard.aspx",
      "displayName": "Project Dashboard",
      "isWebRelative": true
    },
    {
      "verb": "createPage",
      "fileName": "ProjectDashboard.aspx",
      "pageData": {
        "Title": "Project Dashboard",
        "BannerImageUrl": "/_layouts/15/images/sitepagethumbnail.png",
        "CanvasContent1": "[{\"controlType\":3,\"displayMode\":2,\"id\":\"12345678-1234-1234-1234-123456789012\",\"position\":{\"controlIndex\":1,\"sectionIndex\":1,\"columnIndex\":1,\"layoutIndex\":1},\"webPartId\":\"12345678-1234-1234-1234-123456789012\",\"webPartData\":{\"id\":\"12345678-1234-1234-1234-123456789012\",\"instanceId\":\"12345678-1234-1234-1234-123456789012\",\"title\":\"Project Overview\",\"description\":\"Shows project statistics and progress\",\"properties\":{\"showCharts\":true,\"chartType\":\"donut\",\"refreshInterval\":300}}}]",
        "LayoutWebpartsContent": "[{\"id\":\"12345678-1234-1234-1234-123456789012\",\"instanceId\":\"12345678-1234-1234-1234-123456789012\",\"title\":\"Project Overview\",\"description\":\"Project management dashboard\",\"properties\":{\"listName\":\"Project Tasks\",\"viewType\":\"dashboard\",\"showMetrics\":true}}]"
      },
      "layout": "Article",
      "promoteAs": "HomePage"
    }
  ],
  "bindata": {},
  "version": 1
}

SPFx Component Properties Configuration

When associating SPFx extensions, you can pass configuration properties as JSON strings:

Application Customizer Properties

{
  "verb": "associateExtension",
  "title": "Custom Header",
  "location": "ClientSideExtension.ApplicationCustomizer",
  "clientSideComponentId": "12345678-1234-1234-1234-123456789012",
  "clientSideComponentProperties": "{\"logoUrl\":\"https://yourtenant.sharepoint.com/sites/assets/logo.png\",\"companyName\":\"Contoso Ltd\",\"showDateTime\":true,\"timeFormat\":\"12\",\"backgroundColor\":\"#0078d4\",\"textColor\":\"#ffffff\",\"showUserMenu\":true,\"customLinks\":[{\"title\":\"Help\",\"url\":\"https://contoso.com/help\"},{\"title\":\"Support\",\"url\":\"https://contoso.com/support\"}]}"
}

Field Customizer Properties

{
  "verb": "associateExtension",
  "title": "Priority Field Customizer",
  "location": "ClientSideExtension.FieldCustomizer",
  "clientSideComponentId": "87654321-4321-4321-4321-210987654321",
  "clientSideComponentProperties": "{\"colorMapping\":{\"Critical\":\"#d13438\",\"High\":\"#ff8c00\",\"Medium\":\"#ffaa44\",\"Low\":\"#00b294\"},\"showTooltip\":true,\"tooltipTemplate\":\"Priority: {value} - {description}\"}",
  "scope": "Web",
  "registrationId": "Priority",
  "registrationType": "Field"
}

Command Set Properties

{
  "verb": "associateExtension",
  "title": "Document Actions",
  "location": "ClientSideExtension.ListViewCommandSet",
  "clientSideComponentId": "11111111-2222-3333-4444-555555555555",
  "clientSideComponentProperties": "{\"commands\":[{\"key\":\"CONVERT_PDF\",\"title\":\"Convert to PDF\",\"iconImageUrl\":\"https://yourtenant.sharepoint.com/sites/assets/pdf-icon.png\"},{\"key\":\"SEND_EMAIL\",\"title\":\"Email Document\",\"iconImageUrl\":\"https://yourtenant.sharepoint.com/sites/assets/email-icon.png\"}],\"enableBulkOperations\":true}",
  "scope": "Web",
  "registrationId": "101",
  "registrationType": "List"
}

Best Practices for SPFx Integration

1. Solution Deployment Strategy

# Deploy SPFx solution to tenant app catalog first
Add-PnPApp -Path "project-management-spfx.sppkg" -Scope Tenant -Publish -Overwrite

# Then reference in site script
$siteScriptContent = @"
{
  "actions": [
    {
      "verb": "installSolution",
      "id": "$(Get-PnPApp -Name 'project-management-spfx' | Select-Object -ExpandProperty Id)",
      "name": "project-management-spfx"
    }
  ]
}
"@

2. Extension Scoping

{
  "verb": "associateExtension",
  "title": "Site-wide Extension",
  "location": "ClientSideExtension.ApplicationCustomizer",
  "clientSideComponentId": "12345678-1234-1234-1234-123456789012",
  "scope": "Site"
},
{
  "verb": "associateExtension",
  "title": "Web-specific Extension",
  "location": "ClientSideExtension.ApplicationCustomizer",
  "clientSideComponentId": "87654321-4321-4321-4321-210987654321",
  "scope": "Web"
}

3. Error Handling for SPFx Components

When SPFx solutions aren't available, the site script will continue executing other actions. Always ensure your SPFx solutions are properly deployed and tested before including them in production site scripts.

Managing SPFx Components with PowerShell

# Get information about installed SPFx solutions
Get-PnPApp | Where-Object {$_.Title -like "*project*"}

# Check extension associations
Get-PnPCustomAction -Scope Web | Where-Object {$_.Location -eq "ClientSideExtension.ApplicationCustomizer"}

## Monitoring Site Script Execution

You can monitor the execution of site scripts:

```powershell
# Get site script run status
Get-PnPSiteScriptFromWeb -Url "https://yourtenant.sharepoint.com/sites/projectsite"

Conclusion

Site scripts and site designs provide a powerful way to standardize SharePoint site creation and ensure consistency across your organization. By understanding the various field types, proper JSON structure, and PowerShell commands, you can create sophisticated templates that save time and reduce errors.

Remember to thoroughly test your site scripts in a development environment before deploying them to production. Start with simple scripts and gradually add complexity as you become more comfortable with the syntax and capabilities.

The examples provided in this guide should give you a solid foundation for creating your own custom SharePoint solutions using site scripts and site designs. Whether you're building project management sites, document libraries, or custom business applications, these tools will help you automate and standardize your SharePoint deployments.

Comments

Popular posts from this blog

React and SPFx Interview Questions with Scenarios and Code Examples

Automating ShareGate PreCheck Reports: From Manual Drudgery to Streamlined Efficiency