Authenticate an Azure Function with Azure Active Directory

[This post is updated on 19th July 2020 to reflect the latest UI of both Azure Portal and Postman. I’d like to take this chance to correct some of my mistakes made in earlier post, as friendly pointed out by readers in the comment section.]

Today is the first working day of a new year. Today is the second half of year 2020 where I have been instructed to work from home for months. I thus decided to work on a question raised previously by the senior developer in my previous job back in 2018: How do we authenticate an Azure Function?

The authentication tool that I am using is the Azure Active Directory (Azure AD). Azure AD provides an identity platform with enhanced security, access management, scalability, and reliability for connecting users with all our apps.

Setting up Azure Function

The Azure Function that I’m discussing here is the Azure Function app with .NET Core 3.1 runtime stack and published as Code instead as Docker Container.

🎨 Creating a new Function that will be deployed on Windows. 🎨

The whole Function creation process takes about 2 minutes. Once it is successfully created, we can proceed to add a new function to it. In this case, we are going to choose a HTTP trigger, as shown in the screenshot below. We choose to use a HTTP trigger function because later we will show only authenticated users can get the results when sending a POST request to this function.

🎨 Creating a function which runs when it received an HTTP request. 🎨

Once the trigger is created, we will see that there is a default C# code template given which will return the caller a greeting message if a name is provided in the body of HTTP request (or through query string).

🎨 Default template code for HTTP Trigger. 🎨

HTTP Methods and Function Keys

Before we continue, there are a few things we need to handle. The steps below are optional but I think they are useful for the readers.

Firstly, by default, the Function accepts both GET and POST requests. If you would like to only allow POST request, changing only the C# codes above is not going to help much. The correct way is to choose the accepted HTTP methods for this Function under its “Integration” section, as shown in the screenshot below.

🎨 This shows where to locate the “Selected HTTP methods”. 🎨

In our case, since we will only accept POST request, we will tick only the POST option.

As you notice in the “Authorization level” dropdown which is right above the “Selected HTTP methods”, it currently says “Function”. Later we must change this but for now we keep it as it is. If you would like to manage the Function Key, or checkout the default one, you can find the keys in the “Function Keys” section of the Function.

Secondly, what is the URL of this Function? Unlike the previous version of Azure Function, the URL of the Function can be retrieved at both the Overview section and the Code + Test section of the Function. However, the URL in the Overview section has no HTTPS, so we will be using the HTTPS URL found in Code + Test, as shown in the screenshot below.

🎨 Getting the function URL (in HTTPS). 🎨

Now if we send a GET request to the Function, we shall receive 404 Not Found, as shown in the following screenshot, because we only open for POST requests.

🎨 GET request sent to our Function will now be rejected. 🎨

Thus, when we send another HTTP request but make it to be a POST request, we will receive the message that is found in the C# codes in the Function, as shown in the following screenshot.

🎨 Yay, the POST requests are allowed. 🎨

Now, everyone can send a POST request and get the message as long as they know the Function Key. So how do we add Authentication to this Function?

Authorization Level for the Function

Remember in the earlier section above, we talked about the Authorization Level of the Function? It has three options: Function, Admin, and Anonymous.

We must change the Authorization Level of the Function to be “Anonymous”, as shown in the screenshot below. This is because for both “Function” and “Admin” levels, they are using keys. What we need here is user-based authentication, hence we must choose “Anonymous” instead.

🎨 Without setting the Authorization Level to be Anonymous, the Azure AD authorisation will not work as expected. 🎨

This step is very important because if we forgot to change the Authorization Level to “Anonymous”, the Function will still need the Function Key as the query string even though the request comes in with a valid access token.

Enable App Service Authorization

After that, we need to visit the App Service of the Function App to turn on App Service Authentication. This feature is at App Service level instead of the Function itself. So please pay attention to where to look for the feature.

🎨 This is the place to turn on the “App Service Authentication” for the Function app. 🎨

After the Authentication is turned on, we need to specify “log in with Azure Active Directory” as the action to be taken when the request is not authenticate, as illustrated below. This step is also very important because if we forgot to change it and “Allow Anonymous requests (no action)”, then no matter whether we set the Authentication Providers or not, people can still access the Function. Hence, please remember to change this setting accordingly.

🎨 Turning on App Service Authentication. 🎨

Next, please click on the Azure Active Directory which is listed as one of the Authentication Providers. It is currently labelled as “Not Configured”. Don’t worry, we will now proceed to configure it.

Firstly, we choose the Express mode as management mode. Then we can proceed to create a new Azure AD. The Portal then will help us to setup a new AD Application (or choose from existing AD Application). You can go to Advanced directly if you are experienced with Azure AD.

