Microsoft built the features to be able to easily use Azure KeyVault as a configuration provider for IOptions. There's a limitation though, you can't use periods or dots in the names of values in Key Vault.

You can find the companion source code to this post here: https://github.com/veccsolutions/Vecc.Examples.Vault

As we move into using more features of Azure platform, we are finding we need to use something like the Key Vault to store our secrets. Our configuration naming standard is to use the full type name of the options class in the appsettings.json. This makes it very clear as to what configuration means what. It has served us well and has made managing configuration files very easy.

For this exercise, my options class looks like this

namespace Vecc.Examples.Vault
{
    public class TestOptions
    {
        public string Value1 { get; set; }
        public string Value2 { get; set; }
    }
}

My appsettings.json will contain this

{
  "Vecc.Examples.Vault.TestOptions": {
    "Value1": "example #1",
    "Value2": "example #2"
  }
}

My startup.cs file contains this line in the ConfigureServices method:

services.Configure<TestOptions>(this.Configuration.GetSection(typeof(TestOptions).FullName));

I have a single controller that returns the options object. This way it's easy to determine what the values are.

Without Azure Key Vault configured, the OptionsController returns exactly what I had expected it to, the same JSON value as what was in the config file.

Adding the Key Vault to IOptions is as simple as following the guidance from Microsoft's documentation. Here's a link to the documentation if you need it: https://docs.microsoft.com/en-us/aspnet/core/security/key-vault-configuration?view=aspnetcore-3.1

The problem I ran into was when I have periods, or dots in the name. You can't do that with names in Azure Key Vault. You can only use letters, numbers and dashes and it must be between 1 and 127 characters long.

A little insight, when using appsettings and the configuration extensions each level is represented as colon. This applies to all of the providers from Microsoft, the environment variables, command line parameters, the Azure Key Vault, etc.

An example is that in the configuration provider, my appsettings.json file above could actually be represented as this:

Vecc.Examples.Vault.TestOptions:Value1=example #1
Vecc.Examples.Vault.TestOptions:Value2=example #2

The default implementation of the Key Vault configuration provider only changes 2 dashes, `--` into a colon :.  With my example appsettings above, if I could use periods, the key name Vecc.Examples.Vault.TestOptions--Value1 would override the value of Value1.

Here's how I got around it.

I used a custom key vault secret manager that changes 3 dashes into a period and 2 dashes into a colon to follow the same standard Microsoft has set in their providers.

My class is as follows:

using Microsoft.Azure.KeyVault.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.AzureKeyVault;

namespace Vecc.Examples.Vault
{
    public class DottableKeyVaultSecretManager : DefaultKeyVaultSecretManager
    {
        public override string GetKey(SecretBundle secret)
        {
            var result = secret.Id.Replace("---", ".").Replace("--", ConfigurationPath.KeyDelimiter);

            return result;
        }
    }
}

After the class was built, I then modified the line that adds the Key Vault configuration provider in program.cs to use the new manager by adding it as a parameter.

configurationBuilder.AddAzureKeyVault(vault, clientId, clientSecret, new DottableKeyVaultSecretManager());

After doing that, I can now use the key name of Vecc---Examples---Vault---TestOptions--Value1 and have it override my options, just as it should.

You can find the necessary changes in this commit: https://github.com/veccsolutions/Vecc.Examples.Vault/commit/d38d0e296172c617ca12b4f931f5feb5bb71ce25#diff-1d378d003638d9e3d5e250cbe69839d5

Conclusion

Key Vault is pretty nice, I really like the built-in support for IOptions. I wish it allowed periods, but it doesn't. At least the work around was easy.

Finding the original source code for the DefaultKeyVaultSecretManager was difficult. It's in a repository that is marked as archived. https://github.com/aspnet/Configuration/blob/master/src/Config.AzureKeyVault/DefaultKeyVaultSecretManager.cs