ConfigurationManager and Azure Key Vault
Tags: dotnet, csharp
Published: 2023-01-03 09:18:56

One of fundamentals of [The Twelve-Factor](https://12factor.net/) is the strict separation of config from code and storing them in environment variables.

A common way is to store default values in JSON or XML files and have the possibility to override them with environment variables.

That's why in ASP.NET Core, we usually find this kind of builder:

```csharp
var builder = new ConfigurationBuilder()
    .SetBasePath(env.ContentRootPath)
    .AddJsonFile("appsettings.json")
    .AddJsonFile($"appsettings.{env.EnvironmentName}.json")
    .AddEnvironmentVariables();
Configuration = builder.Build();
```

1. `appsettings.json` contains all the default configuration.
1. `appsettings.{env}.json` is merged with the default one. In this case the environment name is required as an environment variable.
1. Finally the merged values are overridden by environment variables having the same name.

But what if we have to support `System.Configuration.ConfigurationManager`?

## ConfigurationManager

With `ConfigurationManager` there are instead XML files, such as `app.config` and `app.{env}.config`.
Unfortunately the artefact cannot be immutable and be shipped with all environments conf.
During the build process, the final config file is transformed with a XDT tool (Xml Document Transformation).

Since .NET Framework 4.7.1, there is a handy NuGet that allows us to override the default values by environment variables.

- Add [Microsoft.Configuration.ConfigurationBuilders.Environment](https://www.nuget.org/packages/Microsoft.Configuration.ConfigurationBuilders.Environment/) NuGet.
- Add the following markup in the config file:

   ```xml
   <configuration>

      <configSections>
        <section name="configBuilders" type="System.Configuration.ConfigurationBuildersSection, System.Configuration" restartOnExternalChanges="false" requirePermission="false" />
      </configSections>

      <configBuilders>
        <builders>
          <add name="EnvConfigBuilder" type="Microsoft.Configuration.ConfigurationBuilders.EnvironmentConfigBuilder, Microsoft.Configuration.ConfigurationBuilders.Environment" />
        </builders>
      </configBuilders>

      <appSettings configBuilders="EnvConfigBuilder">
        ...
      </appSettings>

      <connectionStrings configBuilders="EnvConfigBuilder">
        ...
      </connectionStrings>

   </configuration>
   ```

- If an environment variable with the same key/name exists, then it will be overridden during execution.

💡 For more details please follow the [documentation](https://learn.microsoft.com/en-us/aspnet/config-builder).

## Azure Key Vault

It's best to stick with the Twelve-Factor methodology for your App Service or Azure Functions and to not use other `ConfigurationBuilders` libraries.

For this you can setup [Key Vault references](https://learn.microsoft.com/en-us/azure/app-service/app-service-key-vault-references).
So without requiring any code changes, all secrets will be added as environment variables and consumed by `EnvironmentConfigBuilder`.

![Azure application settings screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yvasgz17mer4bc83jj23.png)

## Tips

If environment variable names cannot match the config, it's possible to customize like this:

```csharp
using Microsoft.Configuration.ConfigurationBuilders;

public class EnvConfigBuilder : EnvironmentConfigBuilder
{
    public override string GetValue(string key) => key switch
    {
        "MyConfName" => base.GetValue("MyEnvVarName"),
        _ => null // means do not override the default value
    }
}
```

```xml
<configBuilders>
  <builders>
    <add name="EnvConfigBuilder" type="MyNamespace.EnvConfigBuilder, MyAssemblyName" />
  </builders>
</configBuilders>
```

## Resources

{% github https://github.com/aspnet/MicrosoftConfigurationBuilders %}