loading...

Accessing application settings from Azure Function Apps in a Docker Container

codeprototype profile image Kevin Le ・3 min read

Abstract

In my previous article, the business logic in the Function App is practically none. But one of the first common things of all software programs, Azure Function Apps or otherwise, in a real-world scenario is reading environment variables. In this article, I will refer to these environment variables as application settings. The reason is that I don't want us to get mixed up with environment variables that are passed to the Docker container.

Application settings in this context are, for example, the Function App needs to read the API key and/or secret of a third-party service in order to query for some data. Another example might be the connection string of the database. Generally, it is considered a bad programming to hard-code these values in the code itself. Since these values are configurable data, they should be decoupled from the program code and should live in an environment variable file or something like that. The key concept here is they should be decoupled.

The goal is we want to allow Function Apps to read application settings so that they can run locally and run inside a Docker container on the Desktop, as well as on Azure, inside and outside of an Azure Docker container.

Review: Non Docker way

Forget about Docker for a moment, let's say we write the Azure Function App on our development computer, we can keep all application settings in a file named local.settings.json. This file is in JSON format, and looks something like this:

{
    "SomeAPIClientKey": "...",
    "SomeAPIClientSecret": "..."
}

The function can access these application settings and be written as:

[FunctionName("kleSampleFunc")]
public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, ILogger log, ExecutionContext context)
    {
        var configuration = new ConfigurationBuilder()
            .SetBasePath(context.FunctionAppDirectory)                    
            .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
            .AddEnvironmentVariables()
            .Build();

        var apiKey = configuration["SomeAPIClientKey"];
        var apiSecret = configuration["SomeAPIClientSecret"];
        log.LogInformation($"apiKey: {apiKey}");
        log.LogInformation($"apiSecret : {apiSecret}");

        // .... Business logic that makes use of apiKey and apiSecret

        return OkObjectResult(new {
            //whatever        
        });         
    }

We can run it locally using the following command:

$ func start --build

We might choose NOT to commit the file local.settings.json to Source Control, so it won't get deployed. But it doesn't matter because when this Function App runs on Azure, the environment variables are no longer coming from the file local.settings.json. Instead, there's an Application Settings blade on Azure Portal where we can configure them.

Docker

From my previous article, the Function App running in the local Docker Desktop will not be able to read any environment variable from the local.settings.json file.

Assume we already run

$ docker build -t kle/sampledockerimg .
$ docker run -p 7071:80 kle/sampledockerimg

Test from Postman or curl, you will see the 2 log.LogInformation() statements above outputting null values for apiKey and apiSecret.

So launch another Terminal or Console, and run this command

$ docker ps

You will see the output in tabular format with 7 columns. The last column is NAMES. Note the value of this column for the docker image of your Function App. It's some kind of made-up name and varies each time the image is rebuilt. For example, I see mine as vigorous_bhaskara.

So now run

$ docker exec -it vigorous_bhaskara /bin/bash

The command above allows us accessing the file system, similar to SSH.

So if you run the linux ls command

$ ls /home/site/wwwroot

You will NOT see the file local.settings.json

So if the file containing the environment variables is not present, how can can anything read their values? The fix is simple. All we have to do is copying the file to Docker container using the Docker copy command

$ docker cp local.settings.json 16527b513905:/home/site/wwwroot

Run Postman or curl again, everything should work.

On the other hand, the same Function App, deployed to Azure, will be able to read all environment variables that have been configured in the Application Settings blade on Azure Portal. So we don't have to do anything there.

Conclusion

Long story short, just remember these 3 commands:

$ docker ps
$ docker exec -it vigorous_bhaskara /bin/bash
$ docker cp local.settings.json 16527b513905:/home/site/wwwroot

The first command tells us the container name and container id. In the example, that's vigorous_bhaskara and 16527b513905 respectively.

The second one allows us to access the file system of the Docker Container, in an SSH-like way.

The third one copies the local.settings.json from the host computer to the container.

Discussion

pic
Editor guide