Scott Hanselman

What is Serverless Computing? Exploring Azure Functions

August 27, 2016 Comment on this post [40] Posted in Azure | nodejs
Sponsored By

There's a lot of confusing terms in the Cloud space. And that's not counting the term "Cloud." ;)

  • IaaS (Infrastructure as a Services) - Virtual Machines and stuff on demand.
  • PaaS (Platform as a Service) - You deploy your apps but try not to think about the Virtual Machines underneath. They exist, but we pretend they don't until forced.
  • SaaS (Software as a Service) - Stuff like Office 365 and Gmail. You pay a subscription and you get email/whatever as a service. It Just Works.

"Serverless Computing" doesn't really mean there's no server. Serverless means there's no server you need to worry about. That might sound like PaaS, but it's higher level that than.

Serverless Computing is like this - Your code, a slider bar, and your credit card. You just have your function out there and it will scale as long as you can pay for it. It's as close to "cloudy" as The Cloud can get.

Serverless Computing is like this. Your code, a slider bar, and your credit card.

With Platform as a Service, you might make a Node or C# app, check it into Git, deploy it to a Web Site/Application, and then you've got an endpoint. You might scale it up (get more CPU/Memory/Disk) or out (have 1, 2, n instances of the Web App) but it's not seamless. It's totally cool, to be clear, but you're always aware of the servers.

New cloud systems like Amazon Lambda and Azure Functions have you upload some code and it's running seconds later. You can have continuous jobs, functions that run on a triggered event, or make Web APIs or Webhooks that are just a function with a URL.

I'm going to see how quickly I can make a Web API with Serverless Computing.

I'll go to http://functions.azure.com and make a new function. If you don't have an account you can sign up free.

Getting started with Azure Functions

You can make a function in JavaScript or C#.

Getting started with Azure Functions - Create This Function

Once you're into the Azure Function Editor, click "New Function" and you've got dozens of templates and code examples for things like:

  • Find a face in an image and store the rectangle of where the face is.
  • Run a function and comment on a GitHub issue when a GitHub webhook is triggered
  • Update a storage blob when an HTTP Request comes in
  • Load entities from a database or storage table

I figured I'd change the first example. It is a trigger that sees an image in storage, calls a cognitive services API to get the location of the face, then stores the data. I wanted to change it to:

  • Take an image as input from an HTTP Post
  • Draw a rectangle around the face
  • Return the new image

You can do this work from Git/GitHub but for easy stuff I'm literally doing it all in the browser. Here's what it looks like.

Azure Functions can be done in the browser

I code and iterate and save and fail fast, fail often. Here's the starter code I based it on. Remember, that this is a starter function that runs on a triggered event, so note its Run()...I'm going to change this.

#r "Microsoft.WindowsAzure.Storage"
#r "Newtonsoft.Json"

using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using Newtonsoft.Json;
using Microsoft.WindowsAzure.Storage.Table;
using System.IO; 

public static async Task Run(Stream image, string name, IAsyncCollector<FaceRectangle> outTable, TraceWriter log)
{
    var image = await req.Content.ReadAsStreamAsync();
    
    string result = await CallVisionAPI(image); //STREAM
    log.Info(result); 

    if (String.IsNullOrEmpty(result))
    {
        return req.CreateResponse(HttpStatusCode.BadRequest);
    }

    ImageData imageData = JsonConvert.DeserializeObject<ImageData>(result);
    foreach (Face face in imageData.Faces)
    {
        var faceRectangle = face.FaceRectangle;
        faceRectangle.RowKey = Guid.NewGuid().ToString();
        faceRectangle.PartitionKey = "Functions";
        faceRectangle.ImageFile = name + ".jpg";
        await outTable.AddAsync(faceRectangle); 
    }
    return req.CreateResponse(HttpStatusCode.OK, "Nice Job");  
}

