Deploy Managed Metadata fields using site designs and site scripts

(Notice: This post may show unsupported methods for provisioning fields)

You may be in a scenario where you want to create Managed Metadata columns using modern provisioning. This post is going to outline a method that can be used to provision Managed Metadata columns using site designs. Site designs are a new model for provisioning assets (fields,content types, lists, etc…) in modern SharePoint. Information on site designs and site scripts can be found here.

Multiple ways to create columns

Using a site script,  you can create both list and site columns using two different actions — “addSPField” and “addSiteColumn”. The first will deploy a list column and the second action will deploy a site column. However, these two actions only support basic field types in SharePoint. These field types include:

  • Text
  • Note
  • Number
  • Boolean
  • User
  • DateTime

Complex columns such as people fields and managed metadata fields need to be created using the Field Element definition. Using site designs and site scripts, we can provision complex fields using the Field definition with the addSPFieldXml and createSiteColumnXML actions.

There are many ways you can get the Field definition after they have been created in the SharePoint UI. Here are a couple options you could use to export out the schema XML for a list and get the field definitions.

https://yoursite/_vti_bin/owssvr.dll?Cmd=ExportList&List={YOUR_LIST_GUID}

You could also use PnP PowerShell to export out the schema

 $list = Get-PnPList -Identity "your list" -Includes SchemaXml
 $schema = $list.SchemaXml

Which complex fields are supported?

If look through the site script documentation you may notice, it doesn’t actually define which field types are supposed through XML. In regards to a complex field such as a Managed Metadata field, we took to twitter to see if Microsoft had any input. Sean Squires mentioned that currently they do not support Managed Metadata Fields, at least in an elegant way (which to me means, if it works great! if it doesn’t…it’s not officially supported).

How it can be achieved

Managed Metadata Fields are complex fields that actually require two fields to be deployed in order to function properly. One field is a TaxonomyFieldType and another is a Note field. This means we actually have to deploy two fields during the provisioning process.

When you export out a list schema and find your Managed Metadata field, you’ll notice it references the Note field using the ID of the Note field in the TextField node. You may also notice there are tokens for properties such as WebId, SiteId, ListId in your Field definition. In order for the deployment of the field to work we need to remove these token properties.

Secondly, we’ll need to update the <Property> nodes inside the <Customziation> node for the following properties.

  • SspId
  • TermSetId
  • AnchorId

By default, these properties may be using braces to identify the GUID for the Term store, Term set and Anchor. We need to remove the braces and leave the rest of the GUID string.

Here is an example of a cleaned up XML for a Managed Metadata field. Notice how I have removed the {braces} from around the GUIDs for SspdId, TermSetId and AnchorId (in bold).

<Field Type="TaxonomyFieldType" Name="MyTestMMD" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="MyTestMMD" DisplayName="My Test Tax" Group="Test group" ShowField="Term1033" Required="FALSE" EnforceUniqueValues="FALSE" Mult="FALSE"> <Default /> <Customization> <ArrayOfProperty> <Property> <Name>SspId</Name> <Value xmlns:p4="http://www.w3.org/2001/XMLSchema-instance" xmlns:q1="http://www.w3.org/2001/XMLSchema" p4:type="q1:string">1ba8a7c0-a373-4bb7-8ee0-2d130933743a</Value> </Property> <Property> <Name>GroupId</Name> <Value xmlns:p4="http://www.w3.org/2001/XMLSchema-instance" xmlns:q2="http://www.w3.org/2001/XMLSchema" p4:type="q2:string">0e8f395e-ff58-4d45-9ff7-e331ab728beb</Value> </Property> <Property> <Name>TermSetId</Name> <Value xmlns:p4="http://www.w3.org/2001/XMLSchema-instance" xmlns:q2="http://www.w3.org/2001/XMLSchema" p4:type="q2:string">487ba508-4fba-49f1-be4f-ceee2624da0a</Value> </Property> <Property> <Name>TextField</Name> <Value xmlns:p4="http://www.w3.org/2001/XMLSchema-instance" xmlns:q6="http://www.w3.org/2001/XMLSchema" p4:type="q6:string">{44a5d269-ce40-4a8f-b0d6-6b7bcb95ff71}</Value> </Property> <Property> <Name>AnchorId</Name> <Value xmlns:p4="http://www.w3.org/2001/XMLSchema-instance" xmlns:q3="http://www.w3.org/2001/XMLSchema" p4:type="q3:string">00000000-0000-0000-0000-000000000000</Value> </Property> <Property> <Name>IsPathRendered</Name> <Value xmlns:p4="http://www.w3.org/2001/XMLSchema-instance" xmlns:q7="http://www.w3.org/2001/XMLSchema" p4:type="q7:boolean">false</Value> </Property> <Property> <Name>IsKeyword</Name> <Value xmlns:p4="http://www.w3.org/2001/XMLSchema-instance" xmlns:q8="http://www.w3.org/2001/XMLSchema" p4:type="q8:boolean">false</Value> </Property> <Property> <Name>Open</Name> <Value xmlns:p4="http://www.w3.org/2001/XMLSchema-instance" xmlns:q5="http://www.w3.org/2001/XMLSchema" p4:type="q5:boolean">false</Value> </Property> </ArrayOfProperty> </Customization> </Field>

