Fingent-Azure-KeyVault 1.0.1

dotnet add package Fingent-Azure-KeyVault --version 1.0.1
                    
NuGet\Install-Package Fingent-Azure-KeyVault -Version 1.0.1
                    
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="Fingent-Azure-KeyVault" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Fingent-Azure-KeyVault" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Fingent-Azure-KeyVault" />
                    
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 Fingent-Azure-KeyVault --version 1.0.1
                    
#r "nuget: Fingent-Azure-KeyVault, 1.0.1"
                    
#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 Fingent-Azure-KeyVault@1.0.1
                    
#: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=Fingent-Azure-KeyVault&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Fingent-Azure-KeyVault&version=1.0.1
                    
Install as a Cake Tool

Fingent Azure Key Vault

A simple and developer-friendly helper library for managing secrets, keys, and certificates in Azure Key Vault. Makes it easy to securely retrieve and manage sensitive configuration values in .NET applications, with built-in integration for ASP.NET Core.

?? Features

  • Secret Management: Secure retrieval and storage of application secrets
  • Key Management: Cryptographic key operations and management
  • Certificate Management: SSL/TLS certificate handling and rotation
  • Configuration Integration: Seamless ASP.NET Core configuration provider
  • Caching Support: Optional caching for improved performance
  • Managed Identity: Support for Azure Managed Identity authentication
  • Multiple Authentication: Service Principal, Connection String, and DefaultAzureCredential
  • Error Handling: Comprehensive error handling and retry policies

?? Installation

dotnet add package Fingent-Azure-KeyVault

?? Dependencies

  • .NET 8.0
  • Azure.Identity 1.12.0
  • Azure.Security.KeyVault.Secrets 4.6.0
  • Microsoft.Extensions.Logging.Abstractions 8.0.1
  • Microsoft.Extensions.Options 8.0.2

?? Usage

Basic Setup

// Program.cs
var builder = WebApplication.CreateBuilder(args);

// Add Azure Key Vault configuration
builder.Configuration.AddAzureKeyVault(
    vaultUri: new Uri("https://your-keyvault.vault.azure.net/"),
    credential: new DefaultAzureCredential());

// Add Key Vault services
builder.Services.AddAzureKeyVault(options =>
{
    options.VaultUri = "https://your-keyvault.vault.azure.net/";
    options.EnableCaching = true;
    options.CacheExpiryMinutes = 30;
});

var app = builder.Build();

Configuration

{
  "AzureKeyVault": {
    "VaultUri": "https://your-keyvault.vault.azure.net/",
    "TenantId": "your-tenant-id",
    "ClientId": "your-client-id",
    "ClientSecret": "your-client-secret",
    "EnableCaching": true,
    "CacheExpiryMinutes": 30,
    "RetryAttempts": 3
  }
}

Secret Management

public class ConfigurationService
{
    private readonly IAzureKeyVaultService _keyVaultService;
    private readonly ILogger<ConfigurationService> _logger;
    
    public ConfigurationService(IAzureKeyVaultService keyVaultService, ILogger<ConfigurationService> logger)
    {
        _keyVaultService = keyVaultService;
        _logger = logger;
    }
    
    public async Task<string> GetDatabaseConnectionStringAsync()
    {
        try
        {
            var connectionString = await _keyVaultService.GetSecretAsync("database-connection-string");
            _logger.LogInformation("Database connection string retrieved from Key Vault");
            return connectionString;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to retrieve database connection string from Key Vault");
            throw;
        }
    }
    
    public async Task<string> GetApiKeyAsync(string serviceName)
    {
        var secretName = $"{serviceName}-api-key";
        return await _keyVaultService.GetSecretAsync(secretName);
    }
    
    public async Task SetSecretAsync(string secretName, string secretValue)
    {
        await _keyVaultService.SetSecretAsync(secretName, secretValue);
        _logger.LogInformation("Secret {SecretName} updated in Key Vault", secretName);
    }
}

Advanced Secret Operations