static async Task<string> CallVisionAPI(Stream image)
{
    using (var client = new HttpClient())
    {
        var content = new StreamContent(image);
        var url = "https://api.projectoxford.ai/vision/v1.0/analyze?visualFeatures=Faces";
        client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", Environment.GetEnvironmentVariable("Vision_API_Subscription_Key"));
        content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
        var httpResponse = await client.PostAsync(url, content);

        if (httpResponse.StatusCode == HttpStatusCode.OK){
            return await httpResponse.Content.ReadAsStringAsync();
        }
    }
    return null;
}

public class ImageData {
    public List<Face> Faces { get; set; }
}

public class Face {
    public int Age { get; set; }
    public string Gender { get; set; }
    public FaceRectangle FaceRectangle { get; set; }
}

public class FaceRectangle : TableEntity {
    public string ImageFile { get; set; }
    public int Left { get; set; }
    public int Top { get; set; }
    public int Width { get; set; }
    public int Height { get; set; }
}

GOAL: I'll change this Run() and make this listen for an HTTP request that contains an image, read the image that's POSTed in (ya, I know, no validation), draw rectangle around detected faces, then return a new image.

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log) {
var image = await req.Content.ReadAsStreamAsync();

As for the body of this function, I'm 20% sure I'm using too many MemoryStreams but they are getting disposed so take this code as a initial proof of concept. However, I DO need at least the two I have. Regardless, happy to chat with those who know more, but it's more subtle than even I thought. That said, basically call out to the API, get back some face data that looks like this:

2016-08-26T23:59:26.741 {"requestId":"8be222ff-98cc-4019-8038-c22eeffa63ed","metadata":{"width":2808,"height":1872,"format":"Jpeg"},"faces":[{"age":41,"gender":"Male","faceRectangle":{"left":1059,"top":671,"width":466,"height":466}},{"age":41,"gender":"Male","faceRectangle":{"left":1916,"top":702,"width":448,"height":448}}]}

Then take that data and DRAW a Rectangle over the faces detected.

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    var image = await req.Content.ReadAsStreamAsync();

    MemoryStream mem = new MemoryStream();
    image.CopyTo(mem); //make a copy since one gets destroy in the other API. Lame, I know.
    image.Position = 0;
    mem.Position = 0;
    
    string result = await CallVisionAPI(image); 
    log.Info(result); 

    if (String.IsNullOrEmpty(result)) {
        return req.CreateResponse(HttpStatusCode.BadRequest);
    }
    
    ImageData imageData = JsonConvert.DeserializeObject<ImageData>(result);

    MemoryStream outputStream = new MemoryStream();
    using(Image maybeFace = Image.FromStream(mem, true))
    {
        using (Graphics g = Graphics.FromImage(maybeFace))
        {
            Pen yellowPen = new Pen(Color.Yellow, 4);
            foreach (Face face in imageData.Faces)
            {
                var faceRectangle = face.FaceRectangle;
                g.DrawRectangle(yellowPen, 
                    faceRectangle.Left, faceRectangle.Top, 
                    faceRectangle.Width, faceRectangle.Height);
            }
        }
        maybeFace.Save(outputStream, ImageFormat.Jpeg);
    }
    
    var response = new HttpResponseMessage()
    {
        Content = new ByteArrayContent(outputStream.ToArray()),
        StatusCode = HttpStatusCode.OK,
    };
    response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
    return response;
}

I also added a reference to System. Drawing using this syntax at the top of the file and added a few namespaces with usings like System.Drawing and System.Drawing.Imaging. I also changed the input in the Integrate tab to "HTTP" as my input.

#r "System.Drawing

Now I go into Postman and POST an image to my new Azure Function endpoint. Here I uploaded a flattering picture of me and unflattering picture of The Oatmeal. He's pretty in real life just NOT HERE. ;)

Image Recognition with Azure Functions

So in just about 15 min with no idea and armed with just my browser, Postman (also my browser), Google/StackOverflow, and Azure Functions I've got a backend proof of concept.

