Creating a C# Azure Function to Call Microsoft Graph

This post is a part of The Third Annual C# Advent.

Building on my “Introduction to Calling Microsoft Graph from a C# .Net Core Application” post from the 2018 C# Advent event, this year we’ll take what we learned and adapt that code to run in an Azure Function.  I recommend reading that post (and the linked resources in it) first to get the background on creating and authenticating an Azure AD application.

Prerequisites

Background

Azure Functions is one of the serverless options within Azure (read here for more about Azure serverless solutions).  Azure Functions consist of a trigger (HTTP, timer, storage event, etc.) and optionally one or more input / output bindings.  I especially appreciate how triggers and input / output bindings reduce, or even completely remove, the need to write what I call “internal plumbing” code.  “Internal plumbing” code is the code required to connect to an Azure storage account, monitor for a webhook request, or similar event based functionality.  Having that functionality taken care of allows you as the developer to focus on writing the core business logic where the most value can be provided.

Speaking of bindings, you may notice on the Azure Functions documentation that there are Microsoft Graph bindings for Azure Functions as of version 2+.  I’m not on the product group but as far as I’m aware these bindings have been in preview for over 2 years and are not planned to move past preview.  As such this blog post will walk through calling Microsoft Graph using the Microsoft Graph .Net SDK.

The below steps can be seen in the sample repo at BTJ.CSAdvent.AZFunc on GitHub. Additionally the Develop Azure Functions by using Visual Studio Code documentation gives a good overview of the VS Code development process.

Create Azure Function

Open Visual Studio Code and open the Command Pallete (⇧⌘P or F1 on Mac, Ctrl+Shift+P or F1 on Windows).

  1. Search for “Azure Functions: Create New Project…” and select it.CSAdvent-MSGAZFunc1
  2. Choose a folder to create the project in (preferably not in a Visual Studio Code workspace).
  3. Select the language of the project as C#.
    CSAdvent-MSGAZFunc2
  4. Create an empty function project by selecting “Skip for now” when prompted for the trigger type.
    CSAdvent-MSGAZFunc3
  5. Open the Command Palette again and search for “Azure Functions: Create Function…” and select it.
    CSAdvent-MSGAZFunc4
  6. Select a TimerTrigger.
  7. Name the function “GetUserMicrosoftGraph” (or any name you wish, but later references will be impacted).
    CSAdvent-MSGAZFunc5
  8. Provide a namespace for the function.
  9. Use the default timer schedule “0 */5 * * * *” which is the CRON representation of running every 5 minutes.
  10. On the terminal at the root of the project run “dotnet build” to build the project.
    • If you are prompted to choose a storage account and are using Windows, select “Use local emulator”.
    • If you are on Mac / Linux, select “Select storage account” and specify a cloud Azure storage account.CSAdvent-MSGAZFunc6

At this point you should have an Azure Function project that should build successfully.

Add NuGet Packages

Since we’ll be calling Microsoft Graph and the MSAL .Net SDK libraries we’ll add the supporting NuGet packages with the following:

  1. Run “dotnet add package Microsoft.Graph” from the terminal.
  2. Run “dotnet add package Microsoft.Identity.Client” from the terminal.

Add Azure AD App Authentication

Open the local.settings.json file.  It should look similar to the following (AzureWebJobStorage may be different depending on choices above for storage).

CSAdvent-MSGAZFunc7

Add the following key/value pairs to the Values section.  Use the values from the Azure AD application that was created using the references in the linked articles above.

   "timerSchedule": "0 */5 * * * *",
   "AzureADAppTenantId": "YOUR_TENANT_ID_HERE",
   "AzureADAppClientId": "YOUR_CLIENT_ID_HERE",
   "AzureADAppClientSecret": "YOUR_CLIENT_SECRET_HERE",
   "AzureADAppRedirectUri": "YOUR_REDIRECT_URI_HERE"

Implement the sample code (link here) from last year’s post as helper classes to the project.  See AuthHandler.cs and MsalAuthenticationProvider.cs from the example repo.

CSAdvent-MSGAZFunc11

Call Microsoft Graph

