hashicorp / terraform-provider-azurerm

Terraform provider for Azure Resource Manager
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs
Mozilla Public License 2.0
4.47k stars 4.56k forks source link

Updating the value of an azurerm_key_vault_secret does not update its ID when used as a dependency #13337

Open kensykora opened 2 years ago

kensykora commented 2 years ago

Community Note

Terraform (and AzureRM Provider) Version

Terraform Configuration Files

resource "azurerm_key_vault_secret" "service_bus_connection_string" {
  name = "service-bus-connection-string"

  value        = azurerm_servicebus_topic_authorization_rule.mysb.primary_connection_string
  key_vault_id = azurerm_key_vault.main.id
}

resource "azurerm_function_app" "main" {
  name                = "myfn"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name

  app_service_plan_id = azurerm_app_service_plan.main.id

  enable_builtin_logging = true
  https_only             = true
  os_type                = "linux"

  storage_account_name       = azurerm_storage_account.main.name
  storage_account_access_key = azurerm_storage_account.main.primary_access_key

  version = "~3"

  app_settings = {
    AzureWebJobsServiceBus      = "@Microsoft.KeyVault(SecretUri=${azurerm_key_vault_secret.service_bus_connection_string.id})"
  }
}

Description / Feedback

When the underlying value for an azurerm_key_vault_secret changes, the ID for the secret is generated. In the above code sample, the first time terraform apply is run, the app_setting is seeded properly with the resulting ID from the key vault operation with the version of the secret created. The app_settings config value will look something like: @Microsoft.KeyVault(SecretUri=https://hmykv.vault.azure.net/secrets/service-bus-connection-string/809284ceae23484589eb0fa1a6c6e147)

Again, on the initial first apply, this works as expected.

After changing the value (in our use case, rotating the primary access key of the access policy for the service bus account) however, the ID does not change on the 2nd apply. On the 2nd terraform apply, the value of the secret will be updated, but the azurerm_key_vault_secret.service_bus_connection_string.id reference will remain at the old value.

On a 3rd terraform apply, the secret version value in the attribute output reference will be properly updated.

Steps to Reproduce:

  1. Create config similar to above.
  2. Run terraform apply - Observe that the use of the id attribute in azurerm_key_vault_secret is used correctly
  3. Change the value of the azurerm_key_vault_secret
  4. Run terraform apply

Expected: The dependency on the azurerm_key_vault_secret updates the usage of the .id property such that dependencies on the secret in terraform are propagated and updated in their usages
Actual: The value is updated in terraform, but the dependencies on the .id value of the azurerm_key_vault_secret do not reflect the new ID created in Azure.

Workaround: Run terraform apply a third time, and the ID will get updated.

lonegunmanb commented 2 years ago

Hello @kensykora ,

It's hard to say that it is a Terraform or AzureRM Provider's bug. This behavior is by design and I think it's very hard for HashiCorp to change that. Terraform workflow have two different stages: Plan and Apply. If you change value of azurerm_key_vault_secret, it'll cause a re-creation and id will change, but in Plan stage Terraform cannot be aware that, so the execution plan it generated won't realize that the content in your azurerm_function_app will changed. As a rather easy sample as below:

data "azurerm_client_config" "current" {}

resource "azurerm_resource_group" "example" {
  name     = "example"
  location = "East Asia"
}

resource "azurerm_key_vault" "example" {
  name                       = "example"
  location                   = azurerm_resource_group.example.location
  resource_group_name        = azurerm_resource_group.example.name
  tenant_id                  = data.azurerm_client_config.current.tenant_id
  sku_name                   = "standard"
  soft_delete_retention_days = 7

  access_policy {
    tenant_id = data.azurerm_client_config.current.tenant_id
    object_id = data.azurerm_client_config.current.client_id

    key_permissions = [
      "create",
      "get",
    ]

    secret_permissions = [
      "set",
      "get",
      "delete",
      "purge",
      "recover"
    ]
  }
}

resource "azurerm_key_vault_secret" "example" {
  name         = "secret-sauce"
  value        = "szechuan"
  key_vault_id = azurerm_key_vault.example.id
}

resource "local_file" "output" {
  filename = "${path.module}/output.txt"
  content = azurerm_key_vault_secret.example.id
}

In this case, if you change value in azurerm_key_vault_secret, output file's content won't change until next time you run apply.

But, there's a walkaround:

data "azurerm_client_config" "current" {}

resource "azurerm_resource_group" "example" {
  name     = "example"
  location = "East Asia"
}

resource "azurerm_key_vault" "example" {
  name                       = "example"
  location                   = azurerm_resource_group.example.location
  resource_group_name        = azurerm_resource_group.example.name
  tenant_id                  = data.azurerm_client_config.current.tenant_id
  sku_name                   = "standard"
  soft_delete_retention_days = 7

  access_policy {
    tenant_id = data.azurerm_client_config.current.tenant_id
    object_id = data.azurerm_client_config.current.client_id

    key_permissions = [
      "create",
      "get",
    ]

    secret_permissions = [
      "set",
      "get",
      "delete",
      "purge",
      "recover"
    ]
  }
}

resource "azurerm_key_vault_secret" "example" {
  name         = "secret-sauce"
  value        = "szechuan"
  key_vault_id = azurerm_key_vault.example.id
}

resource "null_resource" "example" {
  triggers = {
    trigger = azurerm_key_vault_secret.example.value
  }
}

resource "local_file" "output" {
  filename = "${path.module}/output.txt"
  content = null_resource.example.id != "" ? azurerm_key_vault_secret.example.id : ""
}

We can leverage null_resource, especially triggers to "notify" downstream that the id as been changed. In this case, you will see an immediate forces replacement on local-file.

kensykora commented 2 years ago

but in Plan stage Terraform cannot be aware that

@lonegunmanb Thanks for the reply -- I'm confused, why can't the azurerm_key_vault_secret resource type be aware that if the value changes that a new version id will be created? Seems like that is a rule that can be coded in.

lonegunmanb commented 2 years ago

Hi @kensykora , I think that could be related to how Terraform calculate plan that involved a force new resource's attribute. For now we can use this workaround trick, but we can check if there's issue on Terraform core project. Actually, we can use local_file resource to easily reproduce this issue on our own machine, maybe we can submit an issue along with a minimum sample code there.

kensykora commented 2 years ago

Hey @lonegunmanb -- I've seen other resources behave differently when they know their upstream dependencies are going to change. For example you see (known after apply) in the terraform output for resources with auto generated properties such as identifiers.

Are you saying it's not possible at all for the plugin to mark the version property of azurerm_key_vault_secret (not the resource id terraform uses!) to be marked as (known after apply) so downstream services that depend on the version to be correct can be correct when it's applied?

lonegunmanb commented 2 years ago

Hi @kensykora , I think the problem we are facing here is, when you change azurerm_key_vault_secret.value, it actually changed the resource's id, that means it created a new resource instance, and I think the other resource which referenced this secret's attribute actually referenced the old instance and it saw nothing changed at all. I've added some extra log in provider's code, when you changed value, the id returned by api changed, it was a new id with new version. I think it's a Terraform core's issue and this plugin has nothing to do about that.

orgads commented 9 months ago

Hi @lonegunmanb.

I also faced this issue, and in my case it also causes inconsistent plan issue, when importing a key and changing its value in a single apply.

Is there an issue to track on terraform core?

orgads commented 8 months ago

Reported https://github.com/hashicorp/terraform-plugin-sdk/issues/1267