Azure Functions supports Node.js, C#, F#, Python, PHP *and* Batch, Bash, and PowerShell, which really opens it up to basically anyone. You can use them for anything when you just want a function (or more) out there on the web. Send stuff to Slack, automate your house, update GitHub issues, act as a Webhook, etc. There's some great 3d party Azure Functions sample code in this GitHub repo as well. Inputs can be from basically anywhere and outputs can be basically anywhere. If those anywheres are also cloud services like Tables or Storage, you've got a "serverless backed" that is easy to scale.

I'm still learning, but I can see when I'd want a VM (total control) vs a Web App (near total control) vs a "Serverless" Azure Function (less control but I didn't need it anyway, just wanted a function in the cloud.)


Sponsor: Aspose makes programming APIs for working with files, like: DOC, XLS, PPT, PDF and countless more.  Developers can use their products to create, convert, modify, or manage files in almost any way.  Aspose is a good company and they offer solid products.  Check them out, and download a free evaluation.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
August 27, 2016 4:29
Functions are incredibly powerfull, We have started using powershell functions to write small Devops tasks that we can trigger via a webhook.. Since Functions has the azure sdk already available, its a great place to host and run cloud functions to manage all of your other cloud assets.
August 27, 2016 4:39
This is pretty amazing. There is so much choice available right now to solve peoples problems.
August 27, 2016 5:07
Thanks a lot for the article. It's something new to explore for me
August 27, 2016 5:27
Great article. I always explain on what is serverless like this, Serverless does not mean there is NO server at the backened. Serverless IS your APP MODEL which is not dependent on any server infrastructure. Azure Functions and Azure Logic Apps is one of the best example of Serverless.
August 27, 2016 5:34
but it's higher level than that :-)
August 27, 2016 8:22
Yeah, that looks good.
August 27, 2016 9:13
Sweet!... I have always ignored serverless computing because I find it absurd and too shy to ask. This brief explanation nail it for me.

So what .NET API level is supported? 4.5? 4.62? 2.0?
August 27, 2016 9:48
CGI strikes back!
August 27, 2016 10:21
In my head this is a webjob, queue, and web API endpoint without all the plumbing.
August 27, 2016 18:44
Please don't tell anyone anywhere you made this in 15 minutes. This took you an hour and another 40 minutes to blog at least; and in human IT worker terms that's 1/2 a day... Nice work, just don't say 15 minutes.
August 27, 2016 20:03
Can you add references to nuget packages?
August 27, 2016 20:54
Awesome Scott. Hard to know what solution to use these days, and that's a good thing. I am wondering though, what do we use for a test environment if we go whole hog with this 'functions in the sky' approach? Another test instance on Azure, sure, but anything that I can set up locally on Windows/.Net to have some unit tests running on these functions? I ask out of laziness.
August 27, 2016 21:17
This is a really cool feature of Azure. I already have a couple ideas on how to use this on some of my work items. Thanks for the clear example and walkthrough!
August 27, 2016 22:24
Very nice info i came across. Thanks for the awesome share :)
August 28, 2016 5:09
Very cool, thanks for sharing. I tried to "follow along at home" and had trouble with compiling the code as-is. Things I did to get it running:

1) Add the following:

#r "System.Drawing"

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Microsoft.Azure.WebJobs.Host;

2) Change the inputs and outputs under the "Integrate" tab to "HTTP" (default names are fine)

If MS friends are reading this - Intellisense in the browser would go a long way to help this kind of debugging.
August 28, 2016 10:41
One of the PMs for Azure Functions here, answering some questions I see:

1. It uses .NET 4.6 - we hope to move to dotnet core later on.

2. This is based on WebJobs - in fact, you can find the host open source at a repo which still bares webjobs in the name. https://github.com/Azure/azure-webjobs-sdk-script

3. There is a local host for development. It's still fairly early in development, but our CLI provides a local instance - npm install -g azurefunctions

