Setting managed metadata fields using PnP PowerShell

A few days ago, I was trying to set some managed metadata fields in a SharePoint library using the Set-PnPListItem command – which is greatly documented.
But having worked with SharePoint for quite a few years, I immediately noticed that the examples in documentation for managed metadata fields did not contain the exception case for terms with the ‘&’ character in the label. I had to deal with this case multiple times before, so writing this blog post to hopefully save some time in the future.

If you have taxonomy terms that use the ‘&’ character, behind the scenes it gets converted to ‘&’. Notice that the character looks different, because it is a different character. You can search online for information on this as there are plenty of blog posts explaining it in detail.

To handle the special character when setting the field with Set-PnPListItem, all you have to do is replace it. Then use the new string in the Set-PnPListItem command. This also works for fields with multiple values of course.

$myString = $myString -replace '&', '&'

I have also wrote a blog post with a script to replace a specific term across all documents and folders.

But be warned…

While I was working on this, I had multiple managed metadata fields to be set on the list. To avoid repetitive string replacements in my code, I created a very simple function to do that. The function received a string and simply returned the result after the replacement – super simple. This seemed to be fine and the output on the console was correct. I could even copy/paste the value and manually run the command to set the field myself. But it din’t work within the script. When using the variable with the return of the function, it didn’t work at all!

I suspect that somehow, returning the string from the function was messing up with the characters and something was going wrong (even though console output seemed correct), but never got to the bottom of it. Maybe it’s just a known thing on how PowerShell works that I don’t know…

If you have any idea, please leave a comment below 🙂 I would really like to understand what happened

SPFx Workbench Customizer

workbench customizer web part preview

I recently published a blog post about a web part that I use on the workbench page during development. I have this solution deployed on my dev tenant and simply add it to the bottom of the Workbench page. It allows me to work around some workbench limitations when building the UI of SPFx web parts.

I have recently updated this web part and added it to the PnP web part samples project on GitHub. You can get the code here:


The previous version of the web part allowed you to enable/disable specific CSS overrides. Those overrides were dynamically imported on the page based on your settings. When all the overrides were enabled, the Workbench page would have an interface similar to a modern page. For this to happen, the web part would remove extra margins and add extra styles.

Additionally, the web part now also has a switch button to enable page preview mode by default. When this is enabled, right after the page loads, the web part simulates a click on the Preview button at the top of the page and the editing experience is replaced with the preview.

This is really great when testing smaller screen resolutions as the workbench no longer displays a message telling you to “Widen your browser window”. You can see the difference on the images below

This image has an empty alt attribute; its file name is image-1.png

Give it a try and let me know if you have any feedback. Happy coding 🙂

SPFx solution using PnPjs for Project Online REST API

SPFx Project SharePoint

If you know me or follow me on Twitter/LinkedIn, you must have realized by now how much I like the PnPjs library. Enough to venture myself to speak about it on 3 SharePoint Saturday events last year. The library has packages for SharePoint and Graph endpoints and can be easily used on SPFx solutions. But if you need an SPFx solution that consumes Project Online API, what options do you have?
Kudos to Paweł Hawrylak who started creating the Project module for PnPjs and currently already offers support for a wide range of endpoints. The module is currently in a dev branch and requires additional work and testing, but it’s already a phenomenal effort.

This blog post will cover the required steps to generate a local PnPjs Project package to consume Project Online REST APIs and create a SPFx web part that uses it.

Published npm packaged

2019-12-31 update
If you only want to give the current version a try, there is now a packaged published to npm that you can simply install. Don’t forget to also install the required dependencies of @pnp/common, @pnp/odata, @pnp/logging and you are ready to rock!

npm i pnpjs-project-online-package @pnp/common@1.3.7 @pnp/logging@1.3.7 @pnp/odata@1.3.7 --save-exact

Get the code from GitHub

2019-11-14 update: I now have a repository forked from the main PnPjs project where I added the code for the project module. The good news is that this is using the latest PnPjs version (1.3.7 at the time)

From here, you can download, clone or fork it, what ever makes sense for what you plan to do with it. Just be aware that if you plan to fork it to contribute, and you have also previously forked the original PnPjs repository, you may need to delete the last and fork after that.
You can find the original code under Paweł’s GitHub fork of the main PnPjs project on GitHub.

Build PnPjs packages

Since we have the PnPjs source code, we need to build and generate local packages. You can get additional information of the PnPjs gulp commands on the official documentation.

