{"id":2205,"date":"2016-11-30T20:08:30","date_gmt":"2016-11-30T19:08:30","guid":{"rendered":"http:\/\/www.itidea.nl\/?p=2205"},"modified":"2016-11-30T16:25:22","modified_gmt":"2016-11-30T15:25:22","slug":"extensibility-handler-for-officedev-pnp-provisioning-framework","status":"publish","type":"post","link":"https:\/\/www.itidea.nl\/index.php\/extensibility-handler-for-officedev-pnp-provisioning-framework\/","title":{"rendered":"Extensibility handler for OfficeDev PnP provisioning framework"},"content":{"rendered":"<p>The PnP Provisioning framework can be used to remotely extract and provision standardized sites based on templates. This is a typical requirement for enterprises and we have classically used technologies like site definitions, site templates or web templates to achieve this. Releases of the framework are made recently, but sometimes functionality is desired\u00a0which the framework doesn&#8217;t provide (yet).<\/p>\n<p>To extend the framework yourself Extensibility Handlers can be implemented.<br \/>\nAn extensibility handler can be executed when provisioning and extracting a site as template.<\/p>\n<p>To immediately dive into the details, let&#8217;s start Visual Studio and<\/p>\n<ol>\n<li>Start a new class library project<\/li>\n<li>Add a references to Microsoft.SharePoint.Client, Microsoft.Client.Runtime and OfficeDevPnp.Core<\/li>\n<li>Add a class and implement interface IProvisioningExtensibilityHandler<\/li>\n<\/ol>\n<p>At this point the code looks like this:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic class DoSomethingHandler : IProvisioningExtensibilityHandler\r\n{\r\n  public ProvisioningTemplate Extract(ClientContext ctx, ProvisioningTemplate template,\r\n   ProvisioningTemplateCreationInformation creationInformation,\r\n   PnPMonitoredScope scope, string configurationData)\r\n  {\r\n    throw new NotImplementedException();\r\n  }\r\n\r\n  public IEnumerable&lt;TokenDefinition&gt; GetTokens(ClientContext ctx,\r\n   ProvisioningTemplate template, string configurationData)\r\n  {\r\n    throw new NotImplementedException();\r\n  }\r\n\r\n  public void Provision(ClientContext ctx, ProvisioningTemplate template,\r\n   ProvisioningTemplateApplyingInformation applyingInformation,\r\n   TokenParser tokenParser, PnPMonitoredScope scope, string configurationData)\r\n  {\r\n    throw new NotImplementedException();\r\n  }\r\n}\r\n<\/pre>\n<p>When a site is <em>exported<\/em> the Extract method is called and when a site is <em>created<\/em> the Provision method is called.<br \/>\nThe GetTokens method provides a way to use your own token definitions.<\/p>\n<p>To use the extensibility provider in the template xml a providers section has to be added:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;pnp:Providers&gt;\r\n &lt;pnp:Provider Enabled=&quot;true&quot;\r\n   HandlerType=&quot;&lt;namespace + classname, &lt;assembly name&gt;, &lt;version&gt;, &lt;culture&gt;, &lt;publickeytoken&gt;&quot;&gt;\r\n &lt;pnp:Configuration&gt;\r\n &lt;valid_xml xmlns=&quot;http:\/\/schemas.itidea.com\/extensibilityproviders&quot;&gt;true&lt;\/valid_xml&gt;\r\n &lt;\/pnp:Configuration&gt;\r\n &lt;\/pnp:Provider&gt;\r\n&lt;\/pnp:Providers&gt;\r\n<\/pre>\n<p>The Provider element has two attributes:<\/p>\n<ol>\n<li>Enabled, to enable or (temporarily) disable the handler<\/li>\n<li>HandlerType, which describes where the handler is located.<\/li>\n<\/ol>\n<p>Inside the pnp:Configuration element anything can be placed, as long it&#8217;s valid xml.<\/p>\n<p>The Provisioning engine has to be able to find the assembly referenced in the HandlerType attribute. To accomplish this the PowerShell commandlet Add-Type can be used.<br \/>\nTo use the (right now not so useful) handler three lines of PowerShell are sufficient:<\/p>\n<pre class=\"brush: powershell; title: ; notranslate\" title=\"\">\r\nConnect-SPOnline -Url &lt;url&gt;\r\nAdd-Type -Path C:\\_Tools\\ITIdea.PnPExtensions\\bin\\Debug\\ITIdea.PnPExtensions.dll\r\nApply-SPOProvisioningTemplate -Path C:\\_Tools\\ITIdea.PnPExtensions\\Template\\template.xml\r\n<\/pre>\n<p>Ofcourse this returns a NotImplementedException, because\u00a0the methods aren&#8217;t implemented.<\/p>\n<p>Attach the powershell process to debug the handler in Visual Studio.<br \/>\nThe xml defined inside the handler is captured in a string called configurationData, which can be serialized when needed.<br \/>\n<a href=\"https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-configurationdata.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-2218 size-full\" src=\"https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-configurationdata.png\" alt=\"PnP Extensibility Handler - configurationdata\" width=\"932\" height=\"174\" srcset=\"https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-configurationdata.png 932w, https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-configurationdata-300x56.png 300w, https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-configurationdata-600x112.png 600w, https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-configurationdata-900x168.png 900w\" sizes=\"auto, (max-width: 932px) 100vw, 932px\" \/><\/a><\/p>\n<h4>Token definition<\/h4>\n<p>The Provisioning Framework has lots of token definitions implemented, like {sitename}, {roledefinition:Reader}, {sitecollectiontermstoreid} and much more.<br \/>\n<a href=\"https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-list-of-tokendefinitions.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-2216 size-full\" src=\"https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-list-of-tokendefinitions.png\" alt=\"PnP Extensibility Handler - list of tokendefinitions\" width=\"303\" height=\"629\" srcset=\"https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-list-of-tokendefinitions.png 303w, https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-list-of-tokendefinitions-144x300.png 144w\" sizes=\"auto, (max-width: 303px) 100vw, 303px\" \/><\/a><\/p>\n<p>When a custom token is needed it can be implemented by creating a class derived from TokenDefinition and override the method GetReplaceValue.<br \/>\nSuppose the title of the rootweb of the site collection is needed in a subsite.<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic class SiteCollectionNameToken : TokenDefinition\r\n{\r\n  public SiteCollectionNameToken(Web web)\r\n   : base(web, &quot;{sitecollectionname}&quot;)\r\n  {\r\n  }\r\n\r\n  public override string GetReplaceValue()\r\n  {\r\n    if (CacheValue == null)\r\n    {\r\n      this.Web.EnsureProperty(w =&gt; w.Url);\r\n      using (ClientContext context = this.Web.Context.Clone(this.Web.Url))\r\n      {\r\n        var site = context.Site;\r\n        context.Load(site, s =&gt; s.RootWeb.Title);\r\n        context.ExecuteQueryRetry();\r\n        CacheValue = context.Site.RootWeb.Title;\r\n      }\r\n    }\r\n    return CacheValue;\r\n  }\r\n}\r\n<\/pre>\n<p>The token which can be used in the template xml is listed in the constructor. In this case {sitecollectionname} can be used.<\/p>\n<p>This token will be replaced by the actual value in the method GetReplaceValue.<br \/>\nOnce the tokendefinition is in place it can be used in the GetTokens method in the extensibility handler.<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic IEnumerable&lt;TokenDefinition&gt; GetTokens(ClientContext ctx,\r\n ProvisioningTemplate template, string configurationData)\r\n{\r\n  var customTokens = new List&lt;TokenDefinition&gt;();\r\n  customTokens.Add(new SiteCollectionNameToken(ctx.Web));\r\n  return customTokens;\r\n}\r\n<\/pre>\n<p>And it can be used in the xml which describes the template.<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;pnp:Providers&gt;\r\n  &lt;pnp:Provider Enabled=&quot;true&quot;\r\n   HandlerType=&quot;ITIdea.PnPExtensions.Extensions.DoSomethingHandler, ITIdea.PnPExtensions,\r\n   Version=1.0.0.0, Culture=neutral, PublicKeyToken=null&quot;&gt;\r\n    &lt;pnp:Configuration&gt;\r\n      &lt;valid_xml xmlns=&quot;http:\/\/schemas.itidea.com\/extensibilityproviders&quot;&gt;{sitecollectionname}&lt;\/valid_xml&gt;\r\n    &lt;\/pnp:Configuration&gt;\r\n  &lt;\/pnp:Provider&gt;\r\n&lt;\/pnp:Providers&gt;\r\n<\/pre>\n<p>To show the output the Provision method writes the configurationData to the console:<\/p>\n<p><a href=\"https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-replaced-token1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2240\" src=\"https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-replaced-token1.png\" alt=\"PnP Extensibility Handler - replaced token\" width=\"795\" height=\"33\" srcset=\"https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-replaced-token1.png 795w, https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-replaced-token1-300x12.png 300w, https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-replaced-token1-600x24.png 600w\" sizes=\"auto, (max-width: 795px) 100vw, 795px\" \/><\/a><br \/>\n<output><br \/>\nAs can be seen the token is replaced by its value.<\/output><\/p>\n<h4>Provision<\/h4>\n<p>Suppose permissions are inherited at subsite level. The permissions of a list at subsite level are broken and some sitegroups have to be deleted from this list by the provisioning framework.<br \/>\nThe title of the rootweb of the site collection is &#8216;Dev Anita&#8217; in this example, so the sitegroups are called &#8216;Dev Anita Members&#8217;, &#8216;Dev Anita Owners&#8217; and &#8216;Dev Anita Visitors&#8217;.<br \/>\nThe token defined earlier gets the title of the rootweb of the site collection, convenient&#8230;<\/p>\n<p>To be able to delete groups from a list a couple of things are needed, like the list, the group names and roledefinitions. I came up with the following XML:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;pnp:Providers&gt;\r\n  &lt;pnp:Provider Enabled=&quot;true&quot;\r\n   HandlerType=&quot;ITIdea.PnPExtensions.Extensions.DoSomethingHandler, ITIdea.PnPExtensions,\r\n   Version=1.0.0.0, Culture=neutral, PublicKeyToken=null&quot;&gt;\r\n    &lt;pnp:Configuration&gt;\r\n      &lt;List Name=&quot;Documents&quot; xmlns=&quot;http:\/\/schemas.itidea.com\/extensibilityproviders&quot;&gt;\r\n        &lt;SiteGroups&gt;\r\n          &lt;SiteGroup Name=&quot;Approvers&quot; RoleDefinitionName=&quot;Approve&quot;\/&gt;\r\n          &lt;SiteGroup Name=&quot;{sitecollectionname} Members&quot; RoleDefinitionName=&quot;{roledefinition:Editor}&quot;\/&gt;\r\n        &lt;\/SiteGroups&gt;\r\n      &lt;\/List&gt;\r\n    &lt;\/pnp:Configuration&gt;\r\n  &lt;\/pnp:Provider&gt;\r\n&lt;\/pnp:Providers&gt;\r\n<\/pre>\n<p>{roledefinition:Editor} is a token which is implemented by the OfficeDev PnP provisioning framework.<br \/>\nInside the Provision method the xml configurationdata is received as a string and written to the console for convenience:<br \/>\n<a href=\"https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-serialized-xml-provision.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2245\" src=\"https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-serialized-xml-provision.png\" alt=\"PnP Extensibility Handler - serialized xml provision\" width=\"841\" height=\"50\" srcset=\"https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-serialized-xml-provision.png 841w, https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-serialized-xml-provision-300x17.png 300w, https:\/\/www.itidea.nl\/wp-content\/uploads\/2016\/11\/PnP-Extensibility-Handler-serialized-xml-provision-600x35.png 600w\" sizes=\"auto, (max-width: 841px) 100vw, 841px\" \/><\/a><\/p>\n<p>To be able to process the XML nicely it&#8217;s serialized to a custom object &#8216;MyList&#8217; which consists of the items the XML does.<\/p>\n<p>The code to remove the roledefinition from the list is easy:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n\r\npublic void Provision(ClientContext ctx, ProvisioningTemplate template,\r\n ProvisioningTemplateApplyingInformation applyingInformation,\r\n TokenParser tokenParser, PnPMonitoredScope scope, string configurationData)\r\n{\r\n  Console.WriteLine(configurationData);\r\n\r\n  var reader = new StringReader(configurationData);\r\n  var serializer = new XmlSerializer(typeof(MyList));\r\n  var listConfig = (MyList)serializer.Deserialize(reader);\r\n\r\n  List theList = ctx.Web.Lists.GetByTitle(listConfig.Name);\r\n  ctx.Load(theList);\r\n  ctx.ExecuteQueryRetry();\r\n\r\n  SiteGroups groups = listConfig.SiteGroups;\r\n  foreach (var group in groups.Groups)\r\n  {\r\n    theList.RemovePermissionLevelFromGroup(group.Name, group.RoleDefinitionName);\r\n  }\r\n\r\n  theList.Update();\r\n}\r\n<\/pre>\n<h4>Summary<\/h4>\n<p>The OfficeDev PnP framework provides a lot of functionality to remotely extract and provision sites. To implement specific functionality an extensibility handler can be developed using CSOM.<br \/>\nBe aware that handlers are always executed last irrespectively their location in the xml file.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The PnP Provisioning framework can be used to remotely extract and provision standardized sites based on templates. This is a typical requirement for enterprises and we have classically used technologies like site definitions, site templates or web templates to achieve &#8230; <a class=\"more-link\" href=\"https:\/\/www.itidea.nl\/index.php\/extensibility-handler-for-officedev-pnp-provisioning-framework\/\">Read More &raquo;<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[39,22,35],"tags":[37,43,44],"class_list":["post-2205","post","type-post","status-publish","format-standard","hentry","category-office-365","category-powershell","category-sharepoint-2013","tag-office365","tag-powershell","tag-sharepoint-2013"],"_links":{"self":[{"href":"https:\/\/www.itidea.nl\/index.php\/wp-json\/wp\/v2\/posts\/2205","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.itidea.nl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.itidea.nl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.itidea.nl\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.itidea.nl\/index.php\/wp-json\/wp\/v2\/comments?post=2205"}],"version-history":[{"count":36,"href":"https:\/\/www.itidea.nl\/index.php\/wp-json\/wp\/v2\/posts\/2205\/revisions"}],"predecessor-version":[{"id":2315,"href":"https:\/\/www.itidea.nl\/index.php\/wp-json\/wp\/v2\/posts\/2205\/revisions\/2315"}],"wp:attachment":[{"href":"https:\/\/www.itidea.nl\/index.php\/wp-json\/wp\/v2\/media?parent=2205"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.itidea.nl\/index.php\/wp-json\/wp\/v2\/categories?post=2205"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.itidea.nl\/index.php\/wp-json\/wp\/v2\/tags?post=2205"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}