Telerik blogs
870x220_Telerik-Blogs

A deep dive into triggers and bindings in Azure Functions using a real world example.

I'm glad that you and I were on the same flight to our next software developer conference. We started off talking about all things Serverless and took a quick look at Microsoft's serverless cloud offering, Azure Functions. Now that we've landed and reached our hotel, it's time to take a bit more of a deep-dive into using Azure Functions to build something real world. And have some fun doing so.

This is turning out to be a great three-part conversation:

Let us dive a little deeper into how to use Azure Functions to build interesting development workflows. How about we build a working serverless application that - hang tight - taunts our friends by sending a text message everytime our favorite team beats theirs :). Sometimes it is fun to be a little evil, isn't it?

I'm a big Milwaukee Brewers fan, and for a change they are finally winning games. So I really want to build a cloud workflow that lets me talk trash to my non-Brewers fan friends, without me actually having to remember to do the trash talking. This application is going to demonstrate how to use various triggers to kick off Azure Functions, and also how Azure Functions manages bindings to other services, like Azure Storage, Azure Cosmos DB, and Twilio - to the point where we don't have to worry about their lifecycle at all!

The What

Here is the basic cloud workflow we're looking to build:

The parts of the Function app

There is an Applet built in IFTTT that monitors for final scores of ball games. Once the game is over, it makes an HTTP call. That HTTP call will be the trigger for our first Azure Function. The first Azure Function then breaks down the incoming message and writes it to an Azure Storage Queue - which then kicks off a second Azure Function.

This second Azure Function has several bindings associated with it. It's automatically set to look at Azure Cosmos DB to pull out any friends we have whose favorite team match the opponent that lost. It's also set to have a write connection to Azure Cosmos DB, so we can save the game results. Finally, it's bound to Twilio - which makes it super easy to send SMS messages.

Let's break down the constituent parts one by one to see how you can build a serverless app just like this.

You can find all of the code for the Azure Functions discussed in this article at this GitHub Repo.

IFTTT Configuration

First things first. We need to configure something that will invoke our HTTP triggered Azure Function, but one that also monitors baseball game scores. For that I turned to IFTTT - as they'll allow me to build an Applet that waits for final scores of the Brewers games using the ESPN service (that's the If part). The Then part is that it will invoke a webhook, aka our Azure Function.

IFTTT configuration

As part of the definition for the webhook portion, I setup the JSON that IFTTT will post, and the URL it will post to. Once that's finished, we're on to our first Azure Function - the HTTP trigger.

HTTP Triggered Function

All of the functions I am building in this example are using C#. You can also build Functions using JavaScript or C# Script. I'm using compiled C# because it's what I'm most comfortable with.

This first Azure Function will be triggered by an HTTP POST, be responsible for taking the incoming JSON message, parsing it, and then writing it to an Azure Storage Queue. To do that, we need to both define the function as having an HTTPTrigger and also define it as having an Output Binding of a Azure Storage Queue.

The beauty of Azure Functions is that setting up the trigger and output binding is done in the function definition - via function and parameter attributes. When the runtime sees those attributes, it takes care of properly instantiating the appropriate HTTP listeners and queue connection for us - we don't have to do anything further.

Let's take a look at the whole function to better illustrate:

[FunctionName("IFTTTTrigger")]
[return: Queue("game-results")]
public static GameResult Run(
  [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]
  IFTTTGameResult result,
  TraceWriter log)
{
  // Break the incoming text apart to get the scores
  var gameResult = Helpers.BreakIFTTTApart(result);

  return gameResult;
}

Here the FunctionName attribute determines what the function's name will be when viewed in the portal. The Queue("game-results") attribute news up a connection to the appropriate Azure Storage connection and attaches to the game-results queue. Finally, the HttpTrigger attribute tells the Azure Function to look for a POST method and the incoming body should match whatever the IFTTTGameResult class's structure is.

Notice how the Queue("game-results") trigger is prefaced by the keyword return. This is telling the runtime that whatever is returned by the function, go ahead and simply put it into the queue. No further action is needed on our part.

And that's the whole first Azure Function - being triggered by an HTTP Post and then writing to a queue. The fun stuff happens next.

Queue Triggered and Azure Cosmos DB / Twilio Bound Function

Once the appropriate record is written to the queue, we need to take action on it so we can taunt our friends - assuming of course, the Brewers win. And in this next Azure Function, you'll see the true power of bindings.

Here's the entire Function, in full, first. Then we'll talk through each individual part.