public class AdvancedSecretService
{
    private readonly IAzureKeyVaultService _keyVaultService;
    private readonly ILogger<AdvancedSecretService> _logger;
    
    public AdvancedSecretService(IAzureKeyVaultService keyVaultService, ILogger<AdvancedSecretService> logger)
    {
        _keyVaultService = keyVaultService;
        _logger = logger;
    }
    
    public async Task<SecretBundle> GetSecretWithMetadataAsync(string secretName)
    {
        var secret = await _keyVaultService.GetSecretWithPropertiesAsync(secretName);
        return new SecretBundle
        {
            Name = secret.Name,
            Value = secret.Value,
            Version = secret.Properties.Version,
            CreatedOn = secret.Properties.CreatedOn,
            UpdatedOn = secret.Properties.UpdatedOn,
            ExpiresOn = secret.Properties.ExpiresOn,
            Tags = secret.Properties.Tags
        };
    }
    
    public async Task<List<SecretInfo>> ListAllSecretsAsync()
    {
        var secrets = new List<SecretInfo>();
        
        await foreach (var secretProperty in _keyVaultService.GetSecretsAsync())
        {
            secrets.Add(new SecretInfo
            {
                Name = secretProperty.Name,
                Version = secretProperty.Version,
                CreatedOn = secretProperty.CreatedOn,
                UpdatedOn = secretProperty.UpdatedOn,
                Enabled = secretProperty.Enabled ?? true,
                Tags = secretProperty.Tags
            });
        }
        
        return secrets;
    }
    
    public async Task<List<string>> GetSecretVersionsAsync(string secretName)
    {
        var versions = new List<string>();
        
        await foreach (var version in _keyVaultService.GetSecretVersionsAsync(secretName))
        {
            versions.Add(version.Version);
        }
        
        return versions;
    }
    
    public async Task DeleteSecretAsync(string secretName)
    {
        await _keyVaultService.StartDeleteSecretAsync(secretName);
        _logger.LogInformation("Secret {SecretName} deletion initiated", secretName);
    }
    
    public async Task BackupAndRestoreSecretAsync(string secretName, string backupSecretName)
    {
        // Get the original secret
        var originalSecret = await _keyVaultService.GetSecretAsync(secretName);
        
        // Create a backup with timestamp
        var backupName = $"{backupSecretName}-backup-{DateTime.UtcNow:yyyyMMddHHmmss}";
        await _keyVaultService.SetSecretAsync(backupName, originalSecret, new Dictionary<string, string>
        {
            { "backup-of", secretName },
            { "backup-date", DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") }
        });
        
        _logger.LogInformation("Secret {SecretName} backed up as {BackupName}", secretName, backupName);
    }
}

Batch Operations

public class BatchSecretService
{
    private readonly IAzureKeyVaultService _keyVaultService;
    private readonly ILogger<BatchSecretService> _logger;
    
    public BatchSecretService(IAzureKeyVaultService keyVaultService, ILogger<BatchSecretService> logger)
    {
        _keyVaultService = keyVaultService;
        _logger = logger;
    }
    
    public async Task<Dictionary<string, string>> GetMultipleSecretsAsync(IEnumerable<string> secretNames)
    {
        var secrets = new Dictionary<string, string>();
        var tasks = secretNames.Select(async name =>
        {
            try
            {
                var value = await _keyVaultService.GetSecretAsync(name);
                return new { Name = name, Value = value, Success = true };
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Failed to retrieve secret: {SecretName}", name);
                return new { Name = name, Value = (string)null, Success = false };
            }
        });
        
        var results = await Task.WhenAll(tasks);
        
        foreach (var result in results.Where(r => r.Success))
        {
            secrets[result.Name] = result.Value;
        }
        
        return secrets;
    }
    