A couple notes

If you fail to do this successfully, it may look like your column is working. However, SharePoint will not create managed or crawled properties correctly, thus making your column not searchable.

Final site script actions

The next thing we want to do is escape the XML so can we can use it inside a JSON site script. Then finally, we can add the Field definitions to our site script using the “createSiteColumnXml” action.

Here is an example of the two site script actions required to deploy a Managed Metadata field using site designs.

 {
   "verb": "createSiteColumnXml",  
    "schemaXml": "<Field ID=\"{44a5d269-ce40-4a8f-b0d6-6b7bcb95ff71}\" SourceID=\"http:\/\/schemas.microsoft.com\/sharepoint\/v3\" Type=\"Note\" Name=\"MyTestMMDTaxHTField\" StaticName=\"MyTestMMDTaxHTField\" DisplayName=\"MyTestMMDTaxHTField\" Group=\"Test group\" ShowInViewForms=\"FALSE\" Required=\"FALSE\" Hidden=\"TRUE\" CanToggleHidden=\"TRUE\" />"  
 },   
 {  
  "verb": "createSiteColumnXml",  
  "schemaXml": "<Field Type=\"TaxonomyFieldType\" Name=\"MyTestMMD\" SourceID=\"http:\/\/schemas.microsoft.com\/sharepoint\/v3\" StaticName=\"MyTestMMD\" DisplayName=\"My Test Tax\" Group=\"Test group\" ShowField=\"Term1033\" Required=\"FALSE\" EnforceUniqueValues=\"FALSE\" Mult=\"FALSE\"> <Default></Default> <Customization> <ArrayOfProperty> <Property> <Name>SspId</Name> <Value xmlns:q1=\"http://www.w3.org/2001/XMLSchema\" p4:type=\"q1:string\" xmlns:p4=\"http://www.w3.org/2001/XMLSchema-instance\">1ba8a7c0-a373-4bb7-8ee0-2d130933743a</Value> </Property> <Property> <Name>GroupId</Name> <Value xmlns:q2=\"http://www.w3.org/2001/XMLSchema\" p4:type=\"q2:string\" xmlns:p4=\"http://www.w3.org/2001/XMLSchema-instance\">0e8f395e-ff58-4d45-9ff7-e331ab728beb</Value> </Property> <Property> <Name>TermSetId</Name> <Value xmlns:q2=\"http://www.w3.org/2001/XMLSchema\" p4:type=\"q2:string\" xmlns:p4=\"http://www.w3.org/2001/XMLSchema-instance\">487ba508-4fba-49f1-be4f-ceee2624da0a</Value> </Property> <Property> <Name>TextField</Name> <Value xmlns:q6=\"http://www.w3.org/2001/XMLSchema\" p4:type=\"q6:string\" xmlns:p4=\"http://www.w3.org/2001/XMLSchema-instance\">{44a5d269-ce40-4a8f-b0d6-6b7bcb95ff71}</Value> </Property> <Property> <Name>AnchorId</Name> <Value xmlns:q3=\"http://www.w3.org/2001/XMLSchema\" p4:type=\"q3:string\" xmlns:p4=\"http://www.w3.org/2001/XMLSchema-instance\">00000000-0000-0000-0000-000000000000</Value> </Property> <Property> <Name>IsPathRendered</Name> <Value xmlns:q7=\"http://www.w3.org/2001/XMLSchema\" p4:type=\"q7:boolean\" xmlns:p4=\"http://www.w3.org/2001/XMLSchema-instance\">false</Value> </Property> <Property> <Name>IsKeyword</Name> <Value xmlns:q8=\"http://www.w3.org/2001/XMLSchema\" p4:type=\"q8:boolean\" xmlns:p4=\"http://www.w3.org/2001/XMLSchema-instance\">false</Value> </Property> <Property> <Name>Open</Name> <Value xmlns:q5=\"http://www.w3.org/2001/XMLSchema\" p4:type=\"q5:boolean\" xmlns:p4=\"http://www.w3.org/2001/XMLSchema-instance\">false</Value> </Property> </ArrayOfProperty> </Customization> </Field>"  
  },

