Karine Bosch’s Blog

On SharePoint

Working with Microsoft Graph to access o365 Planner

I’m currently working on a project where I have to migrate classic SharePoint tasks lists to Planner in O365. It cannot be done using CSOM, but there is a Planner REST API you can use.

This post does not contain new stuff, but I had to read trough hundreds of posts to get it all together. Therefore, I decided to write a post for whom it may be helpful.

I wrote a simple MVC-based Azure web site to do the work, nothing fancy, as it is just to migrate existing tasks lists. But you cannot communicate with your O365 tenant through Graph using SharePointOnlineCredentials. Therefore, I first registered an Azure App to have a ClientID/ClientSecret combination:

The Redirect URL is the URL to where I deployed my Azure web site (which is not described in this blog post)

Planner is part of O365 groups, so you have to give this Azure App at least read permissions to the O365 groups in your tenant. Via the Required permissions setting, I can give access to my application to as many APIs as available.

I selected the Graph API, and gave my application the permission to read from site collections and to read/write O365 groups:

I also had to click the Grant Permissions button. As an you then consent to an application’s delegated permissions on behalf of all the users in your tenant.

Read more Azure App registrations.

This gives me the permission to request data about all O365 groups in my tenant.

Now it is coding time ūüôā

First I need to  get an access token based on the client ID and client secret of the Azure App I just registered:

