具有动态内容的 Terraform 动态块

Terrafrom dynamic block with dynamic content

我正在尝试为 aws_route_table 创建一个 terraform 模块,这里是这个资源定义的一个例子:

resource "aws_route_table" "example" {
  vpc_id = aws_vpc.example.id

  route {
    cidr_block = "10.0.1.0/24"
    gateway_id = aws_internet_gateway.example.id
  }

  route {
    ipv6_cidr_block        = "::/0"
    egress_only_gateway_id = aws_egress_only_internet_gateway.example.id
  }

  tags = {
    Name = "example"
  }
}

我正在尝试通过使用动态块使其更具动态性。问题是我总是必须在内容块

中定义键
resource "aws_route_table" "example" {
...

   dynamic "route" {
        for_each = var.route
        content {
            cidr_block  = route.value.cidr_block  
            gateway_id  = route.value.gateway_id  
        }
    }

...
}

所以在这种情况下,我需要写两个动态块,一个用于 cidr_blockgateway_id 的内容,一个用于 ipv6_cidr_block 和 [=16 的内容=].

有没有办法在不显式定义键的情况下做到这一点。像这样:

   dynamic "route" {
        for_each = var.route
        content {
          var.route.map
        }
    }

是的,您可以动态创建 route,因为块 route 充当 Attributes as Blocks。所以你可以做(​​例子)

# define all your routes in a variable (example content)

variable "routes" {

  default = [
    {
      cidr_block = "0.0.0.0/0"
      gateway_id = "igw-0377483faa64bf010"
    },
    {
      cidr_block = "172.31.0.0/20"
      instance_id = "i-043fc97db72ad1b59"     
    }
  ]
}


# need to provide default values (null) for all possibilities
# in route

locals {

  routes_helper = [
      for route in var.routes: merge({
          carrier_gateway_id = null
          destination_prefix_list_id = null
          egress_only_gateway_id = null 
          ipv6_cidr_block = null 
          local_gateway_id = null
          nat_gateway_id = null
          network_interface_id = null
          transit_gateway_id = null 
          vpc_endpoint_id = null 
          instance_id = null
          gateway_id = null
          vpc_peering_connection_id = null
      }, route)
    ]

}


resource "aws_route_table" "example" {
  vpc_id = aws_vpc.example.id

  # route can be attribute, instead of blocks
  route = local.routes_helper

  tags = {
    Name = "example"
  }
}

文档通常不建议使用它,但我认为路线是一个很好的例子,它可以接受。

Marcin 提到的使用“属性作为块”向后兼容垫片的块类型的一般答案是依赖于 Terraform 提供程序参数的事实省略参数或将其显式设置为 null.

在含义上没有区别

这意味着您可以编写一个表达式来决定是否填充特定参数,方法是在适当的情况下将其 return 显式 null。例如:

  dynamic "route" {
    for_each = var.route
    content {
      cidr_block      = try(route.value.cidr_block, null)
      gateway_id      = try(route.value.gateway_id, null)
      ipv6_cidr_block = try(route.value.ipv6_cidr_block, null)
      # ...
    }
  }

像这样的嵌套块更像是一个固定的数据结构而不是一个集合,因此没有任何语法可以动态构造它。

话虽如此,在这种特殊情况下,正如 Marcin 指出的那样,route 实际上实际上只是对象类型的一个属性,Terraform Core 允许将其与块语法一起使用,作为对向后的让步与旧版 Terraform 的兼容性。出于这个原因,这两种方法在实践中并没有任何显着差异,但“属性作为块”机制仅适用于提供者设计为假设 Terraform v0.11 验证错误的某些情况,因此条件 null 我上面描述的方法是更一般的答案,它也应该适用于普通的嵌套块。