Configuration in Azure Functions - What's in the box?
TL;DR - This series of posts talks about configuration in Azure Functions. The first post looks at what we get out of the box; subsequent posts will look at some best practices for working with configuration in Azure Functions, as well as looking at how to integrate other configuration sources.
Anyone who's worked with Azure Functions will be familiar with the various ways in which configuration information can be accessed. However this also leads to some confusion.
Examples tend to show using Environment.GetEnvironmentVariable
to retrieve settings, or how to use the IConfigurationBuilder
to setup an instance of IConfiguration
that you can use to access the data in your function's app configuration.
Dig a bit deeper and you will find that behind the scenes, Azure Functions does quite a lot of this for you. If you use dependency injection in your functions, you'll see that the container comes pre-supplied with an instance of IConfiguration
, set up by the runtime and supplied with various providers by default.
In this post, we'll take a look at what's going on here and what the functions runtime gives you out of the box. There's a sample Visual Studio solution that you can use to easily see some of this for yourself - you can find it on GitHub at https://github.com/jongeorge1/azure-functions-configuration-sample
The standard IConfiguration instance
If you use dependency injection to get an instance of IConfiguration
injected into your function class (as shown in the sample project), you can drop a breakpoint in your code and have a look at what's in there. Here's what you'll see:
As the screenshot shows, we have a ConfigurationRoot
instance which contains 6 providers. Let's have a look at these in detail.
The first is a ChainedConfigurationProvider
; this is essentially a wrapper around another instance of IConfigurationRoot
. In this case, it's set up by the runtime and you'll find its contents to be slightly different depending on whether you're running locally or on Azure. Locally it contains two MemoryConfigurationProvider
instances with the first being empty and the second contains a single value which specifies that you're running in the Development
environment. This section is runtime specific and can safely be ignored.
Moving on, we have another MemoryConfigurationProvider
containing a single item with the key AzureWebJobsConfigurationSection
. This is part of the glue that ties the Azure Functions host to the underlying WebJobs SDK, telling the WebJobs SDK how to locate hosting configuration during startup. As you can see, the value the AzureWebJobsConfigurationSection
, AzureFunctionsJobHost
, matches the root element in the host.json
file, which is where we configure global options that affect all of the functions in our app.
Next up, we have the HostJsonFileConfigurationProvider
which is where things start to get interesting. As you might conclude, this gives the runtime access to the values specified in the host.json
file.
This immediately gives rise to a question. You'll have seen that a host.json
file looks something like this:
{
"version": "2.0",
"http": {
"routePrefix": ""
},
"logging": {
"applicationInsights": {
"samplingExcludedTypes": "Request",
"samplingSettings": {
"isEnabled": true
}
}
}
}
However, a brief dig into any of the providers that inherit ConfigurationProvider
(which HostJsonFileConfigurationProvider
does) reveals that they store their values in an IDictionary<string, string>
. So how does this work?
As you can see, the structured JSON file is collapsed down to key-value pairs using colons to indicate structure. This is fundamental to how the configuration system works - values can be retrieved from this dictionary directly, or you can use some of the additional methods provided to bind objects to the data, allowing you to map it back to a hierarchical structure.
Next up, we have a JsonConfigurationProvider
which exposes the contents of the appsettings.json
file, should it be present. Those who've used Microsoft.Extensions.Configuration
in other types of project will be familiar with this but it can be a source of confusion in the world of functions, as a cursory glance over the documentation suggests that we use local.settings.json
rather than appsettings.json
. Nevertheless, here it is - and, as this suggests, you are free to add an appsettings.json
file to your project and use it in the normal way.
We'll come back to the differences between appsettings.json
and local.settings.json
in a future post; for now, let's finish examining the other configuration providers.
The penultimate provider is an EnvironmentVariablesConfigurationProvider
. This is likely to be the most heavily populated, as it is exactly what it sounds like - it allows you access to all of the environment variables you currently have defined.
And finally, one last MemoryConfigurationProvider
. For me, this contains a single value: AzureFunctionsJobHost:logging:console:isEnabled
, set to false.
This gives rise to some observations, and also to one big question.
We know when you request a value from an instance of IConfiguration
, it's located by starting with the last provider and working backwards through the list until the value is found or you run out of providers. One point of interest here for me is that we can override configuration that's in host.json
using configuration from one of the higher-priority providers - e.g. the EnvironmentVariablesConfigurationProvider
. The main use I've found for this so far is temporarily changing the default logging level of a deployed function in order to diagnose problems. If you're using ZIP deploy (as we do), then you can't make any changes to the content of host.json
, so overriding its values with app settings is a shortcut to making that kind of temporary change.
With that said, I haven't found any documentation that suggests this is officially supported, so it's quite possible that future versions of Azure Functions may load host.json
settings differently.
So now, onto the question:
Where's the provider for local.settings.json
?
After encountering the JsonConfigurationProvider
for appsettings.json
, it might have been reasonable to expect something similar for local.settings.json
. However, you will likely have noticed that it's not in the list. So where do the values from this file go?
The answer is that everything in the Values
section of the local.settings.file
ends up being added to the EnvironmentVariablesConfigurationProvider
. If you add some data in there and then drill into the providers in the same way as we did before, you should be able to find keys inside the EnvironmentVariablesConfigurationProvider
for your local.settings.json
data items.
In my next post, we'll dig into this in more detail to understand why local.settings.json
is treated in this way.