You should now see the page which looks like what is shown in the following screenshot.

🎨 Creating a new Azure AD Application for the Function in an easy way. (Waypoint 1) 🎨

There is one thing that may catch your attention. It is the last item in the page called “Grant Common Data Service Permissions”. Common Data Service, or CDS, is Microsoft way of providing a secure and cloud-based storage option for our data. There is a one-hour Microsoft online course about CDS, you can take the course to understand more. Grace MacJones, Microsoft Azure Customer Engineer, also gave us a short-and-sweet explanation about this setting on GitHub.

We basically can leave everything as default in the page and proceed to click the “OK” button at the bottom of the page.

After this, the Azure AD will be labelled as “Configure (Express Mode: Create)”. We can then proceed to save the changes.

🎨 Do not forget to save the settings! 🎨

After the settings are saved, we can refresh the page and realising the Azure AD is now labelled as “Configure (Express: Existing App)”. That means the Azure AD app has been created successfully.

🎨 The status of Azure AD for the Function is updated. 🎨

Now, click in to the Azure AD under the Authentication Providers list again. We will be brought to the section where we specified the management node earlier. Instead of choosing Express mode, now we can proceed to choose the Advanced mode.

We will then be shown with Client ID, Issuer Url, and Client Secret, as shown in the following screenshot. According to Ben Martens’ advise, we have to add one more record, which is the domain URL of the Function, to the “Allowed Token Audiences” list to make Azure AD work with this Function, as shown in the following screenshot. (This step is no longer needed with the new interface since October 2019 so I strikethrough it)

🎨 Getting the Azure AD important parameters. 🎨

When you leave this page, the Azure Portal may prompt you to save it. You can choose not to save it. It is optional. If you save it, the Azure AD mode will be changed from Express to Advanced and this will not affect our setup.

Testing on Postman

Now, let’s test our setup above.

When we send the same POST request to the Function again (with the code query string removed since it’s no longer necessary), this time with the App Service Authorization enabled for the Function App, we will no longer be able to get the same message back. Instead, we are told to be 401 Unauthorised and “You do not have permission to view this directory or page”, as shown in the screenshot below.

🎨 Yup, we are unauthorised now. 🎨

Now, let’s try to authenticate ourselves.

To do so, we will make a POST request with the body containing Grant Type, Client ID, Client Secret, and Resource to the following URL:
https://login.microsoftonline.com/<Tenant ID>/oauth2/token to retrieve the access token, as shown in the following screenshot.

🎨 Yesh, we got the access token. 🎨

If we use the access token to send POST request to our Function, we will be told that we are now authorised and the message found in C# code is presented to us, as shown in the following screenshot.

🎨 Yay, we can access our Function again! 🎨

Conclusion

If you would like to get the claims in the Azure Function, you can refer to the following code which loops through all the claims. If you would like to allow a certain client app to call the Azure Function, you can check for the value of the claim having “appid” as its type.

foreach(var claim in principal.Claims)
{
    log.LogInformation($"CLAIM TYPE: {claim.Type}; CLAIM VALUE: {claim.Value}");
}

That’s all it takes to setup a simple authentication for Azure Function with Azure AD. If you find anything wrong above, feel free to correct me by leaving a message in the comment section. Thanks!

References

Exploring Azure Functions for Scheduler

azure-function-documentdb.png

During my first job after finishing my undergraduate degree in NUS, I worked in a local startup which was then the largest bus ticketing portal in Southeast Asia. In 2014, I worked with a senior to successfully migrate the whole system from on-premise to Microsoft Azure Virtual Machines, which is the IaaS option. Maintaining the virtual machines is a painful experience because we need to setup the load balancing with Traffic Manager, database mirroring, database failover, availability set, etc.

In 2015, when I first worked in Singapore Changi Airport, with the support of the team, we made use of PaaS technologies such as Azure Cloud Services, Azure Web Apps, and Azure SQL, we successfully expanded our online businesses to 7 countries in a short time. With the help of PaaS option in Microsoft Azure, we can finally have a more enjoyable working life.

Azure Functions

Now, in 2017, I decided to explore Azure Functions.

Azure Functions allows developers to focus on the code for only the problem they want to solve without worrying about the infrastructure like we do in Azure Virtual Machines or even the entire applications as we do in Azure Cloud Services.

There are two important benefits that I like in this new option. Firstly, our development can be more productive. Secondly, Azure Functions has two pricing models: Consumption Plan and App Service Plan, as shown in the screenshot below. The Consumption Plan lets us pay per execution and the first 1,000,000 executions are free!

Screen Shot 2017-02-01 at 2.22.01 PM.png
Two hosting plans in Azure Functions: Consumption Plan vs. App Service Plan

