{"id":5932,"date":"2021-02-09T06:00:36","date_gmt":"2021-02-09T14:00:36","guid":{"rendered":"https:\/\/officedevblogs.wpengine.com\/?p=5932"},"modified":"2021-02-09T06:00:36","modified_gmt":"2021-02-09T14:00:36","slug":"microsoft-graph-mailbag-blazor-web-assembly-and-microsoft-graph-with-azure-functions","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/microsoft-graph-mailbag-blazor-web-assembly-and-microsoft-graph-with-azure-functions\/","title":{"rendered":"Microsoft Graph Mailbag &#8211; Blazor Web Assembly and Microsoft Graph with Azure Functions"},"content":{"rendered":"<p>In today\u2019s 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.<\/p>\n<p><img decoding=\"async\" class=\"size-full wp-image-5933 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/2021-02-09-Image1.jpg\" alt=\"\" width=\"620\" height=\"150\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/2021-02-09-Image1.jpg 620w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/2021-02-09-Image1-300x73.jpg 300w\" sizes=\"(max-width: 620px) 100vw, 620px\" \/><\/p>\n<p>Please be sure to follow this blog series using\u202f<a href=\"https:\/\/aka.ms\/MSGraphMailbag\">https:\/\/aka.ms\/MSGraphMailbag<\/a>\u202for with RSS using\u202f<a href=\"https:\/\/developer.microsoft.com\/en-us\/graph\/blogs\/feed\/?tag=MSGraphMailbag\">https:\/\/developer.microsoft.com\/en-us\/graph\/blogs\/feed\/?tag=MSGraphMailbag<\/a>.<\/p>\n<h2>Introduction<\/h2>\n<p>Good day folks, I hope you are doing well and keeping safe.\u00a0 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.\u00a0 For years now I have been a huge advocate for using Azure Functions for accessing and manipulating data.\u00a0 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.\u00a0 My specialty for years has always been in data and the middle layer to backend.\u00a0 So if you are like me and are interested in how to build an end to end application in lightning speed\u2026 lets Blaze [pun intended] ahead with some Blazor and Azure Functions with the Microsoft Graph.<\/p>\n<p>First things first, the code for everything we will cover may be found on my GitHub repo <a href=\"https:\/\/github.com\/fabianwilliams\/blazorwithgraphonazurefunct\">here<\/a>.\u00a0 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.\u00a0 I am also planning to employ additional Azure Functions in different languages and platform SDKs. I\u2019ve also built this demo using publicly available and up-to-date guidance that I will be calling out along the way.\u00a0 Let\u2019s create a list and chronology of tasks in this solution:<\/p>\n<ul>\n<li><a href=\"https:\/\/docs.microsoft.com\/graph\/tutorials\/aspnet-core?tutorial-step=2\">Register an Azure AD Application to access Microsoft 365 Data<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/azure\/azure-functions\/functions-create-your-first-function-visual-studio\">Create an Azure Function<\/a> to access the Microsoft 365 Data via the Microsoft Graph using Visual Studio (backed) so this could also be done in VS Code or other X-Platform as well<\/li>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/premier-developer\/exploring-blazor-with-visual-studio-2019\/\">Create a Blazor Web Assembly Application<\/a> to surface that data.<\/li>\n<\/ul>\n<p>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.<\/p>\n<h2>Application Registration<\/h2>\n<p>Our solution will perform 2 unique functions:<\/p>\n<ol>\n<li>return a list of all the Users in our Microsoft 365 Tenant<\/li>\n<li>return the Top User.<\/li>\n<\/ol>\n<p>To do this we will be targeting the \u201c<a href=\"https:\/\/docs.microsoft.com\/graph\/api\/user-list\">List Users<\/a>\u201d endpoint under the User resource type. We will be only reading for now so our permissions need only be User.Read.All. \u00a0\u00a0We are not going to be prompting the user for credentials, so use Application permissions as opposed to Delegated.<\/p>\n<p><img decoding=\"async\" class=\"size-full wp-image-5938 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/permissions.jpg\" alt=\"\" width=\"928\" height=\"315\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/permissions.jpg 928w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/permissions-300x102.jpg 300w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/permissions-768x261.jpg 768w\" sizes=\"(max-width: 928px) 100vw, 928px\" \/><\/p>\n<p>Following the instructions in the 1<sup>st<\/sup> bullet point above, ensure that your Permission Scope is set to User.Read.All as shown in the following screenshot:<\/p>\n<p><img decoding=\"async\" class=\"size-large wp-image-5934 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/apipermissions-1024x461.jpg\" alt=\"\" width=\"1024\" height=\"461\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/apipermissions-1024x461.jpg 1024w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/apipermissions-300x135.jpg 300w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/apipermissions-768x346.jpg 768w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/apipermissions.jpg 1282w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<p>This will come up later when we get into the Authentication Provider but I will be using the <a href=\"https:\/\/docs.microsoft.com\/graph\/sdks\/choose-authentication-providers\">Confidential Cient Application<\/a> 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].\u00a0 Suffice to say, the genesis of that process begins by selecting the proper Authentication scheme.<\/p>\n<h2>Create an Azure Function<\/h2>\n<p>My favorite bit, I am admittedly biased when it comes to my Web API\u2019s, I love Azure Functions for this.\u00a0 Working with Microsoft Graph and Azure Functions its relatively straight forward. \u00a0The main steps are:<\/p>\n<ol>\n<li>Install the proper Microsoft Graph SDK [for me it\u2019s the Microsoft Graph <a href=\"https:\/\/docs.microsoft.com\/graph\/sdks\/sdk-installation\">.NET SDK<\/a>]<\/li>\n<li>Choose the <a href=\"https:\/\/docs.microsoft.com\/graph\/sdks\/choose-authentication-providers\">appropriate Authentication Provider<\/a><\/li>\n<li>Create the <a href=\"https:\/\/docs.microsoft.com\/graph\/sdks\/create-client\">Graph Client<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/graph\/sdks\/create-requests\">Make Your Request<\/a> to the Graph resource<\/li>\n<li>Manage your Return Object[s] aka Payload [you will see this in the Blazor Section]<\/li>\n<\/ol>\n<p>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.\u00a0 In this blog post I planned on creating 2 Functions calls in Azure Function for the 2 pages I have planned in Blazor:<\/p>\n<ol>\n<li>get the Top User<\/li>\n<li>get a List of All Users<\/li>\n<\/ol>\n<p>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.<\/p>\n<pre class=\"\">private static GraphServiceClient GetAuthenticatedGrahClient()\n{\n    \/\/The below comment block is how you should go about securing your configurable keys etc. as it will allow you to \n    \/\/ send them to Azure API settings upon publish as well as set them up for KeyValult.\n    var clientId = Environment.GetEnvironmentVariable(\"AzureADAppClientId\", EnvironmentVariableTarget.Process);\n    var tenantID = Environment.GetEnvironmentVariable(\"AzureADAppTenantId\", EnvironmentVariableTarget.Process);\n    var clientSecret = Environment.GetEnvironmentVariable(\"AzureADAppClientSecret\", EnvironmentVariableTarget.Process);\n\n    \/\/ Build a client application.\n    IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder\n        .Create(clientId)\n        .WithTenantId(tenantID)\n        .WithClientSecret(clientSecret)\n        .Build();\n    ClientCredentialProvider authenticationProvider = new ClientCredentialProvider(confidentialClientApplication);\n    GraphServiceClient graphClient = new GraphServiceClient(authenticationProvider);\n\n    return graphClient;\n}<\/pre>\n<p>The following snippet shows GetAllUsers Azure Function as an example:<\/p>\n<pre class=\"\">[FunctionName(\"GetAllUsers\")]\npublic static async Task&lt;IActionResult&gt; GetAllUsersFromGraph(\n    [HttpTrigger(AuthorizationLevel.Function, \"get\", Route = null)] HttpRequest req,\n    ILogger log)\n{\n    List&lt;FabsterUser&gt; ful = new List&lt;FabsterUser&gt;();\n\n    GraphServiceClient graphClient = GetAuthenticatedGrahClient();\n    List&lt;QueryOption&gt; options = new List&lt;QueryOption&gt;\n    {\n        new QueryOption(\"$select\", \"displayName,givenName,mail\")\n    };\n\n    var graphResult = graphClient.Users.Request(options).GetAsync().Result;\n\n    List&lt;User&gt; usersList = graphResult.CurrentPage.ToList();\n\n    foreach (User u in usersList)\n    {\n        log.LogInformation(\"Showing: \" + u.GivenName + \" - \" + u.Mail);\n    }\n\n    log.LogInformation(\"Graph SDK Result for All Users\");\n\n    string responseMessage = string.IsNullOrEmpty(graphResult.ToString())\n        ? \"Call to Microsoft Graph on Fabster Tenanat App executed successfully.\"\n        : $\"{graphResult}\";\n\n    return new OkObjectResult(usersList);\n}<\/pre>\n<p>You will publish the Azure Functions to your Function App in Azure (see documentation <a href=\"https:\/\/docs.microsoft.com\/azure\/azure-functions\/functions-develop-vs#publish-to-azure\">here<\/a> for Visual Studio or <a href=\"https:\/\/docs.microsoft.com\/azure\/azure-functions\/functions-develop-vs-code#publish-to-azure\">here<\/a> for Visual Studio Code). If you do not have an Azure subscription, you can get one for free <a href=\"https:\/\/azure.microsoft.com\/en-us\/free\/\">here<\/a>. Now you may be asking yourself \u201cwhy use an Azure Function?\u201d From a demo perspective I don\u2019t have to worry about how to serve up a Web API from my local machine [ex. <a href=\"https:\/\/ngrok.com\/\">ngrok<\/a>].\u00a0 This also matches how you might build it from a a real-world production scenario also.\u00a0 Once published you can test your Azure Function calls using your browser or even <a href=\"https:\/\/www.postman.com\/\">PostMan<\/a>.\u00a0 Below you can see us testing the GetAllUsers function.<\/p>\n<p><img decoding=\"async\" class=\"size-large wp-image-5939 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/postmanpayload-1024x620.jpg\" alt=\"\" width=\"1024\" height=\"620\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/postmanpayload-1024x620.jpg 1024w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/postmanpayload-300x182.jpg 300w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/postmanpayload-768x465.jpg 768w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/postmanpayload.jpg 1092w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<p>At this point in time all you have left is the UI.<\/p>\n<h2>Create a Blazor Web Application<\/h2>\n<p>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.<\/p>\n<p><img decoding=\"async\" class=\"size-full wp-image-5937 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/createnewblazorapp.jpg\" alt=\"\" width=\"1024\" height=\"710\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/createnewblazorapp.jpg 1024w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/createnewblazorapp-300x208.jpg 300w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/createnewblazorapp-768x533.jpg 768w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<p>Once your project is created, do the following:<\/p>\n<ol>\n<li>Add 2 new pages [RazorComponents]\n<ol>\n<li>GetTopUser<\/li>\n<li>GetAllUsers<\/li>\n<\/ol>\n<\/li>\n<li>Add 2 Navigation elements to the Shared\\NavMenu.razor page<\/li>\n<\/ol>\n<p><img decoding=\"async\" class=\"size-full wp-image-5940 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/vssolnexplorercallout.jpg\" alt=\"\" width=\"331\" height=\"583\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/vssolnexplorercallout.jpg 331w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/vssolnexplorercallout-170x300.jpg 170w\" sizes=\"(max-width: 331px) 100vw, 331px\" \/><\/p>\n<p>In the new pages, upon inspection of the code <a href=\"https:\/\/github.com\/fabianwilliams\/blazorwithgraphonazurefunct\/blob\/main\/MailbagSeriesFabs\/GraphUsersInBlazorWasm\/Pages\/GetAllUsers.razor\">here<\/a>, 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.<\/p>\n<p>Once you navigate to the GetAllUser page should return a response like the following screenshot:<\/p>\n<p><img decoding=\"async\" class=\"size-large wp-image-5935 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/blazorappui_showAll-1024x511.jpg\" alt=\"\" width=\"1024\" height=\"511\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/blazorappui_showAll-1024x511.jpg 1024w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/blazorappui_showAll-300x150.jpg 300w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/blazorappui_showAll-768x383.jpg 768w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/blazorappui_showAll.jpg 1395w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<p>And if you navigate to the GetTopUser page it should return a response like the following screenshot:<\/p>\n<p><img decoding=\"async\" class=\" wp-image-5936 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/blazorappui_showTopOne-1024x368.jpg\" alt=\"\" width=\"979\" height=\"352\" srcset=\"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/blazorappui_showTopOne-1024x368.jpg 1024w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/blazorappui_showTopOne-300x108.jpg 300w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/blazorappui_showTopOne-768x276.jpg 768w, https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-content\/uploads\/sites\/73\/2021\/02\/blazorappui_showTopOne.jpg 1256w\" sizes=\"(max-width: 979px) 100vw, 979px\" \/><\/p>\n<p>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.\u00a0 To keep to my promise\u2026 we need to end here to keep to a 15 minute read total \ud83d\ude0a<\/p>\n<p>Today\u2019s post was written by Fabian G. Williams, Senior Program Manager on the Microsoft Graph team at Microsoft.\u202f\u00a0You can follow Fabian on twitter @fabianwiliams.\u202f Join us for our next post\u00a0Feb 23, 2021.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Surface your Microsoft 365 data using Microsoft Graph with Microsoft Azure Functions and ASP.Net Core Blazor Web Assembly.<\/p>\n","protected":false},"author":69075,"featured_media":25159,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[3],"tags":[17],"class_list":["post-5932","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-microsoft-graph","tag-msgraphmailbag"],"acf":[],"blog_post_summary":"<p>Surface your Microsoft 365 data using Microsoft Graph with Microsoft Azure Functions and ASP.Net Core Blazor Web Assembly.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/posts\/5932","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/users\/69075"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/comments?post=5932"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/posts\/5932\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/media\/25159"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/media?parent=5932"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/categories?post=5932"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/microsoft365dev\/wp-json\/wp\/v2\/tags?post=5932"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}