Terraform null_resource未与后端运行



我有一个对象的地形变量图,如下所示

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

我还有一个空资源代码,每当根据上面的映射(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资源。

当我在本地机器中保持地形状态时,它按预期运行。但是,当我使用Azure后端将地形状态保存在Azure blob容器中时,它不会运行null_resource。如果我为地形状态使用后端,我应该在配置中做什么更改

我想从这里开始说,它似乎你在这里做的事情可能超出了Terraform的典型范围:通常像npm这样的工具属于"构建";构建/部署管道的第一步,而Terraform并不是真正打算或设计来处理这一系列用例。因为它是一个通用的可编程工具,如果你有创意,你当然可以让它做一些超出它范围的事情,但它通常带有警告和限制。

我将尝试在你写的时候回答你的问题,但我也建议更哲学地思考一下,使用在管道中Terraform之前运行的单独进程来解决这个问题是否会更好,或者整个问题是否会被其他一些明确设计用于支持构建和部署进程的系统更好地解决。


null_resource资源类型是一个特殊的"逃生口";在Terraform中,故意不做任何事情,以便您可以将原本不属于资源的提供程序附加到它。提供程序的预期目的(尽管是最后的手段)是实现特定对象为了完全可操作而需要执行的额外步骤,例如将一些所需的数据写入虚拟机的文件系统。

正常资源类型具有由远程系统定义的固有规则,这些规则规定了哪些类型的更改可以就地应用,哪些类型的更改需要重新创建对象。因为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添加新条目不会影响资源的任何现有实例,因此不会重新运行任何提供程序。但是,如果您编辑了一个现有的键,那么它将使用新的设置重新运行。

如果您期望需要重新运行特定资源的提供程序,即使它的名称或"程序"更改之后,您可能希望向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}
}
}

您几乎已经完成了—您有一个脚本,一个空资源,以及一个您想要触发空资源的定义良好的更改。

首先,构建触发器字符串。如果您希望它特别大,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 }
}

最新更新