Read custom array settings from KeyVault to Azure Function using RBAC

Introduction

I recenlty had a need custom configuration in my Azure function and the default environment variable option was not sufficient as I needed to have an json array as a setting with some secrets in it. I create a simple solution to get settings from Keyvault and use it with DI.

Setup Azure Function to read secret from KeyValut using the latest SDK

Packages to install:

  1. Azure.Identity
  2. Azure.Security.KeyVault.Secrets
  3. Microsoft.Extensions.Configuration.AzureKeyVault

Keyvault setup

  1. Create a new KeyVault resource if you already don’t have one. I will skip this part how to create one in the azure Portal or Az CLI or ARM.
  2. In your KeyVault go to “Access controll” and add assign “Key Vault Secrets User” role to your application.
  3. Take a note of the KeyVault url as that is needed in our next step.

Azure Function setup

Jumping in to the code.

To setup thing on the Azure Function side we need to do some changes.

Firstly lets open local.host.setting.json and add a new envrionment variable AzureKeyVaultEndpoint and for the vaule the KeyVault url that we noted down from previous step.

Secondly we need to override a mehtod called ConfigureAppConfiguration as shown below in the Startup.cs.


        public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
        {
            var builtConfig = builder.ConfigurationBuilder.Build();
            var keyVaultEndpoint = builtConfig["AzureKeyVaultEndpoint"];
            var context = builder.GetContext();

            if (!string.IsNullOrEmpty(keyVaultEndpoint))
            {
                // using Key Vault, either local dev or deployed
                builder.ConfigurationBuilder
                    .SetBasePath(context.ApplicationRootPath)
                    .AddJsonFile("local.settings.json", true)
                    .AddEnvironmentVariables()
                      .AddAzureKeyVault(keyVaultEndpoint, new DefaultKeyVaultSecretManager())
            }
            else
            {
                // local dev no Key Vault
                builder.ConfigurationBuilder
                    .SetBasePath(Environment.CurrentDirectory)
                    .AddJsonFile("local.settings.json", true)
                    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
                    .AddUserSecrets(Assembly.GetExecutingAssembly(), true)
                    .AddEnvironmentVariables()
            }
        }
    }

In this example i have added two options either read the settings from KeyVault if that has been configured or try to read it from localsettings or appsettings..

By default the settings json read from the KeyVault will not be bind to the model you might have created. So to make this work we need to create a ServicesExtension to follow good practises. An Exmple of code would look like this:

  public static IServiceCollection ConfigureMyConfiguration(this IServiceCollection services, IConfiguration configuration)
        {
            string? myConfigurationJson = configuration.GetSection("MyConfiguration")?.Value;
            if (string.IsNullOrEmpty(myConfigurationJson))
                return services;

            var myConfiguration = JsonSerializer.Deserialize<MyConfigurationOption>(myConfigurationJson);

            services.TryAddSingleton(myConfiguration);
            return services;
        }

My custom settings might look something like this:

public class MyConfigurationOption 
{
    public List<MyConfiguration> Setting {get;set;}

    public class MyConfiguration {
        public string Setting1 {get;set;}
        public string Setting2 {get;set;}
        ...
    }
}

Now we can Inject ´MyConfigurationOption´ in a contructor and use the settings we got from the KeyVault, Inaddition all secrets are now protected by the KeyVault.