DotNetSafeConfig 1.0.0

dotnet add package DotNetSafeConfig --version 1.0.0
                    
NuGet\Install-Package DotNetSafeConfig -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="DotNetSafeConfig" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DotNetSafeConfig" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="DotNetSafeConfig" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add DotNetSafeConfig --version 1.0.0
                    
#r "nuget: DotNetSafeConfig, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package DotNetSafeConfig@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=DotNetSafeConfig&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=DotNetSafeConfig&version=1.0.0
                    
Install as a Cake Tool

DotNetSafeConfig

Configuration validation and secrets management to prevent missing config keys, validate settings, and handle secrets safely.

Problems Solved

  • IOptions returning null: IOptions<T> returns null when configuration is missing
  • Missing config keys in production: Configuration keys missing in production cause runtime errors
  • Secrets committed: Secrets accidentally committed to source control
  • No config validation: Configuration not validated until runtime
  • Docker env vars not loading: Environment variables not loaded in Docker containers
  • appsettings overrides confusion: Unclear which configuration takes precedence
  • Azure/AppConfig mismatch: Configuration mismatch between local and Azure
  • Config drift between environments: Configuration differences between dev/staging/prod
  • Secrets logged: Secrets accidentally logged in application logs
  • Runtime config reload issues: Configuration reload not working properly

Installation

dotnet add package DotNetSafeConfig

Quick Start

1. Validate Required Configuration Keys

Problem: Missing configuration keys cause runtime errors in production.

using DotNetSafeConfig;

// ✅ GOOD: Validate configuration on startup
builder.Services.AddConfigValidation(validator =>
{
    validator.RequireKey("Database:ConnectionString");
    validator.RequireKey("AppSettings:ApiKey");
    validator.RequireKey("ExternalServices:PaymentGateway:Url");
});

// Validation happens automatically on startup

2. Validate IOptions with Data Annotations

Problem: IOptions<T> returns null or invalid values without validation.

// ✅ GOOD: Define configuration class with validation
public class AppSettings
{
    [Required]
    public string ApiKey { get; set; } = string.Empty;
    
    [Required]
    [Url]
    public string ApiUrl { get; set; } = string.Empty;
    
    [Range(1, 100)]
    public int MaxRetries { get; set; } = 3;
}

// Register with validation
builder.Services.AddValidatedOptions<AppSettings>(
    configuration,
    "AppSettings"
);

// Use validated options
public class MyService
{
    private readonly AppSettings _settings;
    
    public MyService(AppSettings settings) // Injected as validated instance
    {
        _settings = settings; // Guaranteed to be valid
    }
}

3. Safe Secrets Management

Problem: Secrets accidentally logged or exposed in error messages.

// ❌ BAD: Secret might be logged
logger.LogInformation("API Key: {ApiKey}", apiKey);

// ✅ GOOD: Mask secrets before logging
var apiKey = SecretsHelper.GetRequiredSecret(configuration, "ApiKey", logger);
var masked = SecretsHelper.MaskSecret(apiKey); // "abcd****"
logger.LogInformation("API Key: {ApiKey}", masked);

4. Detect Placeholder Values

Problem: Placeholder values like "your-secret-here" deployed to production.

// ✅ GOOD: Detect and warn about placeholder values
var apiKey = SecretsHelper.GetRequiredSecret(configuration, "ApiKey", logger);

if (SecretsHelper.IsPlaceholderValue(apiKey))
{
    logger.LogWarning("API Key appears to be a placeholder value");
}

5. Filter Secrets from Logs

Problem: Secrets included in configuration dictionaries that get logged.

// ✅ GOOD: Filter secrets before logging
var config = new Dictionary<string, string>
{
    ["ApiKey"] = "secret-key-123",
    ["DatabasePassword"] = "db-pass-456",
    ["PublicSetting"] = "public-value"
};

var safeConfig = SecretsHelper.FilterSecrets(
    config,
    "ApiKey",
    "DatabasePassword"
);

