Skip to content

Instantly share code, notes, and snippets.

@tgreensill
Last active June 26, 2023 16:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tgreensill/26659111871fdc54d0ac20cc21e602e1 to your computer and use it in GitHub Desktop.
Save tgreensill/26659111871fdc54d0ac20cc21e602e1 to your computer and use it in GitHub Desktop.
KeyVaultSecretManager that uses a dictionary to map secrets from the key vault to appSettings.
/// <summary>
/// Custom KeyVaultSecretManager that uses a dictionary in the appsettings to map
/// keyvault secrets to appsettings. Useful for when seret names change between environments.
/// The key part of the dictionary should match the appSetting to override with the secret value.
/// The value part ofthe dictionary should be the name of the secret to pull the value from.
/// </summary>
public class MappedKeyVaultSecretManager : IKeyVaultSecretManager
{
private readonly IReadOnlyDictionary<string, string> _mappings;
public MappedKeyVaultSecretManager(IReadOnlyDictionary<string, string> mappings)
{
if (mappings != null)
{
// Colons cause issues in the keys, so we use double underscore then replace
// them with colons at runtime.
_mappings = mappings.ToDictionary(x => x.Key.Replace("__", ":"), x => x.Value);
}
}
public bool Load(SecretItem secret)
{
// Load the secret if the secret name is in the list of mapping values.
return _mappings.Any(mapping =>
secret.Identifier.Name.Equals(mapping.Value, StringComparison.OrdinalIgnoreCase)
);
}
public string GetKey(SecretBundle secret)
{
// Store the secret value in the appsetting matching the key in the mappings.
return _mappings.FirstOrDefault(mapping =>
secret.SecretIdentifier.Name.Equals(mapping.Value, StringComparison.OrdinalIgnoreCase)
).Key;
}
}
// Example appSettings
{
"SomePassword": "******",
"SomeApi": {
"ApiKey": "******"
},
"KeyVaultName": "MyExampleKeyVault",
"KeyVaultMappings": {
"SomePassword": "name-of-secret-in-keyvault-that-contains-the-password",
"SomeApi__ApiKey": "name-of-secret-in-keyvault-that-contains-the-api-key",
}
}
// Example usage
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
if (context.HostingEnvironment.IsProduction())
{
var builtConfig = config.Build();
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback(
azureServiceTokenProvider.KeyVaultTokenCallback));
// Pull mappings from config
var secretMappings = builtConfig.GetSection("KeyVaultMappings")
.GetChildren()
.Select(x => new KeyValuePair<string, string>(x.Key, x.Value))
.ToDictionary(x => x.Key, x => x.Value);
config.AddAzureKeyVault(
$"https://{builtConfig["KeyVaultName"]}.vault.azure.net/",
keyVaultClient,
new MappedKeyVaultSecretManager(secretMappings));
}
})
.UseStartup<Startup>();
@bert2
Copy link

bert2 commented Jun 26, 2023

Thanks for this worked example. The documentation on this is way too confusing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment