Dockerizing Azure Functions with .NET

Goal

Run Azure Functions in Docker containers with configuration and dependency injection support.

Setup

Install Azure Functions Core Tools: https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local?tabs=windows%2Ccsharp%2Cbash#install-the-azure-functions-core-tools

Create Project

func init Bustroker.HttpEndpoint.Func --dotnet --docker

The --docker flag generates a Dockerfile. Rename the .csproj file if needed (e.g., Bustroker_HttpEndpoint_Func.csprojBustroker.HttpEndpoint.Func.csproj).

Create Function

cd Bustroker.HttpEndpoint.Func
func new --name HttpEndpoint --template HttpTrigger

Configuration Injection

Add required packages:

dotnet add package Microsoft.Azure.Functions.Extensions
dotnet add package Microsoft.NET.Sdk.Functions

Make the function non-static and inject IConfiguration:

namespace Bustroker.HttpEndpoint.Func
{
    public class HttpEndpoint
    {
        private readonly IConfiguration _configuration;

        public HttpEndpoint(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        [FunctionName("HttpEndpoint")]
        public async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "config")] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");
            var setting = req.Query["setting"];
            var value = _configuration.GetValue<string>(setting);
            var response = await Task.FromResult($"setting: {setting}; value:{value}");
            return new OkObjectResult(response);
        }
    }
}

Configuration reads from environment variables:

docker run -p 80:80 -e setting1=value1 -e setting2=value2 <IMAGE>:<TAG>

Dependency Injection

Create a Startup class:

// Startup.cs
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;

[assembly: FunctionsStartup(typeof(Arch.Pocs.Containers.AppService.HttpEndpoint.Func.Startup))]
namespace Arch.Pocs.Containers.AppService.HttpEndpoint.Func
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            // e.g., for http client injection
            builder.Services.AddHttpClient();
        }
    }
}

Inject services directly into the function constructor.

Run Locally

Build and run:

docker build -t function .
docker run -p 80:80 -e cosmosDbConnectionString="AccountEndpoint=https://bustroker-transactions-cosmosdb.documents.azure.com:443/;AccountKey=SUP3R4CC0UNTK3Y==;" function:latest

Notes