Azure App Configuration for .NET: Centralised Settings and Feature Flags
Managing configuration across multiple services, environments, and deployment slots gets messy quickly. Azure App Configuration provides a centralised store for non-secret settings and feature flags, with first-class .NET integration.
Where Key Vault stores secrets, App Configuration stores everything else: feature flags, connection endpoints, UI settings, timeout values, and anything you'd typically put in appsettings.json but want to change without redeploying.
Setting Up the Provider
Install Microsoft.Azure.AppConfiguration.AspNetCore and add it to your host:
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddAzureAppConfiguration(options =>
{
options.Connect(
new Uri("https://my-config.azconfig.io"),
new DefaultAzureCredential())
.Select(KeyFilter.Any, LabelFilter.Null)
.Select(KeyFilter.Any, builder.Environment.EnvironmentName);
});
builder.Services.AddAzureAppConfiguration();
var app = builder.Build();
app.UseAzureAppConfiguration();
The Select calls are important. They load settings in order, with later selections overriding earlier ones. In this example, unlabelled settings are loaded first, then environment-specific settings (e.g., those labelled Production) override them. This gives you a clean layering model.
Labels as Environment Overrides
App Configuration uses labels to distinguish values for different environments. A key like Api:PageSize might have:
| Key | Label | Value |
|---|---|---|
Api:PageSize |
(none) | 25 |
Api:PageSize |
Development |
5 |
Api:PageSize |
Production |
50 |
Your code reads IConfiguration["Api:PageSize"] and gets the right value based on which labels you selected at startup. No conditional logic needed.
Dynamic Refresh
Unlike Key Vault, App Configuration supports efficient change detection using sentinel keys:
builder.Configuration.AddAzureAppConfiguration(options =>
{
options.Connect(
new Uri("https://my-config.azconfig.io"),
new DefaultAzureCredential())
.Select(KeyFilter.Any, LabelFilter.Null)
.ConfigureRefresh(refresh =>
{
refresh.Register("Settings:Sentinel", refreshAll: true)
.SetRefreshInterval(TimeSpan.FromSeconds(30));
});
});
The sentinel pattern works like this: change whatever settings you need, then update the sentinel key last. The provider watches only the sentinel key and, when it changes, reloads everything in one go. This avoids partial updates where some settings have changed but others haven't yet.
The UseAzureAppConfiguration() middleware triggers the refresh check on each HTTP request. It's non-blocking — if the refresh interval hasn't elapsed, it returns immediately.
Feature Flags
App Configuration has built-in feature flag support. Define flags in the Azure portal or via CLI, then use them in code:
builder.Configuration.AddAzureAppConfiguration(options =>
{
options.Connect(
new Uri("https://my-config.azconfig.io"),
new DefaultAzureCredential())
.UseFeatureFlags(flagOptions =>
{
flagOptions.SetRefreshInterval(TimeSpan.FromMinutes(1));
});
});
builder.Services.AddFeatureManagement();
Check flags in your services:
public class CheckoutService
{
private readonly IFeatureManager _featureManager;
public CheckoutService(IFeatureManager featureManager)
{
_featureManager = featureManager;
}
public async Task<CheckoutResult> ProcessAsync(Cart cart)
{
if (await _featureManager.IsEnabledAsync("NewPaymentFlow"))
{
return await ProcessWithNewFlowAsync(cart);
}
return await ProcessWithLegacyFlowAsync(cart);
}
}
Or use feature gates on controllers and endpoints:
app.MapGet("/api/v2/orders", async (IOrderService svc) =>
{
return await svc.GetOrdersV2Async();
})
.WithMetadata(new FeatureGateAttribute("OrdersV2Api"));
Feature flags support filters out of the box — percentage rollouts, time windows, and targeting specific users or groups. These are configured in the portal and evaluated by the Microsoft.FeatureManagement library without any custom code.
Key Vault References
App Configuration can reference Key Vault secrets, combining both stores seamlessly:
options.Connect(
new Uri("https://my-config.azconfig.io"),
new DefaultAzureCredential())
.ConfigureKeyVault(kv =>
{
kv.SetCredential(new DefaultAzureCredential());
});
In App Configuration, set a key's value type to "Key Vault reference" and point it at a secret URI. The configuration provider resolves it transparently. Your code just reads the configuration key as normal.
Practical Tips
- Use prefixes to namespace settings per service:
OrderService:Api:Timeout,PaymentService:Api:Timeout. - Don't store secrets directly in App Configuration. Use Key Vault references instead.
- Version your configuration by using labels like
v1,v2alongside environment labels. - Use the free tier for development. The standard tier adds private endpoints and higher request limits.
App Configuration fills the gap between appsettings.json and Key Vault. It's where your non-secret, runtime-tuneable configuration belongs.