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.
Top comments (1)
Hi, thanks for you article :) I think you could also create .env file e.g.
.localenv
FUNCTIONS_WORKER_RUNTIME=node
MOCK=true
API_URL=http:/my-nice-api/api
and start docker run with
docker run --env-file .localenv myimage
than you could skip docker exec and docker cd step!