PluginBench
Skill
Pass
Audit score 90

configuration

codewithmukesh/dotnet-claude-kit

Options pattern, secrets management, and environment-based configuration for .NET 10 applications.

What is configuration?

Covers .NET configuration best practices including the Options pattern, IOptions vs IOptionsSnapshot, secrets management, and environment layering. Load this when setting up application configuration, managing secrets, or binding configuration sections.

  • Bind configuration sections to strongly-typed classes with validation attributes
  • Use IOptions, IOptionsSnapshot, and IOptionsMonitor for different lifetime scenarios
  • Validate configuration at startup to fail fast on misconfiguration
  • Manage secrets safely with user-secrets in development and Azure Key Vault or environment variables in production
  • Support configuration layering across appsettings.json, environment-specific files, and environment variables
  • Configure multiple named instances of the same options class for different contexts

How to install configuration

npx skills add https://github.com/codewithmukesh/dotnet-claude-kit --skill configuration
Claude Code
Cursor
Windsurf
Cline

How to use configuration

  1. 1.Define a strongly-typed options class with validation attributes (Required, Range, etc.)
  2. 2.Register the options in Program.cs using AddOptions, BindConfiguration, and ValidateOnStart
  3. 3.Inject IOptions<T>, IOptionsSnapshot<T>, or IOptionsMonitor<T> into services based on your lifetime needs
  4. 4.Provide configuration values via appsettings.json structure (without secrets), user-secrets in development, or environment variables in production
  5. 5.For production, configure Azure Key Vault as an additional configuration source using AddAzureKeyVault

Use cases

Good for
  • Setting up database connection strings and retry policies with validation
  • Managing JWT secrets and token expiration settings securely
  • Configuring SMTP options for internal and customer email services separately
  • Loading production secrets from Azure Key Vault while using user-secrets in development
  • Validating complex configuration rules at application startup to prevent runtime failures
Who it's for
  • Backend developers building .NET 10 applications
  • DevOps engineers managing secrets and environment-based configuration
  • Teams implementing secure configuration patterns and secrets management

configuration FAQ

When should I use IOptions vs IOptionsSnapshot vs IOptionsMonitor?

Use IOptions for singleton, immutable configuration read once at startup. Use IOptionsSnapshot for scoped access that re-reads per request (useful for reloadable config). Use IOptionsMonitor for singleton services that need to actively watch for configuration changes.

How do I keep secrets out of source control?

Never put secrets in appsettings.json. Use dotnet user-secrets for development and Azure Key Vault or environment variables for production. The appsettings.json should contain only the structure and non-sensitive defaults.

What happens if configuration validation fails?

With ValidateOnStart(), the application fails immediately at startup with a clear error message, preventing misconfiguration from reaching production. Without it, errors are discovered at runtime when the configuration is first accessed.

Can I have multiple configurations of the same type?

Yes, use named options with AddOptions<T>(name).BindConfiguration(...) and retrieve them with IOptionsSnapshot<T>.Get(name) in your services.

How does configuration layering work?

Configuration sources are applied in order: appsettings.json → appsettings.{Environment}.json → environment variables → user secrets. Later sources override earlier ones, allowing environment-specific overrides.

Full instructions (SKILL.md)

Source of truth, from codewithmukesh/dotnet-claude-kit.


name: configuration description: > Configuration patterns for .NET 10 applications. Covers the Options pattern, IOptionsSnapshot vs IOptions, secrets management, and environment-based configuration. Load this skill when setting up application configuration, managing secrets, binding configuration sections, or when the user mentions "configuration", "appsettings", "Options pattern", "IOptions", "IOptionsSnapshot", "secrets", "user secrets", "environment variables", "connection string", or "config binding".

Configuration

Core Principles

  1. Options pattern always — Never read IConfiguration directly in services. Bind configuration sections to strongly-typed classes with validation.
  2. Validate on startup — Use ValidateDataAnnotations() and ValidateOnStart() to catch misconfiguration before the first request.
  3. Secrets never in source — Use user secrets in development, Azure Key Vault or environment variables in production. Never commit secrets to git.
  4. Configuration layering — appsettings.json → appsettings.{Environment}.json → environment variables → user secrets. Later sources override earlier ones.

Patterns

Options Pattern

// Options class with validation attributes
public class DatabaseOptions
{
    public const string SectionName = "Database";

    [Required]
    public required string ConnectionString { get; init; }

