C#
Sam Lau  

Azure App Configuration with Azure Key Vault and Dynamic Configuration in .NET 8 Application

Azure App Configuration is an Azure service that manage application settings and feature flags. Today, I am going to go through how to set up a .NET 8 application to retrieve settings from Azure App Configuration, reference Azure Key Vault for secrets and dynamically update settings. The sample I am using is a .NET 8 Worker service but an ASP.NET Core App will be very similar.

Prerequisites

NuGet packages

These are the packages you need.

  • Microsoft.Extensions.Configuration.AzureAppConfiguration
  • Azure.Identity

appsettings.json

In appsettings.json, I have set up 2 sections, MyApplicationConfiguration and AzureAppConfiguration. MyApplicationConfiguration is your application setting (you can rename to suit your app, of courses) and both your normal application configuration and secret from Key Vault will populate these settings. AzureAppConfiguration is the required configuration to connect to Azure App Configuration. Strictly speaking, only endpoint / connection string is need but I will explain the use of others later. For now, just substitute your Azure App Configuration Endpoint, copying from Azure portal.

{
  "MyApplicationConfiguration": {
    "ApplicationName": "",
    "ApplicationSecret": ""
  },
  "AzureAppConfiguration": {
    "Endpoint": "<YOUR_AZURE_APP_CONFIGURATION_ENDPOINT>",
    "Label": "MyApplication",
    "SentinelKey": "Sentinel"
  }
}

Create key-value and Key Value reference in Azure App Configuration

Select Operations > Configuration explorer > Create > Key-value to add key-values to Azure App Configuration (https://learn.microsoft.com/en-us/azure/azure-app-configuration/quickstart-azure-app-configuration-create?tabs=azure-portal#create-a-key-value). In this example, they would be MyApplicationConfiguration:ApplicationName and MyApplicationConfiguration:ApplicationSecret. Create an extra key-value with key Sentinel and any value (eg. “v1”). This Sentinel value will be the thing you change to update setting dynamically. For label, use the label for your application, MyApplication in this example. You can skip label if you are not planning to share this Azure App Configuration store with other apps.

MyApplicationConfiguration.cs

Create a class to represent the application configuration. I am being fancy and using record type but normal class will also do.

public record MyApplicationConfiguration
{
    public required string ApplicationName { get; init; }
    public required string ApplicationSecret { get; init; }
}

Program.cs

In Program.cs, add the following code before builder.Build();.

var endpoint = builder.Configuration.GetValue<string>("AzureAppConfiguration:Endpoint");
var label = builder.Configuration.GetValue<string>("AzureAppConfiguration:Label");
var sentinelKey = builder.Configuration.GetValue<string>("AzureAppConfiguration:SentinelKey");

if (!string.IsNullOrWhiteSpace(endpoint))
{
    builder.Configuration.AddAzureAppConfiguration(options =>
    {
        options.Connect(new Uri(endpoint), new DefaultAzureCredential())
        .Select(KeyFilter.Any, label)
        .ConfigureRefresh(refreshOptions =>
        {
            refreshOptions.Register(sentinelKey, label, refreshAll: true);
        })
        .ConfigureKeyVault(keyVaultOptions => keyVaultOptions.SetCredential(new DefaultAzureCredential()));
    });
}

builder.Services.Configure<MyApplicationConfiguration>(builder.Configuration.GetSection("MyApplicationConfiguration"));

The first 3 lines retrieve AzureAppConfiguration. Then, I have a condition to check if endpoint exists because I will not connect to Azure App Configuration during local development in my actual app and endpoint will normally be empty locally. However, this is for demo so let’s continue.

builder.Configuration.AddAzureAppConfiguration() registers Azure App Configuration into our configuration. I connect to the endpoint with Azure credential, which means the app need to able to access Azure App Configuration with managed identities (Use managed identities to access App Configuration – Azure App Configuration | Microsoft Learn). The other way to do it is just connect with connection string with .Connect("<CONNECTION_STRING>"), which could be useful if you want to test locally and don’t want to set up credential.

.Select(KeyFilter.Any, label) filter configuration with the label. If you skipped label earlier, you can skip this as well.

.ConfigureRefresh() configure the app to periodically check the value of the sentinel and refresh setting if sentinel value has changed. The default refresh time is 30 seconds but your can override it with refreshOptions.SetCacheExpiration(). label is optional so you will skip it here as well if you skipped it before.

.ConfigureKeyVault() is where Key Vault connection is set up. Again, the app need to be granted access to Key Vault with managed identities (Tutorial – Use Azure Key Vault with an Azure web app in .NET | Microsoft Learn). You will need to grant Key Vault access to your Azure login if you want to test locally.

builder.Services.Configure<MyApplicationConfiguration>() is your standard configuration binding. With that, you are good to go.

Worker.cs or any classes

In order for dynamic update to work on the configuration, we need to use IOptionsSnapshot<T> instead of IOptions<T>. Other then that, we inject the configuration as normal.

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;
    private readonly MyApplicationConfiguration _configuration;

    public Worker(ILogger<Worker> logger, IOptionsSnapshot<MyApplicationConfiguration> options)
    {
        _logger = logger;
        _configuration = options.Value;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            if (_logger.IsEnabled(LogLevel.Information))
            {
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                _logger.LogInformation("App name: {name}", _configuration.ApplicationName);
                _logger.LogInformation("App sercret: {sercret}", _configuration.ApplicationSecret);
            }
            await Task.Delay(1000, stoppingToken);
        }
    }
}

Conclusion

And that’s it! Now you know how to set up Azure App Configuration in your .NET applications. You can play around with updating the setting and the sentinel value on Azure App Configuration and the setting should be updated accordingly without app restart (after a short delay). Here is the full sample on GitHub: samtc-lau/AzureAppConfigurationSample: Azure App Configuration with .NET 8 Worker Service Sample (github.com)