Last active
October 23, 2023 12:01
-
-
Save liamgold/13cba1617d3dfebead81f4d9ccb570fd to your computer and use it in GitHub Desktop.
Setting up Azure Key Vault in Kentico Xperience 13
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
using CMS; | |
using CMS.Base; | |
using CMS.Core; | |
using Example.Admin.Core.Configuration; | |
using Microsoft.Azure.KeyVault; | |
using Microsoft.Azure.KeyVault.Models; | |
using Microsoft.Azure.Services.AppAuthentication; | |
using System; | |
using System.Collections.Generic; | |
using System.Configuration; | |
using System.Threading.Tasks; | |
// Register a custom implementation of IAppSettingsService | |
[assembly: RegisterImplementation(typeof(IAppSettingsService), typeof(ExampleAppSettingsService), Lifestyle = Lifestyle.Singleton, Priority = RegistrationPriority.Default)] | |
namespace Example.Admin.Core.Configuration | |
{ | |
public class ExampleAppSettingsService : IAppSettingsService | |
{ | |
// Added a whitelist of app settings so we're only looking up keys we want to. | |
private static readonly HashSet<string> WhiteListedAppSettings = new HashSet<string>(StringComparer.OrdinalIgnoreCase) | |
{ | |
"CMSAzureAccountName", | |
"CMSAzureSharedKey", | |
}; | |
/// <summary> | |
/// Gets the specific settings from the application configuration file or from a memory. | |
/// Sets a specific settings with a value into a memory. | |
/// </summary> | |
public string this[string key] | |
{ | |
get | |
{ | |
var appSetting = SettingsHelper.AppSettings[key]; | |
if (appSetting == null) | |
{ | |
if (!WhiteListedAppSettings.Contains(key)) | |
{ | |
return null; | |
} | |
return GetAzureKeyVaultAppSetting(key); | |
} | |
return SettingsHelper.AppSettings[key]; | |
} | |
set | |
{ | |
SettingsHelper.AppSettings[key] = value; | |
} | |
} | |
private string GetAzureKeyVaultAppSetting(string key) | |
{ | |
var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(GetAccessTokenAsync)); | |
var appSetting = Task.Run(() => GetSecretFromKeyVaultAsync(keyVaultClient, key)).GetAwaiter().GetResult(); | |
var appSettingValue = appSetting?.Value; | |
if (!string.IsNullOrWhiteSpace(appSettingValue)) | |
{ | |
return appSettingValue; | |
} | |
return null; | |
} | |
private static async Task<SecretBundle> GetSecretFromKeyVaultAsync(KeyVaultClient keyVaultClient, string secretName) | |
{ | |
var keyVaultName = ConfigurationManager.AppSettings["KeyVaultName"]; | |
var keyVaultUrl = $"https://{keyVaultName}.vault.azure.net/"; | |
try | |
{ | |
var secretIdentifier = $"{keyVaultUrl}secrets/{secretName}"; | |
return await keyVaultClient.GetSecretAsync(secretIdentifier); | |
} | |
catch (KeyVaultErrorException ex) | |
{ | |
// Handle any exception occurred during the secret retrieval process. | |
Console.WriteLine($"Error retrieving secret: {ex.Message}"); | |
return null; | |
} | |
} | |
private static async Task<string> GetAccessTokenAsync(string authority, string resource, string scope) | |
{ | |
var azureServiceTokenProvider = new AzureServiceTokenProvider(); | |
return await azureServiceTokenProvider.GetAccessTokenAsync(resource); | |
} | |
} | |
} |
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
using CMS; | |
using CMS.Base; | |
using CMS.Core; | |
using Example.Admin.Core.Configuration; | |
using Microsoft.Azure.KeyVault; | |
using Microsoft.Azure.KeyVault.Models; | |
using Microsoft.Azure.Services.AppAuthentication; | |
using System; | |
using System.Configuration; | |
using System.Threading.Tasks; | |
// Register a custom implementation of IConnectionStringService | |
[assembly: RegisterImplementation(typeof(IConnectionStringService), typeof(ExampleConnectionStringService), Lifestyle = Lifestyle.Singleton, Priority = RegistrationPriority.Default)] | |
namespace Example.Admin.Core.Configuration | |
{ | |
/// <summary> | |
/// ConnectionStrings service | |
/// </summary> | |
public class ExampleConnectionStringService : IConnectionStringService | |
{ | |
/// <summary> | |
/// Default connection string | |
/// </summary> | |
public string DefaultConnectionString | |
{ | |
get | |
{ | |
return this["CMSConnectionString"]; | |
} | |
} | |
/// <summary> | |
/// Gets the specific connection string from the app config | |
/// </summary> | |
public string this[string name] | |
{ | |
get | |
{ | |
// Get connection string | |
var connString = SettingsHelper.ConnectionStrings[name]; | |
if (connString == null) | |
{ | |
if (!name.Equals("CMSConnectionString", StringComparison.InvariantCultureIgnoreCase)) | |
{ | |
return null; | |
} | |
return GetAzureKeyVaultConnectionString(); | |
} | |
return connString.ConnectionString; | |
} | |
set | |
{ | |
if (String.IsNullOrEmpty(name)) | |
{ | |
throw new ArgumentException(nameof(name)); | |
} | |
SettingsHelper.ConnectionStrings[name] = new ConnectionStringSettings(name, value); | |
} | |
} | |
private string GetAzureKeyVaultConnectionString() | |
{ | |
var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(GetAccessTokenAsync)); | |
var connectionString = Task.Run(() => GetSecretFromKeyVaultAsync(keyVaultClient, "ConnectionStrings--CMSConnectionString")).GetAwaiter().GetResult(); | |
var connectionStringValue = connectionString?.Value; | |
if (!string.IsNullOrWhiteSpace(connectionStringValue)) | |
{ | |
return connectionStringValue; | |
} | |
return null; | |
} | |
private static async Task<SecretBundle> GetSecretFromKeyVaultAsync(KeyVaultClient keyVaultClient, string secretName) | |
{ | |
var keyVaultName = ConfigurationManager.AppSettings["KeyVaultName"]; | |
var keyVaultUrl = $"https://{keyVaultName}.vault.azure.net/"; | |
try | |
{ | |
var secretIdentifier = $"{keyVaultUrl}secrets/{secretName}"; | |
return await keyVaultClient.GetSecretAsync(secretIdentifier); | |
} | |
catch (KeyVaultErrorException ex) | |
{ | |
// Handle any exception occurred during the secret retrieval process. | |
Console.WriteLine($"Error retrieving secret: {ex.Message}"); | |
return null; | |
} | |
} | |
private static async Task<string> GetAccessTokenAsync(string authority, string resource, string scope) | |
{ | |
var azureServiceTokenProvider = new AzureServiceTokenProvider(); | |
return await azureServiceTokenProvider.GetAccessTokenAsync(resource); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment