In the previous blog here, we got an overview of the high level Architecture of a Complex Modern team site provisioning process. In this blog, we will look at the step 1 of the process – Create and Apply template process, in detail.

Before that, below are few links to earlier blogs, as a refresher, to prerequisties for the blog.

  1. Set up a Graph App to call Graph Service using App ID and Secret – link
  2. Sequencing HTTP Trigger Azure Functions for simultaneous calls – link
  3. Adding and Updating owners using Microsoft Graph Async calls – link

Overview

The Create and Apply Template process aims at the following

  1. Create a blank modern team site using Groups Template (Group#0 Site template)
  2. Apply the provisioning template on the created site.

Step 1 : Create a blank Modern team site

For creating a modern team site using CSOM we will use the TeamSiteCollectionCreationInformation class of OfficeDevPnP.  Before we create the site, we will make sure the site doesn’t already exist.

Note: There is an issue with the Site Assets library not getting intialized 
when the site is created using the below code. 
Hence, calling the EnsureSiteAssets library is necessary.
using Microsoft.Online.SharePoint.TenantAdministration;
using Microsoft.SharePoint.Client;
using OfficeDevPnP.Core.Sites;
using (ClientContext ctx = new ClientContext(<TenantAdminUrl>))
{
// create new "modern" team site at the url
// https://%5Btenant%5D.sharepoint.com/sites/mymodernteamsite
string alias = taskParam.TargetWebUrl.Substring(Url.LastIndexOf('/') + 1);
// Before creating let's make sure the site doesn't exist
bool exists = false;
Task existtask = Task.Run(async () => exists = await ctx.AliasExistsAsync(alias));
existtask.Wait();
if (!exists)
{
log.Info("Creating site with a new Async method");
TeamSiteCollectionCreationInformation teamSiteCreation = new TeamSiteCollectionCreationInformation();
// Provide the team site para
teamSiteCreation.Alias = <GroupAlias>;
teamSiteCreation.DisplayName = <WebTitle>;
teamSiteCreation.IsPublic = <true or false>;
teamSiteCreation.Description = <Description>;
ClientContext teamcontext = ctx.CreateSiteAsync(teamSiteCreation).GetAwaiter().GetResult();
teamcontext.Load(teamcontext.Web, w => w.Lists, w => w.Url);
teamcontext.ExecuteQueryRetry();
//Call the Ensure Site Assets Library method to initialize the Site Assets library
teamcontext.Web.Lists.EnsureSiteAssetsLibrary();
teamcontext.ExecuteQueryRetry();
log.Info("The site has been successfully created at: " + teamcontext.Web.Url);
}
}

Step 2:  Apply the Provisioning Template

Note: The Apply template process is a long running process and takes from 60-90 min to complete 
for a complex provisioning template with many site columns, content types and libraries. 
In order to prevent the Azure function from timing out, it is required to host the Azure Function 
using a App Service Plan instead of a Consumption plan so the Azure function 
is not affected by the 10 min time out. 

For the Apply Provisioning Template process, use the below steps.

1. Reading the Template

It is important to note that the XMLPnPSchemaFormatter version (in the code below) must match the PnP version used to generate the PnP template. If the version is older, then set the XMLPnPSchemaFormatter to read from the older version. In order to find the version of the PnP Template, open the xml and look at the start of the file

PnPTemplateVersion

using (Context ctx = new ClientContext(<siteUrl>))
{
string TemplateFilename = "<FileName>.xml";
ctx.Load(ctx.Web, w => w.Lists, w => w.Folders);
Microsoft.SharePoint.Client.File templateFile = ctx.Web.Lists.GetByTitle("Site Assets").RootFolder.Folders.GetByUrl("Templates").GetFile(TemplateFilename);
ctx.ExecuteQuery();
FileInformation fileInfo = Microsoft.SharePoint.Client.File.OpenBinaryDirect(ctx, templateFile.ServerRelativeUrl);
assetContext.ExecuteQuery();
filePath = Path.GetTempPath() + "\\" + TemplateFilename;
using (var fileStream = new FileStream(filePath, FileMode.Create))
fileInfo.Stream.CopyTo(fileStream);
}
using (FileStream fs = System.IO.File.OpenRead(filePath))
{
ITemplateFormatter formatter = XMLPnPSchemaFormatter.LatestFormatter; //Use the same version as the PnP template
Template = formatter.ToProvisioningTemplate(fs);
}

2. Apply the Template

For applying the template, we will use the ProvisioningTemplateApplyingInformation class of the OfficeDevPnP module. The ProvisioningTemplateApplyingInformation also has a property called HandlerToProcess which could be used the invoke the particular handler in the provisioning template process. Below is the code for the same.

using (var ctx = new ClientContext(<site url>))
{
ctx.RequestTimeout = Timeout.Infinite;
Web web = ctx.Web;
ctx.Load(web, w => w.Title);
ctx.ExecuteQueryRetry();
ProvisioningTemplateApplyingInformation ptai
= new ProvisioningTemplateApplyingInformation
{
ProgressDelegate = delegate (String message, Int32 progress, Int32 total)
{
log.Info(String.Format("{0:00}/{1:00} – {2}", progress, total, message));
},
IgnoreDuplicateDataRowErrors = true
};
web.ApplyProvisioningTemplate(Template, ptai);
}

After the apply template process is complete, since the flow will have timed out, we will invoke another flow to do the post process by updating a list item in the SharePoint list.

Conclusion

In this blog, we saw how we could create a modern team site and apply the template on it. The next blog we will finalize the process by doing site specfic changes after applying the template.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s