    [Range(1, 100)]
    public int MaxRetryCount { get; init; } = 3;

    [Range(1, 60)]
    public int CommandTimeoutSeconds { get; init; } = 30;
}

// Registration with validation
builder.Services.AddOptions<DatabaseOptions>()
    .BindConfiguration(DatabaseOptions.SectionName)
    .ValidateDataAnnotations()
    .ValidateOnStart(); // Fails at startup if configuration is invalid
// appsettings.json
{
  "Database": {
    "ConnectionString": "",
    "MaxRetryCount": 3,
    "CommandTimeoutSeconds": 30
  }
}

Injecting Options

// IOptions<T> — singleton, read once at startup, doesn't change
public class OrderService(IOptions<DatabaseOptions> options)
{
    private readonly DatabaseOptions _db = options.Value;
}

// IOptionsSnapshot<T> — scoped, re-reads per request (for reloadable config)
public class OrderService(IOptionsSnapshot<DatabaseOptions> options)
{
    private readonly DatabaseOptions _db = options.Value;
}

// IOptionsMonitor<T> — singleton, actively watches for changes
public class BackgroundWorker(IOptionsMonitor<WorkerOptions> options)
{
    public void DoWork()
    {
        var current = options.CurrentValue; // Always latest
    }
}

Custom Validation (Complex Rules)

builder.Services.AddOptions<JwtOptions>()
    .BindConfiguration("Jwt")
    .Validate(options =>
    {
        if (string.IsNullOrEmpty(options.Key) || options.Key.Length < 32)
            return false;
        if (options.ExpirationMinutes <= 0)
            return false;
        return true;
    }, "JWT key must be at least 32 characters and expiration must be positive")
    .ValidateOnStart();

Azure Key Vault (Production)

// Program.cs — add Key Vault as a configuration source
if (builder.Environment.IsProduction())
{
    var keyVaultUri = new Uri(builder.Configuration["KeyVault:Uri"]!);
    builder.Configuration.AddAzureKeyVault(keyVaultUri, new DefaultAzureCredential());
}

Configuration for Multiple Environments

// Named options — different config per named instance
builder.Services.AddOptions<SmtpOptions>("internal")
    .BindConfiguration("Smtp:Internal");
builder.Services.AddOptions<SmtpOptions>("customer")
    .BindConfiguration("Smtp:Customer");

// Usage
public class EmailService(IOptionsSnapshot<SmtpOptions> options)
{
    public async Task SendInternalEmail(string to, string body)
    {
        var smtp = options.Get("internal");
        // ...
    }
}

Anti-patterns

Don't Read IConfiguration Directly

// BAD — stringly-typed, no validation, hard to test
public class OrderService(IConfiguration config)
{
    public void Process()
    {
        var timeout = int.Parse(config["Database:CommandTimeout"]!);
    }
}

// GOOD — strongly-typed options
public class OrderService(IOptions<DatabaseOptions> options)
{
    public void Process()
    {
        var timeout = options.Value.CommandTimeoutSeconds;
    }
}

Don't Put Secrets in appsettings.json

// BAD — committed to source control
{
  "Jwt": { "Key": "super-secret-key" },
  "Database": { "ConnectionString": "Server=prod;Password=secret" }
}

// GOOD — appsettings.json has defaults/structure only
{
  "Jwt": { "Key": "", "Issuer": "myapp", "Audience": "myapp" },
  "Database": { "ConnectionString": "" }
}
// Secrets provided via user-secrets (dev) or env vars / Key Vault (prod)

Don't Skip Startup Validation

// BAD — misconfiguration discovered at runtime
builder.Services.Configure<JwtOptions>(builder.Configuration.GetSection("Jwt"));

// GOOD — fail fast at startup
builder.Services.AddOptions<JwtOptions>()
    .BindConfiguration("Jwt")
    .ValidateDataAnnotations()
    .ValidateOnStart();

Decision Guide

ScenarioRecommendation
Binding config to classOptions pattern with BindConfiguration
Simple, immutable configIOptions<T>
Config that changes per requestIOptionsSnapshot<T>
Background service watching configIOptionsMonitor<T>
Development secretsdotnet user-secrets
Production secretsAzure Key Vault or environment variables
Validating configValidateDataAnnotations() + ValidateOnStart()
Multiple configs of same typeNamed options with IOptionsSnapshot<T>.Get(name)