private async Task<string> GetAccessToken(string resourceId)
        var authority = ConfigurationManager.AppSettings["ida:AuthorizationUri"] + ConfigurationManager.AppSettings["ida:TenantId"];
        var clientCredential = new ClientCredential(ConfigurationManager.AppSettings["ida:ClientId"],

        AuthenticationContext ac = new AuthenticationContext(authority);
        AuthenticationResult result = await ac.AcquireTokenAsync(resourceId, clientCredential);

        return result.AccessToken;
     catch (Exception ex)
         // TODO: log the exception
         return null;
  • The AuthorizationUri is¬†https://login.windows.net/
  • The TenantId is the ID of your O365 tenant. If you don’t know the ID, you can find it in your Azure portal: navigate to Azure Active Directory > Properties, and in the Map ID, you’ll find your tenant ID
  • The ResourceId is the Graph URI:¬†https://graph.microsoft.com

Tip: You can verify if you have a valid access token by going to https://jwt.io/

Next step is to retrieve the O365 group:

 string restUrl = string.Format("https://graph.microsoft.com/v1.0/kboske.com/groups?$filter=displayname eq '{0}'", groupName);
 Task<string> getGroupTask = Task<string>.Run(() => GetResponse(restUrl, accessToken));

If you get a successful response, you will get something like this:

My GetResponse method is very basic, but I add it in here for completeness:

private async Task<string> GetResponse(string restUrl, string accessToken)
    string jsonresult = null;

       using (HttpClient client = new HttpClient())
          var accept = "application/json";
          client.DefaultRequestHeaders.Add("Accept", accept);
          client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
          using (var response = await client.GetAsync(restUrl))
              if (response.IsSuccessStatusCode)
                  jsonresult = await response.Content.ReadAsStringAsync();
                   throw new Exception("Error getting data: " + response.StatusCode.ToString());
    catch (Exception ex)
       // TODO: handle the exception
    return jsonresult;

Once I get the group Id, I can get to the plan, using the following REST call, no?

restUrl = string.Format("https://graph.microsoft.com/v1.0/recomatics.com/groups/{0}/planner/plans", groupId);

No, you can not!! You get a

But when I try to access Planner, I get a 401 РUnauthorized error instead.

It seems you can only access the plans by using user credentials because you can only access the plans to which you have permissions.

I changed the GetAccessToken method, in order to authenticate with my user credentials:

private async Task<string> GetAccessToken(string resourceId, string userName, string password)
        var authority = ConfigurationManager.AppSettings["ida:AuthorizationLoginUri"] + ConfigurationManager.AppSettings["ida:TenantId"];
        var authContext = new AuthenticationContext(authority);
        var credentials = new UserPasswordCredential(userName, password);
        var authResult = await authContext.AcquireTokenAsync(resourceId, ConfigurationManager.AppSettings["ida:ClientIdNativeClient"], credentials);

        // Get the result
        return authResult.AccessToken;
    catch (Exception ex)
       // TODO: handle the exception

But then I get a different error:

"AADSTS70002: The request body must contain the following parameter: 'client_secret or client_assertion'."

That’s because my Azure App only accepts client id / client secret combination because I registered it as a Web service.¬†To be able to authenticate using my user credentials, I have to register an Azure App as a Native client:

When I now call the REST endpoint to get to the plan of my O365 group, I get a successful response:

Tip: try out your REST calls in the Graph explorer

Helpful posts:


December 18, 2017 Posted by | CSOM, Graph | Leave a comment

Format your columns with JSON

Recently, Microsoft introduced the possibility to customize the formatting of fields using JSON. Currently this is only available in SharePoint Online.

Do know that there is a difference between Column Formatting and the Field Customizer extension of the SharePoint Framework:

  • The Field Customizer is more powerful, because you can use it to write code to control how a field is displayed.
  • Column formatting is more easier but less flexible, because it does not allow for custom code; it only allows for certain predefined elements and attributes.

When you create a site columns or a field on a list or library, you can specify your custom format in the Column Formatting section of the Field settings page.

If the field already exists and you want to apply additional column formatting, you can click the arrow next to the column and choose Column settings > Format this column. The pane on the right of the page will allow you to apply your custom format:

You can use column formatting to apply styles, classes, and icons to fields, depending on the value inside those fields. Column formatting is done using JSON.

You can learn more about the JSON you can use to format fields here:

To preview the formatting, select Preview; to commit your changes, select Save. When you save, anyone who views the list will see the customization that you applied.

This is how you do it when you configure your SharePoint fields in the browser. But if you provision your SharePoint artifacts in code, you can use the new Field attribute CustomFormatter to specify your custom formatting:

<Field ID="{b75d0647-1950-46ef-b4f5-dbf0608191b7}" DisplayName="Amount" Name="Amount" 
       Type="Number" Decimals="2" Percentage="FALSE"  
       CustomFormatter="{<your json>}" Version="1" />

Important to know is that, when a field is deployed this way, the CustomFormatter will only be applied in the modern list view; it will not be applied when the list is in classic view.

I tested this on a document set with a column that is shared with its underlying documents. I created the following content types:

  • a content type “Insurance Dossier” based on the Document set content type
  • a content type “Contract” based on the Document content type

Both content types share the same site column “Balance”.

The first picture shows the modern view:

The second picture shows the classic view (modern view is not yet possible within a document set):


November 24, 2017 Posted by | O365, SharePoint Online | Leave a comment

Making custom list forms with PowerApps

Finally, the long awaited feature to create custom list forms with PowerApps has been released last week!

Custom list forms could be handy when you are facing one of the following scenarios:

  • When you need extra validation of the input data.
  • When you need to visualize certain fields differently. For example, if you need to specify a location, you could visualize it with the Bing control.
  • When you there is a need for cascading dropdowns (list options in a second dropdown must be limited based on the selection made in a first dropdown.
  • When there is a need to hide or show certain sections on the form, based on a selected option

It is very simple to start the creation of a custom form: on the list view, select the PowerApps button and choose Customize forms:

This will open the PowerApps canvas where you can start customizing your form.

There is also a change in the List Settings to give you the possibility to switch between the default forms, InfoPath forms and PowerApps forms: go to List Settings > Forms settings:

To start with this feature, you can read the following:



This feature is only available in SharePoint Online, and only for lists; not (yet) for document libraries.

November 22, 2017 Posted by | PowerApps, SharePoint Online | Leave a comment

SharePoint Online – Permanent links to documents

Sometimes I get the question how you can reference a document stored on SharePoint from within a Word document, or from within CRM, without risking to end up with a broken link, even after the document is moved or renamed.

You can achieve this by activating the Document ID Service feature, which is a site collection feature. This feature already exists for a long time in SharePoint, but as this question comes up regularly, I decided to write a post about it.

This feature assigns a unique id to each document. If you now go to a document in a document library, and you click the Copy link button,

you get a URL similar to this:


If you rename a document, or move it, this URL still works. Also URLs as follows will work, as long as the URL is a valid URL to an existing SharePoint site, and as long as this SharePoint site has the Document ID service feature activated:


If you look at the URL of the opened document, you will see something like this:


You can even get to the document using your OneDrive URL, (but again, the Document ID service feature needs to be activated on your OneDrive, which is a site collection):


If you delete the site or the site collection afterwards, the URL will not work anymore, even if you moved the document to another location. Therefore I advise my users to use the URL of the root SharePoint site:


As long as the document exists in one of your SharePoint Online sites, you will be able to reference it this way.

For the record, in SharePoint 2016 we have durable links. You can find a good blog post on durable links in SharePoint 2016 here. As far as I know, this is different from SharePoint Online. If you know of a different way for SPO, please leave a comment.

November 21, 2017 Posted by | O365, SharePoint Online | Leave a comment

Adding Enterprise Keywords to your lists with CSOM

You can find a lot of posts that show you how to add the Enterprise Keyword to your lists and libraries:

$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)
$ctx.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
$sprootweb = $ctx.Site.RootWeb
$taxKeywordField = $ctx.Site.RootWeb.Fields.GetByInternalNameOrTitle("TaxKeyword");
$list = $sprootweb.Lists.GetListByTitle("Documents");


This code snippet works fine if you have no or only one content type on your list or library. But if you have more content types attached to the list or library, only one content type is correctly modified, and it’s not always the default content type. You have to add the Enterprise Keyword column to the other content types yourself:

# get the Enterprise keyword from the list
 $field = $list.Fields.GetById($taxKeywordField.Id)

# add the Enterprise Keyword to all content types
 $cts = $list.ContentTypes
 foreach ($ct in $cts)
    # get all fields associated with the content type
    $fieldRefCollection = $ct.FieldLinks 
    # add a FieldLink for the Enterprise Keyword
    $fieldLink = New-Object Microsoft.SharePoint.Client.FieldLinkCreationInformation 
    $fieldLink.Field = $field

And it’s the same story with the views: the Enterprise Keyword is not automatically added to the list views.

 # add the enterprise keyword field to all views
 $views = $list.Views

 foreach ($view in $views)

May 1, 2017 Posted by | CSOM, SharePoint 2013, SharePoint Online | Leave a comment

New version of the CAML Designer Released!

Andy Van Steenbergen and I finally released a new version of the CAML Designer; and this is a version we are very proud of!

This version of the CAML Designer now works on SharePoint 2007, SharePoint 2010, SharePoint 2013, SharePoint 2016 and SarePoint Online; AND it supports multi-factor authentication for SharePoint Online!


As you can see we changed the way you can connect to SharePoint: there are only 2 ways to connect:

  • through the client-side object model (CSOM) for SharePoint versions 2010, 2013, 2016 and O365
  • through the web services to be able to connect to SharePoint 2007

Yes, you read it correctly! The CAML Designer now works on ALL versions of SharePoint¬†(except for SharePoint 2001)! We didn’t test the connection to SharePoint 2003, but I bet it will also work.

Another cool and much desired functionality we added to the CAML Designer, is the possibility to connect to SharePoint Online through multi-factor authentication.

The snippets are still generated for:

  • raw CAML
  • Server object model
  • CSOM
  • REST
  • Web Services
  • PowerShell

You can download the tool from the BIWUG site. There you will find a link to download the application.

A detailed explanation on how to use the tool can be found here.

Have fun with it and please, don’t hesitate to give us your feedback at camlfeedback@biwug.be !!

April 27, 2017 Posted by | SharePoint 2010 | Leave a comment

Creating fields using CSOM in SharePoint 2013

Recently I had a reader who asked how to¬†to configure a calculated default value like “=YEAR([Today])”. This is not possible within one field as you cannot use [Today] or [Now]¬†within the formula of¬†a calculated field. The only way to solve this is to create 2 columns:

  • A DateTime field with default value Today. You can set this to hidden if you don‚Äôt want your users to see it.
  • A calculated field that outputs the year of your DateTime field.

Here is the code snippet:

private static void CreateCalculatedFieldBasedOnToday(ClientContext context)
     Web web = context.Web;
     List list = web.Lists.GetByTitle("CSOMFields");

     // Create a DateTime field that yields to Today
     string schemaTodaysDate = "<Field ID='{297B3AA2-85AD-408D-8346-0B64721C8090}' Type='DateTime' Name='TodaysDate' StaticName='TodaysDate' DisplayName='TodaysDate' Format='DateOnly' Hidden='FALSE' >"
           + "<Default>[Today]</Default></Field>";
     Field todaysDateField = list.Fields.AddFieldAsXml(schemaTodaysDate, true, AddFieldOptions.AddFieldInternalNameHint);

     // Create a Calculated field that displays the Year of the Today field
     string formula = "<Formula>=YEAR(TodaysDate)</Formula>";
     string schemaCalculatedField = "<Field ID='{446A6933-1751-474D-A407-9EE0250C708B}' Type='Calculated' Name='TodaysYear' StaticName='TodaysYear' DisplayName='Todays Year' ResultType='Number' Decimals='0' Required='TRUE' ReadOnly='TRUE'>" + formula + "</Field>";
     Field calculatedField = list.Fields.AddFieldAsXml(schemaCalculatedField, true, AddFieldOptions.AddFieldInternalNameHint);

Update: Steve Moucheron sent me his code snippet in which he solves in one go:

string fieldXml = "<Field DisplayName='Year' Type='Text'>"
   + "<DefaultFormula>=CONCATENATE(YEAR(Today))</DefaultFormula>"
   + "</Field>";
Field field = list.Fields.AddFieldAsXml(fieldXml, true, 

Read more on creating fields using CSOM.

January 13, 2017 Posted by | CSOM, SharePoint Online | Leave a comment

What if your sandbox solution contains a Visual WebPart?

As already mentioned in previous post, Microsoft decided to stop support for coded sandbox solutions on SharePoint Online. Developers will have to do the necessary effort to migrate the impacted sandbox solutions into solutions that comply with the recommended coding practices. You can find a lot of code samples and recommendations on the PnP site.

If your sandbox solutions contains a Visual WebPart, you will have to take action, because a Visual WebPart generates an assembly.In general, a Visual Web Part consists of the following:

  • an .ascx file with UI elements like HTML controls and ASP.NET controls
  • an .ascx.cs code behind file with some business logic that interacts with the UI and other data in the SharePoint site
  • a .webpart file that contains the definition of the web part (i.e. title of the web part, description, full name of the assembly, etc)

You could try to replace all ASP.NET controls by HTML controls, and develop the business logic in JavaScript and JSOM or REST. Now you don’t need that DLL anymore, right? Now you can get rid of the the assembly by setting the Include Assembly in Package project property to false in Visual Studio, right?


Wrong! You can even try to remove the <%@Assembly> directives and the <%@Register> directives from the .ascx control:

visual webpart

Even if your Visual WebPart contains nothing else than HTML and JavaScript, that assembly is generated. This is because a Visual WebPart consists of an .ascx control. In SharePoint on premises environment, an .ascx control gets deployed to the /TEMPLATE/CONTROLTEMPLATES folder; but this is not the case with sandbox solutions. The .ascx control gets compiled into the DLL, meaning that all UI elements defined on the .ascx control are generated in code.

Trying to force things by removing the metadata element from the .webpart file, will result in an import error when the page loads:

webpart definition

In case of a complex Visual WebPart, your best option is to create a SharePoint Add-in Part:

  • A SharePoint hosted add-in consisting of HTML, JavaScript and using JSOM or REST to communicate with the host SharePoint site
  • A provider hosted add-in represented by an MVC application using CSOM to communicate with SharePoint

In case of less complex Visual WebPart, you could choose to replace it by embedding JavaScript

But all these approaches require you to go to each page that hosts the old Visual Web Part to replace it by the new development. This can be quite a challenge for large O365 tenants.

Another approach is to keep your .webpart file and change it to point to a JavaScript file. The advantage of this approach is that pages hosting your old Visual WebPart will now automatically host your new web part.

What are the steps you have to take to make this approach work?

  1. Write the JavaScript to replace the logic of your existing Visual Web Part
  2. Point the .webpart file to this JavaScript file
  3. Deactivate and remove the old sandbox solution
  4. Upload your JavaScript file into the Style Library of your site collection
  5. Upload the new .webpart file into the WebPart Gallery

Following image shows how you can modify the .webpart file:

  • Change the <metadata> tag to point to the ScriptEditorWebPart
      <type name="Microsoft.SharePoint.WebPartPages.ScriptEditorWebPart, Microsoft.SharePoint, Version=, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      <importErrorMessage>Cannot import the migrated Demo Visual Web Part.</importErrorMessage>
  • Add a <property> element with name Content
  • Add a reference to your JavaScript file
  • Add a <div> element that will be the container of the UI that you have to develop in the JavaScript file

webpart definition pointing to js

In your JavaScript you will have to retrieve this <div> element, to be able to embed the rest of the UI.

You can even automate steps 4 and 5. You can upload the JavaScript into the Style Library using the following CSOM code:

        private static void UploadFileToStyleLibrary(ClientContext ctx, string sourcePath, string styleLibFolder, string jsFileName)
            // get the Style Library
            List styleLibrary = ctx.Site.RootWeb.GetList(ctx.Url + "/Style%20Library");

            // upload the js file
            string fullSourcePath = sourcePath + jsFileName;
            if (!styleLibFolder.EndsWith("/")) { styleLibFolder += "/"; }
            string fullTargetPath = "/Style%20Library" + styleLibFolder + jsFileName;
            var fileCreationInfo = new FileCreationInformation
                Content = System.IO.File.ReadAllBytes(fullSourcePath),
                Overwrite = true,
                Url = fullTargetPath

            Microsoft.SharePoint.Client.File uploadFile = styleLibrary.RootFolder.Files.Add(fileCreationInfo);

You can upload the new .webpart file into the WebPart Gallery using the following CSOM code:

        private static void UploadWebPartFile(ClientContext ctx, string sourcePath, string wepartFileName)
            // get the WebPart Gallery
            List wpGallery = ctx.Site.RootWeb.GetCatalog((int)ListTemplateType.WebPartCatalog);

            // check if there is a file with the same name
                Microsoft.SharePoint.Client.File file = wpGallery.RootFolder.Files.GetByUrl(wepartFileName);

                // delete the file if it already exists
            catch { }

            // updload the webpart file
            var fileCreationInfo = new FileCreationInformation
                Content = System.IO.File.ReadAllBytes(sourcePath + wepartFileName),
                Overwrite = true,
                Url = wepartFileName

            Microsoft.SharePoint.Client.File uploadFile = wpGallery.RootFolder.Files.Add(fileCreationInfo);

If you prefer to work with a SharePoint Add-in to provision your files, you could follow the PnP approach for App Script Parts.

Good luck!

August 6, 2016 Posted by | SharePoint 2010 | Leave a comment

Microsoft stops support for coded sandbox solutions on SharePoint Online

On July 29th Microsoft announced that it starts with the removal of coded sandbox solutions. You can read the official announcement here. This type of sandbox solutions was already deprecated since summer 2014.

A coded sandbox solution contains a DLL. Sandbox solutions containing declarative XML (f.e. the definition of site columns and content types), CSS, JS, images, etc. continue to be supported.

It is recommended to start migrating coded sandbox solutions as of today! You can find a lot of alternatives and sample code based on recommended coded practices on PnP. Additional migration guidance has been published here.

If you’re not sure if your O365 tenant is impacted by this decision, you can run a script that generates a list of sandbox solutions that are installed on your tenant. The report also displays a flag that indicates whether your sandbox solutions contains an assembly or not.

It is possible that your sandbox solutions contains nothing but CSS, JS, images, etc; but that your sandbox solution is listed as containing an assemby. It is possible that your sandbox solution contains an empty assemby. In that case you can get rid of that assembly by simply setting the Include Assembly in Package property to false. This is a property on the Visual studio project itself:


This is also documented in this KB.

August 4, 2016 Posted by | O365, SharePoint Online | Leave a comment

The SharePoint Framework

Microsoft just announced the new development framework to develop against SharePoint. Don‚Äôt get worried, don‚Äôt throw your Add-ins through the windows, they are there to stay. ūüôā¬† The SharePoint framework is just another tool in your toolbox to to ease the client-side development of pages and (web) parts. This framework will be deployed to SharePoint Online. It is not sure yet if it will be released on SharePoint 2016 on premise, but anyway not on SharePoint 2013 on premise.

You can read more about this brand new SharePoint framework here.

The framework itself is not released yet, but you can start preparing to get started: start learning TypeScript and React, and become familiar with tools like node.js, Gulp, Yeoman, and Git. Waldek Mastykarz has just posted a nice writeup on all these tools and techniques. And more details can be found on the blog of Chris O’Brien.

As a side note, I had the chance to play with a “beta” version during the DevKitchen that was organized in Brussels, a few weeks ago. The SharePoint product group came over to Brussels to give us an introduction to this very new SharePoint framework. And together with the product group, the creme of the european SharePoint community came to Brussels to participate to this event. This was just a week before the explosions, AND in the neighbourhood of one of them. I still get goose bumps when I think about what could have happened to the SharePoint community.

Keep your eyes on the net, because you will see popping up a lot of posts the coming weeks and months ūüôā

Have fun!

May 5, 2016 Posted by | SharePoint Online | Leave a comment