Saturday, 15 July 2023

Ensure Azure App Config Refresh for Keyvault Secret Updates in Terraform

Keyvault secrets can be used in Azure app conciguratoins and can be setup with terraform. However, if the secret is modified then modified secret reference is not get updated to the app configuration. There are two ways to fix this issue in terraform. Let's explore them.

First let's look at the problem.

Consider below secrets defined in terraform for keyvault.

# Secrets
resource "azurerm_key_vault_secret" "secret" {
  for_each = {
    AzureWebJobsStorage = azurerm_storage_account.demo.primary_connection_string
    SqlDBPwd          = azurerm_mssql_server.demo.administrator_login_password
  }
  name         = each.key
  value        = each.value
  key_vault_id = azurerm_key_vault.instancekeyvault.id

  depends_on = [
    azurerm_key_vault.instancekeyvault
  ]
} 

To refer these in the app configuration we can defined them as below.

resource "azurerm_app_configuration_key" "config_vault" {
  for_each = {
    "AzureWebJobsStorage" = azurerm_key_vault_secret.secret["AzureWebJobsStorage"].id
    "SqlDBPwd"     = azurerm_key_vault_secret.secret["SqlDBPwd"].id
  }
  configuration_store_id = azurerm_app_configuration.appconf.id
  key                    = each.key
  type                   = "vault" # keyvault reference
  label                  = azurerm_resource_group.instancerg.name
  vault_key_reference    = each.value
  depends_on = [
    azurerm_role_assignment.appconf_dataowner
  ]
}

The problem of this appraoch: As explained above the azurerm_key_vault_secret.secret["SqlDBPwd"].id is giving the keyvalt reference with the version id. So that the app config will store the value in below format. The Guid represents the version of the keyvault secret.

https://keyvaultname.vault.azure.net/secrets/SqlDBPwd/15061a22c4284082bf0c6e8f3f7cb501

When the the secret value is changed for SqlDBPwd  the terraform for keyvault will udate the secret vaule in keyvault which will have a diffrent GUID. However, this is not getting updated to the app config if we use the above terraform code to set the app configuration values.

The Solution - Option One - Not so good

One option to fix the issue is refer the keyvault secret as data in terraform as shown below and get it applied to the app configuration.This will ensure the new version changes in keyvault secret for SqlDBPwd is always get applied to app configuration. 

# Secrets
resource "azurerm_key_vault_secret" "secret" {
  for_each = {
    AzureWebJobsStorage = azurerm_storage_account.demo.primary_connection_string
    SqlDBPwd          = azurerm_mssql_server.demo.administrator_login_password
  }
  name         = each.key
  value        = each.value
  key_vault_id = azurerm_key_vault.instancekeyvault.id

  depends_on = [
    azurerm_key_vault.instancekeyvault
  ]
} 

# Refer SqlDBPwd as data to get updated value
data "azurerm_key_vault_secret" "sqldbpwd" {
   name         = "SqlDBPwd"
   key_vault_id = azurerm_key_vault.instancekeyvault.id
  depends_on = [
    azurerm_key_vault_secret.secret
  ]
}

resource "azurerm_app_configuration_key" "config_vault" {
  for_each = {
    "AzureWebJobsStorage" = azurerm_key_vault_secret.secret["AzureWebJobsStorage"].id
    "SqlDBPwd"     = data.azurerm_key_vault_secret.sqldbpwd.id
  }
  configuration_store_id = azurerm_app_configuration.appconf.id
  key                    = each.key
  type                   = "vault" # keyvault reference
  label                  = azurerm_resource_group.instancerg.name
  vault_key_reference    = each.value
  depends_on = [
    azurerm_role_assignment.appconf_dataowner
  ]
}

If we need to have same for other secrets we need to define data blocks for each secrets. This would mean lot of unneccessary complexity to code. 

The Solution - Option Two


Use of versionless_id is the best option. Then each of the secret of keyvault will be referred without version in below format.

https://keyvaultname.vault.azure.net/secrets/SqlDBPwd

# Secrets
resource "azurerm_key_vault_secret" "secret" {
  for_each = {
    AzureWebJobsStorage = azurerm_storage_account.demo.primary_connection_string
    SqlDBPwd          = azurerm_mssql_server.demo.administrator_login_password
  }
  name         = each.key
  value        = each.value
  key_vault_id = azurerm_key_vault.instancekeyvault.id

  depends_on = [
    azurerm_key_vault.instancekeyvault
  ]
} 

resource "azurerm_app_configuration_key" "config_vault" {
  for_each = {
    "AzureWebJobsStorage" = azurerm_key_vault_secret.secret["AzureWebJobsStorage"].versionless_id
    "SqlDBPwd"     = data.azurerm_key_vault_secret.sqldbpwd.versionless_id
  }
  configuration_store_id = azurerm_app_configuration.appconf.id
  key                    = each.key
  type                   = "vault" # keyvault reference
  label                  = azurerm_resource_group.instancerg.name
  vault_key_reference    = each.value
  depends_on = [
    azurerm_role_assignment.appconf_dataowner
  ]
}

This will ensure latest version of keyvault secret is always referred by the app configuration so that your application will always obtain the latest version of secret value.

No comments:

Popular Posts