Azure Functions – Serverless coding made easy

Azure Functions is one of Microsoft’s solutions to serverless computing – running small pieces of code without having to worry about managing the server, complicated deployments and integrations.

Functions support several coding languages, can scale on demand and cost only when you use it.

It is very easy to start using Functions, but in some cases, documentation is still missing or exists only partially and some trial and error are required to get it right as some features are still in preview. There are also some glitches with log viewing and changing a function name after it was created is not trivial, but this is a small price to pay for the many benefits that Functions offer.

In Kensee, I use Functions for several different usages:

  • API for easy DB access: co-workers without access to our DB or without the required SQL knowledge can use their browser to get information from our system. I even save it as a CSV file for super-easy usage.
  • DB alerts: Azure SQL Server doesn’t support SQL Reporting Services, so using a timer based Function is an easy way to periodically check the DB for some conditions. Having built-in integration with SendGrid (and a free 25K emails per month for Azure users) also helps.
  • Simple back-end periodic checks: Instead of having a timer running in some back-end application just because it’s there, a Function can do the same and provide an easy and quick way to make changes, without having to re-deploy the whole application.

Here are some code examples, tips and things to watch out for if you want to create these functions as well.

CSV Report From DB

Start by creating a new Function from Azure Portal.

Choose Http Triggered function. My examples are written in C# but other options are also available. Name your function and chose Authorization level.

Regarding authorization and security measures – Besides choosing if a key is required to run the API method, additional protective measures can be added such as Authorization/Authentication with Azure AD, Social networks, and Microsoft account. You can turn that on by going to ‘Function app settings’ and clicking ‘Configure authentication’. Note that turning this feature on is affecting all Http triggered functions hosted on the same Functions app. Other types of functions are not affected as they don’t require user interaction.

auth

Another type of restriction is using CORS. Cross-Origin Resource Sharing (CORS) allows JavaScript code running in a browser on an external host to interact with your function. With this option, you can allow access only to specific front-end or back-end services. Set it up by clicking ‘Configure CORS’ from ‘Functions app settings’ section.

Now back to the code. The function is created with a simple example showing how to read parameters from the URL or request body.

I used this option for reading how many hours of data to fetch from DB. Make sure to properly validate the input parameters and restrict it to reasonable time frame so you won’t stress the DB too much.

 // parse query parameter
string hours = req.GetQueryNameValuePairs()
                .FirstOrDefault(q => string.Compare(q.Key, "hours", true) == 0)
                .Value;

if (hours == null)
{
    log.Info("hours were not specified, using default of 1 hour");
    hours = "1";
}
else
    log.Info($"hours value is: {hours}");

var hrs = int.Parse(hours);
if (hrs > 48 || hrs < 1)
    return req.CreateResponse(HttpStatusCode.BadRequest,
            "Number of hours must be between 1 and 48.");

I suggest using the log as much as possible. It is very helpful in quickly debugging and understanding what’s going on. The result is written to the Logs console on the function’s page in the portal. Sometimes the console stops refreshing. You can usually fix it by going to some other tab (such as the ‘Monitor’ page for example) a then back to the ‘Develop’ page.

For getting the data from DB, I created a dedicated method. Storing connection strings and other sensitive information in the code is a big no-no. For that, you can use ‘Configure app settings’ from ‘Function app settings’ and store them as key-value information like it is done for any other app service in Azure. All configuration parameters are shared between all your functions and can be easily accessed from code like that:

ConfigurationManager.
    ConnectionStrings["devConnectionString"].ConnectionString;

Don’t forget to include reference to System.Configuration and a using statement like so:

#r "System.Configuration" 

Using System.Configuration;

For best performance, the SQL to execute is stored in DB as a Stored Procedure. Here’s the simple code I use to execute it and format the results as CSV string.

public static async Task<string> GetReport(int hours)
{
    var str = ConfigurationManager.
        ConnectionStrings["devConnectionString"].ConnectionString;

    StringBuilder sb = new StringBuilder();
    using (SqlConnection conn = new SqlConnection(str))
    {
        conn.Open();
        var command = "StoredProcedureName";
        using (SqlCommand cmd = new SqlCommand(command, conn))
        {
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.AddWithValue("@hours", hours);
            // Execute the command
            SqlDataReader reader = await cmd.ExecuteReaderAsync();
            try
            {
                //Get All column
                var columnNames = Enumerable.Range(0, reader.FieldCount)
                                        .Select(reader.GetName)
                                        .ToList();
                sb.AppendLine(string.Join(",", columnNames));
                while (reader.Read())
                {
                    for (int i = 0; i < reader.FieldCount; i++)
                    {
                        string value = reader[i].ToString();
                        value = value.Replace("\"","\"\"");
                        if (value.Contains(","))
                            value = "\"" + value + "\"";
                        sb.Append(value.Replace(Environment.NewLine," ") + ",");
                    }
                    sb.Length--; // Remove the last comma
                    sb.AppendLine();
                }
            }
            finally
            {
                // Always call Close when done reading.
                reader.Close();
            }
        }
    }
    return sb.ToString();
}

And finally, for returning the result as CSV file as part of the response (This will open a Save As dialog for saving the file locally)

    var report = await GetReport(hrs);

    if (string.IsNullOrEmpty(report))
        return req.CreateResponse(HttpStatusCode.InternalServerError,
            "Failed preparing report");

    var response = req.CreateResponse(HttpStatusCode.OK);
    response.Content = new StringContent(report);
    response.Content.Headers.Add("ContentType", "text/csv");
    response.Content.Headers.Add("Content-Disposition",
        $"attachment;filename=Report-{DateTime.UtcNow.
        ToString("yyyy-MM-dd_HHmm")}.csv");

    return response;

That’s it. If you have any questions or comments I would really love hearing it. In future posts, I’ll add additional examples, such as how to periodically send an email.

Stay Tuned.

UPDATE: I’ve posted an example for sending an email using SendGrid from Azure Functions 

Advertisements

One thought on “Azure Functions – Serverless coding made easy

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s