logger.LogInformation("Configuration: {Config}", safeConfig);
// Logs: ApiKey: "abcd****", DatabasePassword: "db-p****"

Real-World Example

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        
        // Validate all required configuration
        builder.Services.AddConfigValidation(validator =>
        {
            // Environment variables
            validator.RequireKey("DATABASE_CONNECTION_STRING");
            
            // Configuration keys
            validator.RequireKey("AppSettings:ApiKey");
            validator.RequireKey("AppSettings:JwtSecret");
            validator.RequireKey("ExternalServices:PaymentGateway:Url");
            validator.RequireKey("ExternalServices:PaymentGateway:ApiKey");
        });
        
        // Register validated options
        builder.Services.AddValidatedOptions<AppSettings>(
            builder.Configuration,
            "AppSettings"
        );
        
        builder.Services.AddValidatedOptions<DatabaseSettings>(
            builder.Configuration,
            "Database"
        );
        
        var app = builder.Build();
        
        // Validate startup
        try
        {
            var validator = app.Services.GetRequiredService<ConfigValidator>();
            validator.Validate();
            app.Logger.LogInformation("Configuration validation passed");
        }
        catch (ConfigurationValidationException ex)
        {
            app.Logger.LogCritical(ex, "Configuration validation failed");
            Environment.Exit(1);
        }
        
        app.Run();
    }
}

// Configuration classes with validation
public class AppSettings
{
    [Required]
    [MinLength(32)]
    public string ApiKey { get; set; } = string.Empty;
    
    [Required]
    [Url]
    public string ApiUrl { get; set; } = string.Empty;
    
    [Range(1, 10)]
    public int MaxRetries { get; set; } = 3;
}

public class DatabaseSettings
{
    [Required]
    public string ConnectionString { get; set; } = string.Empty;
    
    [Range(5, 100)]
    public int CommandTimeout { get; set; } = 30;
}

// Service using validated configuration
public class PaymentService
{
    private readonly AppSettings _settings;
    private readonly ILogger<PaymentService> _logger;
    
    public PaymentService(AppSettings settings, ILogger<PaymentService> logger)
    {
        _settings = settings; // Guaranteed to be valid
        _logger = logger;
    }
    
    public async Task ProcessPaymentAsync(PaymentRequest request)
    {
        // Safe to use - configuration is validated
        var maskedKey = SecretsHelper.MaskSecret(_settings.ApiKey);
        _logger.LogInformation("Processing payment with API: {ApiUrl}, Key: {Key}", 
            _settings.ApiUrl, maskedKey);
        
        // Process payment...
    }
}

Configuration Validation Example

// Validate on startup
builder.Services.AddConfigValidation(validator =>
{
    // Required environment variables
    validator.RequireKey("DATABASE_CONNECTION_STRING");
    validator.RequireKey("REDIS_CONNECTION_STRING");
    
    // Required configuration sections
    validator.RequireKey("AppSettings:ApiKey");
    validator.RequireKey("AppSettings:JwtSecret");
    validator.RequireKey("ExternalServices:PaymentGateway:Url");
    
    // Optional but recommended
    validator.RequireKey("Logging:LogLevel:Default");
});

Best Practices

  1. Validate all required configuration on application startup
  2. Use Data Annotations on configuration classes for type validation
  3. Mask secrets before logging using SecretsHelper.MaskSecret()
  4. Detect placeholder values to prevent deploying test values
  5. Filter secrets from dictionaries before logging configuration
  6. Fail fast - exit immediately if required configuration is missing
  7. Use validated options instead of raw IOptions<T>

API Reference

  • ConfigValidator - Validates required configuration keys
  • OptionsValidator<T> - Validates IOptions<T> using Data Annotations
  • SecretsHelper - Utilities for safe secret handling
  • AddConfigValidation() - Extension method to add validation
  • AddValidatedOptions<T>() - Extension method to register validated options
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 183 1/5/2026

Initial release. See README for details.