    public async Task SetMultipleSecretsAsync(Dictionary<string, string> secrets)
    {
        var tasks = secrets.Select(async kvp =>
        {
            try
            {
                await _keyVaultService.SetSecretAsync(kvp.Key, kvp.Value);
                _logger.LogInformation("Secret {SecretName} set successfully", kvp.Key);
                return new { Name = kvp.Key, Success = true };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to set secret: {SecretName}", kvp.Key);
                return new { Name = kvp.Key, Success = false };
            }
        });
        
        var results = await Task.WhenAll(tasks);
        var successCount = results.Count(r => r.Success);
        
        _logger.LogInformation("Batch operation completed: {SuccessCount}/{TotalCount} secrets updated", 
            successCount, secrets.Count);
    }
    
    public async Task RotateSecretsAsync(Dictionary<string, Func<Task<string>>> secretGenerators)
    {
        foreach (var (secretName, generator) in secretGenerators)
        {
            try
            {
                // Generate new secret value
                var newValue = await generator();
                
                // Update in Key Vault
                await _keyVaultService.SetSecretAsync(secretName, newValue, new Dictionary<string, string>
                {
                    { "rotated-date", DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") },
                    { "rotation-type", "automatic" }
                });
                
                _logger.LogInformation("Secret {SecretName} rotated successfully", secretName);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to rotate secret: {SecretName}", secretName);
            }
        }
    }
}

Configuration Provider Integration

public class KeyVaultConfigurationService
{
    private readonly IConfiguration _configuration;
    private readonly IAzureKeyVaultService _keyVaultService;
    
    public KeyVaultConfigurationService(IConfiguration configuration, IAzureKeyVaultService keyVaultService)
    {
        _configuration = configuration;
        _keyVaultService = keyVaultService;
    }
    
    public async Task<AppSettings> GetAppSettingsAsync()
    {
        return new AppSettings
        {
            DatabaseConnectionString = _configuration["database-connection-string"],
            ApiKey = _configuration["external-api-key"],
            JwtSecret = _configuration["jwt-secret"],
            SmtpPassword = _configuration["smtp-password"],
            AzureStorageConnectionString = _configuration["azure-storage-connection-string"]
        };
    }
    
    public async Task RefreshConfigurationAsync()
    {
        // This would typically trigger a configuration reload
        // Implementation depends on your configuration setup
        if (_configuration is IConfigurationRoot configRoot)
        {
            configRoot.Reload();
        }
    }
    
    public string GetConnectionString(string name)
    {
        return _configuration.GetConnectionString(name);
    }
    
    public T GetSection<T>(string sectionName) where T : new()
    {
        var section = new T();
        _configuration.GetSection(sectionName).Bind(section);
        return section;
    }
}

public class AppSettings
{
    public string DatabaseConnectionString { get; set; }
    public string ApiKey { get; set; }
    public string JwtSecret { get; set; }
    public string SmtpPassword { get; set; }
    public string AzureStorageConnectionString { get; set; }
}

Managed Identity Authentication

public class ManagedIdentityKeyVaultService
{
    private readonly IAzureKeyVaultService _keyVaultService;
    private readonly ILogger<ManagedIdentityKeyVaultService> _logger;
    
    public ManagedIdentityKeyVaultService(IAzureKeyVaultService keyVaultService, ILogger<ManagedIdentityKeyVaultService> logger)
    {
        _keyVaultService = keyVaultService;
        _logger = logger;
    }
    
    public async Task<bool> TestManagedIdentityAsync()
    {
        try
        {
            // Try to access a test secret to verify managed identity is working
            await _keyVaultService.GetSecretAsync("test-secret");
            _logger.LogInformation("Managed Identity authentication successful");
            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Managed Identity authentication failed");
            return false;
        }
    }
    