You can see a full site script in this Gist.

Caveats & “uh ohs”

Using the site script actions above, you should be able to successfully create Managed Metadata fields using site designs. Just be aware that if you see some negative impacts when provisioning these…they are not fully supported by Microsoft.

Augment SharePoint site provisioning with messaging in real-time with Socket.io

Modern provisioning in SharePoint has taken on a new framework called Site designs. Site designs are like templates in that they can be used each time a new site is created in your Office 365 tenancy. A site design is essentially a list of actions (site scripts) that you want SharePoint to execute when creating new sites. These actions may include:

  • Creating a new list or library (or modifying the default one created with the site)
  • Creating site columns, content types, and configuring other list settings
  • Applying a theme
  • Setting a site logo
  • Adding navigation
  • Triggering a Microsoft Flow
  • Installing a deployed solution from the app catalog
  • Setting regional settings for the site
  • Setting external sharing capability for the site

Extending site designs

While site designs are continually being improved and expanded on by Microsoft, there are some gaps. From the beginning, Microsoft was aware they wouldn’t be able to fill all  gaps from the start so they allowed us to extend the out of the box provisioning using Microsoft Flow and Azure Functions using the “triggerFlow” action. To learn more about this functionality follow this PnP documentation.

Picture1.png

This model is extremely extensible and allows you to move some of the workload over to an Azure Function or Azure Automation Services to run PowerShell & C# to apply more custom provisioning artifacts such as pre-configured pages and web parts.

No Messaging?

Currently there is a limitation using this model. Given that the provisioning process has moved off into Azure, we are unable to notify the user what the current stage the Azure Function is currently running at.

Here is what the current messaging system looks like

Picture2.png

I’ve been told that that ability to provide our own messaging into this dialog is “coming”, but currently I wanted to overcome this limitation.

Clippy is back!

Months ago when SPFx application customizers were released I jokingly built a Clippy extension to run on modern SharePoint pages. The goal was to show how to call Microsoft Graph from an application customizer.

Clippy.png

I thought I would supercharge the functionality of Clippy by connecting him to the site design provisioning process.

Socket.io + Express

In order to do this, I had to come up with a mechanism for providing Clippy real-time updates during the provisioning process. I had recently started playing around with Socket.io and I believed this would be the best route forward.

I implemented Socket.io inside an Express api which is currently living up in the Azure. The plan is use this Socket.io as a messaging relay between the Azure Functions and my Clippy extension.

This is what the new flow looks like

NewFlow

We start with a site design that sends the new site URL to Microsoft Flow. Microsoft Flow will pick up that trigger and send an HTTP request to a Durable Azure Function orchestrator in Azure.

The Durable Azure Function orchestrator allows me to split out provisioning tasks (create lists, create pages, provision navigation, apply branding) while at the same time, maintaining state across my Azure Function tasks. As the provisioning is happening, I will send updates to Socket.io.

The Socket.io will receive the message from the Azure Function and update Clippy in real-time in SharePoint.

