【问题标题】:Terraform null_resource not running with backendTerraform null_resource 未与后端一起运行
【发布时间】:2021-12-07 14:32:02
【问题描述】:

我有一个 terraform 对象的变量映射,如下所示

variable "MyProj" {
  type = map(object({
  name     = string
  type     = string
 }))
default = {
  "Proj1" = {
      name      = "Proj1"
      programme = "java"
   },
   "Proj2" = {
      name       = "Proj2"
      programme  = "npm"
  }
 }
}

我还有一个空资源代码,只要根据上面的 map(objects()) 和所需状态识别出更改,它就会运行一个批处理脚本。

   resource "null_resource" "nullr" {
   for_each = var.MyProj

   provisioner "local-exec" {
   command = "bash /home/myscript.sh ${each.value.name} ${each.value.progrmme}
  }
}

我的意图是,只要在上面的 map() 中识别出更改,则应该运行 null 资源。

当我在本地机器中保持 terraform 状态时,这会按预期运行。但是,当我使用 azurem 后端将 terraform 状态保存在 Azure blob 容器中时,它并没有运行 null_resource。 即使我将后端用于 terraform 状态,我应该在配置中做哪些更改

【问题讨论】:

  • 您当然需要指定triggers 属性。否则 null_resource 永远不会再次运行。
  • @luk2302 谢谢。但是当我将状态保存在本地时,它运行良好。唯一的问题是当我将状态保留在 azurerm 后端时。我不想总是运行 null_resource 。由于我使用的是 for_each ,因此它的运行方式将根据以下链接 stackoverflow.com/questions/69349022/… 有所不同

标签: azure terraform


【解决方案1】:

你快到了 - 你有一个脚本、一个空资源和一个定义明确的更改,你想触发空资源。

首先,构建您的触发字符串。如果您希望它特别大,md5 函数会有所帮助,但让它易于阅读,调试起来会更容易:

locals {
  script_trigger = join(", ", 
    [ for projKey, ProjObj in var.MyProj
      : "${projKey}(${join(", ", 
        [for key, value in projObj
          : "${key}=${value}"])})" 
    ]
  )
}

然后,将该字符串添加到您的触发器中:

resource "null_resource" "nullr" {
  ...
  triggers = { project_object = locals.script_trigger }
}

【讨论】:

  • 谢谢@Dan。但我在 locals.tf 第 3 行遇到错误:预期用逗号标记下一项的开头。
  • 已修复。开始写python风格的for语句,抱歉。
  • 谢谢@Dan。但是这种方法的问题是,即使我们当时从地图中删除 Proj2,它也会对 Proj1 运行配置,如下面的链接 stackoverflow.com/questions/69349022/… 中所述。
【解决方案2】:

我想从这里开始,似乎您在这里所做的事情可能超出了 Terraform 的典型范围:通常像 npm 这样的工具属于构建的“构建”步骤/deploy 管道,而 Terraform 并不是真正打算或设计用于处理该系列的用例。由于它是一种通用的可编程工具,如果您有创意,您当然可以让它做超出其范围的事情,但它通常带有警告和限制。

我将尝试在您编写问题时回答您的问题,但我还建议您更哲学地思考是否最好使用在 Terraform 之前运行的单独进程来解决打算解决的任何问题一个管道,或者如果整个问题可以通过其他一些明确设计用于支持构建和部署流程的系统来更好地解决。


null_resource 资源类型是 Terraform 中的一种特殊“逃生舱”,它故意不做任何事情,以便您可以将本来不属于资源的供应商附加到它。供应商的预期目的(尽管as a last resort)是实现特定对象需要执行的额外步骤才能完全运行,例如将一些需要的数据写入虚拟机的文件系统。

普通资源类型具有由远程系统定义的关于可以就地应用哪些类型的更改以及哪些类型的更改需要重新创建对象的固有规则。因为null_resource 不代表特定的远程对象类型,所以它没有任何这样的固有 规则,但它确实具有更人为的triggers 参数概念,除了强制替换对象之外什么都不做每当其值与上一次运行不同时。

为了强制替换您的 null_resource 并因此强制其关联的配置程序重新运行,则需要使用通常保持不变的值填充 triggers 参数,但如果更改,应该会导致供应商重新运行。

在您的情况下,var.MyProj 中的值似乎可以用作触发器,但因为triggers 是字符串映射,我们需要先将其编码为字符串。在这种情况下,JSON 编码可能是一个合理的答案:

resource "null_resource" "nullr" {
  for_each = var.MyProj

  triggers = {
    settings = jsonencode(each.value)
  }

  provisioner "local-exec" {
    command = "bash /home/myscript.sh ${each.value.name} ${each.value.progrmme}
  }
}

由于每个资源实例的triggers 仅指当前each.value,因此向var.MyProj 添加新条目不应影响任何现有资源实例,因此不会重新运行任何供应商。但是,如果您编辑现有密钥之一,那么它将使用新设置重新运行。

如果您希望重新运行特定资源的配置程序,即使它的名称或“progrmme”都没有改变,那么您可能希望将第三个属性添加到 var.MyProj 的元素类型,它是一个整数或一些每次需要重新运行时都会更改的特殊字符串标识符。然后,您可以更改该属性的值以强制它重新运行,即使它将重新运行与以前相同的程序。

要考虑的另一种情况是,如果 myscript.sh 的内容发生变化会发生什么情况。如果您想在每次脚本更改时重新运行脚本,则可以添加另一个 triggers 条目来捕获文件的校验和,因此每次文件更改时都会更改:

resource "null_resource" "nullr" {
  for_each = var.MyProj

  triggers = {
    settings        = jsonencode(each.value)
    script_checksum = filesha256("/home/myscript.sh")
  }

  provisioner "local-exec" {
    command = "bash /home/myscript.sh ${each.value.name} ${each.value.progrmme}
  }
}

【讨论】:

  • 谢谢@Martin。但是当我使用本地 tfstate 文件时,我的逻辑运行良好,而当我为 tfstate 使用 azurerm 后端时出错。在使用远程状态文件时,您是否知道任何此类限制?
  • 我的实际需求贴在下面的链接stackoverflow.com/questions/69682098/…你能帮忙吗?
猜你喜欢
  • 2020-03-22
  • 2021-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-21
相关资源
最近更新 更多