忽略不想要的 Terraform provider attribute changes

2025年3月23日 · 阅读约需 3 分钟 · (562 字)

问题所在!

我偶尔会遇到一些 Terraform provider,它们会以一种不太理想的方式来处理某个属性。

这意味着,后续的运行会发现该属性发生了变更(与传递的属性相比),并希望修改它。

举个例子!…

Docker Terraform provider (由 kreuzwerker 提供:D https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs) 会将 “image” 属性修改为镜像的 SHA 摘要…

这意味着,如果我创建:

resource "docker_container" "my_important_container" {
  ...
  image = "base-image-for-my-important-container:v2.20.2"
  ...
}

terraform refresh 之后,image 属性将被读取为 SHA 摘要。 这意味着下次运行 Terraform 时,它将希望重新创建容器,将镜像从 SHA 摘要更改为原始命名镜像。

对于某些资源来说,这可能没问题,也许是一个原地更新,什么都不会改变……但在这种情况下,它会强制重新创建,对于容器来说,这意味着中断。(是的,是的,这是一种非常糟糕的部署容器的方式……但这仅用于 Homelab,嘘!……看看我当时在处理什么 (https://github.com/MatthewJohn/vault-nomad-consul-terraform),然后再抱怨!)。

无知是福

当然,大多数人实现这一目标的方式是简单地忽略对此属性的更改:

resource "docker_container" "my_important_container" {
 ...
 image = "base-image-for-my-important-container:v2.20.2"
 ...
 lifecycle {
  ignore_changes = [image]
 }
}

当然,这会起作用……至少可以阻止资源的重新创建并让 Terraform 闭嘴……

但假设我做了如下更改:

diff
-  image = "base-image-for-my-important-container:v2.20.2"
+  image = "base-image-for-my-important-container:v2.20.3"

好吧,当然,一切都乱套了……我已经进行了更改以部署到新版本,并且我运行了 Terraform,它显示一切良好!……但事实并非如此,我们告诉它忽略它,它也确实这样做了。只是,我们在 3 年后忘记了这一点,我们的流水线显示绿色,表明 Terraform 已经计划/应用,所以一切都很好。

倾听被忽略的

我喜欢使用的一个小技巧是简单地创建一个 null_resource,由我们真正关心的属性值触发,并根据此触发器配置替换。

例如:

locals {
 image = "base-image-for-my-important-container:v2.20.2"
}# Handle changes to image, which are ignored by the container resource
resource "null_resource" "container_image" {
 triggers = {
  image = local.image
 }
}
resource "docker_container" "my_important_container" {
 ...
 
 image = local.image
 ...
 lifecycle {
  ignore_changes = [image]  # When container image name changes
  replace_triggered_by = [
   null_resource.container_image,
  ]
 }
}

瞧……只要镜像名称没有更改,触发器就不会被重建。 docker_container 愉快地使用一些垃圾/不可能真正验证的/我永远不会传入的 SHA 摘要,Terraform 正在忽略它。 一旦镜像发生变化,虽然 docker_container 资源忽略了属性更改,但 null_resource 肯定不会,并且该重新创建随后会触发容器重新创建。

改进?

老实说,也许 docker_containerimage 属性可以设置为 null_resource.container_image.triggers.image,但我总是将镜像作为变量传入,这减少了我的重复。

话虽如此,我喜欢使用所有权所在的属性……我不认为 null_resource 是镜像的“提供者”,而更多的是一个额外的使用者。 所以也许一个 local 变量就足够了。

而且,老实说,我应该创建一个 PR 给 provider,以处理将镜像名称解析为 SHA 摘要,然后在强制重新创建之前进行比较……但是,嘿,有时我们没有时间修复所有小事情!