We run an application which is event-driven and utilizes microservices across several trust boundaries. The application originated from our ‘automate everything you do more than twice’-mantra and is now continuously evolving and making our live as a small DevOps team easier.
The underlying messaging mechanism of our app is an Azure Service Bus (or actually, multiple buses), with several topics and subscriptions upon those topics. As all of our events flow through Azure already, it’s easy to store them in blobstorage and use them for auditing/analysis/what-have-you at a later point in time. Now that the usage is increasing, we felt that it was time to add some alerting and we made plans for a new service that would react to our ‘ActivityFailed’-event, it would then send an email as soon as one of those events (luckily they don’t occur that often) would occur. Sounds easy enough, right?
Dockerize or … ?
As you may know Docker is a great tool to envelope your application into a well-known and well-described format so that it can run anywhere the same as it would on your machine. We would develop the service in .NET Core, so it would be easy enough to Dockerize it and host it somewhere just like some of the other services. But last night I thought to myself ‘Wait, we run in Azure, use the Azure Service Bus and only need to react to messages on the bus..’ and I decided I would try to create an Azure Function to react to the event and send me the mail. It literally took me about 15 minutes to develop. I’ll describe the process below.
Going serverless
Azure Functions are a way to process events in an easy way without having to worry about where you run it. It’s basically ‘just code’ and Azure does the rest for you. I had played with Azure Functions before, but didn’t really find a use-case for it. I do however feel that they are the next step after containerization. It may not fit all problems, but there are certainly use-cases out there which would benefit from a completely serverless architecture.
Step one is going to the Azure Portal and creating a new ‘Function App’. Tip: use a consumption plan if you only want to be billed for your actual usage.
Once your Function App is created, navigate to it. The first time you navigate to your Function App, you won’t have any functions yet, so you will be presented with the Quickstart Wizard. We will not use it, so scroll down and click ‘Create your own custom function’.
Now from the template gallery, select C# as language and ‘Data Processing’ as scenario. Click the ‘ServiceBusTopicTrigger-CSharp’ template and enter the following values in the corresponding fields:
- Name: a meaningful name for your function, pick something like ‘EmailNotifier’
- Topic name: this is the name of the topic on your service bus which you’ll listen to
- Subscription name: The subscription name on top of the topic specified above
- Access Rights: select ‘Manage’, and make this match the SAS Token. As of writing this post, there’s a bug preventing you from using the expected ‘Listen’ permissions. That is – you can use it, but your function will cease to trigger after a few hours.
- Service Bus connection: Service Bus connection strings are saved as Application Setting for your entire Function App and can be shared over multiple functions. Just click ‘new’ the first time and enter the connection string without the EntityPath in it
You will now have a basic function. Congratulations!
Making it do something useful
In order to do something meaningful with our app, we’ll need to go through a few steps. First let’s discover what is created for us. Click the ‘Files’ button on the top right of the editor:
You will see that you have two files:
- function.json – which describes your in- and outputs
- run.csx – which is the code for your function
Take some time to familiarize you with both files and notice that the run.csx isn’t much different from a regular C# program.
It actually has using statements and a public static void Main()
alike function called ‘Run’. Azure Functions provides you with framework libraries such as System and System.Linq and you can include some additional assemblies using the #r
directive. A full list of all available assemblies can be found here. As you can see, using all types/methods within the Microsoft.ServiceBus namespace will be easy. I can just add a the following lines of code to the beginning of run.csx:
#r "Microsoft.ServiceBus" using Microsoft.Servicebus;
I also will be using Newtonsoft.Json to deserialize my messages and SendGrid to send my emails, so I will need some way to restore the NuGet packages. This turns out to be quite easy. I just have to add a new file and tell my function what my dependencies are. Add a file called project.json
to your function like so:
Now add the following code to it:
{ "frameworks": { "net46":{ "dependencies": { "Sendgrid": "8.0.5", "Newtonsoft.Json": "9.0.1" } } } }
This will trigger my function to perform a NuGet restore before executing my function for the first time. Don’t forget to add the using statements to your code.
We’re almost ready to get the code done but first we’ll need to add an output to our function. Head to the ‘Integrate’ section of your function and take note of the ‘Message parameter name’, we will use this later on. Now click ‘New Output’ and select ‘SendGrid’ (currently in preview).
The easiest way to utilize this output, is to enter the from, to, subject and API key here. Mind you that the API key is the name of an Application Setting which contains the actual key!
Save the changes and then add the Application Setting corresponding to the API key name (SendGridApiKey in this example) by clicking ‘Function App Settings’ and then ‘Configure app setings’
Once you’ve added the input, take a look at your function.json
and see how it reflects the changes.
Finally adjust the code for run.csx to reflect your application logic. Notice how I named the ‘Message parameter name’ incomingMessage and added an out Mail message
to the method signature:
#r "SendGrid" #r "Newtonsoft.Json" #r "Microsoft.ServiceBus" using SendGrid.Helpers.Mail; using Newtonsoft.Json; using System; using System.Threading.Tasks; using Microsoft.ServiceBus.Messaging; public static void Run(BrokeredMessage incomingMessage, TraceWriter log, out Mail message) { message = null; // set output to null, it must be set as it is a mandatory out parameter var msgBody = incomingMessage.GetBody(); var msg = JsonConvert.DeserializeObject(msgBody); log.Info($"Event type: {msg.messageType}"); if(msg.messageType == "activityFailed") { log.Info($"Found a failed activity: {msg.processId}"); message = new Mail(); var messageContent = new Content("text/html", $"Activity Failed: {msg.processId}"); message.AddContent(messageContent); } }
That’s it. Click Run and your message will be parsed, checked and you will be alerted in case something goes wrong 🙂
The result
I’ve already received my first alert – even though I triggered it intentionally, it’s still awesome to see that I now have a low-cost, easy to use solution which only runs when it should. Of course there optimizations to be made, but for now it does the trick. And in the meanwhile I’ve learned some more about building serverless applications using Azure Functions.
[This post appeared before on my personal blog]