20 Jun 2020
Why DevOps?
As a professional in software development industry, only experts in one particular technology or language is not enough. We have to understand the whole SDLC to be able to deliver the end to end solutions. DevOps is one of the key parts in the life cycle, it can improve the productivity of both development and operational teams, it’s easier to adopt Agile methodology.
Nowadays, those popular topics e.g. DevSecOps, Infrastructure as Code, Docker, Kubernetes are also included in Azure DevOps, it becomes the one stop portal that can manage the source repositories, projects (epic, user story, task and bug), CI / CD pipelines, artifacts and test plans.
Free Certification from Microsoft Build 2020
Thanks for COVID-19, Microsoft Build 2020 was held 48 hours continuously online, therefore I could simply register and attend the global event from home. If you finish one of the challenges, you will get a free chance for one of the Microsoft certificates.
Useful resources to prepare the exam
Other useful resources
NOTE: I passed the exam before 15 of June 2020, now the content has been updated, please download the up to date skills outline.
13 Dec 2019
In order to register a webhook on SPO list, I need to subscribe from that list. For example:
POST /_api/web/lists('5C77031A-9621-4DFC-BB5D-57803A94E91D')/subscriptions
Accept: application/json
Content-Type: application/json
{
"resource": "https://contoso.sharepoint.com/_api/web/lists('5C77031A-9621-4DFC-BB5D-57803A94E91D')",
"notificationUrl": "https://91e383a5.ngrok.io/api/webhook/handlerequest",
"expirationDateTime": "2016-04-27T16:17:57+00:00"
}
How can I trigger this post without having the access token? I don’t have permission to get the details of AAD, so it’s impossible to acquire access token and attach to the http post request using Postman.
Then I found a useful Chrome extension called “SP Editor” which can list, create or delete webhooks by sharing my SharePoint cookies in browser. It’s much easier to manage webhooks this way.
I got anther obstacle when I tried to add one simple hook. It gave me this error “Failed to validate the notification URL”. In my mind, as long as my function returned 200, the subscription should be able to register. However, it turned out that SharePoint will check if the destination Url can return the same token before creating subscription.
I just updated my code with one more “if” and return the validation token at the beginning of function.
[FunctionName("SharePointWebhook")]
public async Task<IActionResult> SharePointWebhook(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
ILogger log)
{
// Validation from Webhook of SharePoint
string validationToken = req.Query["validationtoken"];
if (!string.IsNullOrEmpty(validationToken))
{
return new OkObjectResult(validationToken);
}
......
}
Code at Github: https://github.com/jay-coder/AZFuncSPO
13 Dec 2019
I had been shocked by SharePoint that not all the components have restful API yet, Term Store is one of them. There is a dodgy way to consume Term Store via list called TaxonomyHiddenList, however, the JSON returned by API only contains one field “Path” that can be utilized to generate tree. Moreover, Path is combined by title (not ID), like “Parent Title:Title:ChildTitle”. Because title is not a unique key, it can cause issue to build the wrong hierarchy if titles are the same.
After discussing with our SharePoint expert, he could build a web part in SharePoint to convert the taxonomy to list, but there is no way to trigger the web part automatically in the latest version of SPO, the platform has been upgraded to use React JS rather than the classic web control now. So, there is no better way than calling the “beautiful” CSOM dll, and it’s the only way I can retrieve Term’s parent to build the hierarchy.
Fortunately, Microsoft supplied the portal dll for taxonomy in netcore45, I just need to include those 2 dlls: Microsoft.SharePoint.Client.Taxonomy.Portable.dll Microsoft.SharePoint.Client.Portable.dll
// SharePointTermStore.cs - get Terms by ClientContext
using (var ctx = new ClientContext(collectionUrl))
{
ctx.Credentials = new SharePointOnlineCredentials(username, password);
var taxonomySession = TaxonomySession.GetTaxonomySession(ctx);
var termStore = taxonomySession.GetDefaultSiteCollectionTermStore();
ctx.Load(termStore,
s => s.Groups.Include(
g => g.TermSets.Include(
ts => ts.Name
)
)
);
await ctx.ExecuteQueryAsync();
_termStore = termStore;
}
At last, I managed to get the correct descendants of Term Store, even with “Custom Sort Order” enabled.
Code at Github: https://github.com/jay-coder/AZFuncSPO
13 Dec 2019
First of all, I am not a SharePoint expert, if there is any incorrect information, please let me know.
I faced 2 challenges when I tried to consume SPO list restful API.
-
Which packages shall I use?
I googled a few blogs, most of them were written before 2019, which means they were using Azure Functions v1.0 (.NET Framework instead of .NET Core), so that they can add “Microsoft.SharePointOnline.CSOM” nuget package without any problems.
However, when I added that package into my project, I received this incompatible warning:

Someone mentioned in Stack Overflow that TTCUE.NetCore.SharepointOnline.CSOM.16.1.8029.1200 package works well on .NET Core application. It is a wrapper of Microsoft dlls that copying from Microsoft.SharePointOnline.CSOM package\lib\netcore45, I grabbed the latest dlls from Microsoft.SharePointOnline.CSOM version 16.1.19515.12000 to my lib folder and added references from there. Then the warning disappeared.
-
How to authenticate?
Most of the related blogs were written based on the fact that Azure AD admin can create an AAD app, grant permissions to the whole instance of SPO and supply the secret. However, in our scenario, I was only allowed to get a service account (username / password) and limited to a particular collection.
The basic authentication of SharePoint is much uglier than what I thought, it’s cookie based not token. I have to call GetAuthenticationCookie method to generate cookie from Microsoft.SharePoint.Client.dll (the one in net45 folder - not compatible with core) as Microsoft.SharePoint.Client.Portal.dll hasn’t implemented such function yet. I have to de-compile the Microsoft.SharePoint.Client.dll and copy them to my own classes.
// Startup.cs - manage HttpClient by Polly
var authBuilder = new SharePointAuthenticationBuilder(config);
builder.Services.AddHttpClient("SharePoint").ConfigureHttpMessageHandlerBuilder((b) =>
authBuilder.BuildHttpMessageHandler(b)
)
.SetHandlerLifetime(TimeSpan.FromMinutes(5))
.AddPolicyHandler(GetRetryPolicy());
// SharePointAuthenticationBuilder.cs - set cookies to HttpClient
public void BuildHttpMessageHandler(HttpMessageHandlerBuilder builder)
{
Uri uri = new Uri(BaseUrl);
var credentials = new SharePointOnlineCredentials(Username, Password);
var handler = new HttpClientHandler()
{
Credentials = credentials
};
handler.CookieContainer.SetCookies(uri, GetAuthenticationCookie(uri, true, false));
builder.PrimaryHandler = handler;
builder.Build();
}
These are the necessary dlls:
- Microsoft.SharePoint.Client.Runtime.Portable
- Microsoft.SharePoint.Client.Runtime.Windows
NOTE: SharePoint is heavily relying on Windows, and so to its dlls. Thus the Azure Functions can ONLY be deployed to Windows and Microsoft.SharePoint.Client.Runtime.Windows.dll (from net45 folder) must be referenced in Azure Functions project.
Code at Github: https://github.com/jay-coder/AZFuncSPO
03 Sep 2019
I recently encountered an issue that the CM site always retrieve data from production Solr (sitecore_sxa_web_index). Which means our marketing managers could not verify their changes on authoring environment until they publish contents to production.
That’s not ideal, why the out of box “Search Results” component not pull data from authoring Solr (sitecore_sxa_master_index)?
I tried to de-compile the SXA dlls but with no luck. Then I found https://doc.sitecore.com/developers/sxa/17/sitecore-experience-accelerator/en/configure-sxa-indexing.html via Google.
After applying Site Grouping > [Site] > Indexing > Indexes to authoring site, it works, hooray!

Then I applied the similar thing for production site.

However, I found another “bug” in “JSON Results” component which tends to ignore the “Indexes” I set as above and pulls data from production still.

I raised this bug to Sitecore support. Hopefully they can supply a hotfix for it.