Last active
June 26, 2023 16:08
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <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>(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for this worked example. The documentation on this is way too confusing.