We were enabled during our innovation sprint to learn about and implement Azure App Configuration, Key Vault and Managed Identity. I was familiar with the concepts, but it took me about a day to start to feel comfortable. After that there was a lot of copy/paste from appsettings.json files into Azure.
Microsoft’s documentation in https://learn.microsoft.com was extremely helpful.
This is what I learned and hope it helps you short circuit your learning time.
First, learn about Azure App Configuration, create one and move your configuration settings there. Check out the import feature before doing it all by hand. The Azure Friday video was a great introduction to get started.
The cli was very helpful to make things move a bit faster (this is for PowerShell)
// create Config, start with free for dev (until we need more), standard for prod
az appconfig create -g $resourceGroupName -n $configName -l $location --sku Free
az appconfig kv set --yes -n $configName --key AppSettings:{yourKey} --value ""
Then, learn about Azure Key Vault . These links were helpful as well: https://learn.microsoft.com/en-us/samples/azure-samples/key-vault-dotnet-core-quickstart/get-started-keyvault-net/ , and in Github . “Treat secrets like Kryptonite”. Create an Azure Key Vault and move your secrets in there.
az keyvault create -n $keyVaultName -g $resourceGroupName -l $location --enable-purge-protection true --retention-days 30
//az keyvault secret set --name "AppSettings--Db--Password" --value "secret-db-password" --vault-name $keyVaultName
Remember that Key Vault needs --
and App Config needs :
between the names. Ex: “AppSettings:Db:Password” for the App Config connection and “AppSettings–Db–Password” for Key Vault
Setup your Azure Key Vault Access Policies for your developers (create an AD group is better than individual users). Lock down your production keys differently than you dev keys.
Link your Azure Key Vault secrets to your App Configuration .
Connect your app through code. I found it easiest to start by using the App Configuration Connection string. We needed help from IT, since we didn’t have permissions to add role assignments that would enable Managed Identity (see below, I found it was too much to try to learn about Managed Identity at the same time).
// Program.cs
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
webBuilder.ConfigureAppConfiguration((hostingContext, config) =>
{
// https://learn.microsoft.com/en-us/azure/azure-app-configuration/use-key-vault-references-dotnet-core?tabs=core5x
var settings = config.Build();
// or https://learn.microsoft.com/en-us/azure/azure-app-configuration/howto-integrate-azure-managed-service-identity?tabs=core5x&pivots=framework-dotnet
// ManagedIdentityCredential() doesn't work with localhost
// https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/identity/Azure.Identity
var appSettings = configurationRoot.GetRequiredSection("AppSettings").Get<AppSettings>();
config.AddAzureAppConfiguration(options =>
{
options.Connect(new Uri(appSettings.AppConfigEndpoint), new DefaultAzureCredential())
.Select("AppSettings:*")
.ConfigureRefresh(refreshOption =>
{
// this is polling
// refresh when AppSettings:Sentinel is changed in the portal
refreshOption.Register("AppSettings:Sentinel", true)
.SetCacheExpiration(TimeSpan.FromDays(15));
})
.ConfigureKeyVault(kv =>
{
kv.SetCredential(new DefaultAzureCredential());
});
});
})
.UseStartup<Startup>())
.Build();
// you can read the value with colons from IConfiguration
var myValue = config["AppSettings:MyValue"];
// but prefer the IOptions pattern with an object (see more below)
The
Azure.Identity
Nuget package is needed. In this readme, they have a graphic explaining how new DefaultAzureCredential()
is used to check different authentication scenarios. I’ve included that image here.
ApplicationSettings:ConnectionString
.
https://chris-ayers.com/2022/12/03/validating-dotnet-configuration
is a good overview of using IOptions
// in Program.cs
var appSettings = configurationRoot.GetRequiredSection("AppSettings").Get<AppSettings>();
// or inject IOptions<AppSettings>() into a constructor
// settings from AppSettings: in App Config (key starts with AppSettings:)
public class AppSettings : SettingsBase{
public ApiUrlsSettings ApiUrls { get; set; }
public bool IsAnyMissing()
{
return IsAnyMissing(this)
|| IsAnyMissing(ApiUrls)
|| IsAnyMissing(Auth);
}
}
public class ApiUrlsSettings : SettingsBase {
public string UrlOne { get; set; }
}
public class SettingsBase
{
public static bool IsAnyMissing(SettingsBase settingsBase)
{
var anyMissing = false;
var props = settingsBase.GetType().GetProperties();
foreach (var prop in props)
{
var type = prop.GetType();
var value = prop.GetValue(settingsBase)?.ToString();
var isInt = int.TryParse(value, out int valueAsInt);
anyMissing |= (isInt && valueAsInt == 0)
|| string.IsNullOrWhiteSpace(value);
}
return anyMissing;
}
}
The Key Vault Authorization is through the Managed Identity of the agentpool setup with AKS cluster with workload identity (preview) - Azure Kubernetes Service through a Service Account (workload-identity-sa) we created through a federated identity to Active Directory. There were a lot of steps, but the docs are well written.
- https://learn.microsoft.com/en-us/azure/aks/operator-best-practices-identity
- [Configuring Managed Service Identities (MSI) for Microsoft Azure Resources](https://app.pluralsight.com/course-player?clipId=6ba6a1ad-67ce-411a-9c4f-19d59f1e400a)
in your.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
selector:
matchLabels:
app: api
replicas: 2
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
template:
metadata:
labels:
app: api
env: dev
spec:
serviceAccountName: workload-identity-sa
automountServiceAccountToken: false
The Dapr Integration looks like a nice way to handle this.
If you want to pass secrets from Key Vault down to Kubernetes through the yml files you need to use CSI
Securing Applications in Microsoft Azure | Pluralsight and https://github.com/HoussemDellai/aks-keyvault are a bit out of date (doesn’t have the workload approach), but were helpful.
This blog was posted as part of the C# Advent Calendar for December 7th, 2022. I really want to thank Matthew D. Groves and Calvin Allen for helping set this up! Look for #csadvent on Twitter! Make sure to check out everyone else’s work when you’re done here.
Merry Christmas!! My you know Jesus, the Christ of Christmas.
“And Mary said, ‘My soul magnifies the Lord, 47 band my spirit rejoices in God my Savior, 48 for he has looked on the humble estate of his servant. For behold, from now on all generations will call me blessed; 49 for fhe who is mighty has done great things for me, and holy is his name.’” ~ Luke 1:46-49 ESV
Please consider using Brave and adding me to your BAT payment ledger. Then you won't have to see ads! (when I get to $100 in Google Ads for a payout, I pledge to turn off ads)
Also check out my Resources Page for referrals that would help me.