Monday, 6 August 2018

Sync SharePoint User Profiles using Azure Durable Functions

Recently, a client had asked us to synchronise user properties from their Azure AD profile to custom properties in their SharePoint UserProfile. This had to be a scheduled process as the data had to be kept up to date as well as it had to cater for any new profiles created in Azure AD/SharePoint.

We decided to use Azure Functions for this given the ease of configuring a timer triggered function (to run on schedule) and also the fact that functions run on a consumption based billing plan. This means that the client would get charged only for when the function executes (oh and also, the first million executions are free every month)

The main challenge we had to overcome was the limitation that an Azure Function has a default timeout of 5 minutes (which can be increased up to 10 minutes at the time of this writing) This means that if we were using a single Azure Function to update SharePoint UserProfile Properties for thousands of users, we were going to hit the timeout sooner or later. 

Fortunately, Durable Functions went GA recently which means that we have a way of managing state in the traditionally "state-less" Azure Functions. With durable functions, we can create an "activity" function to update the SharePoint User Profile properties for a single user. This function can be called in a loop for each user from an "orchestrator" function. Each run of the activity function is treated as a single execution and can be finished in the 5 minute default timeout.

So let's see how this can be done! We are using precompiled C# functions and Visual Studio 2017 to achieve this. Also make sure to have the Durable Functions nuget package installed in your Azure Functions project:
https://www.nuget.org/packages/Microsoft.Azure.WebJobs.Extensions.DurableTask/

The Durable Function workflow can be categorised into three different types of functions:


1) Client Function


These are standard Azure Functions which can be triggered by external events like timers, HTTP requests, queues etc. The only difference being they have an OrchestrationClient binding which is required to start orchestrations.

In our case, the Client Function is a simple timer triggered function which uses the OrchestrationClient to start a new Orchestration Function with the name O_SyncProfileProperties


2) Orchestrator Function


As the name suggests, the Orchestrator function acts as a coordinator of the Durable Functions workflow. It does the job of starting, stopping and waiting for activity functions and is also in charge of passing data (or state) in between them.

In our case, it calls the A_GetUsersToSync activity function to get the user profiles from Azure AD (using the Microsoft Graph API which is out of scope for this article) and then loops through the users to call the A_UpdateSharePointProfile function for each user


3) Activity Functions


As you might have guessed by now the Activity function is the one which actually does all the heavy lifting work. For example, the actual CSOM code which will update the SharePoint UserProfile properties will live in the A_UpdateSharePointProfile activity function:

And that's it! In 3 simple steps, we have a Durable Functions Orchestration set up. For more information on Durable Functions including dos and don'ts, please see the documentation: https://docs.microsoft.com/en-us/azure/azure-functions/durable-functions-overview

Hope you found this post useful!

Monday, 2 July 2018

Using Managed Service Identity with Key Vault from a .NET Azure Function

So Managed Service Identity along with Azure Functions support went GA recently. If you want to read the announcement and also want to get an overview of MSI, head over here: https://blogs.msdn.microsoft.com/appserviceteam/2018/06/26/announcing-general-availability-and-sovereign-cloud-support-of-managed-service-identity-for-app-service-and-azure-functions/

In this post, lets have a look at how easy it is to configure Managed Service Identity for an Azure Function and how it can be used together with Key Vault to secure sensitive information like Client Ids, Client Secrets and Passwords.

For example, when building an Azure Function which will interact with some data in SharePoint Online, we need a way to authenticate the Function with SharePoint. Two common methods used for authentication are 1) By creating an Add-In registration in SharePoint (appregnew.aspx) and 2) By using Azure AD authentication by creating an App Registration in Azure AD.

In both cases, we need to secure the ClientID and ClientSecret for the registration in such a way that only our calling code has access and any non-admin user browsing the Azure Function in the portal is prevented from seeing the sensitive data.  So let's see how we can do that using Managed Service Identity:

1) They very first thing you need to do is make sure that Managed Service Identity is configured for your Function App. You can do this simply by going to Function App Settings -> Managed Service Identity and ensuring that it is turned ON.


2) Create a Key Vault (or go to an existing one) and create two Secrets with names "ClientID" and "ClientSecret". You can also create additional Secrets relevant to your solution here.



3) Now we want our Azure Function App to have permissions to access the Key Vault. To do this, go to Access Policies -> Add New


4) Select the Function App as the principal and make sure under Secret permissions, it has at least the "Get" permission:


5) That's it in terms of the config! Now let's move on to the code to access the secured Client ID and Secret:

Make sure your Azure Function has the following NuGet packages:

Microsoft.Azure.KeyVault
Microsoft.Azure.Services.AppAuthentication

And here is an HTTP triggered .NET pre-compiled function which fetches the ClientId and ClientSecret from the Key Vault:

That's it! I was really surprised how easy MSI support for Azure Functions makes securing sensitive data. This way, any keys, secrets or passwords used by our solutions can be secured and retrieved without worrying about them getting in the wrong hands!

Quick note on pricing for MSI and Key Vault:

Update (5th August 2018): You can also use MSI when debugging locally. The steps to achieve that are added in this tweet:


Saturday, 16 June 2018

Get/Set SharePoint Online Tenant properties with CSOM

SharePoint Online Tenant properties are key/value pairs which can be used to set custom configuration settings on the tenant. These properties can then be consumed by SharePoint Framework components or any other type of customisation.

Here is some quick code I have put together to work with SharePoint Online tenant properties using CSOM.

The important thing to note here is that when setting the properties, we can only use the context of an App Catalog site (either tenant level or site collection level)

When getting the properties, the context of any site can be used.

Set SharePoint Online Tenant Properties in the App Catalog using CSOM:



Get SharePoint Online Tenant Properties using CSOM:


