【问题标题】:Terraform get list index on for_eachTerraform 在 for_each 上获取列表索引
【发布时间】:2020-08-04 05:09:27
【问题描述】:

Terraform 新手在这里。 我想使用for_each 迭代一个列表,但似乎键和值是相同的:

provider "aws" {
  profile = "default"
  region  = "us-east-1"
}

variable "vpc_cidrs" {
  default = ["10.0.0.0/16", "10.1.0.0/16"]
}

resource "aws_vpc" "vpc" {
  for_each             = toset(var.vpc_cidrs)
  cidr_block           = each.value
  enable_dns_hostnames = true
  tags                 = { Name = "Company0${each.key}" }
}

我希望标签名称为"Name" = "Company01""Name" = "Company02",但根据terraform apply,我得到: "Name" = "Company010.0.0.0/16""Name" = "Company010.1.0.0/16" 我错过了什么?

【问题讨论】:

    标签: terraform


    【解决方案1】:

    for_each is used with a seteach.keyeach.value 相同时。

    要生成“Company01”、“Company02”等字符串,您需要列表中每个 CIDR 块的索引。一种方法是使用for expression 创建本地地图,例如:

    locals {
      vpc_cidrs = {for s in var.vpc_cidrs: index(var.vpc_cidrs, s) => s}
    }
    
    resource "aws_vpc" "vpc" {
      for_each             = local.vpc_cidrs
      cidr_block           = each.value
      enable_dns_hostnames = true
      tags                 = { Name = "Company0${each.key}" }
    }
    

    作为奖励,您可以使用format 函数来构建零填充的名称字符串:

    resource "aws_vpc" "vpc" {
      ...
      tags                 = { Name = format("Company%02d", each.key) }
    }
    

    【讨论】:

      【解决方案2】:

      使用index 函数找到了一个简单的解决方案:

      tags = { Name = "Company0${index(var.vpc_cidrs, each.value) + 1}" }
      

      【讨论】:

      • 此解决方案也适用于 for 语句。 [对于 data.aws_availability_zones.available.names 中的 x : cidrsubnet(var.vpc_cidr_block, 8, index(data.aws_availability_zones.available.names, x)) ]
      【解决方案3】:

      还有另一种方法可以在不使用index() 的情况下达到想要的结果:

      更改以下三行:

      for_each   = { for idx, cidr_block in var.vpc_cidrs: cidr_block => idx}
      cidr_block = each.key
      tags       = { Name = format("Company%02d", each.value + 1) }
      
      • for_each 将使用由for 返回的cidr_blockindex 映射的映射
      • cidr_block 然后可以只使用each.key
      • 并且在tags 中也使用format()each.value 有一个两位数与index 的前导零

      完整的例子是:

      provider "aws" {
        profile = "default"
        region  = "us-east-1"
      }
      
      variable "vpc_cidrs" {
        default = ["10.0.0.0/16", "10.1.0.0/16"]
      }
      
      resource "aws_vpc" "vpc" {
        for_each             = { for idx, cidr_block in var.vpc_cidrs: cidr_block => idx}
        cidr_block           = each.key
        enable_dns_hostnames = true
        tags                 = { Name = format("Company%02d", each.value + 1) }
      }
      

      【讨论】:

      • 如何在动态块中做到这一点?
      • 实际上相同....只是引用不同,因为它将以动态块名称为前缀,如dynamic "name" 将变为name.each.value 而不是each.value .. 键相同
      【解决方案4】:

      您可以使用count 函数并使用索引来执行此操作:

      provider "aws" {
        profile = "default"
        region  = "us-east-1"
      }
      
      variable "vpc_cidrs" {
        type = list(string)
        default = ["10.0.0.0/16", "10.1.0.0/16"]
      }
      
      resource "aws_vpc" "vpc" {
        count = length(var.vpc_cidrs)
        cidr_block           = var.vpc_cidrs[count.index]
        enable_dns_hostnames = true
        tags                 = { Name = "Company0${count.index}" }
      }
      

      【讨论】:

      • @Moshe 感谢您提醒我注意这一点,我没有彻底测试。但是我仍然可以使用 count 想出一些替代方法。我刚刚更新了我的答案
      • 抱歉,仍然不正确。循环不会给你 var 值,只是索引。在我说我需要两者的问题中。查看其他建议的解决方案,他们提供了答案
      • @Moshe 该循环将为您提供var.vpc_cidrs[count.index] 上的var 值,随着索引的增加,它将遍历列表中的所有元素
      【解决方案5】:

      其中一个 cmets 询问如何在 dynamic block 内使用 for_each 时使用索引。

      简答

      这可以通过参考<name-of-dynamic-block>.key来完成(key是索引)。


      一个例子

      用例

      我将举一个用例,我有一个 S3 存储桶,里面有多个文件夹,每个文件夹都有一个指向它的专用云端分发。

      我想要在 AWS 范围内的东西

      假设我在 s3 存储桶中有 2 个文件夹:name1name2

      所以我必须创建一个如下所示的aws_s3_bucket_policy

      {
          "Version": "2012-10-17",
          "Statement": [
              {
                  "Sid": "",
                  "Effect": "Allow",
                  "Principal": {
                      "AWS": "<arn-of-cloudfront-distribution-1>"
                  },
                  "Action": "s3:GetObject",
                  "Resource": "arn:aws:s3:::some-bucket/name1/*"
              },
              {
                  "Sid": "",
                  "Effect": "Allow",
                  "Principal": {
                      "AWS": "<arn-of-cloudfront-distribution-2>"
                  },
                  "Action": "s3:GetObject",
                  "Resource": "arn:aws:s3:::some-bucket/name2/*"
              }
          ]
      }
      

      (*) 为简洁起见,删除了云端发行版的创建。

      如何使用 Terraform 实现这一目标

      所以我有一个需要在我的 s3 存储桶中创建的文件夹列表:

      variable "all_s3_folders" {
        type        = list(string)
        default     = []
        description = "All folders inside the bucket"
      }
      

      我为每个文件夹创建了一个 cloudfront 分发(为简洁起见已删除)和一个 cloudfront 源访问身份(请参阅oai)资源。

      我们将根据文件夹数量上的count 创建这些资源:

      resource "aws_cloudfront_origin_access_identity" "oai" {
        count = length(var.all_s3_folders)
        comment = "some-comment"
      }
      

      现在我们需要将每个文件夹(+ 其相关的云端分发和 oai)添加为存储桶策略中的条目。

      我想使用带有for_each 的动态块,但不依赖count.index

      但我仍然需要使用索引遍历 aws_cloudfront_origin_access_identity 资源。

      所以,

      为了在动态块内使用for_each 时使用索引,您只需引用&lt;name-of-dynamic-block&gt;.key - 这里是statement.key

      data "aws_iam_policy_document" "my-doc" {
        dynamic "statement" {
          for_each = var.folders_array
          content {
            effect = "Allow"
      
            actions = [
              "s3:GetObject"
            ]
      
            resources = [
              "${aws_s3_bucket.my_s3_bucket.arn}/${statement.value}/*" # <--Using statement value
            ]
      
            principals {
              type = "AWS"
              identifiers = [some_cloudfront_resource[statement.key].id] # <--Using statement key
            }
          }
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2021-10-06
        • 1970-01-01
        • 2021-07-09
        • 1970-01-01
        • 2021-01-18
        • 2021-03-31
        • 2020-06-23
        • 2020-09-25
        • 2021-08-02
        相关资源
        最近更新 更多