A configuration extension for Microsoft.Extensions.Configuration that integrates with HashiCorp Vault using VaultSharp
- 🔐 Multiple authentication methods: Local token, AWS IAM, or custom authentication
- ⚙️ Seamless integration with
Microsoft.Extensions.ConfigurationandIOptions<T> - 🔄 Immediate secret loading into configuration after build
- ✅ Fluent validation for configuration options
- 🏗️ Full dependency injection support with
IOptions,IOptionsSnapshot, andIOptionsMonitor - 🔌 Direct Vault access via
IVaultService
dotnet add package Vault.Extension.Configuration- .NET 8.0 or later
- Access to a HashiCorp Vault server
- Appropriate authentication credentials (token, AWS IAM role, or custom method)
using Vault.Extentions;
using Vault.Options;
using Vault.Enum;
var builder = WebApplication.CreateBuilder(args);
// Register VaultService and load secrets immediately into configuration
builder.Services.AddVault(
builder.Configuration,
new VaultOptions
{
AuthenticationType = VaultAuthenticationType.Local,
Configuration = new VaultLocalConfiguration
{
VaultUrl = "https://vault.example.com",
MountPoint = "secret",
TokenFilePath = "~/.vault-token"
}
},
environment: "production");
var app = builder.Build();
// Secrets are already loaded and available - no additional initialization required!
app.Run();You can access Vault secrets in three different ways:
Secrets are automatically loaded into the configuration system and can be accessed like any other configuration value:
public class MyController : ControllerBase
{
private readonly IConfiguration _configuration;
public MyController(IConfiguration configuration)
{
_configuration = configuration;
}
public IActionResult GetSecret()
{
// Access secrets directly from configuration
var dbPassword = _configuration["Database:Password"];
var apiKey = _configuration["ApiSettings:ApiKey"];
return Ok(new { hasPassword = !string.IsNullOrEmpty(dbPassword) });
}
}Bind Vault secrets to strongly-typed configuration classes:
// Define your configuration class
public class DatabaseSettings
{
public string ConnectionString { get; set; }
public string Password { get; set; }
public int Timeout { get; set; }
}
// In Program.cs
builder.Services.Configure<DatabaseSettings>(builder.Configuration.GetSection("Database"));
// Use in your services with IOptions (singleton-like behavior)
public class DatabaseService
{
private readonly DatabaseSettings _settings;
public DatabaseService(IOptions<DatabaseSettings> options)
{
_settings = options.Value;
}
public void Connect()
{
var connectionString = $"{_settings.ConnectionString};Password={_settings.Password}";
// Use connection string
}
}
// Use in your scoped services with IOptionsSnapshot (refreshed per scope/request)
public class ScopedDatabaseService
{
private readonly DatabaseSettings _settings;
public ScopedDatabaseService(IOptionsSnapshot<DatabaseSettings> optionsSnapshot)
{
_settings = optionsSnapshot.Value;
}
public void Connect()
{
var connectionString = $"{_settings.ConnectionString};Password={_settings.Password}";
// Use connection string - fresh value for each HTTP request
}
}
// Use IOptionsMonitor for real-time change notifications
public class MonitoredDatabaseService
{
private readonly IDisposable? _changeListener;
public MonitoredDatabaseService(IOptionsMonitor<DatabaseSettings> optionsMonitor)
{
// Get current value
var currentSettings = optionsMonitor.CurrentValue;
// Listen for changes
_changeListener = optionsMonitor.OnChange((newSettings, name) =>
{
Console.WriteLine("Database settings changed!");
});
}
}For dynamic secret retrieval at runtime:
using Vault.Abstractions;
public class SecretManager
{
private readonly IVaultService _vaultService;
public SecretManager(IVaultService vaultService)
{
_vaultService = vaultService;
}
// Get a single secret value
public async Task<string?> GetDatabasePasswordAsync()
{
var password = await _vaultService.GetSecretValueAsync("production", "Database:Password");
return password?.ToString();
}
// Get all secrets for an environment
public async Task<Dictionary<string, object>> GetAllSecretsAsync()
{
return await _vaultService.GetSecretsAsync("production");
}
// Get nested secret using dot notation
public async Task<string?> GetNestedSecretAsync()
{
var value = await _vaultService.GetNestedSecretValueAsync("production", "Api.Settings.Key");
return value?.ToString();
}
// List all available environments
public async Task<IEnumerable<string>> GetEnvironmentsAsync()
{
return await _vaultService.ListEnvironmentsAsync();
}
}Best for development environments:
var vaultOptions = new VaultOptions
{
AuthenticationType = VaultAuthenticationType.Local,
Configuration = new VaultLocalConfiguration
{
VaultUrl = "https://vault.example.com",
MountPoint = "secret",
TokenFilePath = "~/.vault-token",
IgnoreSslErrors = false // Set to true only in development
}
};
builder.Services.AddVault(builder.Configuration, vaultOptions, environment: "development");Ideal for production on AWS infrastructure:
var vaultOptions = new VaultOptions
{
AuthenticationType = VaultAuthenticationType.AWS_IAM,
Configuration = new VaultAwsConfiguration
{
VaultUrl = "https://vault.example.com",
MountPoint = "secret",
Environment = "production",
AwsAuthMountPoint = "aws",
AwsIamRoleName = "my-app-role" // Optional
}
};
builder.Services.AddVault(builder.Configuration, vaultOptions, environment: "production");For AppRole, LDAP, UserPass, Kubernetes, etc.:
using VaultSharp.V1.AuthMethods.AppRole;
var vaultOptions = new VaultOptions
{
AuthenticationType = VaultAuthenticationType.Custom,
Configuration = new VaultDefaultConfiguration
{
VaultUrl = "https://vault.example.com",
MountPoint = "secret"
},
CustomAuthMethodInfo = new AppRoleAuthMethodInfo("my-role-id", "my-secret-id")
};
builder.Services.AddVault(builder.Configuration, vaultOptions, environment: "production");| Property | Type | Description |
|---|---|---|
IsActivated |
bool |
Enable/disable Vault integration (default: true) |
AuthenticationType |
VaultAuthenticationType |
Authentication method: Local, AWS_IAM, or Custom |
Configuration |
VaultDefaultConfiguration |
Base configuration (use specific subclass based on auth type) |
CustomAuthMethodInfo |
IAuthMethodInfo |
Custom authentication info (only for Custom type) |
| Property | Type | Description |
|---|---|---|
VaultUrl |
string |
Vault server URL (e.g., https://vault.example.com) |
MountPoint |
string |
Secret engine mount point (e.g., secret) |
TokenFilePath |
string |
Path to token file (supports environment variables like ~/.vault-token) |
IgnoreSslErrors |
bool |
Ignore SSL certificate errors (default: false, use only in development) |
| Property | Type | Description |
|---|---|---|
VaultUrl |
string |
Vault server URL |
MountPoint |
string |
Secret engine mount point |
Environment |
string |
Environment name (used for default role naming) |
AwsAuthMountPoint |
string |
AWS auth method mount point (default: "aws") |
AwsIamRoleName |
string |
AWS IAM role name (optional, defaults to {MountPoint}-{Environment}-role) |
IgnoreSslErrors |
bool |
Ignore SSL certificate errors |
| Method | Description |
|---|---|
ListEnvironmentsAsync() |
Lists all available environments in the Vault |
GetSecretsAsync(string environment) |
Retrieves all secrets for a given environment |
GetSecretValueAsync(string environment, string key) |
Gets a specific secret value by key |
GetNestedSecretValueAsync(string environment, string path) |
Gets a nested secret using dot notation (e.g., "Level1.Level2.Key") |
Here's a complete example showing all three usage patterns:
using Vault.Extentions;
using Vault.Options;
using Vault.Enum;
using Vault.Abstractions;
var builder = WebApplication.CreateBuilder(args);
// 1. Configure Vault - secrets are loaded immediately into configuration
builder.Services.AddVault(
builder.Configuration,
new VaultOptions
{
AuthenticationType = VaultAuthenticationType.AWS_IAM,
Configuration = new VaultAwsConfiguration
{
VaultUrl = "https://vault.example.com",
MountPoint = "myapp",
Environment = "production"
}
},
environment: "production");
// 2. Register strongly-typed configuration
public class AppSettings
{
public string ApiKey { get; set; }
public string DatabasePassword { get; set; }
}
builder.Services.Configure<AppSettings>(builder.Configuration.GetSection("AppSettings"));
// 3. Register your services
builder.Services.AddScoped<MyService>();
var app = builder.Build();
// No additional initialization required - secrets are already available!
app.MapGet("/config", (IConfiguration config) =>
new { apiKey = config["AppSettings:ApiKey"] });
app.MapGet("/options", (IOptions<AppSettings> options) =>
new { settings = options.Value });
app.MapGet("/vault", async (IVaultService vault) =>
await vault.GetSecretsAsync("production"));
app.Run();- Environment-specific configuration: Use different Vault environments for development, staging, and production
- Secret naming: Use hierarchical naming with colons (e.g.,
Database:Password,Api:Settings:Key) - Error handling: Always handle cases where secrets might not exist
- SSL certificates: Never set
IgnoreSslErrors = truein production - Token security: Store Vault tokens securely and rotate them regularly
- IVaultService usage: Use
IVaultServicefor dynamic secret retrieval,IConfiguration/IOptionsfor startup configuration - Choose the right Options pattern:
- IOptions: Use for singleton services or when you need consistent values throughout the application lifetime
- IOptionsSnapshot: Use for scoped services (e.g., per HTTP request) to get fresh values each scope
- IOptionsMonitor: Use when you need to react to configuration changes at runtime
Ensure the AddVault method is called with builder.Configuration (the configuration builder, not the built configuration):
// Correct - pass the configuration builder
builder.Services.AddVault(
builder.Configuration, // IConfigurationBuilder
vaultOptions,
environment: "production");- Local: Verify token file exists and has valid permissions
- AWS IAM: Ensure IAM role is properly configured and the application has AWS credentials
- Custom: Verify the custom auth method credentials are valid
This project is licensed under the MIT License - see the LICENSE file for details.
See CONTRIBUTING.md for guidelines.
Click to expand template documentation
This project is based on Library.Template.
- Auto-versioning via Nerdbank.GitVersioning
- Static analyzers: Code Analysis and StyleCop
- Read-only source tree (builds to top-level bin/obj folders)
- Builds with a "pinned" .NET SDK for reproducible builds
- Testing on Windows, Linux and macOS
git fetch
git checkout origin/main
.\tools\MergeFrom-Template.ps1
# resolve any conflicts, then commit the merge commit.
git push origin -u HEAD