Terraform-如何在对象列表上使用for_each循环创建资源 [英] Terraform - how to use for_each loop on a list of objects to create resources

查看:223
本文介绍了Terraform-如何在对象列表上使用for_each循环创建资源的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个包含要创建的子网列表的对象.

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"
      }
    ]
  }
}

我以前使用的是count循环结构.所以我曾经将上述结构扁平化为对象列表

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
      }
    ]
  ])
}

然后我将通过创建资源

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"
  }
}

但是它总是显示错误信息

But it keeps giving an error saying

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.

我该如何解决?

推荐答案

我不确定我是否完全遵循您在此处尝试的所有内容,因为您最初的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?

使用您最初定义的variable "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-1aec2的索引,该索引的迁移命令为:

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循环创建资源的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