Serverless in Context
Getting the Most From Azure Functions
*** A newer, updated version of this course is available here. ***
An Introduction to Azure Functions
Serverless Computing has emerged as a capable and low-friction means to execute custom logic in the public cloud. Whether you're using Amazon Lambda, Google Cloud Functions, or Azure Functions, you have a wide variety of target languages, ecosystem integrations, and deployment mechanisms to choose from. All this while leaving the heavy lifting of server provisioning and maintenance to the experts, which gives you plenty of time to focus on your differentiated application functionality.
In this "Introduction to Azure Functions" course, you’ll learn how to build Azure Function applications in the cloud. You'll discover the core feature set of Functions and see how to integrate with a variety of sibling Azure services. You'll explore Function topics like security, monitoring, deployment, and testing best practices. You'll also learn about ideal Functions use cases and the pricing model. Finally, you'll learn about how we've arrived at the serverless computing model, and where serverless is likely to go in the future. By the end of this course, you’ll have a solid foundation to continue exploring Functions on your own, and incorporating Azure Functions capability into your work.
An Introduction to Azure Functions: What You'll Learn
|Lecture||What you'll learn|
|Intro||What to expect from this course|
|Serverless Computing In Context||Understanding what serverless computing is, and how we got here|
|Core Features||A high-level overview of what Azure Functions is, and its basic capabilities|
|Creating Your First Function||A demo of creating your first function in the Azure portal|
|Security||A review of security features in Azure Functions|
|Using API Key Management||A demo of configuring an Azure Function to require API key use|
|HTTP Proxies||A discussion of lightweight HTTP Proxy support|
|Proxying Azure Blob Storage||A demo of using Functions' HTTP Proxy support to front Azure blob storage|
|Triggers and Bindings||Event-based triggering of functions and declarative binding of inputs and outputs|
|Triggering on Queues and Binding to DocumentDB||A demo of Triggering with Azure Queues and Binding Function Output to DocumentDB|
|Testing and Debugging||Tools and techniques for working with Functions during the development cycle|
|Deployment||Options for deploying Azure Function apps into production|
|Deploying From a Local Git Repo||A demo of deploying a complete Azure Function app to the cloud, from a local Git repository|
|Monitoring||Tools for monitoring Azure Functions during dev, test, and release|
|Use Cases||A discussion of ideal use cases for serverless compute and Azure Functions|
|Pricing||A review of how Functions are priced, and a demo of determining price using the Azure Pricing Calculator|
|Serverless in the Future||A short discussion on the future of serverless in the cloud|
|Summary||Course wrap up|
If you have thoughts or suggestions for this course, please contact Cloud Academy at firstname.lastname@example.org.
Okay, so now let's take a closer look at triggers and bindings in Azure Functions and see how those features kind of combine to make it a lot easier to work with functions, to author functions, and to integrate them with other services within the Azure ecosystem.
So to demonstrate this, what I'm gonna demonstrate is kind of a simplified kind of order management system. Where we're going to use an Azure Function to act as the glue between orders that are coming in and registered or queued on an Azure queue, and ultimately those orders being processed in our function and then written to a database. Specifically DocumentDB, the NoSQL database storage in Azure.
So to do that we'll demonstrate the queue trigger. Which will allow us to bind to the incoming data that's coming into the queue and we'll trigger or kick off our function as new messages arrive, and then we'll also bind to the DocumentDB database as output so that we can decoratively define that binding and we don't have to write a bunch of boilerplate code that allows us to both get the item from the queue as well as write it to the database. You'll see how this is a lot simpler because we're using the binding.
So let's create a new function in our function app, and we're going to use the queue binding, the queue trigger, excuse me, and I'll stick with C# since I know that pretty well. We'll stick with our standard name. The queue name that we're gonna use is called orders, and we need to create a new connection to a storage account, and what this is going to do is it'll create a connection string setting in our app settings key value dictionary, and it will create a new name for us and then map it to the actual connection string to our desired storage account. So I already have the storage account created. This is the one that I wanna connect to. So I'm just gonna click on that one, and the nice thing is that that functions portal will actually create all of this stuff for me. So I get this new storage connection string with a default name of name of the storage account_storage. So that's the one that I wanna use. So I'm gonna click create. And this is gonna create kind of some boilerplate code for me. Kind of an initial function that I can then modify and do something more interesting with. This will just take a second.
Okay, so now we have our function, and just to show you kind of that connection string that was created, let's back up a second. We'll go to the, oh yeah, we certainly wanna save our function first. We'll do that and then we'll back up to the top level and go into platform features, and application settings, and if I scroll down, this is kind of the umbrella level at my function app level itself. So if I scroll down, you can see here that I've now got this new connection string in here. Which corresponds to that connection to Azure storage, and that's where my queue is going to live. I'll be reading messages or my function app will be reading messages from that queue. So just wanted to show you how that exists and that's created for you automatically by the Azure Function binding mechanism.
Okay so let's go back to our queue, and we have to do a couple of things if we wanna read the message from this queue or actually do something interesting with it. The first thing we need to do is I need to reference an assembly in .NET called Newtonsoft.Json, and if you are a .NET programmer, you've almost certainly used this library at some point and this is the defacto standard JSON serializer deserializer in .NET. If you're not familiar with .NET, you're a Node programmer or something like that, this is essentially the library that allows you to do JSON serialization or deserialization.
So I'm gonna pull that in here using, in Azure Functions in .NET you have this special syntax hashtag R which allows you to reference assemblies that you want to use in your code. So ordinarily that's not valid C# syntax, but it is valid in this case, in Azure Functions. So pulling a couple of other namespaces here that I am planning on needing. So Newtonsoft.Json. There we go. All right so now we have my kind of boilerplate function code. Obviously it's not doing anything very interesting right now other than talking to the log. I do have, however, because I've set this up to trigger off of a queue. By default I get an argument. Which corresponds to the actual data that's in my queue message. So that's nice. That's going to be JSON data. So I need to turn that into an object that I can do something with. So that's why I pulled in that JSON serializer so that I can actually serialize that string into an object. So let's do that. This JSON is gonna correspond to an order. So I'm just going to convert that, deserialize object, and we'll say my queue item.
All right, that should give me an order that I can then start to pick through and use the attributes from. Just so that we can test it out and make sure it's working, let me just do something like this. We'll say, this is the string. We'll say order.description. Since I'm intending that this order will have a description associated with it. We'll just dump that to the log.
All right, so actually we can go ahead and run this and test it out. So why don't we save this and then I'm gonna come over to the right hand side, click text, and this allows me to kind of invoke this message without actually, I won't actually have to write anything to a queue. This is just kind of a quick shorthand way to test this real quick. So let's say, create a simple order that just has a description and nothing else and we'll say, this is an order. Not very interesting, but enough to test, and then we'll click run. And we'll compile, and yeah, sure enough, down in our log we get this is an order. So looks like it's working. All right, so far so good.
So now we need to write some code. Well we need to set up the binding to our database. Our DocumentDB database, and we need to write the code that's ultimately going to produce the document or produce the data that we're going to write. So let's set up the binding first. So if I go into the integrate tab for my function, then we'll see a couple of things in here already. First of all, we already have an Azure queue trigger set up. That was created when we created the function itself. So now what we wanna do is we wanna add a new output for this function. So I'll click on New Output and we get a variety of options to choose from, and I'm gonna click on Azure DocumentDB. Say select, and then it prompts me for some information. First thing it wants to do is say, well do you wanna use an output argument or do you wanna use the return value of the function corresponding to the document that you're going to write? The JSON that you're going to write to DocumentDB. I'll just use the function output, the function return value. So I'll just click that checkbox. You can use an, if you're familiar with C# and things like out arguments, you can use an output argument instead of the return value of the function, if that's your preference. It's kinda up to you. You get the same functionality either way. The database name, I happen to know that I've just called the database default. I've already set up the database, the DocumentDB account and database already, so I already know what those are, and my collection is called new-orders. So I'll just add that here. Would you like DocumentDB database and collection to be created? We'll say sure but I happen to know they're already created so that's okay. All right so now we need an account connection. So again, this is just like our storage account where we need to connect to a storage account and then create an application setting that maps a name that we can reference to the connection string to storage. We need the same thing for DocumentDB as well. So I'm going to click on new here, and that'll give me, this is gonna go in to create a new application setting for me. Again I already have my DocumentDB account set up so I'm just going to click on the one that I've set up and the portal will churn for a second. Now it's created a new connection string for me. Click save.
Okay so now we have our DocumentDB binding all set up. All we have to do is return a value from our function that value will be a document. Which is automatically written to storage for us. So that's pretty cool. Now to get back to our code here. We get an error saying well you can't return void and that's certainly the case because now we need an actual return value in C#, again, to correspond to this document that we're going to write to doc DB. So now I'll return object instead of void. So we'll just need to create an object and return it from our function and this will be the object which is written bound to our DocumentDB account and written to that collection ultimately as JSON. So let's create some attributes here. We'll say this is datetime.utc. Now for the created attribute of the document, something like unit cost we'll just take directly from the order itself. What's the description? We'll say do something a little bit here, a little bit different. We'll say description and we'll say to lower. Make it all lower case from the incoming message. Units we'll just pull directly from the message itself. If we can omit an ID and DocumentDB will create it for us, but I'll actually add one here, and the last thing we'll just add a static text property or something like that. Okay very good. So now we have a document or we have an object which will be bound to .DB and written as JSON for us. We'll save that real quick.
So before we continue, let's consider for a moment the alternative to using the bindings that we're using for input and output. So because we have a queue trigger, we automatically get a binding created for us for the input message. Which is written to the queue that we read from the queue when we trigger the function. So the alternative to using some sort of a declarative binding like that would be that we have to write a bunch of boilerplate code that connects to the queue and reads the next message off the queue and handles any errors, et cetera, and so that would just be an extra half a dozen or a dozen or so lines of code that would appear at the top of every function like this that wants to read messages from a queue. Similarly if we want to write JSON data to a DocumentDB collection upon completion of this function then we would have a similar 10, 12 lines or so of code that connects to a DocumentDB database, issues the command to write a new document, handles any errors that result, et cetera.
So the other thing to note here if we look in the files that correspond to our function app, we can see if we click on function.json, this is the JSON configuration data that configures our bindings for us. So if you want to, I showed you how to set this up in the Azure portal, but you can actually write this code or write this file out manually, function.json. It has a well known well documented syntax for setting up bindings like this. And you can do this yourself. You can actually specify multiple input and output bindings for a given function, and you can specify all of those properties just in a text file like this called function.json. One for each of the functions that you have in your function app, and there's some well known, if you look at the Azure Functions documentation online there's some well known syntax instead of attributes that you have to specify for each of the binding types.
Okay so let's go ahead and test this real quick and we'll actually add the rest of the attributes that we need to our input. We'll see our unit cost is 25, and units is 100. I think that's all we need for our order to test. So if I click run here, then yes we see function started, function completed with success, and we see that this is an order is written to, which is the description, which is written to our log. So that's what we expect.
So if we go to our DocumentDB database and look, I'll navigate down to document explorer and we're in our new orders collection and you can see we've got a single document in here so I'll click on that and sure enough, we have a new document created. It has a created attribute of UTC now from a few seconds ago. Unit cost is what we added description, is this is an order. Units is 100, tax is 2.1, et cetera. So looks like we got our document written correctly. So again, that's one of the real handy things about functions is we didn't have to write all that boilerplate code to update that document or add that document to the collection.
Okay so the final thing we wanna see is we want to add a message to the queue so that we can actually see this happen kind of end to end. So to do that, I'm using a tool called the Azure storage explorer. Which I'll switch over to here. If you're interested, if you use Azure storage and you're interested in storage explorer you can actually download it for free at storageexplorer.com, and this tool, it's a desktop application that runs locally and allows you to interact with blobs, tables, queues, et cetera in an Azure storage account. So I'm going to use it, I've already connected to my storage account. You can see I've got an orders queue in my queue that my function is listening on. So I'm gonna add a new message to the queue. In fact I'm going to cheat. Go back to my test window and I'll copy and paste all this data. Here we go. And I'll change some things up a little bit. We'll say these are widgets. Unit cost is 17.50 and units we'll say 15,000. So I'm going to, when I click add message, this will queue the message in the queue, but we'll see it disappear almost immediately. At least we should, because the function should be listening for it to arrive. So we see it's there, and if I refresh real quick, we see it's gone. So it's already been written, presumably, by the function infrastructure. So if I switch back, we should see in the log. Yes, we see that the function was started, and we see widgets was written to the, which is our description written to the console or the log and then we see the function completed successfully.
So last thing we'll check is just to make sure. We'll check our DocumentDB database. And it looks like we have a new document added, and yes. So we have a new document. Unit cost is 17 and a half. Widgets is our description. Units is 15,000, tax is 2.1. So it looks like we got, that document got added correctly as well.
About the Author
Josh Lane is a Microsoft Azure MVP and Azure Trainer and Researcher at Cloud Academy. He’s spent almost twenty years architecting and building enterprise software for companies around the world, in industries as diverse as financial services, insurance, energy, education, and telecom. He loves the challenges that come with designing, building, and running software at scale. Away from the keyboard you'll find him crashing his mountain bike, drumming quasi-rythmically, spending time outdoors with his wife and daughters, or drinking good beer with good friends.