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

如何在Terraform中动态创建安全组Ingress规则?

问题:如何基于Terraform变量添加安全组额外Ingress规则?

我正在创建一个包含标准Ingress规则的安全组,同时希望基于变量添加额外的Ingress规则。以下是我的Terraform配置代码:

variable "additional_ingress" {
  type = list(object({
    protocol    = string
    from_port   = string
    to_port     = string
    cidr_blocks = list(string)
  }))
  default = []
}


resource "aws_security_group" "ec2" {
  name        = "my-sg"
  description = "SG for ec2"
  vpc_id      = data.aws_vpc.this.id

  egress {
    to_port     = 0
    from_port   = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    protocol    = "tcp"
    from_port   = 22
    to_port     = 22
    cidr_blocks = ["10.0.0.0/8"]
  } 

  # rdp
  ingress {
    protocol    = "tcp"
    from_port   = 3389
    to_port     = 3389
    cidr_blocks = ["10.0.0.0/8"]
  }

  # additional ingress rules
  ingress {
    for_each    = var.additional_ingress
    protocol    = each.value.protocol
    from_port   = each.value.from_port
    to_port     = each.value.to_port
    cidr_blocks = each.value.cidr_blocks
  }
}

运行时出现如下错误:

A reference to "each.value" has been used in a context in which it unavailable, such as when the configuration no longer contains the value in its "for_each" expression. │ Remove this reference to each.value in your configuration to work around this error.


解决方案

错误原因

你遇到的问题是因为不能直接在单个ingress嵌套块中使用for_each,Terraform要求批量生成嵌套块(如安全组的Ingress规则)时,必须使用dynamic块来循环创建多个独立的ingress块。

修改后的完整配置代码

variable "additional_ingress" {
  type = list(object({
    protocol    = string
    # 建议把端口类型改为number,匹配AWS端口的数值类型,避免类型转换问题
    from_port   = number
    to_port     = number
    cidr_blocks = list(string)
  }))
  default = []
}


resource "aws_security_group" "ec2" {
  name        = "my-sg"
  description = "SG for ec2"
  vpc_id      = data.aws_vpc.this.id

  egress {
    to_port     = 0
    from_port   = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # 标准SSH规则
  ingress {
    protocol    = "tcp"
    from_port   = 22
    to_port     = 22
    cidr_blocks = ["10.0.0.0/8"]
  } 

  # 标准RDP规则
  ingress {
    protocol    = "tcp"
    from_port   = 3389
    to_port     = 3389
    cidr_blocks = ["10.0.0.0/8"]
  }

  # 动态生成额外Ingress规则
  dynamic "ingress" {
    # 将列表转为map,用规则的唯一标识作为键,避免列表顺序变动导致资源重建
    for_each = { for idx, rule in var.additional_ingress : 
                "${rule.protocol}-${rule.from_port}-${rule.to_port}-${idx}" => rule }
    content {
      protocol    = ingress.value.protocol
      from_port   = ingress.value.from_port
      to_port     = ingress.value.to_port
      cidr_blocks = ingress.value.cidr_blocks
    }
  }
}

关键修改点说明

  1. 使用dynamic "ingress"块:这是Terraform批量创建嵌套块的标准方式,通过循环生成多个独立的ingress规则。
  2. 优化for_each的键:将列表转换为map时,用协议-起始端口-结束端口-索引作为唯一键,避免因列表元素顺序变化导致安全组规则被重建(如果规则有唯一名称字段,也可以用名称作为键)。
  3. 端口类型修正:把变量中的from_port和to_port从string改为number,匹配AWS安全组端口的数值类型,消除潜在的类型转换错误。

内容的提问来源于stack exchange,提问作者LP13

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 15:50:26