Run the following commands in the order specified:

  1. npm install – to install the npm packages required by PnPjs
  2. gulp build – to ensure that the solution can build successfully
  3. gulp package – to generate all the different library packages, including the new Project package

Peer dependencies

We are going to install the local Project package that we have just build in our SPFx solution. Unfortunately, the peer dependencies don’t seem to work as expected when you do so and you get errors when trying to use the packages on your web part.
If you inspect the package.json file for the Project package created (dist/packages/project), you can find the following required peerDependencies which are not resolved by default

  "peerDependencies": {
        "@pnp/common": "1.3.7",
        "@pnp/logging": "1.3.7",
        "@pnp/odata": "1.3.7"

Not the ideal solution for sure, but a simple way to get around this problem is to simply install the packages yourself.
Remember that we are only testing the new Project package and all the others are kept untouched, so seems sensible to me to install them directly from npm.
Ensure that your command prompt is on the
dist/packages/project directory and run the following command

npm install @pnp/common@1.3.7 @pnp/logging@1.3.7 @pnp/odata@1.3.7

The Project package is now ready to be consumed by our solution.

Create new SPFx solution

I’m not going to provide any specific instructions here as the official documentation is excellent and give you all the information you need. If you are new to SharePoint Framework development, please check it out and learn how to get started.

Add PnPjs packages to SPFx solution

Again, remember that Project is the only package that is not published to npm, so is the only one that we need to install from a local path.
Start by installing all the common PnPjs packages that you usually install. In this case I’m using the 1.2.5 version as it’s the version that matches the local version.

npm install @pnp/common@1.3.7 @pnp/logging@1.3.7 @pnp/odata@1.3.7 --save-exact

Next, install the local Project package by providing a relative path to the package folder. In my example:

npm install ../PnPjs/dist/packages/project

Establish Context

Following the official guidance, we also need to establish the context when using the library in SPFx. Simply add the following block of code into your web part main file as provided on the documentation:

import { project } from "pnpjs-project-online-package"; // or import from your local package for development

// ...

public onInit(): Promise<void> {

  return super.onInit().then(_ => {

    // other init code may be present

      spfxContext: this.context,
      project: {
        baseUrl: ''

// ...

Please note that we are setting the baseUrl property to be the PWA site. This is to allow the solution to work from any SharePoint sites, not only Project Online.

Use it!

It’s all done and you can now use the fluent library to interact with project online!
All you need to do is to import the Project package when you need it

import { project } from "@pnp/project";

Some usage examples:

// get all projects
const projects = await project.projects.get();

// get projects, filtering by name, returning only the Id and Name properties, and limiting the results count to 1
const projectInfo = await project.projects.filter(`Name eq '${projectName}'`).select('Id,Name').top(1).get();

// create timesheet
const createResult = await project.timeSheetPeriods.getById('XXXXXXXXXXXXXXXX').createTimeSheet();

Resources from my session at SPS Leicester 2018

SPS Leicester

Last weekend, I had the pleasure of speaking at SharePoint Saturday Leicester (SPS Leicester). Very well organized event and with a good number of attendees, especially considering that it was the first event.

You can find the slides from my presentation below.

All the code is available under my GitHub account here and there is a readme file with all the steps that I covered during my demo here.

Please get in touch or leave a comment below if you have any issues or questions.

PnP PeoplePicker reusable control disabled

The PnP PeoplePicker reusable control is one of the amazing reusable controls available from the open-source @pnp/spfx-controls-react project. You can easily include it in your SharePoint Framework projects. Unfortunately, and at the moment, there is a bug that prevents you from completely disabling the control.

Continue reading “PnP PeoplePicker reusable control disabled”

SPS Barcelona – Resources from my session

Had an amazing time at SharePoint Saturday Barcelona (SPS Barcelona) last weekend! The event was very well organized and full of really nice sessions to attend. Really hope that everyone who attended had a great time.

You can find the slides from my presentation below.

All the code is available under my GitHub account here. A readme file contains all the steps that I covered during my demo here.

Please get in touch or leave a comment below if you have/find any issues.

If you are one of the persons who attended my session, thank you!

SPS Barcelona is a great event! But you don’t need to take my word for it, go next year and see that for yourself!

PnP TaxonomyPicker reusable control as a required field

The PnP TaxonomyPicker reusable control doesn’t have a property to let you mark the input control as a required field on a form, but fortunately, this can be easily addressed.

If you have used the PnP TaxonomyPicker reusable control before, you may have noticed that it doesn’t have a property to make it required, nor does it have a property that lets you add a custom CSS class to it. The problem is that your other required input controls on the form will have a ‘*’ after the label, but not the TaxonomyPicker controls.

But there is a very simple way to solve this, because the control also has a Label control, so we can mimic the styles from other Office UI Fabric input controls.

Simply create a new CSS rule that includes a class and targets a label as a child item

.required label::after {
    content: " *";
    color: rgb(168, 0, 0);
    padding-right: 12px;

Now add a new element wrapping the TaxonomyPicker (a div, for example)


And now your TaxonomyPicker field will look exactly like the other required fields.  


  All you have to do now is implement validation.    

Setting the field as required may be an option in the near future, but until then, this is a very simple workaround

Are you also using the PnP PeoplePicker control? Then you may want to check this blog post.

Bulk upload/install SharePoint solutions

A few days ago I was asked for a way to bulk upload and install multiple SharePoint solutions. In a simple and quick way that wouldn’t require much user interaction/scripting or experience.

With this in mind, I have created a simple PowerShell script using PnP PowerShell that reads information from a CSV. The script starts by uploading and deploying all the apps to the SharePoint app catalog. Once this step is completed, it then connects to the target site to install the solutions.

The script is very basic and only accounts for this simple scenario, which was my requirement. But it can be easily extended even if you don’t know much of PowerShell.

For instance, if you want the target site to be configured per app, simply add a new column to the CSV file and connect to that site before adding the app. Another scenario could be that you need your app to be globally available when deployed (for example an SPFx web part to be available across the tenant), and in this case, you would need a new CSV column to define that and add the property in the Publish-PnPApp command. You get the point…

The code is available on the following GitHub repository: Deploy Addins


To use this tool you only need to create your CSV file with the list of app names and titles, copy your app files to the packages folder and run the PowerShell script provided. The repository contains a sample CSV file that is using a solution package from the SharePoint Starter Kit as an example (please don’t use this file as it’s not up to date!)

The script takes the following parameters:

  • appsFile – the name of the CSV file (example: apps.csv)
  • siteUrl – the absolute url of the target site where the apps will be installed (example:
  • appCatalogUrl – the absolute url of the tenant app catalog where the apps will be uploaded (example:

Now simply run the script and have fun 🙂

Target site for PropertyFieldListPicker control

The latest release of PnP Reusable property pane controls (1.8.0) adds an additional property (webAbsoluteUrl) to the PropertyFieldListPicker control. It allows a target site to be specified for loading the lists.

Information Architecture

Nowadays, the trend is to not use subsites as you can’t even create a modern SharePoint site as a sub-site. But in the past, the sub-site approach was commonly adopted by organizations when planning the Information Architecture of SharePoint sites. It was common to see global information stored within custom lists of the root web level, and surfaced on sub-sites.

A different approach was to use multiple site collections. Using one of the site collections as a central repository of information that would be consumed by the others.

The problem

If you have a project that follows one of the models described above, you were not able to use the PnP PropertyFieldListPicker control on your SharePoint Framework web parts to load data from a different site.

The PropertyFieldListPicker only had support to load the lists of the current site by default. It was not possible to specify a different target site.


The changes included in the 1.8.0 version introduce a new optional property (webAbsoluteUrl). It allows you to specify the web absolute URL of the target site to load the lists from.

This can be as simple as:


In this example we use the current web from the page context, which you really don’t need as that’s the default behavior, but you get the point.

One important thing to keep in mind is that you need to ensure that the users have permissions to access the list if the list is on a separate site/site collection.

My session at SharePoint Saturday Madrid

Last Saturday I had the pleasure to speak at SharePoint Saturday Madrid. You can find here all the materials used for the presentation: the presentation slides and the video recording. On the slides you can find links to the relevant resources and also a link to my GitHub repository that contains a working version of the demo and also a guide in case you want to recreate the demo yourself.

First, a special thanks to the organizers for giving me the change. It really was a pleasure to be part of such an amazing event. Everything was very well organised: constant updates during the days before the conference, speakers activities the day before (unfortunately my flight was delayed and I missed the first one), easy and quick way to gain access to the venue, excellent facilities, food, drinks, and the list goes on…

Second, a special thanks to CPS for supporting me and making this possible.

Now back to the session. Unfortunately the recording had some minor sound problems which were probably caused by how I positioned the microphone. I really hope these are not a major problem for you and that you can still understand it’s contents.

The presentation slides and recording are available below. If you would like to try the demo, you will find all the information on my GitHub repository: it contains a “final” folder with a working version and also a “start” folder that contains a guide to walk you through the demo steps .

Hope you find this useful! Use the comments below for any feedback.