The following is a blog post written by one of our many amazing summer interns, Glenna. I had the pleasure of mentoring Glenna this past summer as she worked on a very exciting internship project: The Microsoft Graph bindings for Azure Functions, which was recently announced at Microsoft Ignite 2017. Glenna worked tirelessly on this project and was responsible for large parts of the implementation as well as presenting the project to her peers and local executives (and she did an excellent job at both). Unfortunately she had to head back to school before we could publish this post, but now that it's been productized and announced we are finally free to unleash it. :)
Thank you Glenna for your awesome contributions to our team and to our customers. We can't wait to have you come back and join us full-time after your graduation!
As an aside, it's very important to us on the Azure App Service team that our interns get to work on projects that are fun, challenging, have a legitimate business impact, and actually ship to production. The Microsoft Graph bindings and associated Azure Functions portal work is just one example among many others, and we're really excited to be able to finally showcase it.
Here is Glenna in her own words (with a few edits from the team based on some changes that were made after she returned to school). Consider the content of this post as supplementary to our official documentation.
Personal Introduction
My name is Glenna and I am a software engineering intern on the Azure Functions team in Redmond, WA. As I write this, I am finishing up my last day on my twelfth and final week at Microsoft. My internship was a truly incredible experience. I learned a great deal not just about specific programming languages, but also about good engineering design practices. I got to work on both the nuts and bolts of my features as well as the Azure Functions Portal UX and UI.
I attend the University of Virginia in Charlottesville, Virginia. In May of 2018, I will graduate with a Bachelor of Science in Computer Science.
Introduction
Microsoft Graph bindings extend the existing Microsoft Graph SDK and WebJobs framework to allow users easy access to Microsoft Graph data (emails, OneDrive files, events, etc.) from Azure Functions.
By using the Office input, output, and trigger bindings, users can bind to user-defined types, primitives, or directly to Microsoft Graph SDK objects like
WorkbookTables and
Messages. These bindings handle Azure AD authentication, Azure AD token exchange, and token refresh, allowing users to focus on writing code that utilizes Microsoft Graph data.
Features
Currently, the bindings can be grouped into four main categories:
Excel, Outlook, OneDrive, and Webhooks.
Excel [Input + Output]
The Excel binding allows users to read/write Excel workbooks and tables using different data types, like lists of user-defined types or 2D string arrays.
Outlook [Output]
The Outlook binding is an output only binding, and allows users to send emails from their Office 365 email accounts.
OneDrive [Input + Output]
The OneDrive binding allows users to read/write files stored in their OneDrive using several different data types (e.g. DriveItems and Streams).
Graph Webhooks [Trigger + Input + Output]
There are two bindings associated with Graph webhooks:
GraphWebhookTrigger and
GraphWebhookSubscription.
GraphWebhookSubscription [Input + Output]
The
GraphWebhookSubscription input binding allows the retrieval of Microsoft Graph webhook subscriptions that the function app has created. The
GraphWebhookSubscription output binding handles webhook subscription creation, as well as renewal and deletion. Without renewal, most Microsoft Graph webhooks expire in
3 days. Deleting a webhook subscription removes the subscription from the Microsoft Graph account, as well as all references to that subscription in your Function app. Both the refresh and delete output bindings are best used in conjunction with the input binding, as they operate on current webhook subscription ids.
GraphWebhookTrigger [Trigger]
The GraphWebhookTrigger binding allows users to subscribe to notifications about supported Microsoft Graph resources, including email messages, OneDrive root, contacts, and events.
If you examine the wire protocol, the notification payload from Microsoft Graph is very lightweight; it only contains a webhook subscription ID and the subscribed resource. In order to provide detailed information to the function code, the webhook trigger internally uses a local store of webhook subscription data (stored by the GraphWebhookSubscription binding at subscription creation), maps the webhook subscription ID to a user, performs a silent GET request for the specified resource and transforms that payload into either a JSON object or a Microsoft Graph SDK type (e.g. Message, Contact) which can then be accessed directly by the function code.
Identity
Actions mentioned can be performed using the current Office 365 user's identity, or using the Azure AD service principal identity of the function app. Which identity is used is up to the Function author. In order to authenticate against the Microsoft Graph
as a specific user, either an ID token or an Azure AD Principal ID must be given to the binding. This identifier can come from a number of different sources. Examples of these sources include the currently authenticated Azure AD user (which can be captured from a session cookie or a bearer token), the content of an HTTP request, a queue, or an app setting.
Examples
An easily imagined scenario for these bindings is a business owner with customers who subscribe to their monthly newsletter. Customers provide their names and email addresses, which then must be added to an Excel file of customers.
Using the Excel output binding, the business owner can select which Excel table to modify.
[caption id="attachment_5996" align="alignnone" width="997"]
Excel output binding[/caption]
The function code to append the Excel row is only a few lines long. In this example, the function receives a POST request with a user's name and email address and converts it into a custom
EmailRow
(POCO) type using the runtime's dynamic binding capabilities to remove the need for JSON manipulation.
using System.Net;
public static async Task Run(
HttpRequestMessage req,
IAsyncCollector<EmailRow> outputTable)
{
// Get request body
dynamic data = await req.Content.ReadAsAsync<object>();
// Use body data to set row data
var output = new EmailRow {
Name = data?.name,
Email = data?.email
};
await outputTable.AddAsync(output);
}
public class EmailRow {
public string Name {get; set;}
public string Email {get; set;}
}
Every month, a timer trigger (or some other trigger type) fires and an email, the contents of which are determined by a OneDrive file, is sent out to each customer.
The business owner can select the same Customers Excel file (Excel input binding)...
[caption id="attachment_5995" align="alignnone" width="984"]
Excel input binding[/caption]
...determine which OneDrive file to get the email contents from (OneDrive input binding)...
[caption id="attachment_6015" align="alignnone" width="978"]
OneDrive input binding[/caption]
...and indicate that they would like to send emails via the Outlook output binding.
[caption id="attachment_6026" align="alignnone" width="993"]
Outlook mail output binding[/caption]
The code below quickly scans the Excel table and sends out one email per row (customer).
#r "Microsoft.Graph"
using System;
using Microsoft.Graph;
// Send one email per customer
public static void Run(TimerInfo myTimer, TraceWriter log,
List<EmailRow> inputTable, string file, ICollector<Message> emails)
{
// Iterate over the rows of customers
foreach(var row in inputTable) {
var email = new Message {
Subject = "Monthly newsletter",
Body = new ItemBody {
Content = file, //contents of email determined by OneDrive file
ContentType = BodyType.Html
},
ToRecipients = new Recipient[] {
new Recipient {
EmailAddress = new EmailAddress {
Address = row.Email,
Name = row.Name
}
}
}
};
emails.Add(email);
}
}
public class EmailRow {
public string Name { get; set; }
public string Email { get; set; }
}
The aforementioned goals can be accomplished using just a few lines of code.
No manual data entry, no hardwiring, and no additional services.