![image]()
Previous article: Module 16 of 40 : ASP.NET Core Middleware Mastery: Custom Logging, Auth & Pipeline Control
Table of Contents
Introduction to Configuration in ASP.NET Core
Understanding the Configuration System
AppSettings.json Deep Dive
Environment-Specific Configuration
User Secrets for Development
Environment Variables Configuration
Command-Line Configuration
Custom Configuration Providers
Azure Key Vault Integration
AWS Secrets Manager
Database Configuration Provider
Configuration Best Practices
Security Considerations
Troubleshooting Common Issues
Real-World Implementation Examples
1. Introduction to Configuration in ASP.NET Core
Configuration management is a critical aspect of modern web application development. In ASP.NET Core, the configuration system has been completely redesigned to be more flexible, extensible, and cloud-ready. Let's explore why proper configuration management is essential:
Why Configuration Management Matters
Real-Life Scenario : Imagine you're developing an e-commerce application that needs to connect to different databases in development, staging, and production environments. Without proper configuration management, you'd be constantly changing connection strings, risking security breaches and deployment failures.
// Problematic approach - hardcoded configuration
public class DatabaseService
{
private string _connectionString = "Server=prod-db;Database=Shop;User=admin;Password=secret123";
public void Connect()
{
// This will fail in development and staging!
}
}
Evolution of Configuration in ASP.NET
From Web.config to Modern Approach :
ASP.NET Web Forms: XML-based Web.config with complex sections
ASP.NET MVC: Still Web.config with some improvements
ASP.NET Core: JSON-based, environment-aware, cloud-native
2. Understanding the Configuration System
Configuration Building Blocks
The ASP.NET Core configuration system is built around several key concepts:
IConfiguration Interface
public interface IConfiguration
{
string this[string key] { get; set; }
IConfigurationSection GetSection(string key);
IEnumerable<IConfigurationSection> GetChildren();
IChangeToken GetReloadToken();
}
Configuration Sources Hierarchy
ASP.NET Core loads configuration from multiple sources in a specific order:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
// Configuration sources are loaded in this order:
// 1. appsettings.json
// 2. appsettings.{Environment}.json
// 3. User Secrets (Development environment only)
// 4. Environment Variables
// 5. Command-line arguments
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Configuration Providers Deep Dive
// Detailed configuration setup with multiple providers
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
var env = context.HostingEnvironment;
// Base appsettings
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
// Environment-specific appsettings
config.AddJsonFile($"appsettings.{env.EnvironmentName}.json",
optional: true, reloadOnChange: true);
// User Secrets for development
if (env.IsDevelopment())
{
config.AddUserSecrets<Program>();
}
// Environment variables
config.AddEnvironmentVariables();
// Command-line arguments
if (args != null)
{
config.AddCommandLine(args);
}
// Last provider wins - command line has highest priority
});
3. AppSettings.json Deep Dive
Structured Configuration with JSON
Real-Life Example : E-commerce application configuration
{
"StoreSettings": {
"StoreName": "TechMart Online",
"MaxProductsPerPage": 50,
"EnableWishlist": true,
"Currency": "USD",
"SupportedCountries": ["US", "CA", "UK", "AU"]
},
"Database": {
"ConnectionString": "Server=localhost;Database=TechMart;Trusted_Connection=true;",
"Timeout": 30,
"RetryCount": 3
},
"PaymentGateway": {
"ApiKey": "pk_test_123456789",
"BaseUrl": "https://api.payments.com/v1",
"WebhookSecret": "whsec_987654321",
"Settings": {
"Timeout": 10000,
"RetryPolicy": "ExponentialBackoff"
}
},
"EmailSettings": {
"SmtpServer": "smtp.techmart.com",
"Port": 587,
"Username": "[email protected]",
"FromAddress": "[email protected]",
"Templates": {
"OrderConfirmation": "templates/order-confirmation.html",
"PasswordReset": "templates/password-reset.html"
}
},
"CacheSettings": {
"RedisConnection": "localhost:6379",
"DefaultExpiration": 3600,
"Enabled": true
},
"Security": {
"Jwt": {
"SecretKey": "your-super-secret-key-here",
"Issuer": "TechMart",
"Audience": "TechMartUsers",
"ExpirationMinutes": 60
},
"Cors": {
"AllowedOrigins": ["https://techmart.com", "https://admin.techmart.com"]
}
}
}
Strongly-Typed Configuration Classes
// Configuration classes for strongly-typed access
public class StoreSettings
{
public string StoreName { get; set; }
public int MaxProductsPerPage { get; set; }
public bool EnableWishlist { get; set; }
public string Currency { get; set; }
public string[] SupportedCountries { get; set; }
}
public class DatabaseSettings
{
public string ConnectionString { get; set; }
public int Timeout { get; set; }
public int RetryCount { get; set; }
}
public class PaymentGatewaySettings
{
public string ApiKey { get; set; }
public string BaseUrl { get; set; }
public string WebhookSecret { get; set; }
public PaymentSettings Settings { get; set; }
}
public class PaymentSettings
{
public int Timeout { get; set; }
public string RetryPolicy { get; set; }
}
public class EmailSettings
{
public string SmtpServer { get; set; }
public int Port { get; set; }
public string Username { get; set; }
public string FromAddress { get; set; }
public EmailTemplates Templates { get; set; }
}
public class EmailTemplates
{
public string OrderConfirmation { get; set; }
public string PasswordReset { get; set; }
}
public class CacheSettings
{
public string RedisConnection { get; set; }
public int DefaultExpiration { get; set; }
public bool Enabled { get; set; }
}
public class SecuritySettings
{
public JwtSettings Jwt { get; set; }
public CorsSettings Cors { get; set; }
}
public class JwtSettings
{
public string SecretKey { get; set; }
public string Issuer { get; set; }
public string Audience { get; set; }
public int ExpirationMinutes { get; set; }
}
public class CorsSettings
{
public string[] AllowedOrigins { get; set; }
}
Configuration Binding and Validation
// Startup configuration with validation
public class Startup
{
private readonly IConfiguration _configuration;
public Startup(IConfiguration configuration)
{
_configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
// Bind configuration sections to strongly-typed classes
services.Configure<StoreSettings>(_configuration.GetSection("StoreSettings"));
services.Configure<DatabaseSettings>(_configuration.GetSection("Database"));
services.Configure<PaymentGatewaySettings>(_configuration.GetSection("PaymentGateway"));
services.Configure<EmailSettings>(_configuration.GetSection("EmailSettings"));
services.Configure<CacheSettings>(_configuration.GetSection("CacheSettings"));
services.Configure<SecuritySettings>(_configuration.GetSection("Security"));
// Advanced binding with validation
var storeSettings = new StoreSettings();
_configuration.GetSection("StoreSettings").Bind(storeSettings);
// Validate critical settings
if (string.IsNullOrEmpty(storeSettings.StoreName))
{
throw new InvalidOperationException("StoreName is required in configuration");
}
// Register services that use configuration
services.AddSingleton(storeSettings);
// Database configuration with validation
var dbSettings = _configuration.GetSection("Database").Get<DatabaseSettings>();
if (string.IsNullOrEmpty(dbSettings?.ConnectionString))
{
throw new InvalidOperationException("Database connection string is required");
}
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(dbSettings.ConnectionString));
}
}
4. Environment-Specific Configuration
Environment-Based Configuration Strategy
Real-Life Scenario : Different configurations for Development, Staging, and Production environments.
// appsettings.Development.json
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Debug"
}
},
"Database": {
"ConnectionString": "Server=localhost;Database=TechMart_Dev;Trusted_Connection=true;",
"Timeout": 30,
"RetryCount": 3
},
"PaymentGateway": {
"BaseUrl": "https://api.sandbox.payments.com/v1",
"ApiKey": "pk_test_development_key"
},
"Features": {
"EnableSwagger": true,
"EnableDetailedErrors": true,
"UseInMemoryCache": true
}
}
// appsettings.Staging.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"Database": {
"ConnectionString": "Server=staging-db.techmart.com;Database=TechMart_Staging;User Id=appuser;Password=staging_password;",
"Timeout": 30,
"RetryCount": 5
},
"PaymentGateway": {
"BaseUrl": "https://api.staging.payments.com/v1",
"ApiKey": "pk_test_staging_key"
},
"Features": {
"EnableSwagger": false,
"EnableDetailedErrors": true,
"UseInMemoryCache": false
}
}
// appsettings.Production.json
{
"Logging": {
"LogLevel": {
"Default": "Warning",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Warning"
}
},
"Database": {
"ConnectionString": "Server=prod-db.techmart.com;Database=TechMart_Prod;User Id=appuser;Password=production_password;",
"Timeout": 30,
"RetryCount": 5
},
"PaymentGateway": {
"BaseUrl": "https://api.payments.com/v1",
"ApiKey": "pk_live_production_key"
},
"Features": {
"EnableSwagger": false,
"EnableDetailedErrors": false,
"UseInMemoryCache": false
}
}
Environment Detection and Configuration
// Program.cs with environment-specific setup
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
var env = context.HostingEnvironment;
Console.WriteLine($"Environment: {env.EnvironmentName}");
Console.WriteLine($"Content Root: {env.ContentRootPath}");
Console.WriteLine($"Web Root: {env.WebRootPath}");
// Load environment-specific configuration
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json",
optional: true, reloadOnChange: true);
// Additional configuration based on environment
if (env.IsDevelopment())
{
// Development-specific configuration
config.AddUserSecrets<Program>();
}
else if (env.IsStaging())
{
// Staging-specific configuration
config.AddJsonFile("appsettings.Staging.Secrets.json",
optional: true, reloadOnChange: true);
}
else if (env.IsProduction())
{
// Production-specific configuration
// Rely on environment variables and secure vaults
}
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Environment-Based Service Registration
// Environment-specific service configuration
public class Startup
{
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
Configuration = configuration;
Environment = env;
}
public IConfiguration Configuration { get; }
public IWebHostEnvironment Environment { get; }
public void ConfigureServices(IServiceCollection services)
{
// Common services for all environments
services.AddControllers();
services.AddHttpClient();
// Environment-specific services
if (Environment.IsDevelopment())
{
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "TechMart API", Version = "v1" });
});
}
// Database configuration based on environment
if (Environment.IsDevelopment())
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
.EnableSensitiveDataLogging());
}
else
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}
// Cache configuration
if (Configuration.GetValue<bool>("Features:UseInMemoryCache"))
{
services.AddMemoryCache();
}
else
{
var redisConnection = Configuration.GetValue<string>("CacheSettings:RedisConnection");
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = redisConnection;
});
}
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TechMart API v1"));
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
5. User Secrets for Development
Why Use User Secrets?
Real-Life Problem : Developers committing sensitive data to source control
// Before User Secrets - sensitive data in appsettings.json
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=MyApp;User Id=sa;Password=MySuperSecretPassword123!"
},
"ApiKeys": {
"SendGrid": "SG.abc123def456ghi789",
"Stripe": "sk_test_1234567890abcdef"
}
}
Setting Up User Secrets
Step 1. Initialize User Secrets
# In the project directory
dotnet user-secrets init
Step 2. Add Secrets
# Add connection string
dotnet user-secrets set "ConnectionStrings:DefaultConnection" "Server=localhost;Database=MyApp;User Id=sa;Password=DevPassword123!"
# Add API keys
dotnet user-secrets set "ApiKeys:SendGrid" "SG.devkey123456789"
dotnet user-secrets set "ApiKeys:Stripe" "sk_test_devkey123456789"
# Add JWT secret
dotnet user-secrets set "Security:Jwt:SecretKey" "dev-super-secret-jwt-key-2024"
Step 3. secrets.json File Structure
{
"ConnectionStrings:DefaultConnection": "Server=localhost;Database=MyApp_Dev;User Id=sa;Password=DevPassword123!",
"ApiKeys": {
"SendGrid": "SG.devkey123456789",
"Stripe": "sk_test_devkey123456789"
},
"Security:Jwt:SecretKey": "dev-super-secret-jwt-key-2024",
"PaymentGateway": {
"ApiKey": "pk_test_dev_123456789",
"WebhookSecret": "whsec_dev_987654321"
}
}
Programmatic Access to User Secrets
// Advanced User Secrets management
public class DevelopmentSecretsManager
{
private readonly IConfiguration _configuration;
public DevelopmentSecretsManager(IConfiguration configuration)
{
_configuration = configuration;
}
public void ValidateDevelopmentSecrets()
{
var requiredSecrets = new[]
{
"ConnectionStrings:DefaultConnection",
"ApiKeys:SendGrid",
"Security:Jwt:SecretKey"
};
var missingSecrets = requiredSecrets
.Where(secret => string.IsNullOrEmpty(_configuration[secret]))
.ToList();
if (missingSecrets.Any())
{
throw new InvalidOperationException(
$"Missing required development secrets: {string.Join(", ", missingSecrets)}. " +
"Run 'dotnet user-secrets set [key] [value]' to set them.");
}
}
public string GetSecret(string key)
{
var value = _configuration[key];
if (string.IsNullOrEmpty(value))
{
throw new ArgumentException($"Secret '{key}' not found in user secrets");
}
return value;
}
public T GetSecret<T>(string key)
{
var value = GetSecret(key);
return (T)Convert.ChangeType(value, typeof(T));
}
}
// Integration in Startup
public class Startup
{
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
Configuration = configuration;
Environment = env;
}
public IConfiguration Configuration { get; }
public IWebHostEnvironment Environment { get; }
public void ConfigureServices(IServiceCollection services)
{
// Validate secrets in development
if (Environment.IsDevelopment())
{
var secretsManager = new DevelopmentSecretsManager(Configuration);
secretsManager.ValidateDevelopmentSecrets();
}
// Use secrets in services
var connectionString = Configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
}
}
User Secrets Best Practices
// Secure development practices
public static class SecretsBestPractices
{
public static void EnsureSecretsAreNotInAppSettings(IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
var appSettingsPath = Path.Combine(env.ContentRootPath, "appsettings.json");
var appSettingsContent = File.ReadAllText(appSettingsPath);
var sensitivePatterns = new[]
{
@"password=([^;""']+)",
@"pwd=([^;""']+)",
@"api[_-]?key=([^;""']+)",
@"secret=([^;""']+)"
};
foreach (var pattern in sensitivePatterns)
{
var matches = System.Text.RegularExpressions.Regex.Matches(appSettingsContent, pattern,
System.Text.RegularExpressions.RegexOptions.IgnoreCase);
if (matches.Count > 0)
{
throw new InvalidOperationException(
"Sensitive data found in appsettings.json. Move to user secrets.");
}
}
}
}
}
6. Environment Variables Configuration
Environment Variables in Different Environments
Real-Life Scenario : Deployment to various environments (Docker, Azure, AWS, Kubernetes).
// Environment variable configuration provider
public static class EnvironmentVariablesConfig
{
public static void ConfigureEnvironmentVariables(this IConfigurationBuilder config)
{
config.AddEnvironmentVariables();
// Add prefix for application-specific variables
config.AddEnvironmentVariables("TECHMART_");
}
}
// Usage in Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
config.AddJsonFile("appsettings.json", optional: false)
.AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json",
optional: true)
.AddEnvironmentVariables("TECHMART_") // Application-specific
.AddEnvironmentVariables(); // All environment variables
});
Environment Variable Naming Conventions
// Environment variable mapping examples
public static class EnvVarConstants
{
// Database
public const string DB_CONNECTION = "TECHMART_DB_CONNECTION";
public const string DB_TIMEOUT = "TECHMART_DB_TIMEOUT";
// API Keys
public const string SENDGRID_API_KEY = "TECHMART_SENDGRID_API_KEY";
public const string STRIPE_API_KEY = "TECHMART_STRIPE_API_KEY";
// Application Settings
public const string ENVIRONMENT = "ASPNETCORE_ENVIRONMENT";
public const string URLs = "ASPNETCORE_URLS";
// Feature Flags
public const string FEATURE_SWAGGER = "TECHMART_FEATURE_SWAGGER";
public const string FEATURE_CACHE = "TECHMART_FEATURE_CACHE";
}
// Environment variable helper class
public class EnvironmentConfig
{
private readonly IConfiguration _configuration;
public EnvironmentConfig(IConfiguration configuration)
{
_configuration = configuration;
}
public string DatabaseConnection =>
GetRequiredValue(EnvVarConstants.DB_CONNECTION);
public string SendGridApiKey =>
GetRequiredValue(EnvVarConstants.SENDGRID_API_KEY);
public bool EnableSwagger =>
GetValue(EnvVarConstants.FEATURE_SWAGGER, false);
public int DatabaseTimeout =>
GetValue(EnvVarConstants.DB_TIMEOUT, 30);
private string GetRequiredValue(string key)
{
var value = _configuration[key];
if (string.IsNullOrEmpty(value))
{
throw new InvalidOperationException($"Required environment variable '{key}' is missing");
}
return value;
}
private T GetValue<T>(string key, T defaultValue)
{
var value = _configuration[key];
if (string.IsNullOrEmpty(value))
{
return defaultValue;
}
try
{
return (T)Convert.ChangeType(value, typeof(T));
}
catch (Exception ex)
{
throw new InvalidOperationException(
$"Unable to convert environment variable '{key}' value '{value}' to type {typeof(T).Name}", ex);
}
}
}
Docker and Container Configuration
# Dockerfile with environment variables
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["TechMart.Web/TechMart.Web.csproj", "TechMart.Web/"]
RUN dotnet restore "TechMart.Web/TechMart.Web.csproj"
COPY . .
WORKDIR "/src/TechMart.Web"
RUN dotnet build "TechMart.Web.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "TechMart.Web.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
# Environment variables for container
ENV ASPNETCORE_ENVIRONMENT=Production
ENV ASPNETCORE_URLS=http://+:80
ENV TECHMART_DB_TIMEOUT=30
ENV TECHMART_FEATURE_SWAGGER=false
ENTRYPOINT ["dotnet", "TechMart.Web.dll"]
# docker-compose.yml with environment variables
version: '3.8'
services:
techmart.web:
image: techmart/web:latest
build:
context: .
dockerfile: TechMart.Web/Dockerfile
environment:
- ASPNETCORE_ENVIRONMENT=Development
- TECHMART_DB_CONNECTION=Server=techmart.db;Database=TechMart_Dev;User Id=sa;Password=DevPassword123!
- TECHMART_SENDGRID_API_KEY=SG.docker_dev_key
- TECHMART_STRIPE_API_KEY=sk_test_docker_key
- TECHMART_FEATURE_SWAGGER=true
ports:
- "5000:80"
depends_on:
- techmart.db
techmart.db:
image: mcr.microsoft.com/mssql/server:2019-latest
environment:
- SA_PASSWORD=DevPassword123!
- ACCEPT_EULA=Y
ports:
- "1433:1433"
7. Command-Line Configuration
Command-Line Arguments in ASP.NET Core
Real-Life Scenario : Overriding configuration for specific deployments or debugging.
// Command-line configuration setup
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
// Configure command-line arguments with custom mappings
var switchMappings = new Dictionary<string, string>
{
{ "--urls", "urls" },
{ "--environment", "environment" },
{ "--db-connection", "ConnectionStrings:DefaultConnection" },
{ "--enable-swagger", "Features:EnableSwagger" },
{ "--cache-timeout", "CacheSettings:DefaultExpiration" },
{ "--log-level", "Logging:LogLevel:Default" }
};
config.AddCommandLine(args, switchMappings);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Advanced Command-Line Processing
// Custom command-line argument processor
public class CommandLineConfig
{
private readonly string[] _args;
public CommandLineConfig(string[] args)
{
_args = args;
}
public Dictionary<string, string> ParseArguments()
{
var config = new Dictionary<string, string>();
for (int i = 0; i < _args.Length; i++)
{
var arg = _args[i];
if (arg.StartsWith("--"))
{
var key = arg.Substring(2);
string value = "true"; // Default for flags
// Check if next argument is a value (not another flag)
if (i + 1 < _args.Length && !_args[i + 1].StartsWith("--"))
{
value = _args[i + 1];
i++; // Skip the value in next iteration
}
config[key] = value;
}
else if (arg.StartsWith("/"))
{
// Windows-style arguments: /key value or /key:value
var parts = arg.Substring(1).Split(':');
var key = parts[0];
var value = parts.Length > 1 ? parts[1] : "true";
config[key] = value;
}
else if (arg.Contains("="))
{
// Key=value format
var parts = arg.Split('=');
if (parts.Length == 2)
{
config[parts[0]] = parts[1];
}
}
}
return config;
}
public void ValidateRequiredArguments(Dictionary<string, string> arguments)
{
var requiredArgs = new[] { "db-connection", "environment" };
var missingArgs = requiredArgs.Where(arg => !arguments.ContainsKey(arg)).ToList();
if (missingArgs.Any())
{
throw new ArgumentException(
$"Missing required command-line arguments: {string.Join(", ", missingArgs)}");
}
}
}
// Integration with configuration
public static class CommandLineConfigExtensions
{
public static IConfigurationBuilder AddCustomCommandLine(
this IConfigurationBuilder builder,
string[] args)
{
var commandLineConfig = new CommandLineConfig(args);
var arguments = commandLineConfig.ParseArguments();
// Validate required arguments
commandLineConfig.ValidateRequiredArguments(arguments);
// Add to configuration
foreach (var (key, value) in arguments)
{
Environment.SetEnvironmentVariable($"CMDLINE_{key}", value);
}
return builder.AddInMemoryCollection(arguments);
}
}
Real-World Command-Line Usage Examples
# Development with specific settings
dotnet run --urls "https://localhost:5001" --environment "Development" --db-connection "Server=localhost;Database=DevDB;Trusted_Connection=true" --enable-swagger true --log-level "Debug"
# Production with minimal logging
dotnet run --urls "http://*:80" --environment "Production" --db-connection "Server=prod-db;Database=ProdDB;User Id=appuser;Password=ProdPass123!" --enable-swagger false --log-level "Warning"
# Docker container with environment overrides
docker run -p 8080:80 techmart/app --db-connection "Server=sql-server;Database=AppDB;User Id=sa;Password=DockerPass123!" --cache-timeout 3600
8. Custom Configuration Providers
Building Custom Configuration Providers
Real-Life Scenario : Reading configuration from a custom XML file or external service.
// Custom XML configuration provider
public class XmlConfigurationProvider : ConfigurationProvider
{
private readonly string _filePath;
public XmlConfigurationProvider(string filePath)
{
_filePath = filePath;
}
public override void Load()
{
var data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
try
{
if (File.Exists(_filePath))
{
var doc = XDocument.Load(_filePath);
ParseXmlElement(doc.Root, "", data);
}
}
catch (Exception ex)
{
throw new InvalidOperationException($"Failed to load XML configuration from {_filePath}", ex);
}
Data = data;
}
private void ParseXmlElement(XElement element, string prefix, Dictionary<string, string> data)
{
foreach (var child in element.Elements())
{
var currentKey = string.IsNullOrEmpty(prefix)
? child.Name.LocalName
: $"{prefix}:{child.Name.LocalName}";
if (child.HasElements)
{
ParseXmlElement(child, currentKey, data);
}
else
{
data[currentKey] = child.Value;
}
}
}
}
public class XmlConfigurationSource : IConfigurationSource
{
public string FilePath { get; set; }
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new XmlConfigurationProvider(FilePath);
}
}
public static class XmlConfigurationExtensions
{
public static IConfigurationBuilder AddXmlFile(
this IConfigurationBuilder builder,
string path,
bool optional = false)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (string.IsNullOrEmpty(path))
{
throw new ArgumentException("File path must not be null or empty", nameof(path));
}
var source = new XmlConfigurationSource
{
FilePath = path,
Optional = optional
};
builder.Add(source);
return builder;
}
}
Database Configuration Provider
// Database configuration provider for dynamic configuration
public class DatabaseConfigurationProvider : ConfigurationProvider, IDisposable
{
private readonly DatabaseConfigurationSource _source;
private readonly Timer _refreshTimer;
private bool _disposed = false;
public DatabaseConfigurationProvider(DatabaseConfigurationSource source)
{
_source = source;
if (_source.ReloadOnChange && _source.ReloadInterval > 0)
{
_refreshTimer = new Timer(RefreshConfiguration, null,
TimeSpan.FromSeconds(_source.ReloadInterval),
TimeSpan.FromSeconds(_source.ReloadInterval));
}
}
public override void Load()
{
RefreshConfiguration();
}
private void RefreshConfiguration(object state = null)
{
try
{
var newData = LoadConfigurationFromDatabase();
// Only update if configuration changed
if (!Data.SequenceEqual(newData))
{
Data = newData;
OnReload(); // Notify about configuration changes
}
}
catch (Exception ex)
{
// Log error but don't crash the application
Console.WriteLine($"Failed to refresh configuration from database: {ex.Message}");
}
}
private Dictionary<string, string> LoadConfigurationFromDatabase()
{
var data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
using var connection = new SqlConnection(_source.ConnectionString);
connection.Open();
var command = new SqlCommand(
"SELECT [Key], [Value], [Environment] FROM Configuration WHERE [Environment] = @Environment OR [Environment] IS NULL",
connection);
command.Parameters.AddWithValue("@Environment", _source.Environment);
using var reader = command.ExecuteReader();
while (reader.Read())
{
var key = reader.GetString(0);
var value = reader.GetString(1);
data[key] = value;
}
return data;
}
public void Dispose()
{
if (!_disposed)
{
_refreshTimer?.Dispose();
_disposed = true;
}
}
}
public class DatabaseConfigurationSource : IConfigurationSource
{
public string ConnectionString { get; set; }
public string Environment { get; set; }
public bool ReloadOnChange { get; set; }
public int ReloadInterval { get; set; } = 30; // seconds
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new DatabaseConfigurationProvider(this);
}
}
public static class DatabaseConfigurationExtensions
{
public static IConfigurationBuilder AddDatabaseConfiguration(
this IConfigurationBuilder builder,
string connectionString,
string environment,
bool reloadOnChange = false,
int reloadInterval = 30)
{
return builder.Add(new DatabaseConfigurationSource
{
ConnectionString = connectionString,
Environment = environment,
ReloadOnChange = reloadOnChange,
ReloadInterval = reloadInterval
});
}
}
9. Azure Key Vault Integration
Securing Secrets with Azure Key Vault
Real-Life Scenario : Enterprise application with multiple environments and compliance requirements.
// Azure Key Vault configuration provider
public class Program
{
public static async Task Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
// Validate Key Vault connectivity
await ValidateKeyVaultConnection(host.Services);
await host.RunAsync();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
var builtConfig = config.Build();
// Add Azure Key Vault
var keyVaultEndpoint = builtConfig["AzureKeyVault:Endpoint"];
if (!string.IsNullOrEmpty(keyVaultEndpoint))
{
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback(
azureServiceTokenProvider.KeyVaultTokenCallback));
config.AddAzureKeyVault(
keyVaultEndpoint,
keyVaultClient,
new DefaultKeyVaultSecretManager());
}
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
private static async Task ValidateKeyVaultConnection(IServiceProvider services)
{
try
{
var configuration = services.GetRequiredService<IConfiguration>();
var keyVaultEndpoint = configuration["AzureKeyVault:Endpoint"];
if (!string.IsNullOrEmpty(keyVaultEndpoint))
{
var secretClient = new SecretClient(new Uri(keyVaultEndpoint),
new DefaultAzureCredential());
// Test connection by trying to list secrets
await secretClient.GetPropertiesOfSecretsAsync().FirstAsync();
Console.WriteLine("✅ Azure Key Vault connection successful");
}
}
catch (Exception ex)
{
Console.WriteLine($"❌ Azure Key Vault connection failed: {ex.Message}");
throw;
}
}
}
Advanced Key Vault Management
// Key Vault secret management service
public class KeyVaultSecretManager
{
private readonly SecretClient _secretClient;
private readonly ILogger<KeyVaultSecretManager> _logger;
public KeyVaultSecretManager(IConfiguration configuration, ILogger<KeyVaultSecretManager> logger)
{
var keyVaultEndpoint = configuration["AzureKeyVault:Endpoint"];
if (string.IsNullOrEmpty(keyVaultEndpoint))
{
throw new InvalidOperationException("Azure Key Vault endpoint is not configured");
}
_secretClient = new SecretClient(new Uri(keyVaultEndpoint), new DefaultAzureCredential());
_logger = logger;
}
public async Task<string> GetSecretAsync(string secretName)
{
try
{
var secret = await _secretClient.GetSecretAsync(secretName);
return secret.Value.Value;
}
catch (RequestFailedException ex) when (ex.Status == 404)
{
_logger.LogWarning("Secret '{SecretName}' not found in Key Vault", secretName);
return null;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to retrieve secret '{SecretName}' from Key Vault", secretName);
throw;
}
}
public async Task SetSecretAsync(string secretName, string secretValue)
{
try
{
await _secretClient.SetSecretAsync(secretName, secretValue);
_logger.LogInformation("Secret '{SecretName}' updated in Key Vault", secretName);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to set secret '{SecretName}' in Key Vault", secretName);
throw;
}
}
public async Task<IEnumerable<string>> ListSecretsAsync()
{
var secrets = new List<string>();
await foreach (var secretProperties in _secretClient.GetPropertiesOfSecretsAsync())
{
secrets.Add(secretProperties.Name);
}
return secrets;
}
public async Task<bool> SecretExistsAsync(string secretName)
{
try
{
await _secretClient.GetSecretAsync(secretName);
return true;
}
catch (RequestFailedException ex) when (ex.Status == 404)
{
return false;
}
}
}
// Startup configuration for Key Vault
public class Startup
{
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
Configuration = configuration;
Environment = env;
}
public IConfiguration Configuration { get; }
public IWebHostEnvironment Environment { get; }
public void ConfigureServices(IServiceCollection services)
{
// Register Key Vault service
services.AddSingleton<KeyVaultSecretManager>();
// Configure database using Key Vault
services.AddDbContext<ApplicationDbContext>(options =>
{
var connectionString = Configuration.GetConnectionString("DefaultConnection");
options.UseSqlServer(connectionString);
});
// Configure other services with secrets from Key Vault
services.Configure<PaymentGatewaySettings>(options =>
{
options.ApiKey = Configuration["PaymentGateway--ApiKey"];
options.WebhookSecret = Configuration["PaymentGateway--WebhookSecret"];
});
}
}
Key Vault Secret Rotation
// Secret rotation service
public class SecretRotationService : BackgroundService
{
private readonly KeyVaultSecretManager _secretManager;
private readonly IConfiguration _configuration;
private readonly ILogger<SecretRotationService> _logger;
public SecretRotationService(
KeyVaultSecretManager secretManager,
IConfiguration configuration,
ILogger<SecretRotationService> logger)
{
_secretManager = secretManager;
_configuration = configuration;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
await RotateSecretsIfNeeded();
await Task.Delay(TimeSpan.FromHours(24), stoppingToken); // Check daily
}
catch (Exception ex)
{
_logger.LogError(ex, "Secret rotation failed");
await Task.Delay(TimeSpan.FromHours(1), stoppingToken); // Retry after 1 hour
}
}
}
private async Task RotateSecretsIfNeeded()
{
var secretsToRotate = new[] { "DatabasePassword", "ApiKey--Primary", "JwtSecret" };
foreach (var secretName in secretsToRotate)
{
if (await ShouldRotateSecret(secretName))
{
await RotateSecret(secretName);
}
}
}
private async Task<bool> ShouldRotateSecret(string secretName)
{
// Check secret age or other rotation criteria
var secret = await _secretManager.GetSecretAsync(secretName);
if (secret == null) return false;
// Implement rotation logic based on your requirements
return false; // Placeholder
}
private async Task RotateSecret(string secretName)
{
_logger.LogInformation("Rotating secret: {SecretName}", secretName);
// Generate new secret value
var newSecretValue = GenerateNewSecret(secretName);
// Set new version in Key Vault
await _secretManager.SetSecretAsync(secretName, newSecretValue);
_logger.LogInformation("Successfully rotated secret: {SecretName}", secretName);
}
private string GenerateNewSecret(string secretName)
{
return secretName switch
{
"DatabasePassword" => GenerateSecurePassword(),
"ApiKey--Primary" => GenerateApiKey(),
"JwtSecret" => GenerateJwtSecret(),
_ => Guid.NewGuid().ToString()
};
}
private string GenerateSecurePassword()
{
const string validChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*";
var random = new Random();
return new string(Enumerable.Repeat(validChars, 32)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
private string GenerateApiKey() => $"key_{Guid.NewGuid():N}";
private string GenerateJwtSecret() => Convert.ToBase64String(Guid.NewGuid().ToByteArray());
}
10. AWS Secrets Manager
AWS Secrets Manager Integration
Real-Life Scenario : Multi-cloud deployment with AWS infrastructure
// AWS Secrets Manager configuration provider
public class AwsSecretsManagerConfigurationProvider : ConfigurationProvider
{
private readonly string _region;
private readonly string _secretName;
private readonly IAmazonSecretsManager _secretsClient;
public AwsSecretsManagerConfigurationProvider(string region, string secretName)
{
_region = region;
_secretName = secretName;
_secretsClient = new AmazonSecretsManagerClient(RegionEndpoint.GetBySystemName(region));
}
public override void Load()
{
try
{
var secret = GetSecretValueAsync().GetAwaiter().GetResult();
var data = ParseSecretString(secret);
Data = data;
}
catch (Exception ex)
{
throw new InvalidOperationException($"Failed to load secrets from AWS Secrets Manager: {ex.Message}", ex);
}
}
private async Task<string> GetSecretValueAsync()
{
var request = new GetSecretValueRequest
{
SecretId = _secretName
};
var response = await _secretsClient.GetSecretValueAsync(request);
return response.SecretString;
}
private Dictionary<string, string> ParseSecretString(string secretString)
{
var data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
try
{
var json = JsonDocument.Parse(secretString);
ParseJsonElement(json.RootElement, "", data);
}
catch (JsonException)
{
// If not JSON, treat as simple key-value pairs
data[_secretName] = secretString;
}
return data;
}
private void ParseJsonElement(JsonElement element, string prefix, Dictionary<string, string> data)
{
switch (element.ValueKind)
{
case JsonValueKind.Object:
foreach (var property in element.EnumerateObject())
{
var currentKey = string.IsNullOrEmpty(prefix)
? property.Name
: $"{prefix}:{property.Name}";
ParseJsonElement(property.Value, currentKey, data);
}
break;
case JsonValueKind.Array:
var index = 0;
foreach (var item in element.EnumerateArray())
{
var currentKey = $"{prefix}:{index}";
ParseJsonElement(item, currentKey, data);
index++;
}
break;
default:
data[prefix] = element.ToString();
break;
}
}
}
public class AwsSecretsManagerConfigurationSource : IConfigurationSource
{
public string Region { get; set; }
public string SecretName { get; set; }
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new AwsSecretsManagerConfigurationProvider(Region, SecretName);
}
}
public static class AwsSecretsManagerConfigurationExtensions
{
public static IConfigurationBuilder AddAwsSecretsManager(
this IConfigurationBuilder builder,
string region,
string secretName)
{
return builder.Add(new AwsSecretsManagerConfigurationSource
{
Region = region,
SecretName = secretName
});
}
}
Multi-Cloud Secret Management
// Unified secret management service
public interface ISecretManager
{
Task<string> GetSecretAsync(string secretName);
Task SetSecretAsync(string secretName, string secretValue);
Task<bool> SecretExistsAsync(string secretName);
}
public class MultiCloudSecretManager : ISecretManager
{
private readonly KeyVaultSecretManager _azureSecretManager;
private readonly AwsSecretManager _awsSecretManager;
private readonly IConfiguration _configuration;
private readonly ILogger<MultiCloudSecretManager> _logger;
public MultiCloudSecretManager(
KeyVaultSecretManager azureSecretManager,
AwsSecretManager awsSecretManager,
IConfiguration configuration,
ILogger<MultiCloudSecretManager> logger)
{
_azureSecretManager = azureSecretManager;
_awsSecretManager = awsSecretManager;
_configuration = configuration;
_logger = logger;
}
public async Task<string> GetSecretAsync(string secretName)
{
// Determine which cloud provider to use based on configuration
var cloudProvider = _configuration["CloudProvider"] ?? "Azure";
try
{
return cloudProvider switch
{
"Azure" => await _azureSecretManager.GetSecretAsync(secretName),
"AWS" => await _awsSecretManager.GetSecretAsync(secretName),
_ => throw new InvalidOperationException($"Unsupported cloud provider: {cloudProvider}")
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to retrieve secret '{SecretName}' from {CloudProvider}",
secretName, cloudProvider);
throw;
}
}
public async Task SetSecretAsync(string secretName, string secretValue)
{
var cloudProvider = _configuration["CloudProvider"] ?? "Azure";
try
{
switch (cloudProvider)
{
case "Azure":
await _azureSecretManager.SetSecretAsync(secretName, secretValue);
break;
case "AWS":
await _awsSecretManager.SetSecretAsync(secretName, secretValue);
break;
default:
throw new InvalidOperationException($"Unsupported cloud provider: {cloudProvider}");
}
_logger.LogInformation("Secret '{SecretName}' updated in {CloudProvider}",
secretName, cloudProvider);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to set secret '{SecretName}' in {CloudProvider}",
secretName, cloudProvider);
throw;
}
}
public async Task<bool> SecretExistsAsync(string secretName)
{
var cloudProvider = _configuration["CloudProvider"] ?? "Azure";
return cloudProvider switch
{
"Azure" => await _azureSecretManager.SecretExistsAsync(secretName),
"AWS" => await _awsSecretManager.SecretExistsAsync(secretName),
_ => throw new InvalidOperationException($"Unsupported cloud provider: {cloudProvider}")
};
}
}
// AWS Secret Manager implementation
public class AwsSecretManager
{
private readonly IAmazonSecretsManager _secretsClient;
private readonly ILogger<AwsSecretManager> _logger;
public AwsSecretManager(string region, ILogger<AwsSecretManager> logger)
{
_secretsClient = new AmazonSecretsManagerClient(RegionEndpoint.GetBySystemName(region));
_logger = logger;
}
public async Task<string> GetSecretAsync(string secretName)
{
try
{
var request = new GetSecretValueRequest { SecretId = secretName };
var response = await _secretsClient.GetSecretValueAsync(request);
return response.SecretString;
}
catch (ResourceNotFoundException)
{
_logger.LogWarning("Secret '{SecretName}' not found in AWS Secrets Manager", secretName);
return null;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to retrieve secret '{SecretName}' from AWS Secrets Manager", secretName);
throw;
}
}
public async Task SetSecretAsync(string secretName, string secretValue)
{
try
{
var request = new PutSecretValueRequest
{
SecretId = secretName,
SecretString = secretValue
};
await _secretsClient.PutSecretValueAsync(request);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to set secret '{SecretName}' in AWS Secrets Manager", secretName);
throw;
}
}
public async Task<bool> SecretExistsAsync(string secretName)
{
try
{
await GetSecretAsync(secretName);
return true;
}
catch (ResourceNotFoundException)
{
return false;
}
}
}
11. Database Configuration Provider
Dynamic Database Configuration
Real-Life Scenario : Application that needs runtime configuration changes without redeployment
// Advanced database configuration provider
public class DynamicDatabaseConfigurationProvider : ConfigurationProvider, IDisposable
{
private readonly DynamicDatabaseConfigurationSource _source;
private readonly Timer _refreshTimer;
private readonly ILogger<DynamicDatabaseConfigurationProvider> _logger;
private bool _disposed = false;
public DynamicDatabaseConfigurationProvider(DynamicDatabaseConfigurationSource source)
{
_source = source;
_logger = source.LoggerFactory?.CreateLogger<DynamicDatabaseConfigurationProvider>();
if (_source.ReloadOnChange)
{
_refreshTimer = new Timer(async _ => await RefreshConfigurationAsync(),
null,
TimeSpan.FromSeconds(_source.ReloadInterval),
TimeSpan.FromSeconds(_source.ReloadInterval));
}
}
public override void Load()
{
LoadAsync().GetAwaiter().GetResult();
}
private async Task LoadAsync()
{
try
{
await RefreshConfigurationAsync();
}
catch (Exception ex)
{
_logger?.LogError(ex, "Failed to load configuration from database");
throw;
}
}
private async Task RefreshConfigurationAsync()
{
try
{
var newData = await LoadConfigurationFromDatabaseAsync();
if (!Data.SequenceEqual(newData))
{
Data = newData;
OnReload();
_logger?.LogInformation("Configuration reloaded from database");
}
}
catch (Exception ex)
{
_logger?.LogError(ex, "Failed to refresh configuration from database");
}
}
private async Task<Dictionary<string, string>> LoadConfigurationFromDatabaseAsync()
{
var data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
await using var connection = new SqlConnection(_source.ConnectionString);
await connection.OpenAsync();
var command = new SqlCommand(_source.Query, connection);
command.Parameters.AddWithValue("@Environment", _source.Environment);
command.Parameters.AddWithValue("@Application", _source.ApplicationName);
await using var reader = await command.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
var key = reader.GetString(0);
var value = reader.IsDBNull(1) ? null : reader.GetString(1);
data[key] = value;
}
return data;
}
public void Dispose()
{
if (!_disposed)
{
_refreshTimer?.Dispose();
_disposed = true;
}
}
}
public class DynamicDatabaseConfigurationSource : IConfigurationSource
{
public string ConnectionString { get; set; }
public string Query { get; set; } =
"SELECT [Key], [Value] FROM Configuration WHERE ([Environment] = @Environment OR [Environment] IS NULL) AND ([Application] = @Application OR [Application] IS NULL)";
public string Environment { get; set; }
public string ApplicationName { get; set; }
public bool ReloadOnChange { get; set; } = true;
public int ReloadInterval { get; set; } = 30;
public ILoggerFactory LoggerFactory { get; set; }
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new DynamicDatabaseConfigurationProvider(this);
}
}
public static class DynamicDatabaseConfigurationExtensions
{
public static IConfigurationBuilder AddDynamicDatabaseConfiguration(
this IConfigurationBuilder builder,
string connectionString,
string environment,
string applicationName = null,
bool reloadOnChange = true,
int reloadInterval = 30,
ILoggerFactory loggerFactory = null)
{
return builder.Add(new DynamicDatabaseConfigurationSource
{
ConnectionString = connectionString,
Environment = environment,
ApplicationName = applicationName,
ReloadOnChange = reloadOnChange,
ReloadInterval = reloadInterval,
LoggerFactory = loggerFactory
});
}
}
Configuration Management API
// API for managing configuration in database
[ApiController]
[Route("api/[controller]")]
public class ConfigurationController : ControllerBase
{
private readonly ApplicationDbContext _context;
private readonly IConfiguration _configuration;
private readonly ILogger<ConfigurationController> _logger;
public ConfigurationController(
ApplicationDbContext context,
IConfiguration configuration,
ILogger<ConfigurationController> logger)
{
_context = context;
_configuration = configuration;
_logger = logger;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<ConfigurationItem>>> GetConfiguration(
[FromQuery] string environment = null,
[FromQuery] string application = null)
{
var query = _context.Configuration.AsQueryable();
if (!string.IsNullOrEmpty(environment))
{
query = query.Where(c => c.Environment == environment || c.Environment == null);
}
if (!string.IsNullOrEmpty(application))
{
query = query.Where(c => c.Application == application || c.Application == null);
}
var items = await query.ToListAsync();
return Ok(items);
}
[HttpPost]
public async Task<ActionResult<ConfigurationItem>> CreateConfiguration(
ConfigurationItem item)
{
// Validate the configuration key
if (string.IsNullOrEmpty(item.Key))
{
return BadRequest("Key is required");
}
// Check if configuration already exists
var existing = await _context.Configuration
.Where(c => c.Key == item.Key &&
c.Environment == item.Environment &&
c.Application == item.Application)
.FirstOrDefaultAsync();
if (existing != null)
{
return Conflict($"Configuration with key '{item.Key}' already exists");
}
_context.Configuration.Add(item);
await _context.SaveChangesAsync();
_logger.LogInformation("Configuration created: {Key} for {Environment}/{Application}",
item.Key, item.Environment, item.Application);
return CreatedAtAction(nameof(GetConfigurationItem),
new { id = item.Id }, item);
}
[HttpPut("{id}")]
public async Task<IActionResult> UpdateConfiguration(int id, ConfigurationItem item)
{
if (id != item.Id)
{
return BadRequest();
}
var existing = await _context.Configuration.FindAsync(id);
if (existing == null)
{
return NotFound();
}
existing.Value = item.Value;
existing.UpdatedAt = DateTime.UtcNow;
existing.UpdatedBy = User.Identity.Name;
await _context.SaveChangesAsync();
_logger.LogInformation("Configuration updated: {Key}", existing.Key);
return NoContent();
}
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteConfiguration(int id)
{
var item = await _context.Configuration.FindAsync(id);
if (item == null)
{
return NotFound();
}
_context.Configuration.Remove(item);
await _context.SaveChangesAsync();
_logger.LogInformation("Configuration deleted: {Key}", item.Key);
return NoContent();
}
[HttpGet("{id}")]
public async Task<ActionResult<ConfigurationItem>> GetConfigurationItem(int id)
{
var item = await _context.Configuration.FindAsync(id);
if (item == null)
{
return NotFound();
}
return item;
}
}
// Configuration entity model
public class ConfigurationItem
{
public int Id { get; set; }
[Required]
[StringLength(200)]
public string Key { get; set; }
public string Value { get; set; }
[StringLength(50)]
public string Environment { get; set; }
[StringLength(50)]
public string Application { get; set; }
public string Description { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public string CreatedBy { get; set; }
public DateTime? UpdatedAt { get; set; }
public string UpdatedBy { get; set; }
public bool IsSensitive { get; set; }
}
12. Configuration Best Practices
Security Best Practices
// Security-focused configuration validation
public class SecurityConfigurationValidator
{
private readonly IConfiguration _configuration;
private readonly ILogger<SecurityConfigurationValidator> _logger;
public SecurityConfigurationValidator(
IConfiguration configuration,
ILogger<SecurityConfigurationValidator> logger)
{
_configuration = configuration;
_logger = logger;
}
public void ValidateSecuritySettings()
{
ValidateNoSecretsInAppSettings();
ValidateConnectionStrings();
ValidateApiKeys();
ValidateJwtSettings();
}
private void ValidateNoSecretsInAppSettings()
{
var appSettingsPath = "appsettings.json";
if (File.Exists(appSettingsPath))
{
var content = File.ReadAllText(appSettingsPath);
var sensitivePatterns = new[]
{
@"(?i)password[=:""']([^""']+)",
@"(?i)pwd[=:""']([^""']+)",
@"(?i)secret[=:""']([^""']+)",
@"(?i)key[=:""']([^""']+)",
@"(?i)token[=:""']([^""']+)",
@"sk_live_[a-zA-Z0-9]+",
@"SG\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+"
};
foreach (var pattern in sensitivePatterns)
{
var matches = System.Text.RegularExpressions.Regex.Matches(content, pattern);
if (matches.Count > 0)
{
_logger.LogWarning(
"Potential secrets found in appsettings.json. Pattern: {Pattern}. " +
"Consider moving these to secure storage.", pattern);
}
}
}
}
private void ValidateConnectionStrings()
{
var connectionStrings = _configuration.GetSection("ConnectionStrings").GetChildren();
foreach (var connectionString in connectionStrings)
{
if (string.IsNullOrEmpty(connectionString.Value))
{
throw new InvalidOperationException(
$"Connection string '{connectionString.Key}' is empty");
}
// Check for weak passwords in connection strings
if (ContainsWeakPassword(connectionString.Value))
{
_logger.LogWarning(
"Connection string '{Key}' may contain weak password",
connectionString.Key);
}
}
}
private void ValidateApiKeys()
{
var apiKeysSection = _configuration.GetSection("ApiKeys");
if (apiKeysSection.Exists())
{
foreach (var apiKey in apiKeysSection.GetChildren())
{
if (string.IsNullOrEmpty(apiKey.Value))
{
throw new InvalidOperationException($"API key '{apiKey.Key}' is empty");
}
if (apiKey.Value.Length < 16)
{
_logger.LogWarning("API key '{Key}' may be too short", apiKey.Key);
}
}
}
}
private void ValidateJwtSettings()
{
var jwtSection = _configuration.GetSection("Security:Jwt");
if (jwtSection.Exists())
{
var secretKey = jwtSection["SecretKey"];
if (string.IsNullOrEmpty(secretKey))
{
throw new InvalidOperationException("JWT secret key is required");
}
if (secretKey.Length < 32)
{
throw new InvalidOperationException(
"JWT secret key must be at least 32 characters long");
}
if (secretKey.Equals("your-super-secret-key-here", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(
"JWT secret key must be changed from the default value");
}
}
}
private bool ContainsWeakPassword(string connectionString)
{
var weakPasswords = new[] { "password", "123456", "admin", "sa", "test" };
return weakPasswords.Any(weak =>
connectionString.Contains($"Password={weak}") ||
connectionString.Contains($"Pwd={weak}"));
}
}
Performance Optimization
// Configuration performance optimization
public class OptimizedConfigurationService
{
private readonly IConfiguration _configuration;
private readonly ConcurrentDictionary<string, object> _cache;
private readonly TimeSpan _cacheDuration;
public OptimizedConfigurationService(IConfiguration configuration)
{
_configuration = configuration;
_cache = new ConcurrentDictionary<string, object>();
_cacheDuration = TimeSpan.FromMinutes(5);
}
public T GetValue<T>(string key, T defaultValue = default)
{
var cacheKey = $"{typeof(T).Name}:{key}";
if (_cache.TryGetValue(cacheKey, out var cachedValue) &&
cachedValue is CacheItem<T> cacheItem &&
cacheItem.Expiry > DateTime.UtcNow)
{
return cacheItem.Value;
}
var value = _configuration.GetValue(key, defaultValue);
_cache[cacheKey] = new CacheItem<T>(value, DateTime.UtcNow.Add(_cacheDuration));
return value;
}
public T GetSection<T>(string sectionKey) where T : class, new()
{
var cacheKey = $"{typeof(T).Name}:{sectionKey}";
if (_cache.TryGetValue(cacheKey, out var cachedValue) &&
cachedValue is CacheItem<T> cacheItem &&
cacheItem.Expiry > DateTime.UtcNow)
{
return cacheItem.Value;
}
var section = _configuration.GetSection(sectionKey);
var value = new T();
section.Bind(value);
_cache[cacheKey] = new CacheItem<T>(value, DateTime.UtcNow.Add(_cacheDuration));
return value;
}
public void ClearCache()
{
_cache.Clear();
}
public void RemoveFromCache(string key)
{
_cache.TryRemove(key, out _);
}
private class CacheItem<T>
{
public T Value { get; }
public DateTime Expiry { get; }
public CacheItem(T value, DateTime expiry)
{
Value = value;
Expiry = expiry;
}
}
}
// Configuration change detection and cache invalidation
public class ConfigurationChangeNotifier : IDisposable
{
private readonly IConfiguration _configuration;
private readonly OptimizedConfigurationService _configService;
private readonly ILogger<ConfigurationChangeNotifier> _logger;
private readonly List<IDisposable> _changeCallbacks;
public ConfigurationChangeNotifier(
IConfiguration configuration,
OptimizedConfigurationService configService,
ILogger<ConfigurationChangeNotifier> logger)
{
_configuration = configuration;
_configService = configService;
_logger = logger;
_changeCallbacks = new List<IDisposable>();
SetupChangeDetection();
}
private void SetupChangeDetection()
{
// Monitor specific sections for changes
var sectionsToMonitor = new[] { "Database", "Features", "CacheSettings" };
foreach (var section in sectionsToMonitor)
{
var changeToken = _configuration.GetReloadToken();
var callback = changeToken.RegisterChangeCallback(_ => OnConfigurationChanged(section), section);
_changeCallbacks.Add(callback);
}
}
private void OnConfigurationChanged(string section)
{
_logger.LogInformation("Configuration section '{Section}' changed, clearing cache", section);
_configService.ClearCache();
// Re-register for changes
SetupChangeDetection();
}
public void Dispose()
{
foreach (var callback in _changeCallbacks)
{
callback?.Dispose();
}
_changeCallbacks.Clear();
}
}
13. Security Considerations
Comprehensive Security Validation
// Advanced security configuration analyzer
public class SecurityConfigurationAnalyzer
{
private readonly IConfiguration _configuration;
private readonly IWebHostEnvironment _environment;
private readonly ILogger<SecurityConfigurationAnalyzer> _logger;
public SecurityConfigurationAnalyzer(
IConfiguration configuration,
IWebHostEnvironment environment,
ILogger<SecurityConfigurationAnalyzer> logger)
{
_configuration = configuration;
_environment = environment;
_logger = logger;
}
public SecurityAnalysisResult Analyze()
{
var result = new SecurityAnalysisResult();
result.Issues.AddRange(CheckForHardcodedSecrets());
result.Issues.AddRange(CheckWeakEncryption());
result.Issues.AddRange(CheckInsecureProtocols());
result.Issues.AddRange(CheckExposedEndpoints());
result.Issues.AddRange(CheckCorsSettings());
result.Issues.AddRange(CheckJwtSecurity());
result.Score = CalculateSecurityScore(result.Issues);
return result;
}
private IEnumerable<SecurityIssue> CheckForHardcodedSecrets()
{
var issues = new List<SecurityIssue>();
// Check appsettings for common secret patterns
var appSettingsPath = Path.Combine(_environment.ContentRootPath, "appsettings.json");
if (File.Exists(appSettingsPath))
{
var content = File.ReadAllText(appSettingsPath);
var secretPatterns = new[]
{
new { Pattern = @"(?i)[""']?password[""']?\s*[:=]\s*[""']([^""']+)[""']", Type = "Hardcoded Password" },
new { Pattern = @"(?i)[""']?apikey[""']?\s*[:=]\s*[""']([^""']+)[""']", Type = "Hardcoded API Key" },
new { Pattern = @"(?i)[""']?secret[""']?\s*[:=]\s*[""']([^""']+)[""']", Type = "Hardcoded Secret" },
new { Pattern = @"sk_live_[a-zA-Z0-9]{24}", Type = "Stripe Secret Key" },
new { Pattern = @"SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}", Type = "SendGrid API Key" }
};
foreach (var pattern in secretPatterns)
{
var matches = System.Text.RegularExpressions.Regex.Matches(content, pattern.Pattern);
foreach (Match match in matches)
{
issues.Add(new SecurityIssue
{
Type = pattern.Type,
Severity = SecuritySeverity.High,
Message = $"{pattern.Type} found in appsettings.json",
Recommendation = "Move to secure storage like Azure Key Vault or AWS Secrets Manager"
});
}
}
}
return issues;
}
private IEnumerable<SecurityIssue> CheckWeakEncryption()
{
var issues = new List<SecurityIssue>();
var jwtSecret = _configuration["Security:Jwt:SecretKey"];
if (!string.IsNullOrEmpty(jwtSecret) && jwtSecret.Length < 32)
{
issues.Add(new SecurityIssue
{
Type = "Weak JWT Secret",
Severity = SecuritySeverity.High,
Message = "JWT secret key is too short (minimum 32 characters recommended)",
Recommendation = "Generate a strong random secret key with at least 32 characters"
});
}
return issues;
}
private IEnumerable<SecurityIssue> CheckInsecureProtocols()
{
var issues = new List<SecurityIssue>();
var paymentUrl = _configuration["PaymentGateway:BaseUrl"];
if (!string.IsNullOrEmpty(paymentUrl) && paymentUrl.StartsWith("http://"))
{
issues.Add(new SecurityIssue
{
Type = "Insecure Protocol",
Severity = SecuritySeverity.Medium,
Message = "Payment gateway URL uses HTTP instead of HTTPS",
Recommendation = "Use HTTPS for all external service communications"
});
}
return issues;
}
private IEnumerable<SecurityIssue> CheckExposedEndpoints()
{
var issues = new List<SecurityIssue>();
if (_environment.IsProduction())
{
var enableSwagger = _configuration.GetValue<bool>("Features:EnableSwagger");
if (enableSwagger)
{
issues.Add(new SecurityIssue
{
Type = "Exposed Development Tool",
Severity = SecuritySeverity.Medium,
Message = "Swagger is enabled in production environment",
Recommendation = "Disable Swagger in production for security"
});
}
}
return issues;
}
private IEnumerable<SecurityIssue> CheckCorsSettings()
{
var issues = new List<SecurityIssue>();
var corsOrigins = _configuration.GetSection("Security:Cors:AllowedOrigins").Get<string[]>();
if (corsOrigins != null && corsOrigins.Contains("*"))
{
issues.Add(new SecurityIssue
{
Type = "Overly Permissive CORS",
Severity = SecuritySeverity.High,
Message = "CORS policy allows all origins (*)",
Recommendation = "Restrict CORS to specific trusted domains"
});
}
return issues;
}
private IEnumerable<SecurityIssue> CheckJwtSecurity()
{
var issues = new List<SecurityIssue>();
var expiration = _configuration.GetValue<int>("Security:Jwt:ExpirationMinutes");
if (expiration > 1440) // 24 hours
{
issues.Add(new SecurityIssue
{
Type = "Long JWT Expiration",
Severity = SecuritySeverity.Medium,
Message = $"JWT token expiration ({expiration} minutes) is too long",
Recommendation = "Set JWT expiration to 15-60 minutes for better security"
});
}
return issues;
}
private int CalculateSecurityScore(List<SecurityIssue> issues)
{
var baseScore = 100;
foreach (var issue in issues)
{
baseScore -= issue.Severity switch
{
SecuritySeverity.High => 20,
SecuritySeverity.Medium => 10,
SecuritySeverity.Low => 5,
_ => 0
};
}
return Math.Max(0, baseScore);
}
}
public class SecurityAnalysisResult
{
public List<SecurityIssue> Issues { get; set; } = new List<SecurityIssue>();
public int Score { get; set; }
public DateTime AnalyzedAt { get; set; } = DateTime.UtcNow;
}
public class SecurityIssue
{
public string Type { get; set; }
public SecuritySeverity Severity { get; set; }
public string Message { get; set; }
public string Recommendation { get; set; }
}
public enum SecuritySeverity
{
Low,
Medium,
High
}
14. Troubleshooting Common Issues
Configuration Troubleshooting Tools
// Comprehensive configuration diagnostics
public class ConfigurationDiagnostics
{
private readonly IConfiguration _configuration;
private readonly ILogger<ConfigurationDiagnostics> _logger;
public ConfigurationDiagnostics(IConfiguration configuration, ILogger<ConfigurationDiagnostics> logger)
{
_configuration = configuration;
_logger = logger;
}
public async Task<ConfigurationDiagnosticResult> RunDiagnosticsAsync()
{
var result = new ConfigurationDiagnosticResult();
result.Issues.AddRange(await CheckConfigurationSourcesAsync());
result.Issues.AddRange(CheckRequiredSettings());
result.Issues.AddRange(CheckConfigurationValues());
result.Issues.AddRange(CheckExternalDependenciesAsync().GetAwaiter().GetResult());
return result;
}
private async Task<List<DiagnosticIssue>> CheckConfigurationSourcesAsync()
{
var issues = new List<DiagnosticIssue>();
// Check if appsettings files exist
var expectedFiles = new[] { "appsettings.json", $"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json" };
foreach (var file in expectedFiles)
{
if (!File.Exists(file))
{
issues.Add(new DiagnosticIssue
{
Level = DiagnosticLevel.Warning,
Category = "Configuration Files",
Message = $"Configuration file '{file}' not found",
Details = "This file is optional but recommended for environment-specific settings"
});
}
}
// Check environment variables
var requiredEnvVars = new[] { "ASPNETCORE_ENVIRONMENT" };
foreach (var envVar in requiredEnvVars)
{
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(envVar)))
{
issues.Add(new DiagnosticIssue
{
Level = DiagnosticLevel.Warning,
Category = "Environment Variables",
Message = $"Environment variable '{envVar}' is not set",
Details = "Defaulting to 'Production' environment"
});
}
}
return issues;
}
private List<DiagnosticIssue> CheckRequiredSettings()
{
var issues = new List<DiagnosticIssue>();
var requiredSettings = new[]
{
"ConnectionStrings:DefaultConnection",
"Security:Jwt:SecretKey",
"Security:Jwt:Issuer",
"Security:Jwt:Audience"
};
foreach (var setting in requiredSettings)
{
var value = _configuration[setting];
if (string.IsNullOrEmpty(value))
{
issues.Add(new DiagnosticIssue
{
Level = DiagnosticLevel.Error,
Category = "Required Settings",
Message = $"Required configuration setting '{setting}' is missing or empty",
Details = "This will cause application startup to fail"
});
}
}
return issues;
}
private List<DiagnosticIssue> CheckConfigurationValues()
{
var issues = new List<DiagnosticIssue>();
// Check numeric values
var dbTimeout = _configuration.GetValue<int>("Database:Timeout", 0);
if (dbTimeout <= 0)
{
issues.Add(new DiagnosticIssue
{
Level = DiagnosticLevel.Warning,
Category = "Configuration Values",
Message = "Database timeout is not set or invalid",
Details = "Using default timeout of 30 seconds"
});
}
// Check URL formats
var paymentUrl = _configuration["PaymentGateway:BaseUrl"];
if (!string.IsNullOrEmpty(paymentUrl) && !Uri.IsWellFormedUriString(paymentUrl, UriKind.Absolute))
{
issues.Add(new DiagnosticIssue
{
Level = DiagnosticLevel.Error,
Category = "Configuration Values",
Message = "Payment gateway URL is not a valid URI",
Details = "This will cause payment operations to fail"
});
}
return issues;
}
private async Task<List<DiagnosticIssue>> CheckExternalDependenciesAsync()
{
var issues = new List<DiagnosticIssue>();
// Check database connectivity
var connectionString = _configuration.GetConnectionString("DefaultConnection");
if (!string.IsNullOrEmpty(connectionString))
{
try
{
await using var connection = new SqlConnection(connectionString);
await connection.OpenAsync();
await connection.CloseAsync();
}
catch (Exception ex)
{
issues.Add(new DiagnosticIssue
{
Level = DiagnosticLevel.Error,
Category = "External Dependencies",
Message = "Cannot connect to database",
Details = $"Database connection failed: {ex.Message}"
});
}
}
return issues;
}
}
public class ConfigurationDiagnosticResult
{
public List<DiagnosticIssue> Issues { get; set; } = new List<DiagnosticIssue>();
public DateTime DiagnosedAt { get; set; } = DateTime.UtcNow;
public bool HasErrors => Issues.Any(i => i.Level == DiagnosticLevel.Error);
}
public class DiagnosticIssue
{
public DiagnosticLevel Level { get; set; }
public string Category { get; set; }
public string Message { get; set; }
public string Details { get; set; }
}
public enum DiagnosticLevel
{
Information,
Warning,
Error
}
// Configuration debugging middleware
public class ConfigurationDebugMiddleware
{
private readonly RequestDelegate _next;
private readonly IConfiguration _configuration;
private readonly ILogger<ConfigurationDebugMiddleware> _logger;
public ConfigurationDebugMiddleware(
RequestDelegate next,
IConfiguration configuration,
ILogger<ConfigurationDebugMiddleware> logger)
{
_next = next;
_configuration = configuration;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
// Only enable in development
if (context.Request.Query.ContainsKey("debug-config"))
{
await WriteConfigurationDebugInfo(context);
return;
}
await _next(context);
}
private async Task WriteConfigurationDebugInfo(HttpContext context)
{
context.Response.ContentType = "application/json";
var debugInfo = new
{
Environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"),
ConfigurationSources = GetConfigurationSources(),
AllSettings = GetAllConfigurationValues(),
EnvironmentVariables = GetRelevantEnvironmentVariables()
};
var json = System.Text.Json.JsonSerializer.Serialize(debugInfo, new System.Text.Json.JsonSerializerOptions
{
WriteIndented = true
});
await context.Response.WriteAsync(json);
}
private List<string> GetConfigurationSources()
{
// This would need reflection to access internal configuration providers
return new List<string>
{
"appsettings.json",
$"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json",
"Environment Variables",
"Command Line Arguments",
"User Secrets (Development only)"
};
}
private Dictionary<string, string> GetAllConfigurationValues()
{
var values = new Dictionary<string, string>();
// Get all configuration values (be careful with secrets!)
foreach (var (key, value) in _configuration.AsEnumerable())
{
if (!string.IsNullOrEmpty(key) && !IsSensitiveKey(key))
{
values[key] = value;
}
}
return values;
}
private Dictionary<string, string> GetRelevantEnvironmentVariables()
{
var envVars = new Dictionary<string, string>();
var relevantVars = Environment.GetEnvironmentVariables()
.Cast<System.Collections.DictionaryEntry>()
.Where(e => e.Key.ToString().StartsWith("ASPNETCORE_") ||
e.Key.ToString().StartsWith("TECHMART_"))
.ToDictionary(e => e.Key.ToString(), e => e.Value.ToString());
return relevantVars;
}
private bool IsSensitiveKey(string key)
{
var sensitiveKeywords = new[] { "password", "pwd", "secret", "key", "token", "connectionstring" };
return sensitiveKeywords.Any(keyword =>
key.Contains(keyword, StringComparison.OrdinalIgnoreCase));
}
}
15. Real-World Implementation Examples
Enterprise-Grade Configuration Setup
// Complete enterprise configuration setup
public class EnterpriseConfigurationBuilder
{
public IHostBuilder CreateEnterpriseHostBuilder(string[] args, string applicationName)
{
return Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
var env = context.HostingEnvironment;
ConfigureEnterpriseAppSettings(config, env);
ConfigureEnterpriseSecrets(config, env, applicationName);
ConfigureEnterpriseExternalSources(config, env, applicationName);
ConfigureEnterpriseMonitoring(config);
})
.ConfigureLogging((context, logging) =>
{
logging.AddConfiguration(context.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
logging.AddApplicationInsights();
})
.UseDefaultServiceProvider((context, options) =>
{
options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
options.ValidateOnBuild = true;
});
}
private void ConfigureEnterpriseAppSettings(IConfigurationBuilder config, IHostEnvironment env)
{
// Base configuration
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
// Environment-specific configuration
config.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
// Environment-specific machine configuration (for overrides)
config.AddJsonFile($"appsettings.{env.EnvironmentName}.{Environment.MachineName}.json",
optional: true, reloadOnChange: true);
}
private void ConfigureEnterpriseSecrets(IConfigurationBuilder config, IHostEnvironment env, string applicationName)
{
if (env.IsDevelopment())
{
// User secrets for development
config.AddUserSecrets<Program>();
}
else
{
// Cloud secrets for non-development environments
var builtConfig = config.Build();
// Azure Key Vault
var azureKeyVaultEndpoint = builtConfig["AzureKeyVault:Endpoint"];
if (!string.IsNullOrEmpty(azureKeyVaultEndpoint))
{
config.AddAzureKeyVault(
new Uri(azureKeyVaultEndpoint),
new DefaultAzureCredential());
}
// AWS Secrets Manager
var awsRegion = builtConfig["AWS:Region"];
var awsSecretName = builtConfig["AWS:Secrets:ApplicationSecretName"];
if (!string.IsNullOrEmpty(awsRegion) && !string.IsNullOrEmpty(awsSecretName))
{
config.AddAwsSecretsManager(awsRegion, awsSecretName);
}
}
}
private void ConfigureEnterpriseExternalSources(IConfigurationBuilder config, IHostEnvironment env, string applicationName)
{
var builtConfig = config.Build();
// Database configuration
var dbConnectionString = builtConfig.GetConnectionString("ConfigurationDb");
if (!string.IsNullOrEmpty(dbConnectionString))
{
config.AddDynamicDatabaseConfiguration(
dbConnectionString,
env.EnvironmentName,
applicationName,
reloadOnChange: true,
reloadInterval: 60);
}
// Consul configuration for microservices
var consulAddress = builtConfig["Consul:Address"];
if (!string.IsNullOrEmpty(consulAddress))
{
config.AddConsulKeyValue(consulAddress, $"{applicationName}/{env.EnvironmentName}");
}
}
private void ConfigureEnterpriseMonitoring(IConfigurationBuilder config)
{
// Application Insights
config.AddApplicationInsightsSettings();
// Health check configuration
config.AddJsonFile("healthchecks.json", optional: true);
}
}
// Real-world startup configuration
public class EnterpriseStartup
{
public EnterpriseStartup(IConfiguration configuration, IWebHostEnvironment env)
{
Configuration = configuration;
Environment = env;
// Validate configuration on startup
ValidateConfiguration();
}
public IConfiguration Configuration { get; }
public IWebHostEnvironment Environment { get; }
private void ValidateConfiguration()
{
var validator = new EnterpriseConfigurationValidator(Configuration, Environment);
var result = validator.Validate();
if (!result.IsValid)
{
throw new InvalidOperationException(
$"Configuration validation failed: {string.Join(", ", result.Errors)}");
}
}
public void ConfigureServices(IServiceCollection services)
{
// Configuration validation
services.AddSingleton(new EnterpriseConfigurationValidator(Configuration, Environment));
// Strongly-typed configuration
services.Configure<EnterpriseAppSettings>(Configuration);
services.Configure<DatabaseSettings>(Configuration.GetSection("Database"));
services.Configure<SecuritySettings>(Configuration.GetSection("Security"));
services.Configure<ExternalServicesSettings>(Configuration.GetSection("ExternalServices"));
// Configuration-based service registration
ConfigureDatabaseServices(services);
ConfigureSecurityServices(services);
ConfigureExternalServices(services);
ConfigureApplicationServices(services);
// Health checks
services.AddHealthChecks()
.AddSqlServer(Configuration.GetConnectionString("DefaultConnection"))
.AddUrlGroup(new Uri(Configuration["ExternalServices:PaymentGateway:HealthCheck"]))
.AddApplicationInsightsPublisher();
// Caching
ConfigureCaching(services);
}
private void ConfigureDatabaseServices(IServiceCollection services)
{
var databaseSettings = Configuration.GetSection("Database").Get<DatabaseSettings>();
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(databaseSettings.ConnectionString, sqlOptions =>
{
sqlOptions.EnableRetryOnFailure(
maxRetryCount: databaseSettings.RetryCount,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null);
});
if (Environment.IsDevelopment())
{
options.EnableSensitiveDataLogging();
options.EnableDetailedErrors();
}
});
}
private void ConfigureSecurityServices(IServiceCollection services)
{
var securitySettings = Configuration.GetSection("Security").Get<SecuritySettings>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = securitySettings.Jwt.Issuer,
ValidAudience = securitySettings.Jwt.Audience,
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(securitySettings.Jwt.SecretKey))
};
});
services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy =>
policy.RequireRole("Administrator"));
});
}
private void ConfigureExternalServices(IServiceCollection services)
{
var externalServices = Configuration.GetSection("ExternalServices").Get<ExternalServicesSettings>();
// Configure HTTP clients with retry policies
services.AddHttpClient<IPaymentService, PaymentService>(client =>
{
client.BaseAddress = new Uri(externalServices.PaymentGateway.BaseUrl);
client.DefaultRequestHeaders.Add("X-API-Key", externalServices.PaymentGateway.ApiKey);
})
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreakerPolicy());
services.AddHttpClient<IEmailService, EmailService>(client =>
{
client.BaseAddress = new Uri(externalServices.EmailService.BaseUrl);
})
.AddPolicyHandler(GetRetryPolicy());
}
private void ConfigureApplicationServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Enterprise API", Version = "v1" });
});
// Feature flags
var featureSettings = Configuration.GetSection("Features").Get<FeatureSettings>();
if (featureSettings.EnableCaching)
{
services.AddMemoryCache();
}
if (featureSettings.EnableBackgroundServices)
{
services.AddHostedService<DataSyncService>();
services.AddHostedService<CleanupService>();
}
}
private void ConfigureCaching(IServiceCollection services)
{
var cacheSettings = Configuration.GetSection("Cache").Get<CacheSettings>();
if (cacheSettings.Type == "Redis")
{
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = cacheSettings.RedisConnection;
options.InstanceName = cacheSettings.InstanceName;
});
}
else
{
services.AddDistributedMemoryCache();
}
}
private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
.WaitAndRetryAsync(
retryCount: 3,
sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
onRetry: (outcome, timespan, retryCount, context) =>
{
// Log retry attempts
});
}
private static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.CircuitBreakerAsync(
handledEventsAllowedBeforeBreaking: 3,
durationOfBreak: TimeSpan.FromSeconds(30));
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Enterprise API v1"));
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHealthChecks("/health");
});
// Configuration diagnostics endpoint (development only)
if (env.IsDevelopment())
{
app.UseMiddleware<ConfigurationDebugMiddleware>();
}
}
}
// Enterprise configuration classes
public class EnterpriseAppSettings
{
public string ApplicationName { get; set; }
public string Version { get; set; }
public string Environment { get; set; }
public FeatureSettings Features { get; set; }
public CacheSettings Cache { get; set; }
}
public class FeatureSettings
{
public bool EnableCaching { get; set; }
public bool EnableBackgroundServices { get; set; }
public bool EnableApiVersioning { get; set; }
public bool EnableResponseCompression { get; set; }
}
public class CacheSettings
{
public string Type { get; set; }
public string RedisConnection { get; set; }
public string InstanceName { get; set; }
public int DefaultExpiration { get; set; } = 3600;
}
public class ExternalServicesSettings
{
public PaymentGatewaySettings PaymentGateway { get; set; }
public EmailServiceSettings EmailService { get; set; }
public AnalyticsServiceSettings AnalyticsService { get; set; }
}
public class PaymentGatewaySettings
{
public string BaseUrl { get; set; }
public string ApiKey { get; set; }
public string WebhookSecret { get; set; }
public string HealthCheck { get; set; }
}
public class EnterpriseConfigurationValidator
{
private readonly IConfiguration _configuration;
private readonly IWebHostEnvironment _environment;
public EnterpriseConfigurationValidator(IConfiguration configuration, IWebHostEnvironment environment)
{
_configuration = configuration;
_environment = environment;
}
public ValidationResult Validate()
{
var errors = new List<string>();
// Validate required settings
if (string.IsNullOrEmpty(_configuration.GetConnectionString("DefaultConnection")))
{
errors.Add("Default database connection string is required");
}
// Validate security settings
var jwtSecret = _configuration["Security:Jwt:SecretKey"];
if (string.IsNullOrEmpty(jwtSecret) || jwtSecret.Length < 32)
{
errors.Add("JWT secret key must be at least 32 characters long");
}
// Validate external services
var paymentUrl = _configuration["ExternalServices:PaymentGateway:BaseUrl"];
if (string.IsNullOrEmpty(paymentUrl))
{
errors.Add("Payment gateway base URL is required");
}
return new ValidationResult
{
IsValid = !errors.Any(),
Errors = errors
};
}
}
public class ValidationResult
{
public bool IsValid { get; set; }
public List<string> Errors { get; set; } = new List<string>();
}
This comprehensive guide covers ASP.NET Core configuration and secrets management from basic to advanced enterprise-level scenarios. The examples provided demonstrate real-world patterns and best practices that you can adapt for your specific application needs.
Remember to always prioritize security when handling configuration, especially when dealing with secrets and sensitive information. Use secure storage solutions like Azure Key Vault or AWS Secrets Manager in production environments, and never commit secrets to source control.