After setting up the Function App, we can choose “Quick Start” to have a simpler user interface to get started with Azure Function.

Under “Quick Start” section, there are three triggers available for us to choose, i.e. Timer, Data Processing, and Webhook + API. Today, I’ll only talk about Timer. We will see how we can achieve the scheduler functionality on Microsoft Azure.

Screen Shot 2017-02-05 at 11.16.40 PM.png
Quick Start page in Azure Function.

Timer Trigger

Timer Trigger will execute the function according to a schedule. The schedule is defined using CRON expression. Let’s say if we want our function to be executed every four hours, we can write the schedule as follows.

0 0 */4 * * *

This is similar to how we did in the cron job. The CRON expression consists of six fields. The first one is second (0-59), followed by minute (0 – 59), followed by hour (0 – 23), followed by day of month (1 – 31), followed by month (1 – 12) and day of week (0-6).

Similar to the usual Azure Web App, the default time zone used in Azure Functions is also UTC. Hence, if we would like to change it to use another timezone, what we need to do is just add the WEBSITE_TIME_ZONE application setting in the Function App.

Companion File: function.json

So, where do we set the schedule? The answer is in a special file called function.json.

In the Function App directory, there always needs a function.json file. The function.json file will contain the configuration metadata for the function. Normally, a function can only have a single trigger binding and can have none or more than one I/O bindings.

The trigger binding will be the place we set the schedule.

{
    "bindings": [
        {
            "name": "myTimer",
            "type": "timerTrigger",
            "direction": "in",
            "schedule": "0 0 */4 * * *"
        },
        ...
    ],
    ...
}

The name attribute is to specify the name of the parameter used in the C# function later. It is used for the bound data in the function.

The type attribute specifies the binding time. Our case here will be timerTrigger.

The direction attribute indicates whether the binding is for receiving data into the function (in) or sending data from the function (out). For scheduler, the direction will be “in” because later in our C# function, we can actually retrieve info from the myTimer parameter.

Finally, the schedule attribute will be where we put our schedule CRON expression at.

To know more about binding in Azure Function, please refer to the Azure Function Developer Guide.

Function File: run.csx

2nd file that we must have in the Function App directory is the function itself. For C# function, it will be a file called run.csx.

The .csx format allows developers to focus on just writing the C# function to solve the problem. Instead of wrapping everything in a namespace and class, we just need to define a Run method.

#r "Newtonsoft.Json"

using System;
using Newtonsoft.Json;
...

public static async Task Run(TimerInfo myTimer, TraceWriter log)
{
    ...
}

Assemblies in .csx File

Same as how we always did in C# project, when we need to import the namespaces, we just need to use the using clause. For example, in our case, we need to process the Json file, so we need to make use of the library Newtonsoft.Json.

using Newtonsoft.Json;

To reference external assemblies, for example in our case, Newtonsoft.Json, we just need to use the #r directive as follows.

#r "Newtonsoft.Json"

The reason why we are allowed to do so is because Newtonsoft.Json and a few more other assemblies are “special case”. They can be referenced by simplename. As of Jan 2017, the assemblies that are allowed to do so are as follows.

  • Newtonsoft.Json
  • Microsoft.WindowsAzure.Storage
  • Microsoft.ServiceBus
  • Microsoft.AspNet.WebHooks.Receivers
  • Microsoft.AspNet.WebHooks.Common
  • Microsoft.Azure.NotificationHubs

For other assemblies, we need to upload the assembly file, for example MyAssembly.dll, into a bin folder relative to the function first. Only then we can reference is as follows.

#r "MyAssembly.dll"

Async Method in .csx File

Asynchronous programming is recommended best practice. To make the Run method above asynchronous, we need to use the async keyword and return a Task object. However, developers are advised to always avoid referencing the Task.Result property because it will essentially do a busy-wait on a lock of another thread. Holding a lock creates the potential for deadlocks.

Inputs in .csx File and DocumentDB

latest-topics-on-dotnet-sg-facebook-group
This section will display the top four latest Facebook posts pulled by Azure Function.

For our case, the purpose of Azure Function is to process the Facebook Group feeds and then store the feeds somewhere for later use. The “somewhere” here is DocumentDB.

To gets the inputs from DocumentDB, we first need to have 2nd binding specified in the functions.json as follows.

{
    "bindings": [
        ...
        {
            "type": "documentDB",
            "name": "inputDocument",
            "databaseName": "feeds-database",
            "collectionName": "facebook-group-feeds",
            "id": "41f7adb1-cadf-491e-9973-28cc3fca57df",
            "connection": "dotnetsg_DOCUMENTDB",
            "direction": "in"
        }
    ],
    ...
}

In the DocumentDB input binding above, the name attribute is, same as previous example, used to specify the name of the parameter in the C# function.

The databaseName and collectionName attributes correspond to the names of the database and collection in our DocumentDB, respectively. The id attribute is the Document Id of the document that we want to retrieve. In our case, we store all the Facebook feeds in one document, so we specify the Document Id in the binding directly.

The connection attribute is the name of the Azure Function Application Setting storing the connection string of the DocumentDB account endpoint. Yes, Azure Function also has Application Settings available. =)

Finally, the direction attribute must be “in”.

We can then now enhance our Run method to include inputs from DocumentDB as follows. What it does is basically just reading existing feeds from the document and then update it with new feeds found in the Singapore .NET Facebook Group

#r "Newtonsoft.Json"

using System;
using Newtonsoft.Json;
...

private const string SG_DOT_NET_COMMUNITY_FB_GROUP_ID = "1504549153159226";

public static async Task Run(TimerInfo myTimer, dynamic inputDocument, TraceWriter log)
{
    string sgDotNetCommunityFacebookGroupFeedsJson = 
        await GetFacebookGroupFeedsAsJsonAsync(SG_DOT_NET_COMMUNITY_FB_GROUP_ID);
    
    ...

    var existingFeeds = JsonConvert.DeserializeObject(inputDocument.ToString());

    // Processing the Facebook Group feeds here...
    // Updating existingFeeds here...

    inputDocument.data = existingFeeds.Feeds;
}

Besides getting input from DocumentDB, we can also have DocumentDB output binding as follows to, for example, write a new document to DocumentDB database.

{
    "bindings": [
        ...
        {
            "type": "documentDB",
            "name": "outputDocument",
            "databaseName": "feeds-database",
            "collectionName": "facebook-group-feeds",
            "id": "41f7adb1-cadf-491e-9973-28cc3fca57df",
            "connection": "dotnetsg_DOCUMENTDB",
            "createIfNotExists": true,
            "direction": "out"
        }
    ],
    ...
}

We don’t really use this in our dotnet.sg case. However, as we can see, there are only two major differences between DocumentDB input and output bindings.

Firstly, we have a new createIfNotExists attribute which specify whether to create the DocumentDB database and collection if they don’t exist or not.

Secondly, we will have to set the direction attribute to be “out”.

Then in our function code, we just need to have a new parameter with “out object outputDocument” instead of “in dynamic inputDocument”.

You can read more at the Azure Functions DocumentDB bindings documentation to understand more about how they work together.

Application Settings in Azure Functions

Yes, there are our familiar features such as Application Settings, Continuous Integration, Kudu, etc. in Azure Functions as well. All of them can be found under “Function App Settings” section.

Screen Shot 2017-02-18 at 4.40.24 PM.png
Azure Function App Settings

As what we have been doing in Azure Web Apps, we can also set the timezone, store the App Secrets in the Function App Settings.

Deployment of Azure Functions with Github

We are allowed to link the Azure Function with variety of Deployment Options, such as Github, to enable the continuous deployment option too.

There is one thing that I’d like to highlight here is that if you are also starting from setting up your new Azure Function via Azure Portal, then when in the future you setup the continuous deployment for the function, please make sure that you first create a folder having the same name as the name of your Azure Function. Then all the files related to the function needs to be put in the folder.

For example, in dotnet.sg case, we have the Azure Function called “TimerTriggerCSharp1”. we will have the following folder structure.

Screen Shot 2017-02-18 at 4.49.11 PM.png
Folder structure of the TimerTriggerCsharp1.

When I first started, I made a mistake when I linked Github with Azure Function. I didn’t create the folder with the name “TimerTriggerCSharp1”, which is the name of my Azure Function. So, when I deploy the code via Github, the code in the Azure Function on the Azure Portal is not updated at all.

In fact, once the Continuous Deployment is setup, we are no longer able to edit the code directly on the Azure Portal. Hence, setting up the correct folder structure is important.

Screen Shot 2017-02-18 at 4.52.17 PM.png
Read only once we setup the Continuous Deployment in Azure Function.

If you would like to add in more functions, simply create new folders at the same level.

Conclusion

Azure Function and the whole concept of Serverless Architecture are still very new to me. However, what I like about it is the fact that Azure Function allows us to care about the codes to solve a problem without worrying about the whole application and infrastructure.

In addition, we are also allowed to solve the different problems using the programming language that best suits the problem.

Finally, Azure Function is cost-saving because we can choose to pay only for the time our code is being executed.

If you would like to learn more about Azure Functions, here is the list of references I use in this learning journey.

You can check out my code for TimerTriggerCSharp1 above at our Github repository: https://github.com/sg-dotnet/FacebookGroupFeedsProcessor.