问题描述
我有一个包含我要创建的子网列表的对象.
I have an object containing the list of subnets I want to create.
variable "subnet-map" {
default = {
ec2 = [
{
cidr_block = "10.0.1.0/24"
availability_zone = "eu-west-1a"
}
],
lambda = [
{
cidr_block = "10.0.5.0/24"
availability_zone = "eu-west-1a"
},
{
cidr_block = "10.0.6.0/24"
availability_zone = "eu-west-1b"
},
{
cidr_block = "10.0.7.0/24"
availability_zone = "eu-west-1c"
}
],
secrets = [
{
cidr_block = "10.0.8.0/24"
availability_zone = "eu-west-1a"
},
{
cidr_block = "10.0.9.0/24"
availability_zone = "eu-west-1b"
},
{
cidr_block = "10.0.10.0/24"
availability_zone = "eu-west-1c"
}
],
rds = [
{
cidr_block = "10.0.11.0/24"
availability_zone = "eu-west-1a"
},
{
cidr_block = "10.0.12.0/24"
availability_zone = "eu-west-1b"
},
{
cidr_block = "10.0.13.0/24"
availability_zone = "eu-west-1c"
}
]
}
}
之前我使用的是计数循环结构.所以我曾经把上面的结构扁平化成一个对象列表
Earlier I was using the count loop construct. So I used to flatten the above structure into a list of objects
locals {
subnets = flatten([
for resource in keys(var.subnet-map) : [
for subnet in var.subnet-map[resource] : {
resource = resource
cidr_block = subnet.cidr_block
availability_zone = subnet.availability_zone
}
]
])
}
然后我会通过做来创建资源
And then I would create the resources by doing
resource "aws_subnet" "aws-subnets" {
count = length(local.subnets)
vpc_id = aws_vpc.aws-vpc.id
cidr_block = local.subnets[count.index].cidr_block
availability_zone = local.subnets[count.index].availability_zone
tags = {
Name = "subnet-${local.subnets[count.index].resource}-${local.subnets[count.index].availability_zone}"
}
}
现在我想使用 for_each 循环.但我不知道该怎么做.这就是我迄今为止所做的.
Now I want to use the for_each loop. But I cannot figure out how to do it. This is what I've done so far.
resource "aws_subnet" "subnets-dev" {
for_each = var.subnet-map
vpc_id = aws_vpc.vpc-dev.id
cidr_block = each.value.cidr_block
availability_zone = each.value.availability_zone
tags = {
Name = "subnet-dev-${each.value.resource}-${each.value.availability_zone}"
environment = "dev"
}
}
但它一直提示错误
Error: Unsupported attribute
on vpc/main.tf line 93, in resource "aws_subnet" "subnets-dev":
93: Name = "subnet-dev-${each.value.resource}-${each.value.availability_zone}"
|----------------
| each.value is tuple with 3 elements
This value does not have any attributes.
我该如何解决这个问题?
How could I fix this?
推荐答案
我不确定我是否完全遵循您在此处尝试的所有内容,因为您的 var.subnet-map
初始片段显示了它是对象列表映射的映射,但稍后当您使用 for_each = var.subnet-map
时,它似乎已将其视为列表映射.在这里尝试 for_each
之前,您是否删除了额外的地图级别(默认"键)?
I'm not sure I fully follow all of what you tried here because your initial snippet of var.subnet-map
shows it being a map of maps of lists of objects, but later on when you used for_each = var.subnet-map
it seems to have treated it as a map of lists instead. Did you remove that extra level of maps (the "default" key) before trying for_each
here?
使用您对 变量subnet-map"
的原始定义,您使用 for_each
的第一步将类似于您使用 count
所做的>:你需要将结构展平,这次变成对象的映射而不是对象的列表.到达那里的最简单方法是从现有的扁平化列表中获取地图:
Working with your original definition of variable "subnet-map"
, your first step with for_each
will be similar to what you did with count
: you need to flatten the structure, this time into a map of objects rather than a list of objects. The easiest way to get there is to derive a map from your existing flattened list:
locals {
subnets = flatten([
for resource in keys(var.subnet-map) : [
for subnet in var.subnet-map[resource] : {
resource = resource
cidr_block = subnet.cidr_block
availability_zone = subnet.availability_zone
}
]
])
subnets_map = {
for s in local.subnets: "${s.resource}:${s.availability_zone}" => s
}
}
这里我假设您的资源"字符串和可用区一起是一个合适的子网唯一标识符.如果没有,您可以将 "${s.resource}:${s.availability_zone}"
部分调整为您想用于这些的任何唯一键.
Here I assumed that your "resource" string and your availability zone together are a suitable unique identifier for a subnet. If not, you can adjust the "${s.resource}:${s.availability_zone}"
part to whatever unique key you want to use for these.
现在您可以将扁平化的地图用作 for_each
地图:
Now you can use the flattened map as the for_each
map:
resource "aws_subnet" "subnets-dev" {
for_each = local.subnets_map
vpc_id = aws_vpc.vpc-dev.id
cidr_block = each.value.cidr_block
availability_zone = each.value.availability_zone
tags = {
Name = "subnet-dev-${each.value.resource}-${each.value.availability_zone}"
environment = "dev"
}
}
这将为您提供地址如 aws_subnet.subnets-dev["ec2:eu-west-1a"]
的实例.
This will give you instances with addresses like aws_subnet.subnets-dev["ec2:eu-west-1a"]
.
请注意,如果您要从 count
迁移到您希望保留的现有子网,您还需要执行一次性迁移步骤以告诉 Terraform 现有状态中的哪些索引对应新配置中的哪些键.
Note that if you are migrating from count
with existing subnets that you wish to retain, you'll need to also do a one-time migration step to tell Terraform which indexes from the existing state correspond to which keys in the new configuration.
例如,如果(且仅当)索引 0
之前是 eu-west-1a
中 ec2
的索引,迁移那个命令是:
For example, if (and only if) index 0
was previously the one for ec2
in eu-west-1a
, the migration command for that one would be:
terraform state mv 'aws_subnet.subnets-dev[0]' 'aws_subnet.subnets-dev["ec2:eu-west-1a"]'
如果您不确定它们之间的关系,您可以在添加 for_each
后运行 terraform plan
并查看 Terraform 计划销毁的实例.如果您依次处理其中的每一个,将 Terraform 当前知道的地址与 Name
标记中显示的资源和可用区名称一起使用,您可以将它们中的每一个迁移到其新地址,以便Terraform 不会再认为您要求它销毁编号的实例并用命名的实例替换它们.
If you're not sure how they correlate, you can run terraform plan
after adding for_each
and look at the instances that Terraform is planning to destroy. If you work through each one of those in turn, taking the address Terraform currently knows along with the resource and availability zone names shown in the Name
tag, you can migrate each of them to its new address so that Terraform will no longer think you're asking for it to destroy the numbered instances and replace them with named ones.
这篇关于Terraform - 如何在对象列表上使用 for_each 循环来创建资源的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!