Real cost of developing a project in Azure

Recently I've moved one of my side projects fully to the cloud. In this short post I'd like to show you what is the real cost of developing a fairly small yet multi-dimensional project and how Azure gives me and my client flexibility to select what is really needed in this particular moment.

The project

I won't go into details here. Just to make a long story short - we're gathering data from many electronic devices and then provide reports based on different time intervals, places and custom properties to the clients. There's also a need to handle the old legacy system and migrate data from the old database to the new one.

Azure components

Mentioned project is built using following elements:

  • Storage account
  • Function App
  • 2x Web App
  • SQL database
  • Azure B2C
  • Application Insights

worth mentioning here is the fact, that by default we're using free tiers for Web Apps and SQL server.

Taking into consideration all above our monthly cost of developing this project is 1,50 EUR on average.

Caveats

Because we're using free tiers, we have to be aware of limits - like available CPU minutes per day - but on the other hand, there's no problem to scale up when needed. This is what really made us into cloud - if only small features are being developed, free tier is more than enough. If we're during a strenuous period of developing new features, we can just go to the portal and change a tier.

Additionally we have to be aware, that on production we won't be able to use free versions of Azure components because of lacking features and much higher traffic. On the other hand, saving money in such way instead of paying much more money for resources we won't be able to utilize is a much smarter decision.

Detailed cost

There're two resources which make the most of our cost: Storage account and Function App. This is because they're the "hot path" in the system - one of the functions fetches data from FTP and pushes each record to a queues. Other functions take data from queues and perform some transformations, store data and push it further. When I checked my Billing page, the cost looks like this:

  • Storage account 0,62 EUR
  • Function App 0,55 EUR
  • B2C 0,09EUR
  • The rest of resources 0 EUR

We didn't need more power this month so we could keep the lowest cost possible.

Summary

Carefully designed cloud solution could really lower monthly cost of developing a project. On the other hand I've seen many examples, where developing a product in the cloud was much more expensive than an old-fashioned VM(or even a production environment also hosted somewhere in Azure!). Pay attention to resources used, selected tiers and their utilization and you won't be surprised when you see a bill at the end of a month.

Imperative bindings in Azure Functions

In-built bindings provided by Azure Functions for most cases are more than sufficient - you don't have to manage and configure them on your own, all you're supposed to do is to focus on business logic and your codebase. There's a gap however when using bindings from a function.json file - what if I have to create a binding at runtime? Fortunately even this case is covered and you can easily build bindings, even using data delivered in your HTTP request.

Use case

Let's say you'd like to create a function, which fetches data from a CosmosDB instance. The issue here is, that you want to query a particular collection of documents. Normally you'd use following binding:

/
{
  "name": "inputDocument",
  "type": "documentDB",
  "databaseName": "MyDatabase",
  "collectionName": "{collectionId}",
  "connection": "MyAccount_COSMOSDB",     
  "direction": "in"
}

but there's no way to dynamically replace {collectionId} using data from a HTTP request(it works for other triggers like queue for the sqlQuery parameter however). We need to find another way of doing this avoiding building our own repositories and services, which would obscure the whole function.

If you read documentation carefully(especially C# reference), you'll find, that there's a possibility to use dynamic bindings via imperative bindings. The example of usage could like this:

/
using (var output = await binder.BindAsync<T>(new BindingTypeAttribute(...)))
{
    ...
}

With such feature we can implement our dynamic CosmosDB collection binding.

Implementing imperative binding in your function

I'll show you an example of using CosmosDB imperative binding in a CSX file, but the same rules apply to regular C# files. The first thing we need here is to reference a binding attribute. In our case it will be DocumentDBAttribute from Microsoft.Azure.WebJobs.Extensions

/
#r "Microsoft.Azure.WebJobs.Extensions.DocumentDB"

Then we have to update function's signature so it accepts Binder type:

/
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, Binder binder, TraceWriter log)

Now we can use imperative binding e.g. to load a collection dynamically:

/
var collection = await binder.BindAsync<IEnumerable<dynamic>>(
        new DocumentDBAttribute("foo", "{dynamic_parameter}"));

The whole function could look like this(don't pay attention to all those dynamics, normally I'd use slightly other structures and methods):

/
#r "Microsoft.Azure.WebJobs.Extensions.DocumentDB"

using System.Net;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, Binder binder, TraceWriter log)
{
	dynamic data = await req.Content.ReadAsAsync<object>();
	using(var collection = await binder.BindAsync<IEnumerable<dynamic>>(
			new DocumentDBAttribute("foo", data.collectionId.Value.ToString()))) 
	{
		var firstDocumentId = collection.First().id.Value.ToString();
		return req.CreateResponse(HttpStatusCode.OK, new {id = firstDocumentId});
	}
}

When I called my function using a following payload:

/
{
    "collectionId": "bar"
}

I'll get following result:

/
{"id":"I am FOO!"}

Summary

This was just a simple example of how you can extend your functions using imperative bindings. The best things is that is allows you to avoid implementing typical patterns usable in traditional web applications and keeps functions relatively clean. Feel free to experiment with those - using dynamic bindings is a really powerful feature and for sure will make your function even more powerful.