Azure App Configuration, Key Vault and Managed Identity Implementation

December 9, 2022    Azure Configuration Security

Azure App Configuration, Key Vault and Managed Identity Implementation

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.

Recommend Path (from experience)

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.

DefaultAzureCredential auth path from Github

Hints

  • It is recommended to have a different instance of App Configuration and key vault for each environment. There are labels in App Configuration, that could be used, but splitting them let’s you setup access to these differently for each environment.
  • At first, I stacked Managed Identity on top of moving the config and key vault, but that was too much to learn at once. First, get it working with the Azure App Config connection string, then get Managed Identity working. Unless you’ve got a brand new setup and can just follow the commands and start with Managed Identity.
  • Document the CLI commands or put them into Terraform or Powershell so you can easily recreate the setup (especially helpful when you have a test and a production environment). Save that in Git.
  • When adding Application Insights , make sure to use the key ApplicationSettings:ConnectionString.

Filling Objects from App Config

https://chris-ayers.com/2022/12/03/validating-dotnet-configuration is a good overview of using IOptions, but I didn’t see it soon enough. So I’m using config.GetRequiredSection(“AppSettings”) and my custom reflection code.

// 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;
    }
}

Azure Kubernetes Service

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.

Azure Managed Identity

- 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.

CSI

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.

C# Christmas Advent

C# Christmas Advent 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



Watch the Story for Good News
I gladly accept BTC Lightning Network tips at [email protected]

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)

Use Brave

Also check out my Resources Page for referrals that would help me.


Swan logo
Use Swan Bitcoin to onramp with low fees and automatic daily cost averaging and get $10 in BTC when you sign up.