For_each conditionally add block in resource

I would like to conditionally added a block to a resource. If the condition is false, I would like the block to not exist on the resource at all, not just be nil.

resource "google_storage_bucket" "default" {
  for_each      = local.gcp_buckets
  depends_on    = [google_project.default]
  name          = each.value.name
  project       = each.value.project_identifier
  location      = each.value.region
  storage_class = each.value.storage_class
  versioning { enabled = each.value.versioning }

  dynamic "cors" {
    for_each = each.value.cors == null ? null : each.value.cors
    content {
      origin          = cors.value.origin
      method          = cors.value.method
      response_header = cors.value.response_header
      max_age_seconds = cors.value.max_age_seconds
    }
  }
}

Variable definition:

variable "cloud_storage" {
  type = map(object({
    cloud_provider = string
    name           = optional(string, "")
    region         = optional(string, "")
    storage_class  = optional(string, "")
    versioning     = optional(bool, true)
    cors2          = optional(object({
      origin          = optional(set(string))
      method          = optional(set(string))
      response_header = optional(set(string))
      max_age_seconds = optional(number)
    }), null)

  }))
  default = {}
}

Variable file:

cloud_storage = {
  gcp-unique-bucket-name = {
    cloud_provider = "gcp"
    name           = "unique-bucket-name"
    region         = "us-west1"
    storage_class  = "STANDARD"
    versioning     = false
    cors2           = {
      origin = ["*"]
      method = ["GET"]
      response_header = ["*"]
      max_age_seconds = 3600
    }
  }
}

The result is:

β”‚ Error: Invalid dynamic for_each value
β”‚
β”‚   on resources_gcp.tf line 103, in resource "google_storage_bucket" "default":
β”‚       for_each = each.value.cors == null ? null : each.value.cors
β”‚     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚     β”‚ each.value.cors is null
β”‚
β”‚ Cannot use a null value in for_each.

Are you using local.gcp_buckets to transform the data structure in var.cloud_storage, and if so, can you share what that looks like?
Are you updating cors2 to cors in there?

Here is the gcp_buckets:

locals {
  gcp_buckets = { for bucket_key, item in var.cloud_storage : bucket_key => merge(item, {
    project_identifier = coalesce(item.grouping, google_project.ecosystem.project_id)
    bucket_name        = item.ecosystem_prefix ? "${local.ecosystem}-${coalesce(item.bucket_name, bucket_key)}" : coalesce(item.bucket_name, bucket_key)
  }) if item.cloud_provider == "gcp" }
}

I think the cors2 is a typo from some testing. It should be cors.

Your primary problem is this line right here that can’t result in anything except null:

for_each = each.value.cors == null ? null : each.value.cors

The expression reads as:

if each.value.cors is null, then assign null, else assign each.value.cors which is null.

1 Like