【问题标题】:Terraform conditional `for_each` with downstream dependencies具有下游依赖项的 Terraform 条件“for_each”
【发布时间】:2021-12-13 23:07:18
【问题描述】:

给定一些条件/过滤的for_each 语句,我如何将剩余的对象用于下游依赖项?

注意:Terraform 0.13.7

例如,如果用户没有 s3 存储桶,terraform 应该创建一个并设置它的通知策略。如果他们确实有一个存储桶,那么 terraform 应该查找存储桶并设置它的通知。

到目前为止,我已经尝试像这样格式化我的有效负载:

"snowpipes": {
  . . .
  "create_staging_bucket": true,
  "staging_bucket": {
    "name": "existing-bucket-deployment",
    "url": "old-dirty-bucket",
    "arn": "arn:aws:s3:::old-dirty-bucket"
  },
  . . .
}

然后像这样构建我的 terraform:

resource "aws_s3_bucket" "staging_bucket" {
  for_each = {for k, v in var.snowpipes : k => v if v.create_staging_bucket == true}
  bucket = lower(each.value.staging_bucket.url)
}

resource "aws_s3_bucket_notification" "bucket_notification" {
  for_each = var.snowpipes
  bucket = aws_s3_bucket.staging_bucket[each.key].id
  . . .
}

然后我得到这样的错误,表明给定的密钥被过滤掉了:

Error: Invalid index

  on main.tf line 504, in resource "aws_s3_bucket_notification" "bucket_notification":
 504:   bucket = aws_s3_bucket.staging_bucket[each.key].id
    |----------------
    | aws_s3_bucket.staging_bucket is object with no attributes
    | each.key is "existing-bucket-deployment"

The given key does not identify an element in this collection value.

不确定是否有办法在 resourcedata 对象之间来回交换?

【问题讨论】:

    标签: amazon-s3 terraform terraform-provider-aws terraform0.12+


    【解决方案1】:

    我通常建议通过艰难决定创建存储桶是否属于其范围的一部分,然后让调用模块始终声明其自己的 S3 存储桶,以简化共享模块您决定 S3 存储桶不在其范围内,但我也可以看到,有时以这种方式灵活很方便,但这样做可能会牺牲一些额外的配置复杂性。

    让我们首先展示我将在其余部分中假设的变量声明:

    variable "snowpipes" {
      type = map(object({
        create_staging_bucket = bool
        staging_bucket = object({
          name = string
          url  = string
          arn  = string
        })
        # (and whatever else you need, immaterial to this question)
      }))
    }
    

    接下来让我们为这些元素的子集声明 aws_s3_bucket 资源,这些元素设置了 create_staging_bucket,这与您已经编写的内容相同:

    resource "aws_s3_bucket" "staging_bucket" {
      for_each = {
        for k, v in var.snowpipes : k => v
        if v.create_staging_bucket == true
      }
    
      bucket = lower(each.value.staging_bucket.url)
    }
    

    到目前为止,我希望我只是基本上重复了您已经拥有的内容。我的下一步是将此资源的结果合并到原始变量的设置中,以便创建所有暂存存储桶的平面图,无论它们是否在此处创建:

    locals {
      staging_buckets = merge(
        { for k, sp in var.snowpipes : k => sp.staging_bucket }
        {
          for k, b in aws_s3_bucket.staging_bucket : k => {
            name = b.bucket
            url  = b.bucket # (not sure about this, but following your example above)
            arn  = b.arn
          }
        }
      }
    }
    

    现在我们回到了一个映射,它的所有键都与我们在 var.snowpipes 中开始时的键相同,其中一些元素只是输入中的逐字记录,而其他元素是根据我们声明的资源合成的.由于merge 的优先行为,它会更喜欢使用第二个映射中的键,而不是映射键发生冲突的第一个映射中的键。

    我们可以将它用于存储桶通知资源:

    resource "aws_s3_bucket_notification" "bucket_notification" {
      for_each = local.staging_buckets
    
      bucket = each.value.name
      # ...
    }
    

    【讨论】:

    • 谢谢,我什至不知道有合并,也绝对没有意识到使用局部变量的威力。来自很多 TF12,我最终创建了两个以地图为条件的资源(resource.s3 和 data.s3)和两个通知资源,但这看起来更干净。
    猜你喜欢
    • 1970-01-01
    • 2021-07-21
    • 2011-07-11
    • 1970-01-01
    • 1970-01-01
    • 2014-09-05
    • 1970-01-01
    • 1970-01-01
    • 2014-08-27
    相关资源
    最近更新 更多