Note: While working with this code, I noticed SharePoint Online Tenant properties are stored as a web property bag entry in the root site of the App Catalog site collection. The properties are serialized to a JSON string and stored in the property bag entry with the key "storageentitiesindex":

(click to zoom)

Monday, 14 May 2018

SPFx: Calling back to SharePoint from an AAD secured Azure Function on behalf of a user

This post is part of a series where we explore consuming Azure AD secured Azure Functions from SharePoint Framework components. Articles in the series:

1) SharePoint Framework: Calling AAD secured Azure Function on behalf of a user
2) SharePoint Framework: Calling Microsoft Graph API from an AAD secured Azure Function on behalf of a user
3) Calling back to SharePoint from an AAD secured Azure Function on behalf of a user (this post)

The functionality shown in the post is preview only and not be used in production at this time.

In the previous post, we were successfully able to call the Microsoft Graph API from an AAD secured Azure Function and return data back to the SharePoint Framework web part.

Now in this post, lets see how we can make a call back to SharePoint on behalf of the logged in user from the Azure Function. We will only focus on the code in the Azure Function here, to fully understand the set up and auth process, I recommend you check out the previous posts in the series.


Updates to the Azure AD app registration:


In order to make a call back to SharePoint, we will need to add a Client Secret to the Azure AD app registration. Skip this step if you have already done this as part of the previous post.



We will also need to add the Office 365 SharePoint Online permissions scope to the Azure AD app registration. For this post, I am selecting the "Read and write items in all Site Collections" delegated scope. You can select the scope according to the operations you want to perform in SharePoint


Don't forget to grant the permissions again as a subscription admin. This is so that each user does not have to do this individually:


Here is what we are going to do in the code:

When the Azure Function executes, we already have an access token sent by the SharePoint Framework AadHttpClient in the Authorization header. This access token has the "user_impersonation" scope which only allows it to access the Azure Function. It cannot be directly used to call back to SharePoint.

In order to obtain new access token that will work with SharePoint, we will have to request it using the existing access token.

Once this function is called from the SharePoint Framework, we are able to get the data back to the web part:


Thanks for reading!

SPFx: Calling Microsoft Graph API from an AAD secured Azure Function on behalf of a user

This post is part of a series where we explore consuming Azure AD secured Azure Functions from SharePoint Framework components. Articles in the series:

1) SharePoint Framework: Calling AAD secured Azure Function on behalf of a user
2) Calling Microsoft Graph API from an AAD secured Azure Function on behalf of a user (this post)
3) SharePoint Framework: Calling back to SharePoint from an AAD secured Azure Function on behalf of a user

The functionality shown in the post is preview only and not be used in production at this time.

In the previous post, we were successfully able to call an AAD secured Azure Function from a SharePoint Framework web part.

Now once we are in the Function, lets see how to make a call to the Microsoft Graph on behalf of the logged in user. We will only focus on the Azure Function here, to fully understand the set up and auth process, I recommend you check out the previous post.



Updates to the Azure AD app registration:


In order to call the Microsoft Graph, we will need to add a Client Secret to the Azure AD app registration.



We don't need to add any new permission scopes for the purposes of this post as we are going to make a call to the /v1.0/me endpoint which requires the User.Read permission. According to the SPFx docs, if we exchange the SPFx generated token for a MS Graph token, it will automatically have the User.Read.All permission scope.

If you want to do anything beyond this with the Microsoft Graph, you will need to add the relevant permissions scope and grant permissions to it. In the next post, we will see how to do this by granting permissions to the "Office 365 SharePoint Online" permissions scope.

Here is what we are going to do in this post:

When the Azure Function executes, we already have an access token sent by the SharePoint Framework AadHttpClient in the Authorization header. This access token has the "user_impersonation" scope which only allows it to access the Azure Function. It cannot be directly used to call the Microsoft Graph.

In order to obtain new access token that will work with the Microsoft Graph, we will have to request it using the existing access token. Once we have the new token, we are able to make a call to the Microsoft Graph:

Once this function is called from our SharePoint Framework web part, we are able to get data back from the graph:


Thanks for reading!

Thursday, 12 April 2018

Azure Functions: Add a message to a storage queue after a delay

I was looking at a specific problem today dealing with Azure Functions and adding messages to storage queues: I needed my Function to add a message to an output queue in order to trigger another function, but the challenge was, the message should not get added immediately and the second queue trigger function should not fire immediately as well.

We needed a delay between the completion of the first function and execution the second queue triggered function.

A not-so-great way of achieving this would be to add a Thread.Sleep before the message is added to the output queue but we really wanted to avoid that as it would keep the function running and would not be a truly "serverless" way of doing things.

I had a look at the visibilityTimeout setting in the host.json file but turns out it is meant for configuring the delay after which a retry is made when a function fails: https://docs.microsoft.com/en-us/azure/azure-functions/functions-host-json#queues

Fortunately, Jeff Hollan on twitter suggested a nice solution for this which utilised the initialVisibilityDelay property of the CloudQueue.AddMessage function. The great thing is this works seamlessly with Azure Function output bindings so we don't have to use the SDK ourselves.

Here is a sample of how my final code looks and it works pretty great!

Hope you find this useful!

Saturday, 31 March 2018

Working with SharePoint Online Hub sites using CSOM

With SharePoint Online Hub sites launched for Targeted release tenants, here is some quick code I put together to work with them using CSOM:

1) Register a Hub site, Connect a site to a Hub site, Disconnect a site from a Hub site and Unregister a Hub site:



2) Grant and Revoke specific users rights to connect sites to a Hub site:


When a Hub site is registered, it is public by default. Any user is able to connect their site to the hub site. If you want only a specific set of users to be able to connect their site to the Hub site, you can grant "Join" rights to these users:

Hope this helps!