1.8.7.1. 利用 null_resource 搭配 replace_triggered_by 更新无法从服务端读取内容的属性

曾经处理的一个提问,有人写了这样一段 Terraform 代码:

resource "azurerm_container_group" "this" {
  name                = var.name
  location            = var.location
  resource_group_name = var.resource_group_name
  ip_address_type     = "Private"
  network_profile_id  = azurerm_network_profile.this.id
  os_type             = "Linux"

  container {
    name   = "someName"
    image  = "someImage"
    cpu    = "0.5"
    memory = "0.5"

    commands = ["some", "commands"]

    ports {
      port     = 53
      protocol = "UDP"
    }

    volume {
      mount_path = "/app/conf"
      name       = "someName"
      read_only  = true
      secret = {
        Corefile = base64encode(someContent)
      }
    }

  }

  tags = var.tags
}

结果每次执行 apply 操作时,都会发现 Terraform 试图重建这个容器:

# module.dns_forwarder.azurerm_container_group.this must be replaced
-/+ resource "azurerm_container_group" "this" {
      ~ exposed_port        = [
          - {
              - port     = 53
              - protocol = "UDP"
            },
        ] -> (known after apply)
      + fqdn                = (known after apply)
      ~ id                  = "/subscriptions/<mySubId>/resourceGroups/<myRgName>/providers/Microsoft.ContainerInstance/containerGroups/<myContainerGroupName>" -> (known after apply)
      ~ ip_address          = "someIp" -> (known after apply)
        name                = "someName"
      - tags                = {} -> null
        # (6 unchanged attributes hidden)

      ~ container {
          - environment_variables        = {} -> null
            name                         = "someName"
          - secure_environment_variables = (sensitive value)
            # (4 unchanged attributes hidden)


          ~ volume {
                name       = "someName"
              ~ secret     = (sensitive value) # forces replacement
                # (3 unchanged attributes hidden)
            }
            # (1 unchanged block hidden)
        }
    }

这个问题的原因是 API 在读取容器信息时不会返回 volumesecret 数据,这其实是一个还挺合理的设定,机密数据的确不应该可以直接从 API 返回,但这就导致 Terraform 每次制定变更计划时都会试图重新设置这个值(因为会理解成服务端这个值被修改成了空),而容器是不可变的,要修改容器的任何配置都会导致容器被重建。

有没有办法能够避免这种问题?经验告诉我们,可以使用 ignore_changes 让 Terraform 忽略这个属性的变更来避免重建,但如果 secret 真的变了怎么办?

我们可以这样干,第一,在 azurerm_container_group 添加这样一段 lifecycle 块:

lifecycle {
    ignore_changes       = [container[0].volume[0].secret]
    replace_triggered_by = [null_resource.secret_trigger.id]
}

这会忽略 secret 的变化,但我们同时声明了一个 replace_triggered_by,在 null_resource.secret_trigger.id 的值发生变化时可以删除重建 azurerm_container_group 实例。

其次,我们把 secret 的内容提取到一个 local 里,这时 azurerm_container_groupvolume 看起来大概是这样的:

volume {
    mount_path = "/app/conf"
    name       = "somename"
    read_only  = true
    secret     = {
      Corefile = local.secret
    }
}

local.secret 存放着使用的机密数据。这时我们再定义一个 null_resource 充当触发器:

locals {
  secret = base64encode("abcdefg")
}

resource "null_resource" "secret_trigger" {
  triggers = {
    trigger = local.secret
  }
}

这样在机密数据真的发生变化的时候,triggers 会触发 null_resource 的重建,导致 null_resource.secret_trigger.id 发生变化,进而触发 azurerm_container_group 的重建。

results matching ""

    No results matching ""