Terraform:处理由于特性标志而成为条件的局部变量



我正在构建一个Terraform模块,该模块使用一些变量作为特征标志,并使用局部变量存储一些计算值。当一个标志为真时,我遇到了一些错误。

标志(作为变量保存的布尔值)在每个资源上,并使用这个约定,这似乎是Terraform的标准:

resource "provider_resource_id" "resource_name" {
...
count = var.disable_resource ? 0 : 1
...
}

提供程序在制作这些资源时输出id,因为count强迫我在它们上添加索引,所以我将它们作为局部变量保存在局部变量中。简化Tf文件:

locals {
resource_name_sid = provider_resource_id.resource_name[0].sid
}

我现在运行terraform apply时,disable_resource = true和得到这个错误:Invalid index: provider_resource_id.resource_name[0].sid (provider_resource_id.resource_name is empty tuple)。我发现在没有创建资源时定义本地是一个问题。我把局部注释掉了。现在我在所有期望本地的资源上得到其他错误:Reference to undeclared local value: (resource_name_sid has not been declared)由于标志,这些资源实际上不会被构建,但它们仍然期望本地(我无法定义,因为资源没有被构建)。

我敢打赌我可以在每个局部都放一个三进制,例如:

locals {
resource_name_sid = var.disable_resource ? "" : provider_resource_id.resource_name[0].sid
}

但是这又变得啰嗦了。也许我不能在外部化这些局部变量的同时使用特性标志。(我确实尝试过将本地文件移动到资源文件中,但得到了相同的结果。)也许我需要放弃使用局部变量来存储它们,而只是将它们内联地放在资源中。还是我遗漏了什么?

没有办法避免向Terraform解释在对象不存在的情况下应该发生什么,但是有一些更简短的方法来表达当资源的零实例时使用回退值作为占位符的想法。


一个简洁的选择是使用one,这是一个函数,用于处理将0或1个元素的列表转换为可能为null的值的常见情况:

locals {
resource_name_sid = one(provider_resource_id.resource_name[*].sid)
}

provider_resource_id.resource_name[*].sid生成一个长度匹配provider_resource_id.resource_name计数的列表。在您的配置中,计数只能为0或1,这符合one的期望。

因此local.resource_name_sid要么是一个单独的sid值,要么是null


另一种可能性是使用try让元素查找[0]失败,并提供一个回退值,如果失败则使用:

locals {
resource_name_sid = try(provider_resource_id.resource_name[0].sid, null)
}

这个选项可以让你选择一个不同的回退值来代替null,如果你喜欢的话,尽管null是在Terraform中表示值缺失的典型方式,所以我建议使用它,除非你有一些其他工作SID值用作回退。

使用null的好处是,您可以将local.resource_name_sid直接分配给另一个资源的参数,然后在它的null的情况下,它将完全无法区分提供者完全省略了该参数,因为null也表示没有参数。


最后一个选项是直接测试provider_resource_id.resource_name的长度,看是否有第零索引:

locals {
resource_name_sid = (
length(provider_resource_id.resource_name) > 0 ?
provider_resource_id.resource_name[0].sid :
null
}

这类似于你的问题中包含的条件,但它直接测试是否有provider_resource_id.resource_name[0],而不是重复对var.disable_resource的引用。

直接测试资源意味着,如果您将来更改count定义,那么您将不需要更新此表达式,只要您的新count表达式仍然在0或1元素之间进行选择。

然而,这是最冗长的选项,需要在两个地方重复长表达式provider_resource_id.resource_name,所以如果我需要一个非空的回退值,我通常使用上面的try选项,如果null是一个足够的回退值,我通常使用one选项。

one函数也具有优于其他函数的优点,如果有多个provider_resource_id.resource_name实例,它将失败,因此,如果您更新此模块以在将来拥有该资源的多个实例,那么您将被错误提醒更新其他表达式以处理两个或更多SID值。其他表达式将静默地忽略其他sid。

不幸的是,目前还没有更好的方法来定义它。您可以在大多数流行的模块(例如https://github.com/terraform-aws-modules/terraform-aws-vpc/blob/master/main.tf)中随处看到对value的索引引用。

甚至我也希望当我们想要有条件地创建资源(特性标记)时,有一种更简单的方法来处理这个问题。

单次使用count将会级联其他的一切。因此,您还必须检查代码中其他地方的var.disable_resource == true条件。这包括locals,您可以这样写:

locals {
resource_name_sid = var.disable_resource ? null : provider_resource_id.resource_name[0].sid
}

这将成功跳过resource_name_sid的创建。但是很明显,现在您必须在任何其他将使用resource_name_sid的地方继续使用该条件。

最新更新