    public async Task InitializeSecretsAsync()
    {
        var requiredSecrets = new[]
        {
            "database-connection-string",
            "jwt-secret",
            "external-api-key"
        };
        
        var missingSecrets = new List<string>();
        
        foreach (var secretName in requiredSecrets)
        {
            try
            {
                await _keyVaultService.GetSecretAsync(secretName);
                _logger.LogInformation("Secret {SecretName} found", secretName);
            }
            catch (Exception)
            {
                missingSecrets.Add(secretName);
                _logger.LogWarning("Secret {SecretName} not found", secretName);
            }
        }
        
        if (missingSecrets.Any())
        {
            throw new InvalidOperationException($"Missing required secrets: {string.Join(", ", missingSecrets)}");
        }
    }
}

Certificate Management

public class CertificateService
{
    private readonly IAzureKeyVaultService _keyVaultService;
    private readonly ILogger<CertificateService> _logger;
    
    public CertificateService(IAzureKeyVaultService keyVaultService, ILogger<CertificateService> logger)
    {
        _keyVaultService = keyVaultService;
        _logger = logger;
    }
    
    public async Task<X509Certificate2> GetCertificateAsync(string certificateName)
    {
        var certificate = await _keyVaultService.GetCertificateAsync(certificateName);
        _logger.LogInformation("Certificate {CertificateName} retrieved", certificateName);
        return certificate;
    }
    
    public async Task<List<CertificateInfo>> ListCertificatesAsync()
    {
        var certificates = new List<CertificateInfo>();
        
        await foreach (var certProperty in _keyVaultService.GetCertificatesAsync())
        {
            certificates.Add(new CertificateInfo
            {
                Name = certProperty.Name,
                Version = certProperty.Version,
                CreatedOn = certProperty.CreatedOn,
                UpdatedOn = certProperty.UpdatedOn,
                ExpiresOn = certProperty.ExpiresOn,
                Enabled = certProperty.Enabled ?? true
            });
        }
        
        return certificates;
    }
    
    public async Task<List<CertificateInfo>> GetExpiringCertificatesAsync(int daysThreshold = 30)
    {
        var allCertificates = await ListCertificatesAsync();
        var threshold = DateTime.UtcNow.AddDays(daysThreshold);
        
        return allCertificates
            .Where(c => c.ExpiresOn.HasValue && c.ExpiresOn.Value <= threshold)
            .OrderBy(c => c.ExpiresOn)
            .ToList();
    }
    
    public async Task MonitorCertificateExpirationAsync()
    {
        var expiringCertificates = await GetExpiringCertificatesAsync(30);
        
        foreach (var cert in expiringCertificates)
        {
            var daysUntilExpiry = (cert.ExpiresOn.Value - DateTime.UtcNow).Days;
            
            if (daysUntilExpiry <= 7)
            {
                _logger.LogError("Certificate {CertificateName} expires in {Days} days", cert.Name, daysUntilExpiry);
            }
            else if (daysUntilExpiry <= 30)
            {
                _logger.LogWarning("Certificate {CertificateName} expires in {Days} days", cert.Name, daysUntilExpiry);
            }
        }
    }
}

Environment-Specific Configuration

public class EnvironmentKeyVaultService
{
    private readonly IAzureKeyVaultService _keyVaultService;
    private readonly IHostEnvironment _environment;
    private readonly ILogger<EnvironmentKeyVaultService> _logger;
    
    public EnvironmentKeyVaultService(
        IAzureKeyVaultService keyVaultService, 
        IHostEnvironment environment,
        ILogger<EnvironmentKeyVaultService> logger)
    {
        _keyVaultService = keyVaultService;
        _environment = environment;
        _logger = logger;
    }
    
    public async Task<string> GetEnvironmentSecretAsync(string baseName)
    {
        var environmentSpecificName = $"{baseName}-{_environment.EnvironmentName.ToLower()}";
        
        try
        {
            // Try environment-specific secret first
            var secret = await _keyVaultService.GetSecretAsync(environmentSpecificName);
            _logger.LogInformation("Using environment-specific secret: {SecretName}", environmentSpecificName);
            return secret;
        }
        catch (Exception)
        {
            // Fall back to base secret name
            _logger.LogInformation("Environment-specific secret not found, using base secret: {SecretName}", baseName);
            return await _keyVaultService.GetSecretAsync(baseName);
        }
    }
    
