Remigiusz ZalewskiRemigiusz Zalewski

Azure App Configuration in .NET

azure-app-configuration-dotnet

Introduction

The moment an app runs as more than one instance, appsettings.json stops being a great place for anything that changes at runtime - updating a setting means redeploying every instance, and there's no single source of truth for what the current value even is across all of them. Azure App Configuration centralizes settings and feature flags in one place that every instance reads from, with the option to pick up changes without a restart.

🎬 Watch the full video here:


Wiring the configuration provider

Azure App Configuration plugs into .NET's configuration system as just another provider, sitting alongside appsettings.json and environment variables in the same IConfiguration:

builder.Configuration.AddAzureAppConfiguration(options =>
{
    options.Connect(connectionString)
           .Select(KeyFilter.Any, LabelFilter.Null)
           .ConfigureRefresh(refresh => refresh
               .Register("Sentinel", refreshAll: true)
               .SetRefreshInterval(TimeSpan.FromSeconds(30)));
});

Because it's just another configuration provider, existing code using IOptions<T> or IConfiguration doesn't change at all - only where the values ultimately come from does.

Dynamic refresh without a restart

The Sentinel key in the snippet above is the trigger: App Configuration only re-reads values from Azure when that specific key's value changes, keeping normal request traffic from constantly polling the service for every setting. Bumping the Sentinel key after updating other settings tells every running instance to refresh on its next check - refreshAll: true means all watched settings reload, not just the sentinel itself.

app.Use(async (context, next) =>
{
    await context.RequestServices.GetRequiredService<IConfigurationRefresherProvider>()
        .Refreshers.First().TryRefreshAsync();
    await next();
});

This middleware checks (cheaply, respecting the configured interval) whether a refresh is due on each request, rather than running a background poll independent of traffic.

Feature flags: shipping code dark, then turning it on

App Configuration's feature flag support integrates with Microsoft.FeatureManagement, letting a feature ship in a deploy while staying off until it's explicitly enabled - no redeploy needed to turn it on:

builder.Services.AddFeatureManagement();

if (await featureManager.IsEnabledAsync("NewCheckoutFlow"))
{
    // new code path
}

Flipping NewCheckoutFlow on in the Azure portal takes effect on the next refresh cycle across every instance simultaneously - useful for gradual rollouts, A/B-style comparisons, or an instant kill switch if a new feature misbehaves in production.

Labels for environment-specific values

A single App Configuration store can hold different values for the same key across environments using labels (Development, Staging, Production), rather than needing a separate store per environment:

options.Select(KeyFilter.Any, builder.Environment.EnvironmentName);

Common pitfalls

  • Polling too aggressively. A short refresh interval on a high-traffic app means constant calls to App Configuration; tune the interval to how quickly changes actually need to propagate.
  • Forgetting the sentinel key pattern and refreshing every key independently, which is both slower and less atomic when multiple related settings change together.
  • Putting genuine secrets directly in App Configuration instead of a Key Vault reference - App Configuration supports referencing Key Vault secrets rather than storing them itself.
  • Not scoping keys by label per environment, leading to one environment accidentally picking up another's settings.
  • Assuming feature flag changes are instant. They apply on the next refresh cycle, not immediately.

Key Takeaways

  • Azure App Configuration plugs into IConfiguration as a provider - existing IOptions<T> code doesn't need to change.
  • A sentinel key pattern batches refresh checks so instances don't poll Azure per setting on every request.
  • Feature flags via Microsoft.FeatureManagement let code ship dark and turn on without a redeploy.
  • Labels let one store hold environment-specific values instead of needing a separate store per environment.
  • Reference Key Vault for actual secrets rather than storing them directly in App Configuration.

Get the Full Source Code

The complete runnable solution - the App Configuration wiring, the sentinel-based refresh middleware, and the feature flag setup - is available to Patreon supporters. If you want centralized, dynamically-refreshing configuration in your own app instead of rebuilding it from the walkthrough above, you can find it on Patreon.

Resources