Back to Blog
4 min read

Secure Secret Management with Azure Key Vault in .NET

Storing secrets securely is a fundamental requirement for any application. With the increase in remote development and cloud deployments, Azure Key Vault has become essential. Here is how to integrate it properly with your .NET applications.

Creating a Key Vault

# Create a Key Vault
az keyvault create \
    --name kv-myapp-2020 \
    --resource-group rg-security \
    --location australiaeast \
    --enable-soft-delete true \
    --enable-purge-protection true

# Add a secret
az keyvault secret set \
    --vault-name kv-myapp-2020 \
    --name "DatabaseConnectionString" \
    --value "Server=tcp:myserver.database.windows.net..."

# Add another secret
az keyvault secret set \
    --vault-name kv-myapp-2020 \
    --name "ApiKey" \
    --value "your-api-key-here"

Managed Identity Setup

Use managed identity for secure, credential-free access:

# Enable managed identity on App Service
az webapp identity assign \
    --name mywebapp \
    --resource-group rg-app

# Get the principal ID
principalId=$(az webapp identity show \
    --name mywebapp \
    --resource-group rg-app \
    --query principalId -o tsv)

# Grant access to Key Vault
az keyvault set-policy \
    --name kv-myapp-2020 \
    --object-id $principalId \
    --secret-permissions get list

.NET Core Configuration Integration

Add the NuGet packages:

dotnet add package Azure.Extensions.AspNetCore.Configuration.Secrets
dotnet add package Azure.Identity

Configure in Program.cs:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((context, config) =>
            {
                var builtConfig = config.Build();

                var keyVaultEndpoint = new Uri($"https://{builtConfig["KeyVaultName"]}.vault.azure.net/");

                config.AddAzureKeyVault(keyVaultEndpoint, new DefaultAzureCredential());
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

In appsettings.json:

{
  "KeyVaultName": "kv-myapp-2020"
}

Accessing Secrets

Secrets are available through standard configuration:

public class MyService
{
    private readonly string _connectionString;
    private readonly string _apiKey;

    public MyService(IConfiguration configuration)
    {
        // Key Vault secret names use -- instead of : for hierarchy
        _connectionString = configuration["DatabaseConnectionString"];
        _apiKey = configuration["ApiKey"];
    }
}

Direct Key Vault Client Usage

For more control, use the SDK directly:

using Azure.Identity;
using Azure.Security.KeyVault.Secrets;

public class KeyVaultService
{
    private readonly SecretClient _client;

    public KeyVaultService(string keyVaultName)
    {
        var keyVaultUri = new Uri($"https://{keyVaultName}.vault.azure.net/");
        _client = new SecretClient(keyVaultUri, new DefaultAzureCredential());
    }

    public async Task<string> GetSecretAsync(string secretName)
    {
        var secret = await _client.GetSecretAsync(secretName);
        return secret.Value.Value;
    }

    public async Task SetSecretAsync(string secretName, string value)
    {
        await _client.SetSecretAsync(secretName, value);
    }

    public async Task<List<string>> ListSecretsAsync()
    {
        var secrets = new List<string>();

        await foreach (var secret in _client.GetPropertiesOfSecretsAsync())
        {
            secrets.Add(secret.Name);
        }

        return secrets;
    }
}

Working with Certificates

using Azure.Security.KeyVault.Certificates;

public class CertificateService
{
    private readonly CertificateClient _client;

    public CertificateService(string keyVaultName)
    {
        var keyVaultUri = new Uri($"https://{keyVaultName}.vault.azure.net/");
        _client = new CertificateClient(keyVaultUri, new DefaultAzureCredential());
    }

    public async Task<X509Certificate2> GetCertificateAsync(string certificateName)
    {
        var certificate = await _client.GetCertificateAsync(certificateName);
        return new X509Certificate2(certificate.Value.Cer);
    }
}

Caching Secrets

For performance, implement caching:

public class CachedKeyVaultService
{
    private readonly SecretClient _client;
    private readonly IMemoryCache _cache;
    private readonly TimeSpan _cacheExpiration = TimeSpan.FromMinutes(5);

    public CachedKeyVaultService(SecretClient client, IMemoryCache cache)
    {
        _client = client;
        _cache = cache;
    }

    public async Task<string> GetSecretAsync(string secretName)
    {
        return await _cache.GetOrCreateAsync($"kv-{secretName}", async entry =>
        {
            entry.AbsoluteExpirationRelativeToNow = _cacheExpiration;
            var secret = await _client.GetSecretAsync(secretName);
            return secret.Value.Value;
        });
    }
}

Local Development

For local development, use Azure CLI authentication:

# Login to Azure
az login

# Set your subscription
az account set --subscription "your-subscription"

The DefaultAzureCredential will automatically use your Azure CLI credentials locally.

Secret Rotation

Implement secret rotation handling:

public class RotatingSecretService
{
    private readonly SecretClient _client;
    private string _currentSecret;
    private DateTimeOffset _lastRefresh;
    private readonly TimeSpan _refreshInterval = TimeSpan.FromHours(1);

    public async Task<string> GetSecretAsync(string secretName)
    {
        if (_currentSecret == null ||
            DateTimeOffset.UtcNow - _lastRefresh > _refreshInterval)
        {
            var secret = await _client.GetSecretAsync(secretName);
            _currentSecret = secret.Value.Value;
            _lastRefresh = DateTimeOffset.UtcNow;
        }

        return _currentSecret;
    }
}

Best Practices

  1. Use managed identities - Avoid storing any credentials
  2. Enable soft-delete - Protect against accidental deletion
  3. Enable purge protection - For compliance requirements
  4. Use RBAC - Fine-grained access control
  5. Audit access - Enable diagnostics logging

Azure Key Vault provides the foundation for secure secret management in cloud applications.

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.