A modern .NET library for secure access to Azure Key Vault secrets, certificates, and SQL Server Always Encrypted. Supports flexible authentication (Client Secret, Certificate, Managed Identity, DefaultAzureCredential), in-memory caching, structured logging, and built-in Polly retry policies.
$ dotnet add package AzureSecureAccessA modern .NET library for secure access to Azure Key Vault secrets, certificates, and encryption. Simplifies connecting to SQL Server databases using Always Encrypted with Azure Key Vault as the key store.
ILogger<T> across all providers (no secret values exposed)dotnet add package AzureSecureAccess
Choose the authentication method that fits your scenario:
using AzureSecureAccess.Authentication;
// Client Secret (for service principals)
var credential = new ClientSecretCredentialProvider(tenantId, clientId, clientSecret);
// Certificate-based authentication
var credential = new CertificateCredentialProvider(tenantId, clientId, certificate);
// Managed Identity (Azure-hosted: App Service, AKS, VMs)
var credential = new ManagedIdentityCredentialProvider(); // System-assigned
var credential = new ManagedIdentityCredentialProvider(
ManagedIdentityId.FromUserAssignedClientId("client-id")); // User-assigned
// DefaultAzureCredential (tries multiple methods automatically)
var credential = new DefaultCredentialProvider();
using AzureSecureAccess.Secrets;
var secretProvider = new KeyVaultSecretProvider("my-keyvault", credential.GetCredential());
// Get the latest version of a secret
string apiKey = await secretProvider.GetSecretValueAsync("ApiKey");
// Get a specific version
string oldKey = await secretProvider.GetSecretVersionValueAsync("ApiKey", "abc123version");
var baseProvider = new KeyVaultSecretProvider("my-keyvault", credential.GetCredential());
using var cachedProvider = new CachedSecretProvider(baseProvider, TimeSpan.FromMinutes(30));
// First call hits Key Vault, subsequent calls use cache for 30 minutes
string secret = await cachedProvider.GetSecretValueAsync("ApiKey");
using AzureSecureAccess.Certificates;
// From local file
var localProvider = LocalCertificateProvider.FromFile("/path/to/cert.pfx");
var cert = localProvider.GetCertificate();
// From OS certificate store (by thumbprint)
var storeProvider = LocalCertificateProvider.FromStore("ABC123THUMBPRINT");
var cert = storeProvider.GetCertificate();
// From Azure Key Vault
var kvCertProvider = new KeyVaultCertificateProvider("my-keyvault", credential.GetCredential());
var certWithPrivateKey = await kvCertProvider.GetCertificateAsync("MyCertificate");
Notes:
application/x-pem-file) are supported on .NET 8+ and load using the native PEM APIs.using AzureSecureAccess.SqlServer;
var credential = new DefaultCredentialProvider();
AlwaysEncryptedProvider.RegisterKeyVaultProvider(credential.GetCredential());
using var connection = new SqlConnection(
"Server=myserver.database.windows.net;Database=MyDB;Column Encryption Setting=Enabled;");
await connection.OpenAsync();
// Encrypted columns are automatically decrypted!
using AzureSecureAccess.SqlServer;
// Connection string stored directly
var connection = new SecureConnectionBuilder()
.WithCredential(new ManagedIdentityCredentialProvider())
.WithConnectionString("Server=...;Column Encryption Setting=Enabled;")
.Build(); // OK for local connection strings
// Connection string stored in Key Vault (with caching)
var connection = await new SecureConnectionBuilder()
.WithCredential(new DefaultCredentialProvider())
.WithConnectionStringFromKeyVault("my-keyvault", "SqlConnectionString")
.WithCaching(TimeSpan.FromMinutes(45)) // Cache the connection string
.BuildAsync(); // Required for Key Vault to avoid deadlocks
await connection.OpenAsync();
The library includes built-in resilience using Polly to handle transient errors (network issues, throttling) and provides domain-specific exceptions for fatal errors.
KeyVaultSecretNotFoundException: When a secret does not exist (404).KeyVaultCertificateNotFoundException: When a certificate does not exist (404).KeyVaultAccessDeniedException: When permissions are missing (403).KeyVaultOperationException: For other Azure-related failures.All providers in the library support ILogger<T> for structured logging. You can pass your own logger or a ILoggerFactory to the fluent builders.
CachedSecretProvider logs HIT/MISS events to help optimize cache durations.var connection = await new SecureConnectionBuilder()
.WithCredential(credential)
.WithConnectionStringFromKeyVault("my-vault", "MySecret")
.WithLoggerFactory(myLoggerFactory) // All internal operations will be logged
.BuildAsync();
| Provider | Use Case | Example |
|---|---|---|
ClientSecretCredentialProvider | Service principal with secret | Dev/test environments |
CertificateCredentialProvider | Service principal with certificate | High-security production |
ManagedIdentityCredentialProvider | Azure-hosted resources | App Service, AKS, VMs, Functions |
DefaultCredentialProvider | Automatic fallback chain | Local dev + production flexibility |
| Class | Purpose |
|---|---|
ISecretReader | Abstraction for secret retrieval (Read-only) |
ISecretManager | Abstraction for secret management (Read/Write/Delete) |
KeyVaultSecretProvider | Direct access to Azure Key Vault secrets |
CachedSecretProvider | Decorator that caches secrets in memory (implements IDisposable) |
| Class | Purpose |
|---|---|
ICertificateProvider | Abstraction for certificate retrieval |
LocalCertificateProvider | Load from file system or OS certificate store |
KeyVaultCertificateProvider | Download certificates (with private keys) from Key Vault |
| Class | Purpose |
|---|---|
AlwaysEncryptedProvider | Registers Azure Key Vault provider for Always Encrypted |
SecureConnectionBuilder | Fluent API to build SQL connections with encryption + Key Vault integration |
The library is organized by domain, not technical layers:
AzureSecureAccess
├── Authentication/ ← How to authenticate with Azure
├── Secrets/ ← How to manage secrets
├── Certificates/ ← How to retrieve certificates
└── SqlServer/ ← How to connect to SQL Server with encryption
Each namespace answers a specific question:
Authentication: How do I prove I'm authorized?Secrets: Where are my configuration secrets?Certificates: Where are my X509 certificates?SqlServer: How do I connect securely to SQL Server?dotnet test -f net8.0
Integration tests only run when RUN_INTEGRATION_TESTS=true is set.
Required environment variables:
RUN_INTEGRATION_TESTS=trueAZURE_TENANT_IDAZURE_CLIENT_IDAZURE_CLIENT_SECRETAZURE_KEYVAULT_KEY_IDAZURE_KEYVAULT_NAME (required for Key Vault secret/certificate tests)AZURE_TESTS_KEEP_DB=true (optional, skips DB cleanup for debugging)Alternative if you prefer to pass Key Vault name + key:
AZURE_KEYVAULT_NAMEAZURE_KEY_NAMEAZURE_KEY_VERSIONProvision a Key Vault + RSA key + app registration:
./scripts/provision-keyvault.sh <resource-group> <location> <keyvault-name> <app-name>./scripts/provision-keyvault.ps1 -ResourceGroup <rg> -Location <loc> -KeyVaultName <kv> -AppName <app>Run integration tests:
RUN_INTEGRATION_TESTS=true dotnet test -f net8.0
The pipeline expects a variable group named AzureSecureAccess-Integration.
Create it in Azure DevOps and add these variables:
RUN_INTEGRATION_TESTS = true (or false to disable)AZURE_TENANT_IDAZURE_CLIENT_IDAZURE_CLIENT_SECRETAZURE_KEYVAULT_KEY_IDAZURE_KEYVAULT_NAMEAZURE_TESTS_KEEP_DB (optional)If you want a different group name, update azure-pipelines.yml.