如何让 Terraform 用默认值替换空值?

Posted

技术标签:

【中文标题】如何让 Terraform 用默认值替换空值?【英文标题】:How can I make Terraform replace a null value with a default value? 【发布时间】:2021-11-08 21:24:12 【问题描述】:

Terraform 文档表明这应该已经发生了:

https://www.terraform.io/docs/language/expressions/types.html

null:代表缺失或遗漏的值。如果您将资源或模块的参数设置为 null,Terraform 的行为就好像您完全省略了它一样 - 如果参数有默认值,它将使用该参数的默认值,如果该参数是强制性的,则会引发错误。

我正在调用具有以下变量文件的模块“foo”:

variable "bar" 
  type    = string
  default = "HelloWorld"

示例 1

当我使用此代码调用它时:

module "foo" 
  source = "../modules/foo"
  bar = null

结果是一个错误。 “str”参数的值无效:参数不能为空。使用 bar 时触发。

示例 2

当我使用这段代码调用它时(省略它,而不是清空它):

module "foo" 
  source = "../modules/foo"
  # bar = null

结果是它起作用了。 “bar”变量默认为“HelloWorld”。

这似乎是 Terraform 中的一个错误,其他人也提出了但未解决。 https://github.com/hashicorp/terraform/issues/27730

有人知道解决方案或变通方法吗?

版本信息:

Terraform v1.0.5
on linux_amd64
+ provider registry.terraform.io/hashicorp/google v3.51.0
+ provider registry.terraform.io/hashicorp/null v3.1.0
+ provider registry.terraform.io/hashicorp/random v3.1.0
+ provider registry.terraform.io/hashicorp/time v0.7.2

解决方法

根据@Matt Schuchard 的评论和一些研究,有一个使用条件检查的丑陋解决方案:

variable "foo" 
  type    = string
  default = "HelloWorld"

locals 
  foo = var.foo == null ? "HelloWorld" : var.foo

为什么

我的用例是为了避免重复代码。我有 2 个非常相似的模块,一个是另一个的子集。我正在使用的解决方案是将模块按顺序调用每个模块,即祖父母、父母和孩子。

我希望“祖父母”可以使用这些变量,但如果它们被省略,那么“子”下面的模块应该使用默认值设置它们,例如“你好世界”。但是要在整个家族中一直暴露这些变量,我必须将它们包含在所有模块和高级模块(祖父母和父母)中,我想将它们默认为空,允许它们是可选的,但仍然导致它们成为在“child”中设置为默认值。

...我想我需要一张图表。

【问题讨论】:

您是否阅读过原始链接中链接的github.com/hashicorp/terraform/issues/24142?基本上,该错误在文档中,目前不应该说or module。如果您不想更改模块的默认变量而是使用它,则不要指定模块参数。如果有一个用例您需要这样做并且仅有条件地将其设置为默认值以外的任何内容,那么可能值得展示您的用例,最好是minimal reproducible example。 @ydaetskcoR 是否存在与提供者资源/数据可选参数(即<conditional> ? <default_override> : null 三元)等效的模块声明设计模式?假设null 不是模块参数可接受类型的根本原因是否安全是由于object 可选参数和模块变量声明中的default 函数都与实验性语言功能相关联,直到1.0 .x? 据我所知没有。 null 是让它工作的明显方法,但它目前在 Terraform 中不起作用,所以你不能使用它。我没有有条件地设置模块参数的用例(它闻起来有点你在 for_each/count 循环中配置一些东西来做一些完全不同的事情,在这种情况下,它可能无论如何都想分离爆炸半径)但如果我确实有该用例并控制模块源,然后我会退回到旧的 pre-null 天并使用空字符串并有条件地检查它。 感谢您指出链接@ydaetskcor 我错过了那个。看起来这个问题仍然存在,他们想修复它,但它现在已成为预期行为,他们担心它会破坏现有代码。我也试图解释这个要求。用例是为了避免重复代码,所以也许有一个更简单的解决方案。 此 PR 将解决此问题 github.com/hashicorp/terraform/pull/29832 发布 terraform 1.1.0 【参考方案1】:

您可以将默认值设置为null,并通过coalesce 函数在local 中设置真正的默认值

variable "foo" 
  type    = string
  default = null


locals 
  # return var.foo if it's not null
  # return "HelloWorld" if var.foo is null
  foo = coalesce(var.foo, "HelloWorld")


output "foo" 
  value = local.foo

使用null的默认值返回HelloWorld

$ terraform apply -auto-approve

...

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

foo = "HelloWorld"

使用新值会否定 local 中的默认设置

$ terraform apply -auto-approve -var="foo=HelloUniverse"

...

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

foo = "HelloUniverse"

【讨论】:

coalesce() 不是一个完美的解决方案,discard empty string arguments 也是如此。 @GregHensley 您可以在输入变量上设置验证以允许 null 或非空字符串【参考方案2】:

从 Terraform 1.1.0 开始,variable 声明现在支持 nullable argument。它默认为true 以保留现有行为。但是,任何带有nullable=false 且未指定或设置为null 的变量都将被分配默认值。

main.tf

variable "nullable" 
  type    = string
  default = "Used default value"


output "nullable" 
  value = coalesce(var.nullable, "[null]")


variable "non_nullable" 
  type     = string
  default  = "Used default value"
  nullable = false


output "non_nullable" 
  value = coalesce(var.non_nullable, "[null]")

terraform.tfvars

nullable     = null
non_nullable = null

注意在 输出 块中使用coalesce。 Terraform 会忽略任何设置为 null 的输出,因此这可确保任何 null 值仍会在输出中显示某些内容。

应用此配置后,我们可以通过运行terraform output 看到,当nullable=true(默认值)变量保持显式设置的null 值但使用nullable=false 时,任何null 值都会被忽略以支持default

# terraform output
non_nullable = "Used default value"
nullable = "[null]"

【讨论】:

以上是关于如何让 Terraform 用默认值替换空值?的主要内容,如果未能解决你的问题,请参考以下文章

combobox默认值为第一个数据,修改为空值

Access 2010 - 需要用值替换空值

WPF——TargetNullValue(如何在绑定空值显示默认字符)

如何在 DataFrame 中用空值替换数字?

数据框用唯一的纪元时间替换每一行空值

如何在 BigQuery 中将多个列的空值替换为 0?