[FunctionName("TalkTrashFunction")]
public async static Task Run(
  [QueueTrigger(queueName:"game-results", Connection ="AzureWebJobsStorage")]
    GameResult gameResultFromQueue,
  [CosmosDB(databaseName:dbName, collectionName:collName, ConnectionStringSetting =conxString,
    SqlQuery ="SELECT * FROM c WHERE c.FavoriteTeam = {Opponent}")] IEnumerable<FriendInfo> friends,
  [CosmosDB(databaseName:dbName, collectionName:collName, ConnectionStringSetting =conxString)]
    IAsyncCollector<GameResult> writeResultsToCosmos,
  [TwilioSms(AccountSidSetting ="TwilioAccountSID",AuthTokenSetting ="TwilioAuthKey")]
    IAsyncCollector<CreateMessageOptions>smsMessages,
  TraceWriter log)
{
  // First write the game result to Azure Cosmos DB
  await writeResultsToCosmos.AddAsync(gameResultFromQueue);

  // Make sure the Brewers won - if so, text all our friends!
  if (!gameResultFromQueue.BrewersWin)
    return;

  foreach (var friend in friends)
  {
    var msgOptions = new CreateMessageOptions(new PhoneNumber(friend.TelNumber))
    {
      From = new PhoneNumber(GetEnvironmentVariable("TwilioFromPhone")),
      Body = $"Yeah - the Brewers beat the {gameResultFromQueue.Opponent} again {friend.Name}!! HA HA HA HA HA!!!"
    };

    await smsMessages.AddAsync(msgOptions);
  }
}

The Attributes

The first attribute defined in the parameter list is a QueueTrigger. It has various parameters passed to it to tell it which Azure Storage connection to use and which queue to monitor. It also maps directly to a strongly typed variable. As soon as a record that matches the properties of that variable is inserted into the queue, this Azure Function will fire.

The second parameter attribute is very interesting. It's bound to Azure Cosmos DB and its purpose is to pull out records (or our friends) whose favorite team happened to be the Brewers opponent. The first three parameters to that attribute determine the connection, database, and collection it should draw from. The SqlQuery parameter determines what data should be returned and populated into the IEnumerable<FriendInfo> friends variable.

Let's take a look at that query:

SELECT * FROM c WHERE c.FavoriteTeam = {Opponent}

FavoriteTeam is a property in the document we want to pull. And {Opponent} happens to be a property in the GameResult variable that is pulled from the Queue! This means we can declartively setup an Azure Cosmos DB query in our Azure Function definition, and have it depend on other bindings in that Function!

The next attribute is another [CosmosDB] one, but this time its variable type is IAsyncCollector<GameResult>. This lets us write documents that have the same properties as the GameResult class to Azure Cosmos DB.

The final attribute is to Twilio, [TwilioSms]. It takes in my account info and its parameter type is also an IAsyncCollector - which means I can send more than 1 SMS message at a time.

The Logic

The attributes really did most of the work for us - opening up connections to Azure Storage, Azure Cosmos DB, and Twilio. All that's left is to run through some simple logic.

The first step is to write the same result to Azure Cosmos DB using the IAsyncCollector<GameResult>.

await writeResultsToCosmos.AddAsync(gameResultFromQueue);

Next, after making sure the Brewers won, we loop through our friends, and send them text messages:

foreach (var friend in friends)
{
  var msgOptions = new CreateMessageOptions(new PhoneNumber(friend.TelNumber))
  {
    From = new PhoneNumber(GetEnvironmentVariable("TwilioFromPhone")),
    Body = $"Yeah - the Brewers beat the {gameResultFromQueue.Opponent} again {friend.Name}!! HA HA HA HA HA!!!"
  };

  await smsMessages.AddAsync(msgOptions);
}

The CreateMessageOptions class being from the Twilio SDK and indicates parameters needed to send the message. Then all that's needed to send it is to add it to the IAsyncCollector of the [TwilioSms] attribute.

Final Thoughts

With two Azure Functions, we created an app that taunts our friends with text messages everytime the Milwaukee Brewers win! The power of Azure Functions shines through in the bindings to Azure Storage, Azure Cosmos DB, and to Twilio. By defining these connections via attributes, all the work needed to manage the lifetime of the connection is done for us by the Azure Functions runtime and we're free to concentrate on the logic of our Function.

So, there you have it. In a three-part conversation, we started from the basics of Serverless computing, covered an introduction to Azure Functions and built a real-world cloud-powered serverless workflow. The big draw for developers is automation - triggers and bindings in Azure Functions can abstract away much of the heavy lifting. And with pricing of serverless architectures, there are significant cost savings to many cloud workflows. Hope you had fun - the future is cloudy with a chance of epiphanies!


Matt Soucoup
About the Author

Matt Soucoup

Matthew Soucoup is a Senior Cloud Developer Advocate at Microsoft spreading the love of integrating Azure with Xamarin. Matt is also a Pluralsight author, a Telerik Developer Expert and prior to joining Microsoft a founder of a successful consulting firm targeting .NET and web development. Follow Matt on Twitter at @codemillmatt and his personal blog at codemillmatt.com.

Related Posts

Comments

Comments are disabled in preview mode.