Skip to content
Jonathan George By Jonathan George Software Engineer IV
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:

Visual Studio Watch window showing the contents of the provided IConfiguration instance

As the screenshot shows, we have a ConfigurationRoot instance which contains 6 providers. Let's have a look at these in detail.

Azure Weekly is a summary of the week's top Microsoft Azure news from AI to Availability Zones. Keep on top of all the latest Azure developments!

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?

Visual Studio Watch window showing the Data property of the HostJsonFileConfigurationProvider

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.

Programming C# 12 Book, by Ian Griffiths, published by O'Reilly Media, is now available to buy.

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.

Jonathan George

Software Engineer IV

Jonathan George

Jon is an experienced project lead and architect who has spent nearly 20 years delivering industry-leading solutions for clients across multiple industries including oil and gas, retail, financial services and healthcare. At endjin, he helps clients take advantage of the huge opportunities presented by cloud technologies to better understand and grow their businesses.