Monday 11 September 2017

Manage tenant-scoped SPFx extensions across your SharePoint sites

As I mentioned in Use an SPFx Application Customizer to add JavaScript (e.g. header) to every page in a site, it’s now possible to *globally* deploy SPFx extensions (e.g. page headers, footers or other random pieces of JavaScript) or do a controlled roll-out across *many* SharePoint sites - without the app needing to be installed to each individual site. This is great news, and it was a gap for modern pages until now. SPFx web parts deployed at tenant scope will appear everywhere in the picker, but for SPFx extensions there is still something you need to do locally, and that’s “associate” your extension with the site/web/list/field. For Application Customizers, it’s this step which allows you to control exactly which sites use your extension. To do this you add a CustomAction to your site or web, specifying the GUID of your extension in the ClientSideComponentId property (new for SPFx). Although I’m focusing more on site-level customizations (Application Customizers) in this post, it’s a similar story for SPFx Field Customizers too (ClientSideComponentId is specified on the field) and SPFx Command Set Customizers (CustomAction with ClientSideComponentId is specified on the list). All this can be done a couple of ways:

  • Using CSOM or REST – perhaps in PowerShell or C# code
  • As part of PnP XML, if you are applying a custom template to the site – the XML schema and PnP Core provisioning library now supports this (Sept 2017 release onwards)

In this post I’ll provide some PowerShell and C# code to help you apply Application Customizers across your sites – you could modify for other types of customizer without too much trouble. However, there are some prerequisites in all cases - in some ways, the association step is one of the last things you will do. So let's cover that quickly:

Tenant-scoped SPFx extensions - recap/prerequisites

SPFx extensions and webparts are possible from v1.2 of SPFx onwards. Broadly, the prerequisites needed before the script/code in this article can be used are:-

  • You specified "skipFeatureDeployment": true in the package-solution.json file
  • The app was packaged and then installed to the App Catalog, and the administrator checked the box for 'Make this solution available to all sites in the organization' (shown in image below)
  • The JavaScript bundle for the SPFx app has been deployed to a CDN or other web-hosting location

Here's what the administrator will see when installing to the App Catalog, and the checkbox (which they need to check) that appears when "skipFeatureDeployment": true:

SNAGHTMLf7281c2

The PnP XML option

I'll focus on the C#/PowerShell options in this post, but there’s a PnP XML option which is very useful too. This allows you to include the association as part of a custom site template, and therefore is great for any sites being newly-created from such a template. In fact, you could also use it to apply a ‘partial’ template to existing sites, but I think most people might choose PowerShell or C# code at that point. The main info I want to convey here is that this is available from version 2.18.1709 onwards of PnP Core (Sept 2017 release). The 2017-05 schema has places to specify the ClientSideComponentId property on a CustomAction and on a field, since that’s what the association consists of. The XML extract to provision an Application Customizer at the web level would be:

In next sections, I’ll cover PowerShell first and then C#.

Using PowerShell code and PnP PowerShell

Here are some PowerShell functions to add, remove and list the SPFx global extensions across a selection of sites, done by adding a Custom Action at the root web level – tweak if you need something else. I'm using a simple array of site URLs here, but you could fill that array however you like. Other notes:-

  • I'm using PnP PowerShell cmdlets here - you'll need to install those if you don't have them already, and then get connected to your tenant with Connect-PnPOnline etc.
  • At the time of writing, I had an issue with the PnP cmdlet that they provide (Add-PnPCustomAction), so I'm using direct CSOM in the 'add' method. I raised a GitHub issue about this (also noted in comments in script below), and I'm sure the guys will fix it soon (or tell me I'm doing something wrong ;), but the direct CSOM approach works fine too)
Output:

Registering a globally-deployed extension with addSpfxExtensionCustomAction(ctx) will give:

SNAGHTMLf6529dd

Listing the extensions on a site with:

Get-PnPCustomAction -Web $ctx.Web | Where-Object {$_.Location -eq "ClientSideExtension.ApplicationCustomizer" }

will give:

SNAGHTMLf67a240

Removing an extension with

Remove-CustomActionForSPFxExt $spfxExtName $site $ctx

will give:

SNAGHTMLf69e124

Using C# code and PnP core

But instead of PowerShell, perhaps you want to use C# code instead. Some notes on this:-

  • I'm using the PnP Core library here - you'll need to install the NuGet package to your solution/Azure Function/whatever if you don't have it already. Get this from https://www.nuget.org/packages/SharePointPnPCoreOnline
  • In contrast to the PowerShell above I'm only processing a single site here, but it would be trivial to extend the code to run across whatever sites you need

Sample code:

Output:

As you’d expect, registering a globally-deployed extension with addSpfxExtensionCustomAction(ctx) will give:

SNAGHTMLf59848b

Listing the extensions on a site with getCustomActions(ctx) will give:

SNAGHTMLf55b850

Removing an extension with removeSpfxExtensionCustomAction() will give:

SNAGHTMLf5790d9

Another option – CLI scripts

As another option, note that my esteemed colleague Vardhaman Deshpande also has a super-cool CLI tool to help you manage SPFx extensions. He’s so hipster ;) His scripts offers the ability to manage SPFx Command Set Customizers too. See https://github.com/vman/spfx-extensions-cli for more details.

Summary

For tenant-scoped SPFx Application Customizers, you need to ensure the sites or webs which should use it have a CustomAction with the ClientSideComponentId of your extension (in addition to dealing with the other prerequisite steps i.e. getting the app package and corresponding JavaScript bundle deployed). Although not addressed with this code, it’s a similar approach for SPFx Field Customizers and Command Set Customizers too. Hopefully the options presented in this article (together with the underlying PnP awesomeness) are of some use.

No comments: