Centralized Configuration Management with Azure App Configuration
Managing application configuration across multiple environments and services can be challenging. Azure App Configuration provides a centralized service to manage application settings and feature flags, making it easier to maintain consistency and implement dynamic configuration changes.
Why Azure App Configuration?
Traditional configuration approaches using appsettings.json or environment variables have limitations:
- No central management
- Requires redeployment for changes
- Difficult to maintain consistency across services
- No built-in feature flag support
Azure App Configuration solves these problems with:
- Centralized configuration store
- Real-time configuration updates
- Feature flag management
- Key Vault integration for secrets
- Configuration snapshots and versioning
Setting Up Azure App Configuration
# Create App Configuration store
az appconfig create \
--name myappconfig \
--resource-group myResourceGroup \
--location eastus \
--sku Standard
# Add configuration values
az appconfig kv set \
--name myappconfig \
--key "Settings:ServiceEndpoint" \
--value "https://api.example.com" \
--yes
az appconfig kv set \
--name myappconfig \
--key "Settings:MaxRetries" \
--value "3" \
--yes
# Add labeled configurations for different environments
az appconfig kv set \
--name myappconfig \
--key "Settings:ServiceEndpoint" \
--value "https://api-dev.example.com" \
--label "Development" \
--yes
az appconfig kv set \
--name myappconfig \
--key "Settings:ServiceEndpoint" \
--value "https://api-prod.example.com" \
--label "Production" \
--yes
.NET Core Integration
Add the required packages:
dotnet add package Microsoft.Azure.AppConfiguration.AspNetCore
dotnet add package Microsoft.FeatureManagement.AspNetCore
Configure in Program.cs:
// Program.cs
using Microsoft.FeatureManagement;
var builder = WebApplication.CreateBuilder(args);
// Add Azure App Configuration
var connectionString = builder.Configuration.GetConnectionString("AppConfig");
builder.Configuration.AddAzureAppConfiguration(options =>
{
options.Connect(connectionString)
// Load all keys with no label
.Select(KeyFilter.Any, LabelFilter.Null)
// Load keys for current environment
.Select(KeyFilter.Any, builder.Environment.EnvironmentName)
// Enable dynamic configuration refresh
.ConfigureRefresh(refresh =>
{
refresh.Register("Settings:Sentinel", refreshAll: true)
.SetCacheExpiration(TimeSpan.FromSeconds(30));
})
// Load feature flags
.UseFeatureFlags(featureFlags =>
{
featureFlags.CacheExpirationInterval = TimeSpan.FromMinutes(5);
});
});
builder.Services.AddAzureAppConfiguration();
builder.Services.AddFeatureManagement();
var app = builder.Build();
// Enable dynamic configuration
app.UseAzureAppConfiguration();
app.MapGet("/", () => "Hello World!");
app.Run();
Configuration model:
// Settings.cs
public class Settings
{
public string ServiceEndpoint { get; set; }
public int MaxRetries { get; set; }
public TimeSpan Timeout { get; set; }
}
// Configure in startup
builder.Services.Configure<Settings>(
builder.Configuration.GetSection("Settings"));
Using configuration in a service:
// MyService.cs
public class MyService
{
private readonly IOptionsSnapshot<Settings> _settings;
private readonly ILogger<MyService> _logger;
public MyService(IOptionsSnapshot<Settings> settings, ILogger<MyService> logger)
{
_settings = settings;
_logger = logger;
}
public async Task<string> CallExternalServiceAsync()
{
var endpoint = _settings.Value.ServiceEndpoint;
var maxRetries = _settings.Value.MaxRetries;
_logger.LogInformation("Calling {Endpoint} with {MaxRetries} retries",
endpoint, maxRetries);
// Your implementation here
using var client = new HttpClient();
var response = await client.GetStringAsync(endpoint);
return response;
}
}
Feature Flags
Create feature flags in App Configuration:
# Create a simple feature flag
az appconfig feature set \
--name myappconfig \
--feature Beta \
--yes
# Create a feature flag with conditions
az appconfig feature set \
--name myappconfig \
--feature PremiumFeature \
--yes
az appconfig feature filter add \
--name myappconfig \
--feature PremiumFeature \
--filter-name Microsoft.Percentage \
--filter-parameters Value=50
Using feature flags in code:
// Controllers/HomeController.cs
using Microsoft.FeatureManagement;
using Microsoft.FeatureManagement.Mvc;
[ApiController]
[Route("[controller]")]
public class HomeController : ControllerBase
{
private readonly IFeatureManager _featureManager;
public HomeController(IFeatureManager featureManager)
{
_featureManager = featureManager;
}
[HttpGet]
public async Task<IActionResult> Get()
{
if (await _featureManager.IsEnabledAsync("Beta"))
{
return Ok("Welcome to the beta experience!");
}
return Ok("Welcome!");
}
// Use attribute-based feature flag
[FeatureGate("PremiumFeature")]
[HttpGet("premium")]
public IActionResult GetPremium()
{
return Ok("Premium content here!");
}
}
Custom feature filter:
// UserTargetingFilter.cs
using Microsoft.FeatureManagement;
[FilterAlias("UserTargeting")]
public class UserTargetingFilter : IFeatureFilter
{
private readonly IHttpContextAccessor _httpContextAccessor;
public UserTargetingFilter(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)
{
var settings = context.Parameters.Get<UserTargetingSettings>();
var user = _httpContextAccessor.HttpContext?.User;
if (user?.Identity?.IsAuthenticated == true)
{
var userId = user.FindFirst("sub")?.Value;
return Task.FromResult(settings.AllowedUsers.Contains(userId));
}
return Task.FromResult(false);
}
}
public class UserTargetingSettings
{
public List<string> AllowedUsers { get; set; } = new();
}
// Register in Program.cs
builder.Services.AddFeatureManagement()
.AddFeatureFilter<UserTargetingFilter>();
Key Vault References
Store secrets in Key Vault and reference them from App Configuration:
# Create Key Vault and secret
az keyvault create \
--name mykeyvault \
--resource-group myResourceGroup \
--location eastus
az keyvault secret set \
--vault-name mykeyvault \
--name "DatabasePassword" \
--value "super-secret-password"
# Create Key Vault reference in App Configuration
az appconfig kv set-keyvault \
--name myappconfig \
--key "ConnectionStrings:Database:Password" \
--secret-identifier "https://mykeyvault.vault.azure.net/secrets/DatabasePassword" \
--yes
Configure the application to resolve Key Vault references:
builder.Configuration.AddAzureAppConfiguration(options =>
{
options.Connect(connectionString)
.ConfigureKeyVault(kv =>
{
kv.SetCredential(new DefaultAzureCredential());
});
});
Configuration Snapshots
Create immutable snapshots for deployment consistency:
# Create a snapshot
az appconfig snapshot create \
--name myappconfig \
--snapshot-name "release-v1.0.0" \
--filters '{\"key\":\"Settings:*\",\"label\":\"Production\"}'
# List snapshots
az appconfig snapshot list --name myappconfig
# Use snapshot in application
builder.Configuration.AddAzureAppConfiguration(options =>
{
options.Connect(connectionString)
.SelectSnapshot("release-v1.0.0");
});
Conclusion
Azure App Configuration simplifies configuration management for modern applications. By centralizing settings and feature flags, you gain:
- Single source of truth for configuration
- Dynamic updates without redeployment
- Environment-specific settings with labels
- Secure secret management through Key Vault integration
- Powerful feature flag capabilities for progressive rollouts
Start with basic key-value storage and gradually adopt feature flags and Key Vault integration as your needs grow.