How to use Key Vault secrets in application settings

Introduction

According to the 12 factor app principle a web application should store its run-time configuration in the environment (III. Config).

The twelve-factor app stores config in environment variables [...] Env vars are easy to change between deploys without changing any code; unlike config files, there is little chance of them being checked into the code repo accidentally; and unlike custom config files [...] they are a language- and OS-agnostic standard.

This makes sense, as configuration varies substantially across deployments and staging areas, code however does not.

Though there might be situations where you have to use confidential configuration paramters that needs protection from preying eyes. And this is where Azure Key Vault can be leveraged as it plays well together with Azure App Service.

This article will show how you can store and retrieve secrets from a key vault and use them in application settings (read environment variables) in your Azure App Service.

Prerequisites

We are going to need...

  • An App Service Plan (works even with the free plan)
  • An App Service that has a system assigned managed ID
  • A Key Vault with an Access Policy
  • A secret (oh really?)

Step by Step

The high-level steps are:

  1. Assign a system managed ID to the web application
  2. Create an access policy allowing the managed ID to access secrets
  3. Create an application setting that references a secret in the key vault

Key Vault References

A key vault reference takes the form of @Microsoft.KeyVault({referenceString}), where referenceString` can be one of:

  1. @Microsoft.KeyVault(SecretUri=...)
  2. @Microsoft.KeyVault(VaultName=vaultName;SecretName=secretName;SecretVersion=secretVersion)

I am going to use the first version as I find it simpler to script, for example:

@Microsoft.KeyVault(SecretUri=https://<keyvault-name>.vault.azure.net/secrets/<secret-name>/<version-string>\r)

Okay lets walk through this step by step. Of course the scripts below could be shortened, but I've tried to make them as explicit as possible.

Azure CLI

# Enable System Assigned Managed IDs
az webapp identity assign --resource-group <resource-group> --name <webapp>

# Retrieve object ID of managed ID
OBJECTID=$(az webapp identity show --name <webapp> --resource-group <resource-group> --query principalId --output tsv)

# Create an access policy
az keyvault set-policy --name <keyvault> --secret-permissions get --object-id $OBJECTID

# Create a secret
az keyvault secret set --vault-name <keyvault> --name <secretname> --value <secret>

# Retrieve secret ID
SECRETID=$(az keyvault secret show --name secret-1 --vault-name kv-azureblue-1 --query id --output tsv)

# Create an application setting
az webapp config appsettings set --name <webapp> --resource-group <resource-group> --settings KEY_FROM_VAULT=”@Microsoft.KeyVault(SecretUri=$SECRETID)

Azure PowerShell

# Enable System Assigned Managed IDs
Set-AzWebApp -Name <webapp> -AssignIdentity $true -ResourceGroupName <resource-group>

# Retrieve object ID of managed ID
$objectid = (Get-AzWebApp -Name <webapp>).Identity

# Create an access policy
Set-AzKeyVaultAccessPolicy -VaultName <keyvault> -PermissionsToSecrets get -Object $objectid.PrincipalId

# Create a secret
$secret = ConvertTo-SecureString -String “Abracadabra” -AsPlainText
Set-AzKeyVaultSecret -VaultName <keyvault> -Name <secretname> -SecretValue $secret

# Retrieve secret ID
$secretid = (Get-AzKeyVaultSecret -VaultName <keyvault> -Name <secretname>).Id

# Create an application setting

# Retrieve details about web application 
$app = Get-AzWebApp -Name <webapp>

# Store application settings 
$appsettings = $app.SiteConfig.AppSettings

# Create a new hash table 
$newSettings = @{}

# Copy current settings to hash table 
ForEach ($appSetting in $appSettings)
{
	$newSettings[$appSetting.Name] = $appSetting.Value 
}

# Add reference to key vault secret 
$newSettings[”KEY_FROM_VAULT”] = ”@Microsoft.KeyVault(SecretUri=$secretid)” 

# Write back our new application settings 
Set-AzWebApp -Name <webapp> -ResourceGroupName <resource-group> -AppSettings $newSettings

Constraints

There are a few constraints we need to keep in mind:

  1. User assigned managed identities can't be used
  2. We can only retrieve secrets via a key vault reference (no keys or certificates)
  3. Network restrictions needs to be turned off (unless the app is hosted within an app service environment)

Troubleshooting

First make sure that the issue is with the platform configuration and not with your application code. I suggest to use the debug console for this purpose. You can reach it via:

App Service > Development Tools > Console

Then use something like set KEY_FROM_FAULT or env to get a list of environment variables. In case app service wasn't able to resolve the secret, the variable name will hold the reference name. In case the secret is revealed to you, you should check your code! Sorry mate!

Other things you might want to check:

  • Double check reference syntax!
  • Double check reference syntax!
  • Double check reference syntax!
  • Check access permissions (secret not key)
  • Double check object id

Recommendations

Microsoft recommends to use a key vault for each deployment slot in use. You may also want to use secrets with overlapping validity times for effective key roation scenarios.

Conclusion

This setup was pretty easy to create and enhances the security of your public cloud infrastructure without additional cost! Nice! 😎

If you have any comments, questions or suggestions please comment below! Thanks for reading!