Please test in another App in the cloud though. Don't push straight to prod without testing on Azure. :)

Feel free to reach out to our team via twitter or ask questions on stack overflow, we try to stay on top of issues we see.
August 28, 2016 15:43
Until when Microsoft will keep ignoring F#? Like it or not, the future is about functional and reactive programming.
August 28, 2016 15:53
Oh sorry, it does support F#. Deceived by the first screenshot.
August 28, 2016 16:04
"And in 2016, SkyNet became aware..."
August 28, 2016 21:47
Is there a way to call out from a function to another REST service? Or are functions only to sit in the middle of azure? An example would be a timed function that went and retrieved/stored results from a HTTP service.
August 29, 2016 12:07
Hi,

I have been testing MS Azure & Google Cloud at the same time for two different projects.
I have been using them for high traffic websites that require on-demand resources.
But this is something new to me. Interesting article.

Thanks
Tauseef Alam
August 29, 2016 14:17
Scott, is there any info on what CPU my Azure Function will run on when I choose the Dynamic App Service Plan?

Thanks for a really good example of how to use Functions!

Johan
August 29, 2016 18:20
When I tinker with Azure Functions or Lambda, I imagine Doc in Back to the Future: "Server? Where we're going we don't need servers!"

August 29, 2016 19:24
Is there any built in security to control access so some malicious individual can't spam/dos your function and cost you a bunch of money?
August 29, 2016 21:02
Hello, I'm also with the Azure Functions team, helping answer some additional questions:

Karlo: Yes, you can add Nuget references. See more information here.

James: You can absolutely make requests from your functions and consume external services. Similar to the way Scott, is consuming the Cognitive Services API in his example.

Onur: Glad you found that F# is supported. That support is being enhanced in the next release as we've been working closely with the F# team to ensure we have great support for it.

Chad: Yes. Access control is managed at the trigger level, since different triggers offer different mechanisms (e.g. HTTP triggers support request authentication, Storage Queues and Blobs using authenticated publishers, etc.)
August 30, 2016 11:57
How is this different to a really, really small web app deployed to Azure?

From the article there is still a bunch of configuration needed (just as with web app on Azure) and as someone else mentioned there is also testing to consider.

August 30, 2016 18:42
Is it possible to run a legacy win32 exe (i.e. unsafe code) via the "Batch" support?
August 30, 2016 21:19
Thanks for explaining it clearly and quick! Still it does not look secure so far, it is like for prototyping only.
August 31, 2016 2:22
Scott, I was excited about playing for free with this. I immediately made 2 HipChat webhooks in it and they worked great (ported some PHP code I was running on a server somewhere else).

I thought it would be totally free given the quotas. However, I got a notification that $0.01 had already been taken from my free allocation of $200 trial just a few hours later.

I looked into the details and found it was for an auto-created storage bucket. I figured "no big deal" I'll remove that and it'll be free, since my function is totally stateless.

Now, the multi-function app is totally broken. I get:

Error:

An error occurred when trying to create a controller of type 'FunctionController'. Make sure that the controller has a parameterless public constructor.


... when I attempt to even click on the app. I'm guessing now that the functions themselves are stored in that storage bucket, but it still let me remove the bucket and now it's totally broken.

I assume I'll have to destroy the app and re-create it, but I just wanted to warn others that it's not actually free even though "Functions" has a free quota. It requires Storage.

Most disappointing, both in personal-use pricing and in it letting me totally destroy my applications (I saved most of the code in a Gist thankfully, in case I care later).

Thanks.
August 31, 2016 2:46
Hey Chris Benard,

I'm one of the PMs for Azure Functions.

You're right that we have a free grant on Dynamic Tier, but that Dynamic Tier does require Storage, so it will break when you remove it. We don't have warning on the Storage blade for removal because Storage isn't aware on what is using it, but we could probably have better warnings for you as part of our Quickstart/create process. I've created these bugs to track improving that experience:
- https://github.com/projectkudu/AzureFunctionsPortal/issues/517
- https://github.com/projectkudu/AzureFunctionsPortal/issues/516

