Back to Blog
5 min read

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.

Michael John Pena

Michael John Pena

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.