Socket.IO Configuration

Connection

In order to configure Socket.io to be the messaging system, I had to make sure I was working with individual clients and not broadcasting messages across all Clippy extensions in my tenancy. To do this, I am emitting from Clippy a “room” name. This room is the siteURL of the current site Clippy is running.

ioconnect

By doing this, whenever  I send a message from Socket.io to Clippy, I know exactly where to send it to.

Messaging

Messaging to Clippy is done through a POST request to Socket.Io The POST request contains a message, the provisioning status (complete/running) and the URL (unique identifier for the room). I take these parameters and emit the message to the Clippy extensions where room name is equal to the Site URL.

POstMessage

Clippy Configuration

Configuring Clippy is quite simple, it’s as easy as connecting to Socket.io in Azure and subscribing to the “emit” events.

Inside the _renderPlaceHolders() function of my application customizer, I am going to create a new Clippy and register to Socket.io

ClippyRegister

Inside the _registerSocketIO, I instantiate a new socket request to Socket.io. Once connected, I am going to send a message with my “room” name. This room name is going to be uniquely identified by the siteUrl of the current site being provisioned.

regiserIO

I have two basic actions I am subscribing to — “provisioningUpdate” and “provisioningComplete”. When Socket.io receives a message and broadcasts that, Clippy will pick it up and “speak” to the user the current update from provisioning.

The Azure Function

As previously mentioned, I am using a Durable Function to do the provisioning process. To learn about Durable Functions follow this link.  Here is what the Orchestrator tasks looks like.

Asure Function Design.png

Notice that I am passing in the siteUrl coming from my Microsoft Flow HTTP trigger. Inside, I have a couple tasks that are running.

  • Establish site as a hub site
  • Provisioning Lists
  • Provision Pages
  • Provision Terms

Also notice that I have an _emitUpdate() function which is running before and after my asynchronous tasks. During the provisioning process, this _emitUpdate is going to POST a message to Socket.io.

What a task looks like

Let’s take a look at what an individual task looks like.

ProvisionLists

My Azure Function task is going to create a new AppOnly context to SharePoint, load a provisioning template that contains a set of lists to be created and uses ApplyProvisioningTemplate(template) to apply the template to my site.

Inside _emitUpdate

My _emitUpdate is fairly simple. It’s going to take a message from the Orchestrator, create a new HttpClient and execute a POST message to my Socket.io with the current status of the provisioning process.

emitUpdate

Putting it together

Without going to deep into this (because it really is more high level education than anything)… let’s see what these messages look like in real time.

If Gif is missing… wait for it to load. Full size view can be found here.

ClippyProvision-min (1)

Want to learn more?

If you are looking to learn more about how I put this together, join me at SharePoint Saturday Denver on October 6th and I’ll be also be demoing this solution at SharePoint Saturday New England! Sign up!

How To: Create Modern SharePoint site templates using Office 365 Site Designs

One of the biggest questions I see is, “How do we create site templates using Office 365 groups?”. It is no surprise that site templates were one of the most widely used features in previous versions of SharePoint. Site templates give site owners and SharePoint admins a way to incorporate some governance into the creation of team sites and expedite the creation process. However, this out-of-the-box functionality, until recently wasn’t available for new modern sites.  In this blog we’ll step through creating our own using an Office 365 group.


Before we begin you should be aware that we no longer call them “site templates”. In the modern SharePoint world, Microsoft has chosen to designate the names “site designs” for modern templating. So if you find yourself needing to search google, keep this in mind.

Secondly, site designs aren’t the only way to create templated sites within SharePoint. A very common and community driven framework known as PnP Provisioning it also a very common way to create templates and provision new sites into your Office 365 & SharePoint environments. If you would like to know the difference’s between site designs and PnP Provisioning please check out Chris O’Brien’s recent post.


A walk through

The Site Script

