You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 09:53:10