Here we are, at part two. We are diving straight into it. In this post we look at the provisioning engine itself, not the deployment of the engine. That is covered in the third part of this series.
What do we need to build this engine?
- An Azure subscription
- Contributor access to this subscription
- The SharePoint administrator role in Office365
- An existing teamsite which we will use as a template
Solution design
First off I will give you a schematic overview of what we are building:
Now let’s focus on the individual components of the solution. Similar to Microsoft’s blueprint, we have a site design which invokes a site script (1b). However, where Microsoft’s blueprint uses Flow (Power Automate), we will use an Azure Logic App. The primary reason is that, since Microsoft published their guide, the used HTTP-trigger became a premium connector which requires additional licensing.
The next deviation from Microsoft’s blueprint is the way we execute the PowerShell-script to apply the PnP provisioning template (4). I prefer using Azure Automation for running scripts rather than Azure Functions:
- Less complex
- Easier to manage & maintain
- Only Azure Functions V1 are currently compatible with the PnP-module
There is one downside to using the automation account over the function. We don’t have the option to store our site template anywhere. Therefore we will need a storage account.
Let’s start building
We begin by setting up the necessary service principals needed for this solution.
Exporting a site as PnP provisioning template
Note: at the time of writing the version of the SharePoint PnP PowerShell Online module available in the Automation account modules gallery is 3.19.2003. This is the one I used when exporting my demo template. When exporting your own template make sure you use the same version of the module as you are using in the automation account.
- Connect to your template site via the Powershell PnP module
Connect-PnPOnline -url <your template site url>
- Export the site design as a template
Get-PnPProvisioningTemplate -out template.xml
There’s an example template.xml in my github repository. It creates a document library called ‘AppliedSuccessfully’, used to verify the engine itself before spending time on creating your own PnP provisioning templates.
Azure Service Principal
First we need an Azure App Registration. This is used to authorize the Logic App to start runbooks on the Automation Account. Head to the Azure Portal and create a new app registration.
We need to assign permissions to this app registration. I am going to assign the ‘Contributor’-role to my subscription. Technically, for the scope of this part of the guide, the ‘Automation Operator’ on resource group level would suffice. However, in the next part we will be automating the deployment of the solution, which requires a service principal with contributor permissions. If you are implementing this for an organization always consider the least privilege principle. One way to mitigate the risk is using a separate subscription for this solution.
SharePoint Service Principal
Secondly we need a SharePoint-app registration. Use the instruction provided by Microsoft.
Provision the Azure Resources
Now, we move on to creating the needed Azure Resources. In the next post I will show you how to do this programmatically, but for now I’ll assume you do this manually via the Azure portal.
- Create a resource group with a name of your choosing, e.g. ‘PnpProvisioningEngine’
- Provision the following resources in this resource group:
- Automation account
- Storage account
- Logic App
Your resource group should look like this:
Configure the Storage account
Not much to do here, just create a blob and upload your template.xml to it. For this demo I will use a blob with anonymous read access. However, again, for production scenarios you might want to add a layer of authentication to the way the template is read from the storage account. Be sure to copy the URL of the file after uploading.
Configure the Automation account
We need to do 3 things to here;
1. Import the SharePoint PnP PowerShell Online module:
Through the Azure portal, navigate to the modules section of the Automation account and import the SharePointPnpPowershellOnline-module.
2. Set the necessary variables:
We need a bunch of variables to pass to the script so it can connect to the storage account and SharePoint.
Name | Type | Value |
PNP_TemplateUrl | String | The URL of template.xml in the blob container |
SPO_AppId | String | The client ID of your SharePoint Service Principal |
SPO_AppSecret | Encrypted String | The client secret of your SharePoint Service Principal |
After setting these variables the result should look like this:
3. Add the runbook which applies the PnP provisioning template
For this part we’ll use a very simple script which only applies the provisioning template. There’s basically no limit to the actions you can do from this script. I will keep this part simple for now.
Copy or download this script and import it as a runbook in the automation account:
#parameter provided by logic app
Param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$url
)
#first read the automation variables
$appId = Get-AutomationVariable -name "SPO_AppId"
$appSecret = Get-AutomationVariable -name "SPO_AppSecret"
[System.Uri]$templateUrl = Get-AutomationVariable -name "PNP_TemplateUrl"
#download the template.xml to a temporary file on disk
$template = New-TemporaryFile
Invoke-WebRequest -Uri $templateUrl.AbsoluteUri -OutFile $template
#connect to PnPOnline
Connect-PnpOnline -Url $url -AppId $appId -AppSecret $appSecret
#apply the template, clear navigation prevents ending up with duplicate entries in the site navigation
Apply-PnPProvisioningTemplate -Path $template.FullName -clearnavigation
Build the Logic App
Now we start to connect things. First we need to build our logic app to trigger the runbook on an http-request. This is what the site script will invoke in the next stage.
1. Go into the Logic App designer and start with a HTTP-trigger
2. Add the request body JSON schema
This code will allow you to capture the property ‘webUrl’ from the incoming HTTP-request fired by the site script action.
{
"properties": {
"webUrl": {
"type": "string"
},
"parameters": {
"properties": {
"event": {
"type": "string"
},
"product": {
"type": "string"
}
},
"type": "object"
}
},
"type": "object"
}
3. Add the automation connector
Add a new step to the app. In the search bar type ‘Create Job’ and select the Azure Automation Create Job-Action:
Then connect the connector to your tenant and enter the necessary parameters:
Parameter | Value |
Subscription | Your subscription |
Resource Group | The resource group you are using in this solution |
Automation Account | The name of your automation account |
Wait for job | ‘No’ |
Runbook Name | Apply-PnpProvisioningTemplate or whatever name you choose when importing the script |
Runbook Parameter url | Select the webUrl from the dynamic context menu |
The end result should look like this:
4. Finally, press save!
Once the app saves, the HTTP POST URL will be generated. Copy it, we will need it later.
With the app saved we can also test whether it works. From a PowerShell commandline enter the following lines:
$body = "{ 'webUrl':'http://test.me' }"
$uri = '<your HTTP POST URL here>'
Invoke-WebRequest -Uri $uri -Method POST -Body $body -ContentType "application/json" -UseBasicParsing
Be sure to update the $uri with your HTTP POST URL property before running. You should be able to see the request trigger the logic app. Which in turn creates the job in automation, passing the webUrl-parameter along with it. Execution of the script will fail due to us not providing a valid url to a SharePoint-site, although the job will have run successfully.
Add the site design
Now that our back-end is fully operational we can create the site script and attach it to a site design.
First let’s create the site script. This is a simple JSON file. You can use the sample provided below or create one yourself. Be sure to update the ‘url’ with the HTTP POST URL from the previous step.
{
"$schema": "https://developer.microsoft.com/json-schemas/sp/site-design-script-actions.schema.json",
"actions": [
{
"verb": "triggerFlow",
"url": "<your HTTP POST URL>",
"name": "Trigger PnP template",
"parameters": {}
}
],
"bindata": {},
"version": 1
}
Edit the JSON above in notepad and copy it to your clipboard.
Now we will upload this script into the SharePoint admin center and attach it to a site design. At this point we are back in sync with the Microsoft blueprint.
To complete the engine, run the following commands in a PowerShell commandline (this requires the SharePoint Online Management Shell or alternatively you can also do this through the PnP-module):
Connect-SPOService -Url https://[yourtenant]-admin.sharepoint.com
$script = Get-Clipboard -Raw
$siteScript = Add-SPOSiteScript -Title "Apply PnP Provisioning Template" -Content $script
Add-SpoSiteDesign -Title "Apply PnP Provisioning Template" -SiteScripts $siteScript.Id -WebTemplate 64
Presto magic!
You should now have the new site design available in your SharePoint admin center:
And when you create a site your template should be applied. If you used my example template you will see a document library called ‘AppliedSuccessfully’ in the site’s navigation.
Where’s the automation?!
As I stated in part one, I love automation. In the next part of this series we will automate the deployment of this solution with Azure DevOps. This makes it a lot easier to perform updates, roll it out to additional tenants and all kinds of other non-clicky goodness. However I felt it was important to also share the basics of the solution with you. So when you are debugging your release pipelines later you have a good understanding of where the solution ends and the automation begins.
Be First to Comment