Terraform中无ingress时Dynamic block渲染报错的原因及解决
问题:Terraform动态Ingress块报「Cannot use a null value in for_each」错误
在使用Terraform定义AWS安全组资源时,尝试通过dynamic "ingress"块根据var.dbs中的ingress配置生成规则。当security_group字段存在但未定义ingress子字段时,期望该动态块不生成任何规则,但实际触发了「Cannot use a null value in for_each」报错,设置的默认值{}未生效。
资源代码
resource "aws_security_group" "db-sgs" { for_each = var.dbs name = each.value.security_group.name vpc_id = "test" dynamic "ingress" { for_each = can(each.value.security_group) ? each.value.security_group.ingress : {} content { description = ingress.value.description from_port = 5432 to_port = 5432 protocol = "tcp" security_groups = [ingress.value.source_sg] } } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }
测试配置
报错场景(配置1)
dbs: test: ... security_group: name: test
正常场景(配置2)
dbs: test: ... security_group: name: test ingress: test: source_sg: sg-12df601c description: Test Access
问题原因
can(each.value.security_group)仅判断security_group字段是否存在,当security_group存在但未定义ingress时,each.value.security_group.ingress的值为null而非不存在。此时can()会返回true,导致for_each使用null值,违反了Terraform要求for_each必须是集合类型(列表、映射、集合)的规则,从而触发报错。
解决方法
方案1:使用lookup函数指定默认值
lookup函数可直接从映射中取值,若目标字段不存在则返回指定默认值,完美适配当前场景:
dynamic "ingress" { for_each = lookup(each.value.security_group, "ingress", {}) content { description = ingress.value.description from_port = 5432 to_port = 5432 protocol = "tcp" security_groups = [ingress.value.source_sg] } }
方案2:增加null值判断
在原逻辑基础上,补充判断ingress是否为null,确保返回的是合法的集合类型:
dynamic "ingress" { for_each = can(each.value.security_group.ingress) && each.value.security_group.ingress != null ? each.value.security_group.ingress : {} content { description = ingress.value.description from_port = 5432 to_port = 5432 protocol = "tcp" security_groups = [ingress.value.source_sg] } }
修改后的完整资源代码
以方案1为例,修改后的代码如下:
resource "aws_security_group" "db-sgs" { for_each = var.dbs name = each.value.security_group.name vpc_id = "test" dynamic "ingress" { for_each = lookup(each.value.security_group, "ingress", {}) content { description = ingress.value.description from_port = 5432 to_port = 5432 protocol = "tcp" security_groups = [ingress.value.source_sg] } } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }
内容的提问来源于stack exchange,提问作者Murakami
相关产品推荐
相关产品推荐