The first step to creating a modern site template is to create a site script using a JSON file. A site script is a file that defines that actions that SharePoint will run when a user selects your site design. Site scripts are provisioned using SharePoint Online Management Shell. For this example, we are going to create a new department team site template to be used within an Office365 Group. However, we will be adding in some PnP scripts to add a global navigation using the SharePoint Framework to the newly created site. To learn more about what actions are available, click here


   {
 "$schema": "schema.json",
     "actions": [
                    {
           "verb": "applyTheme",
           "themeName": "Custom Black"
         },
         {
             "verb": "createSPList",
             "listName": "Team Projects",
             "templateType": 100,
             "subactions": [
                 {
                     "verb": "SetDescription",
                     "description": "List to hold Team Project Statuses"
                 },
                 {
                     "verb": "addSPField",
                     "fieldType": "Text",
                     "displayName": "Project Status",
                     "addToDefaultView": true,
                     "isRequired": true
                 },
                 {
                     "verb": "addSPField",
                     "fieldType": "User",
                     "displayName": "Project Manager",
                     "addToDefaultView": true,
                     "isRequired": true
                 },
                 {
                     "verb": "addSPField",
                     "fieldType": "Note",
                     "displayName": "Project Notes",
                     "isRequired": false
                 }
             ]
         },
         {
            "verb":"triggerFlow",
            "url":"THIS-IS-THE-URL-TO-YOUR-FLOW",
            "name":"Provision Assets for Group",
            "parameters":{
                "event":"",
                "product":""
            }
         },
         {
            "verb":"setSPFieldCustomFormatter",
            "fieldDisplayName":"Project Status",
            "formatterJSON":{
    "elmType": "div",
    "txtContent": "@currentField",
    "style": {
        "color": "#fff",
        "padding-left": "14px",
        "background-color": {
            "operator": "?",
            "operands": [
                {
                    "operator": "==",
                    "operands": [
                        "@currentField",
                        "Red"
                    ]
                },
                "#e81123",
                {
                    "operator": "?",
                    "operands": [
                        {
                            "operator": "==",
                            "operands": [
                                "@currentField",
                                "Green"
                            ]
                        },
                        "#00B294",
                        {
                            "operator": "?",
                            "operands": [
                                {
                                    "operator": "==",
                                    "operands": [
                                        "@currentField",
                                        "Amber"
                                    ]
                                },
                                "#ff8c00",
                                {
                                    "operator": "?",
                                    "operands": [
                                        {
                                            "operator": "==",
                                            "operands": [
                                                "@currentField",
                                                "Yellow"
                                            ]
                                        },
                                        "#fff100",
                                        ""
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    }
}
         }
     ],
         "bindata": { },
 "version": 1
}

 

In this scenario, we will be using the following actions:

createSPList

This action will create a new list named “Team Projects”. It will also create associated columns pertaining to a typical project.

addSPField

This action will create those new fields on the Team Projects list.

applyTheme

This action will apply a created theme (either out of the box, or a custom one using the new fabric theme generator) and apply it to the newly created site.

triggerFlow

This action is going to fire off a Microsoft Flow, passing the URL of the newly created site. The flow will trigger an Azure Function which is going to provision some SPFx components using PnP PowerShell.

setSPFieldCustomFormatter

This action is going to apply a JSON object for the Column Formatter to apply on the Team Projects list.


Create a Theme

Creating a theme for a modern site is a little bit different than we’ve done in the past using composed looks in classic SharePoint. We are going to want to use the new Theme Generator when creating themes for modern sites. This generator, though limited at the time, allows us to modify a primary color, body color and a background color.

ThemeBuilder.png

Start by playing around with the color selectors. You’ll notice that at the bottom, the Fabric Palette will change colors and you can see how some fonts and controls will be themed when loaded into your SharePoint site. I am going to build a black and yellow theme. You’ll also notice it gives you a specified output in JSON,SASS and PowerShell. These will be used depending on how we upload the new theme to SharePoint — in our scenario, we’ll be using PowerShell.

Copy the contents of the PowerShell output and open up PowerShell. We’ll provision the new theme to our environment using the SharePoint Online Management Shell. For more detailed information on this process, please follow this post. To provision the theme, we’ll connect to our tenant and then using the Add-SPOTheme command we’ll provision the JSONObject


    $themepallette = @{
  "themePrimary" = "#00ffff";
  "themeLighterAlt" = "#f3fcfc";
  "themeLighter" = "#daffff";
  "themeLight" = "#affefe";
  "themeTertiary" = "#76ffff";
  "themeSecondary" = "#39ffff";
  "themeDarkAlt" = "#00c4c4";
  "themeDark" = "#009090";
  "themeDarker" = "#005252";
  "neutralLighterAlt" = "#f8f8f8";
  "neutralLighter" = "#f4f4f4";
  "neutralLight" = "#eaeaea";
  "neutralQuaternaryAlt" = "#dadada";
  "neutralQuaternary" = "#d0d0d0";
  "neutralTertiaryAlt" = "#c8c8c8";
  "neutralTertiary" = "#a6a6a6";
  "neutralSecondaryAlt" = "#767676";
  "neutralSecondary" = "#666666";
  "neutralPrimary" = "#333";
  "neutralPrimaryAlt" = "#3c3c3c";
  "neutralDark" = "#212121";
  "black" = "#000000";
  "white" = "#fff";
  "primaryBackground" = "#fff";
  "primaryText" = "#333"
 }
 
Connect-SPOService -Url https://yoursharepoint-admin.sharepoint.com
Add-SPOTheme -Name "Custom Black" -Palette $themepallette -IsInverted $false

If you have a good eye, you’ll notice the -Name parameter is also the same name that is in the applyTheme action of my site script. This will be the new theme we provision when our new Site Design runs.

If you are wondering where this theme shows up, navigate to your SharePoint site, hit the gear in the top right and select Change the look. You’ll see your new theme!

ThemeChangeTheLook.png

 

Package and Upload SharePoint Framework Navigation

The reason I am deploying a custom global navigation solution is because Hub Sites currently do not exist in Office 365 tenants. So in the meantime, I’d still like to apply a global navigation across all of my sites that I create so that they feel “connected” in the new flat structure.

The first thing you will want to do is head over to the SharePoint extensions GitHub and pull down Paolo’s global navigation application customizer. This SPFx solution, is a global navigation menu that is injected into the header (and/or footer) of your SharePoint sites. It is a tenant-wide deployment, which means it will automatically be available to all site collections without having to add the app through site contents. The documentation provided should give you all the information required to set this up. If you need information on how to build customizations using SPFX, start here.

I have downloaded and built the SharePoint Framework solution and uploaded it to my app catalog within my tenancy. The next thing I need to do, is configure a global navigation that I want all of my modern sites to use and then we’ll proceed to creating some PowerShell scripts.

TermStore

 

Create Azure Function & Call PnP to provision Global Navigation

As mentioned previously, we’ll be calling an Azure Function via the Microsoft Flow trigger. There are a couple ways to do this, either by setting up a Queue Function using App Authentication, or you configure your Azure Function to grab your credentials from environment variables. The preferred method is to set up your credentials using App-Only authentication as outlined in that blog post. You’ll want to use this post to figure out how to add PnP Modules to your Azure Function. In my scenario, I am actually going to set up an HTTPTrigger Azure Function because why not show a different example… and also because I may have a need to call this function directly in other scenarios.

Below is my Azure Function PowerShell. The PowerShell code is also available on Paolo’s github for the menu.


    #incoming webUrl from the Microsoft Flow call
    # POST method: $req
    $requestBody = Get-Content $req -Raw | ConvertFrom-Json
    $name = $requestBody.name
    
    # GET method: each querystring parameter is its own variable
    if ($req_query_name) 
    {
        $webUrl = $req_query_name 
    }

    Connect-PnPOnline -AppId $env:SPO_AppId -AppSecret $env:SPO_AppSecret -Url $webUrl
    
    $context = Get-PnPContext
    $web = Get-PnPWeb
    $context.Load($web)
    Execute-PnPQuery
    
    $ca = $web.UserCustomActions.Add()
    $ca.ClientSideComponentId = "b1efedb9-b371-4f5c-a90f-3742d1842cf3"
    $ca.ClientSideComponentProperties = "{""TopMenuTermSet"":""TenantGlobalNav"",""BottomMenuTermSet"":""TenantGlobalFooter""}"
    $ca.Location = "ClientSideExtension.ApplicationCustomizer"
    $ca.Name = "TenantGlobalNavBarCustomAction"
    $ca.Title = "TenantGlobalNavBarCustomAction"
    $ca.Description = "Custom action for Tenant Global NavBar Application Custom"
    
    $ca.Update()
    
    $context.Load($web.UserCustomActions)
    Execute-PnPQuery

The code is going to recieve a parameter called webUrl and it will be the url of the new site created from my site design. This url is used in the Connect-PnPOnline command.

Create Microsoft Flow

Creating the Microsoft Flow is required because it will be the mechanism for which we pass a webUrl parameter to our azure function (we can’t call an Azure Function directly from a site script).The flow will consist of two actions: When a HTTP request is received and a HTTP requestAs previously mentioned, most tutorials will likely show you using flow to add a message to an Azure queue. I will be calling the Azure Function directly.

Use this JSON to place into your body JSON for your Http Request received 


    {
    "type": "object",
    "properties": {
        "webUrl": {
            "type": "string"
        },
        "parameters": {
            "type": "object",
            "properties": {
                "event": {
                    "type": "string"
                },
                "product": {
                    "type": "string"
                }
            }
        }
    }

}

 

Here is the full Flow. You need to grab the Uri to your Azure Function. You can get this URL by opening up your Azure Function and selecting </> Get function url in the top right of your code window.

Flow.png

You’ll notice that inside the body of the HTTP request, I am passing a parameter called webUrl, which is equal to the web url of the new site I am creating. This property comes through within the Flow request.

 

Update triggerFlow action in site script

Now that we have configured our Flow and our Azure Function, we need to connect the Flow to the site script (JSON file above). To do this, copy the Flow URL, When a HTTP request is received in your Flow (it’s outlined in grey in the image).

Copy and paste this into our site script in the triggerAction verb like so:

OLD

    
         {
            "verb":"triggerFlow",
            "url":"THIS-IS-THE-URL-TO-YOUR-FLOW",
            "name":"Provision Assets for Group",
            "parameters":{
                "event":"",
                "product":""
            }
         }
         

NEW



         
          {
            "verb":"triggerFlow",
            "url":"https://prod-06.westus.logic.azure.com:443/workflows....",
            "name":"Provision Assets for Group",
            "parameters":{
                "event":"",
                "product":""
            }
         }

Upload site script and site design

That’s it! The only thing left to do is add the site script and site design using PowerShell. I have created the site script and saved it into a JSON file called SiteScriptProvisionGlobalNav.json.

Add-SPOSIteScript


    Connect-SPOService -Url 'https://yourtenant-admin.sharepoint.com'
    #get JSON content and add site script
    Get-Content 'C:\Users\Me\SiteScriptProvisionGlobalNav.json'-Raw | Add-SPOSiteScript  -Title "Aerie Team Site"
    

Running this in PowerShell will return the ID of the new site script. We’ll use this ID to add the site design to the tenant. Notice -SiteScripts parameter is equal to the ID of the newly uploaded site script and -WebTemplate is “64”. The value 64 represents an Office 365 Group and the value 68 would represent a communication site.

Add-SPOSiteDesign


    
Add-SPOSiteDesign -Title "Aerie Team Site" -WebTemplate "64" -SiteScripts "7a5c6fd8-0953-4381-a04d-6a4400448d0c" -Description "Creates a new Aerie Team Template Site with Global Nav"-PreviewImageUrl "https://mytenant.sharepoint.com/SiteAssets/logo.jpeg" -PreviewImageAltText "site preview"

    

 

Add your team site!

Finally, the last thing to do is create your new team site from your site design and watch the magic happen!

Navigate to the /_layouts/15/SharePoint.aspx page and select + Create Site

Site Slection

Remember that we’ll be selecting a team site. On the next window, we should see our new site design.

TeamSiteCreation

After clicking next and finish, we should start seeing our site script do it’s work. It will start by applying our Custom Black theme, creating a new Team Projects list and subsequently triggering a flow to add global navigation to our newly created site

SideWindow

 

Now check out the final product! We have a newly created group site, with a SPFx global navigation menu in the header of the site!

Black