    public async Task SetEnvironmentSecretsAsync(Dictionary<string, string> secrets)
    {
        var environmentPrefix = _environment.EnvironmentName.ToLower();
        
        foreach (var (key, value) in secrets)
        {
            var environmentSpecificKey = $"{key}-{environmentPrefix}";
            
            await _keyVaultService.SetSecretAsync(environmentSpecificKey, value, new Dictionary<string, string>
            {
                { "environment", _environment.EnvironmentName },
                { "base-name", key },
                { "created-date", DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") }
            });
            
            _logger.LogInformation("Environment secret set: {SecretName}", environmentSpecificKey);
        }
    }
}

Health Check Integration

public class KeyVaultHealthCheck : IHealthCheck
{
    private readonly IAzureKeyVaultService _keyVaultService;
    private readonly ILogger<KeyVaultHealthCheck> _logger;
    
    public KeyVaultHealthCheck(IAzureKeyVaultService keyVaultService, ILogger<KeyVaultHealthCheck> logger)
    {
        _keyVaultService = keyVaultService;
        _logger = logger;
    }
    
    public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        try
        {
            // Try to access a known secret or list secrets to test connectivity
            await _keyVaultService.GetSecretsAsync().Take(1).ToListAsync(cancellationToken);
            
            _logger.LogInformation("Key Vault health check passed");
            return HealthCheckResult.Healthy("Key Vault is accessible");
        }
        catch (OperationCanceledException)
        {
            _logger.LogWarning("Key Vault health check timed out");
            return HealthCheckResult.Unhealthy("Key Vault health check timed out");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Key Vault health check failed");
            return HealthCheckResult.Unhealthy($"Key Vault is not accessible: {ex.Message}");
        }
    }
}

// In Program.cs
builder.Services.AddHealthChecks()
    .AddCheck<KeyVaultHealthCheck>("keyvault");

?? Configuration Options

public class AzureKeyVaultSettings
{
    public string VaultUri { get; set; }
    public string TenantId { get; set; }
    public string ClientId { get; set; }
    public string ClientSecret { get; set; }
    public bool EnableCaching { get; set; } = true;
    public int CacheExpiryMinutes { get; set; } = 30;
    public int RetryAttempts { get; set; } = 3;
    public TimeSpan RetryDelay { get; set; } = TimeSpan.FromSeconds(2);
    public bool UseManagedIdentity { get; set; } = true;
}

public class SecretBundle
{
    public string Name { get; set; }
    public string Value { get; set; }
    public string Version { get; set; }
    public DateTimeOffset? CreatedOn { get; set; }
    public DateTimeOffset? UpdatedOn { get; set; }
    public DateTimeOffset? ExpiresOn { get; set; }
    public IDictionary<string, string> Tags { get; set; }
}

public class SecretInfo
{
    public string Name { get; set; }
    public string Version { get; set; }
    public DateTimeOffset? CreatedOn { get; set; }
    public DateTimeOffset? UpdatedOn { get; set; }
    public bool Enabled { get; set; }
    public IDictionary<string, string> Tags { get; set; }
}

public class CertificateInfo
{
    public string Name { get; set; }
    public string Version { get; set; }
    public DateTimeOffset? CreatedOn { get; set; }
    public DateTimeOffset? UpdatedOn { get; set; }
    public DateTimeOffset? ExpiresOn { get; set; }
    public bool Enabled { get; set; }
}

??? Architecture

The Azure Key Vault service provides:

  • Security: Secure secret, key, and certificate management
  • Performance: Caching support for improved response times
  • Reliability: Retry policies and comprehensive error handling
  • Flexibility: Multiple authentication methods and configuration options
  • Integration: Seamless ASP.NET Core configuration provider integration

????? Author

Frebin Francis
Fingent Technology Solutions Pvt Ltd

?? License

Copyright � 2025 Fingent Technology Solutions Pvt Ltd

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.1 272 9/24/2025