You can use Functions in a "Free" App Service Plan, which doesn't require Storage (though we still create one by default), but it will pretty much only work with HTTP. Dynamic Tier will still require Storage, for the foreseeable future, by design. That does mean it's not totally free. I'll also follow up with our marketing/pricing material to make sure that is more clear: We have a free grant for our billing meters, but we have dependencies which aren't free.

Sorry for the confusion and some experience issues. Appreciate you reporting them to us so we can improve. :)

-Chris Anderson
August 31, 2016 4:48
I'm one of the program managers on the Azure Functions team.

Ian W: You can use some legacy Win32 code on Functions, but only if they aren't using APIs that are blocked by the underlying App Service application sandbox. For instance, GDI+ APIs can't be used. You can try uploading your .exes and see if it uses any of these.

Artyom K: Could you share more about why you don't think this would be a useful service beyond prototyping? Our goal is to enable real scenarios, but admittedly the portal tooling is not nearly as powerful as Visual Studio. :)
August 31, 2016 13:19
This is really cool- and also worrying: every layer of 'cloud abstraction' is another opportunity to charge the customer. I wonder what kind of margins are at play here, i.e. what are you paying vs. what it would cost to execute a function on the underlying hardware.
August 31, 2016 14:22
@Chris Anderson
The lack of transparency over costs the impossibility to set a spendig limit is killing your efforts to make Azure a widely used service. Individual developers avoid Azure because of this, and if there aren't enough individual developers who use your services the enterprises will not have enough resources to hire so they will not use your services.
It isn't an issue only with Functions, I followed a link from an Azure IoT article few months ago, didn't actually connect any device, yet at the end of the month I was charged $700 USD on my credit card. I was shocked. And it wasn't even possible to tell from Azure Portal which esources were generating cost and which aren't, I had to work with Azure support two days until all unwanted services, storage and others were deleted.
Besides clear visibility and detailed reports a spending limit should also be enabled:
https://feedback.azure.com/forums/170030-billing/suggestions/3238796-spending-limit-or-maximum-cost-cap-for-azure
August 31, 2016 15:59
Hi
That's a cool example.
I followed the links and the Azure Functions Overview page states:
Choice of language - Write functions using C#, Node.js, Python, F#, PHP, batch, bash, Java, or any executable.

I use at least 4 of those languages, so I'm just asking out of curiosity:

1) I note that C++ and VB.Net aren't in the list yet. Are C# and F# considered the .NET languages of choice now?

2) It does state "Any executable" - does that mean a standalone.exe, or can you upload any supporting dlls as well?

Thanks
Diarmuid
August 31, 2016 19:33
Hi,

I'm not familiar with the #r syntax in place of using. My google-fu wasn't strong enough to turn up any results. Anyone have a link that would help clarify?

Thanks!

-Nathan W.
August 31, 2016 20:09
Nathan, I'm guessing that's a Nuget package directive so it will restore it. Not 100% but it makes sense how it's being used.
August 31, 2016 20:24
Thanks Chris,

Found this from a co-worker that explains it also:

https://github.com/scriptcs/scriptcs/wiki/Writing-a-script#referencing-assemblies

September 02, 2016 19:39
When you take into consideration the fact that modern technology grows, you pretty much need to have a wireless router that might be used for the following five years. Prior to buying it, ensure that the router works with your current Personal computer.

You can browse ipaddressdefinition.com for more information
September 06, 2016 14:46
Scott, looks like your post caused something similar to the slashdot effect:

http://news.softpedia.com/news/catastrophic-ddos-attack-pummels-linode-servers-over-the-weekend-507985.shtml

Maybe the Hanselman Effect could become a thing :)
September 26, 2016 5:15
Check out here, the geocode actually suggests a way to test it. it's probably something new in their documentation. cara mengobati sakit ulu hati secara alami

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.