Craig Shoemaker

Runtime binding with Azure Functions

Serverless functions rarely work in isolation. Functions are often paired with other services like a database, a storage service, or perhaps even an email notification service. Azure Functions makes it easy to connect with these services by offering bindings that give you an out-of-the-box connection to an array of cloud services. You can achieve a lot using the standard configurations, but what if you need go beyond the defaults?

Suppose you want to:

These are just a few examples of the type of adjustments which may be difficult using a binding's default settings. This article demonstrates how to configure a C# Azure Functions binding at run time to take full control over a binding's configuration.

tl;dr

To configure a binding at runtime:

  1. Declare the binding parameter as a Binder or IBinder instance.
  2. Create an instance of the binding attribute.
  3. Customize the binding attribute as necessary.
  4. Either use Bind or BindAsync to bind the attribute to a cloud service.
  5. Use the binding

A working example is available on GitHub: azure-functions-runtime-binding.

Default binding

Before diving into the details of how runtime binding works, first consider the default scenario. The following code example implements an HTTP-triggered function that saves a message to an Azure Storage blob container.

[FunctionName("SaveDefault")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]
HttpRequest req,

[Blob("messages/{sys.randguid}.txt", FileAccess.Write)] out string blob,
ILogger log)
{
string message = req.Query["message"];
blob = $"Default binding: {message}";

return new OkResult();
}

For the binding to work in this context, the blob parameter is set up in the following ways:

This setup works great for simple scenarios. However, using this approach makes it difficult to make changes to the binding. In some cases you may want to:

To achieve more flexible binding customization, you can implement runtime binding.

Runtime binding

The final result of the following function is the same as implemented for the default scenario. The difference in this instance is that the binding is imperatively created, giving you the chance to affect a binding's behavior.

[FunctionName("SaveCustom")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]
HttpRequest req,
IBinder binder,
ILogger log)
{
string message = req.Query["message"];

var attribute = new BlobAttribute("messages/{sys.randguid}.txt", FileAccess.Write);
attribute.Connection = "AzureStorageConnectionString";

using(var blob = await binder.BindAsync<TextWriter>(attribute))
{
blob.Write($"Runtime binding: {message}");
}

return new OkResult();
}

The binding is declared as an IBinder type, which gives you the chance to determine the binding configuration.

In this scenario:

NOTE: Instead of typing the binding as a string (as shown in the default scenario), the binding is created as a TextWriter. The TextWriter class is used because a string instance has no knowledge of how to write back to the blob container. The TextWriter is configured to know about the blob container through the call of binder.BindAsync.

Code sample

The sample code is available on GitHub: azure-functions-runtime-binding.

Summary

The built-in Azure Functions bindings give you rich access to a number of different cloud services. While the default behavior may work well in many cases, sometime you need the chance to customize a binding's configuration.

You can customize a binding at runtime via the following steps:

  1. Declare the binding parameter as a Binder or IBinder instance.
  2. Create an instance of the binding attribute.
  3. Customize the binding attribute as necessary.
  4. Either use Bind or BindAsync to bind the attribute to a cloud service.
  5. Use the binding
tags:
azure serverless