具有下游依赖性的 Terraform 条件 `for_each`

Terraform conditional `for_each` with downstream dependencies

给定一些 conditional/filtered 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"
  },
  . . .
}

然后像这样构建我的地形:

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 对象之间来回交换?

我通常建议通过做出关于创建存储桶是否在其范围内的艰难决定,然后让调用模块 always 声明来保持共享模块更简单它自己的 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)
  }))
}

接下来让我们为设置了 create_staging_bucket 的这些元素的子集声明 aws_s3_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
  # ...
}