Microsoft Graph Mailbag – Blazor Web Assembly and Microsoft Graph with Azure Functions

Microsoft Graph Mailbag

In today’s Microsoft Graph Mailbag post we will demonstrate how to surface and interact with your Microsoft 365 data and services using the Microsoft Graph by using Microsoft Azure Functions as the middle tier business logic layer and ASP.Net Core Blazor Web Assembly as the front end UI layer.

Please be sure to follow this blog series using https://aka.ms/MSGraphMailbag or with RSS using https://developer.microsoft.com/en-us/graph/blogs/feed/?tag=MSGraphMailbag.

Introduction

Good day folks, I hope you are doing well and keeping safe.  My objective in this blog post is to show you the art of the possible for working with Microsoft 365 data and services using the Microsoft Graph with two technologies that I find really fascinating.  For years now I have been a huge advocate for using Azure Functions for accessing and manipulating data.  I like it as a middle tier because of the versatility and scale that it offers. I also like Blazor because I [if I am being honest with myself and you] am not the best at creating appealing User Interfaces for applications.  My specialty for years has always been in data and the middle layer to backend.  So if you are like me and are interested in how to build an end to end application in lightning speed… lets Blaze [pun intended] ahead with some Blazor and Azure Functions with the Microsoft Graph.

First things first, the code for everything we will cover may be found on my GitHub repo here.  My plan is to keep this repo / scenario going as time goes by to build out and to add more forms for various use cases.  I am also planning to employ additional Azure Functions in different languages and platform SDKs. I’ve also built this demo using publicly available and up-to-date guidance that I will be calling out along the way.  Let’s create a list and chronology of tasks in this solution:

I want to keep this simple, something that you can do in 15 minutes, so with the above links and the aid of the code in the repo, I will call out the integral steps that I took to get my solution built.

Application Registration

Our solution will perform 2 unique functions:

  1. return a list of all the Users in our Microsoft 365 Tenant
  2. return the Top User.

To do this we will be targeting the “List Users” endpoint under the User resource type. We will be only reading for now so our permissions need only be User.Read.All.   We are not going to be prompting the user for credentials, so use Application permissions as opposed to Delegated.

Following the instructions in the 1st bullet point above, ensure that your Permission Scope is set to User.Read.All as shown in the following screenshot:

This will come up later when we get into the Authentication Provider but I will be using the Confidential Cient Application because my intent is to have the application run without user interaction. If you look at the code in the previous link you will see it is a direct copy into my code with 2 additional lines for the GraphServiceClient [more to come on that].  Suffice to say, the genesis of that process begins by selecting the proper Authentication scheme.

Create an Azure Function

My favorite bit, I am admittedly biased when it comes to my Web API’s, I love Azure Functions for this.  Working with Microsoft Graph and Azure Functions its relatively straight forward.  The main steps are:

  1. Install the proper Microsoft Graph SDK [for me it’s the Microsoft Graph .NET SDK]
  2. Choose the appropriate Authentication Provider
  3. Create the Graph Client
  4. Make Your Request to the Graph resource
  5. Manage your Return Object[s] aka Payload [you will see this in the Blazor Section]

The list above combined with the code in my repo details the steps, but I will call out a few items where I refined it a bit.  In this blog post I planned on creating 2 Functions calls in Azure Function for the 2 pages I have planned in Blazor:

  1. get the Top User
  2. get a List of All Users

I created a static method that I can call both Functions but also any future functions as well. I could have gone further and abstracted the Graph Client away from the Authentication Provider if I foresaw the need to have different Client Flows, but that was not needed here. Suffice to say, this method will return to us an authenticated Graph Client to be used in our method.

private static GraphServiceClient GetAuthenticatedGrahClient()
{
    //The below comment block is how you should go about securing your configurable keys etc. as it will allow you to 
    // send them to Azure API settings upon publish as well as set them up for KeyValult.
    var clientId = Environment.GetEnvironmentVariable("AzureADAppClientId", EnvironmentVariableTarget.Process);
    var tenantID = Environment.GetEnvironmentVariable("AzureADAppTenantId", EnvironmentVariableTarget.Process);
    var clientSecret = Environment.GetEnvironmentVariable("AzureADAppClientSecret", EnvironmentVariableTarget.Process);

    // Build a client application.
    IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
        .Create(clientId)
        .WithTenantId(tenantID)
        .WithClientSecret(clientSecret)
        .Build();
    ClientCredentialProvider authenticationProvider = new ClientCredentialProvider(confidentialClientApplication);
    GraphServiceClient graphClient = new GraphServiceClient(authenticationProvider);

    return graphClient;
}

The following snippet shows GetAllUsers Azure Function as an example:

[FunctionName("GetAllUsers")]
public static async Task<IActionResult> GetAllUsersFromGraph(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
    ILogger log)
{
    List<FabsterUser> ful = new List<FabsterUser>();

    GraphServiceClient graphClient = GetAuthenticatedGrahClient();
    List<QueryOption> options = new List<QueryOption>
    {
        new QueryOption("$select", "displayName,givenName,mail")
    };

    var graphResult = graphClient.Users.Request(options).GetAsync().Result;

    List<User> usersList = graphResult.CurrentPage.ToList();

    foreach (User u in usersList)
    {
        log.LogInformation("Showing: " + u.GivenName + " - " + u.Mail);
    }

    log.LogInformation("Graph SDK Result for All Users");

    string responseMessage = string.IsNullOrEmpty(graphResult.ToString())
        ? "Call to Microsoft Graph on Fabster Tenanat App executed successfully."
        : $"{graphResult}";

    return new OkObjectResult(usersList);
}

You will publish the Azure Functions to your Function App in Azure (see documentation here for Visual Studio or here for Visual Studio Code). If you do not have an Azure subscription, you can get one for free here. Now you may be asking yourself “why use an Azure Function?” From a demo perspective I don’t have to worry about how to serve up a Web API from my local machine [ex. ngrok].  This also matches how you might build it from a a real-world production scenario also.  Once published you can test your Azure Function calls using your browser or even PostMan.  Below you can see us testing the GetAllUsers function.

At this point in time all you have left is the UI.

Create a Blazor Web Application

The final step is put this all together and display our work. My approach here is to build upon the default template you get when you create a Visual Studio project for an ASP.NET Core Web Application using the Blazor WebAssembly App template.

Once your project is created, do the following:

  1. Add 2 new pages [RazorComponents]
    1. GetTopUser
    2. GetAllUsers
  2. Add 2 Navigation elements to the Shared\NavMenu.razor page

In the new pages, upon inspection of the code here, you will see a page directive at the top that will be referenced in the NavMenu.razor page and at the bottom of the page you also see in the OnInitializedAsync method we make the call to the Azure Functions to get our payload.

Once you navigate to the GetAllUser page should return a response like the following screenshot:

And if you navigate to the GetTopUser page it should return a response like the following screenshot:

In a future post, I will update this sample to use native data binding in Blazor to manage changes between Microsoft 365 and the Web Application.  To keep to my promise… we need to end here to keep to a 15 minute read total 😊

Today’s post was written by Fabian G. Williams, Senior Program Manager on the Microsoft Graph team at Microsoft.  You can follow Fabian on twitter @fabianwiliams.  Join us for our next post Feb 23, 2021.

0 comments

Comments are closed. Login to edit/delete your existing comments