Skip to content

Instantly share code, notes, and snippets.

@liamgold
Last active October 23, 2023 12:01
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 liamgold/13cba1617d3dfebead81f4d9ccb570fd to your computer and use it in GitHub Desktop.
Save liamgold/13cba1617d3dfebead81f4d9ccb570fd to your computer and use it in GitHub Desktop.
Setting up Azure Key Vault in Kentico Xperience 13
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);
}
}
}
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