Implement the following code (adapted from last year’s post, link here) inside the GetUserMicrosoftGraph.cs file.

 public static class GetUserMicrosoftGraph
    {
        private static GraphServiceClient _graphServiceClient;

        [FunctionName("GetUserMicrosoftGraph")]
        public static void Run([TimerTrigger("%timerSchedule%")]TimerInfo myTimer, ILogger log)
        {
            log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");

            //Query using Graph SDK (preferred when possible)
            GraphServiceClient graphClient = GetAuthenticatedGraphClient();
            List<QueryOption> options = new List<QueryOption>
            {
                new QueryOption("$top", "1")
            };

            var graphResult = graphClient.Users.Request(options).GetAsync().Result;
            log.LogInformation("Graph SDK Result");
            log.LogInformation(graphResult[0].DisplayName);
        }

        private static GraphServiceClient GetAuthenticatedGraphClient()
        {
            var authenticationProvider = CreateAuthorizationProvider();
            _graphServiceClient = new GraphServiceClient(authenticationProvider);
            return _graphServiceClient;
        }

        private static IAuthenticationProvider CreateAuthorizationProvider()
        {
            var clientId = System.Environment.GetEnvironmentVariable("AzureADAppClientId", EnvironmentVariableTarget.Process);
            var clientSecret = System.Environment.GetEnvironmentVariable("AzureADAppClientSecret", EnvironmentVariableTarget.Process);
            var redirectUri = System.Environment.GetEnvironmentVariable("AzureADAppRedirectUri", EnvironmentVariableTarget.Process);
            var tenantId = System.Environment.GetEnvironmentVariable("AzureADAppTenantId", EnvironmentVariableTarget.Process);
            var authority = $"https://login.microsoftonline.com/{tenantId}/v2.0";

            //this specific scope means that application will default to what is defined in the application registration rather than using dynamic scopes
            List<string> scopes = new List<string>();
            scopes.Add("https://graph.microsoft.com/.default");

            var cca = ConfidentialClientApplicationBuilder.Create(clientId)
                                              .WithAuthority(authority)
                                              .WithRedirectUri(redirectUri)
                                              .WithClientSecret(clientSecret)
                                              .Build();

            return new MsalAuthenticationProvider(cca, scopes.ToArray());;
        }
    }

Test Azure Function

Test out the Azure Function by doing the following:

  1. Run “dotnet build” from the terminal.
  2. Assuming the build is successful you can then execute the Azure Function locally by pressing the F5 key.

See below for sample output.

CSAdvent-MSGAZFunc12

Deploy Azure Function

The Azure Functions extension for Visual Studio Code makes the deployment process much easier.  Search for “Azure Functions: Deploy to Function App…” and select it.

CSAdvent-MSGAZFunc9

I already have a consumption based, Windows hosted Function App deployed in my Azure subscription but you can also create a new one if needed.  Select the subscription and Function App to deploy to.

Upload App Settings

When you develop the Azure Function locally you make use of the local.settings.json file which is automatically loaded as environment variables.  In Azure though you will need to create those configuration values as app settings on the Function App.  Rather than manually add / edit those values, search for “Azure Functions: Upload Local Settings…” and select it.  This will upload any values you have in your local.settings.json as app settings on the Function App.

IMPORTANT: If you specified the AzureWebJobsStorage key to use local storage, do not overwrite that value in Azure.  Instead use the cloud Azure storage account already specified.

CSAdvent-MSGAZFunc10

(Bonus) Add Key Vault Integration for Client Secret

While it is possible to specify the Azure AD app secret in the Function App configuration settings (stored encrypted), anyone with read / edit access to the Function App will be able to view that value as plain text through the Azure portal.  As such it is recommended to store the value in Azure Key Vault or similar location.  To specify a location in Azure Key Vault following the documentation on Use Key Vault references for App Service and Azure Functions.

The reference in your Function App configuration will now look something like the following:

@Microsoft.KeyVault(SecretUri=https://yourvault.vault.azure.net/secrets/yoursecret/ec96f02080254f109c51a1f14cdb1931)

 

Conclusion

In this blog post we walked through the process to create, test, and deploy a C# compiled Azure Function that calls Microsoft Graph.  The sample repo can be found on GitHub at BTJ.CSAdvent.AZFunc.  Thank you for reading along and please open an issue on GitHub repo if you run into any issues with the sample project.  Enjoy the rest of The Third Annual C# Advent.

-Frog Out

Presenting at Collab365 SharePoint Summit 2019

I have the privilege of presenting “Getting Started with Microsoft Graph Development” at the upcoming Collab365 SharePoint Summit taking place Sept 10-12, 2019.  This is a free online conference with MVPs and experts from around the world presenting on developer, IT Pro, and adoption topics.

Title: Getting Started with Microsoft Graph Development

Abstract: “I hear that I need to use Microsoft Graph for developing against Office 365 but I have no clue where to start.” “I want to grant access to company data without throwing in the entire kitchen sink.” Fear not fellow developers and admins.

This session we will ramp you up to a 200 level knowledge on the pertinent parts of Microsoft Graph including endpoints available, syntax, authentication flows, and more. We will also cover useful examples of what can be accomplished using these APIs. Prior experience with Microsoft Graph is not required but can be helpful.

You can also purchase an all-access pass which includes lifetime access to the videos, additional e-books, and more.  Looking forward to participating in this great event.

Introduction to Calling Microsoft Graph from a C# .Net Core Application

This post is a part of The Second Annual C# Advent.

Microsoft Graph is the unified API for any developers working with data inside Office 365, Azure Active Directory (Azure AD), Windows 10, and more.  In this post we’ll cover a quick introduction and share resources from 30 Days of Microsoft Graph blog series to show how to authenticate and to make calls against Microsoft Graph with C# and .Net Core (v2.1 as of the time of writing.)  Each of the referenced articles aims to take 5-15 mins to get you up to speed as quickly as possible while also providing hands-on exercises.  If you’d like to skip the background reading and start from scratch building a .Net Core console application that calls Microsoft Graph read through the README for the base-console-app within dotnetcore-console-sample.

<Update 2019-10-07>Thanks to reader John Guilbert for pointing out that the sample code using MSAL .Net 2.x has deprecated certain APIs.  I’ve updated the sample code to reflect MSAL .Net 4.x.</Update>

Microsoft Graph overview

Microsoft Graph offers developers (and IT pros / admins) the ability to access data and insights in a number of services within Microsoft 365 services.  This includes:

  • Azure AD
  • Office 365 services
    • SharePoint
    • OneDrive
    • Outlook/Exchange
    • Microsoft Teams
    • OneNote
    • Planner
    • Excel
  • Enterprise Mobility and Security services
    • Identity Manager
    • Intune
    • Advanced Threat Analytics
    • Advanced Threat Protection
  • Windows 10 services
    • Activities
    • Devices
  • Education

By providing a unified endpoint for accessing all of these services Microsoft Graph removes a number of barriers including:

  • Discovering the service-specific endpoint URL
  • Authenticating to each endpoint separately
  • Managing different permission models
  • Working with incompatible data formats
  • …and more

All requests made to Microsoft Graph are sent as REST calls to https://graph.microsoft.com and leverage a common authentication model based on Azure AD and OAuth permissions along with a consent framework for users or admins.  The quickest way to see Microsoft Graph requests in action is to navigate to the Microsoft Graph explorer (https://aka.ms/ge, ge = Graph Explorer.)  For more information on using Graph Explorer please read Day 3 – Graph Explorer from the 30 Days of Microsoft Graph series.  Additionally you can make requests against Microsoft Graph using API development tools such as Postman.  Please read Day 13 – Postman to make Microsoft Graph requests for more information on using PostMan with Microsoft Graph.

 

Getting started sample

Seeing requests and their responses in a browser or tool is useful, but making requests in code or scripts is the more common scenario for usage.  In the examples below we will cover C# and .Net Core as .Net Core is available cross-platform, can be built in Visual Studio Code (also cross-platform), and offers many hosting options (console app, web app, serverless functions, and more.)

Authentication

All requests to Microsoft Graph require an authenticated context, either delegated or app-only.  Delegated is a union of the logged-in user’s context along with the application’s context.  App-only (as the name implies) is only the application’s context without any user involvement.  Please read Day 8 – Authentication roadmap and access tokens and Day 9 – Azure AD applications  on V2 endpoint for more information about creating an Azure AD application and getting an authenticated context.  On a similar note, you are highly encouraged to leverage Microsoft Authentication Library (MSAL) for creating your authentication context as this is the forward-focused version as opposed to the older Active Directory Authentication Library (ADAL).

Microsoft Graph SDK

While it is entirely possible to call the Microsoft Graph with an HttpClient (or similar) object, the Azure AD Identity and Microsoft Graph product groups recommend leveraging the Microsoft Graph SDK (Microsoft.Graph on Nuget.)  This SDK provides a number of benefits including:

  • Strongly typed entities and Microsoft Graph responses
  • Fluent API syntax
  • …and more

In future releases Microsoft Graph SDK will also provide abstractions for authentication prerequisites, automatic handling of retry logic or error handling, and more.

Sample solution

As mentioned at the beginning of this post if you’d like to build a working application from scratch (or clone the repo and configure the necessary settings) you can find the base-console-app within dotnetcore-console-sample.  Extracting the bare essential lines of code from this sample results in the following for authenticating to Microsoft Graph.

Note: If you do not see the below Gist please refer to code at this location: CS-Graph_Prepare_GraphServiceClient.cs

The following class implements the IAuthenticationProvider interface used for retrieving and then adding an Azure AD access token to subsequent requests to Microsoft Graph.  An out of the box implementation of this class will be provided at a later date within the Graph SDK.

Note: If you do not see the below Gist please refer to code at this location: CS-Graph_Class_MsalAuthenticationProvider.cs

Finally make a sample request to get a list of users within the Azure AD domain by calling Microsoft Graph.

Note: If you do not see the below Gist please refer to code at this location: CS-Graph_Request_Users_GraphServiceClient.cs

Conclusion

In this blog post we covered a quick introduction of Microsoft Graph and linked to additional resource within the 30 Days of Microsoft Graph blog series for additional background reading.  We also covered a barebones implementation of calling Microsoft Graph in a C# .Net Core console application.  Full instructions can be found on the base-console-app within dotnetcore-console-sample.  Thank you for reading along and please open an issue on GitHub repo if you run into any issues with the sample project.  Enjoy the rest of The Second Annual C# Advent.

 

-Frog Out

30 Days of Microsoft Graph Blog Series

Last week myself and a virtual team of Microsoft and community contributors started a month long blog series called 30 Days of Microsoft Graph on the Microsoft Graph blog.  You can read more about it on the announcement post which is being updated daily with links to each of the posts throughout November.  Please see the below options for following along:

If you have any feedback or suggestions on the blog series please reach out to me or the rest of the team.  We look forward to hearing from you.

 

-Frog Out