Home > database >  Iterate over map and concatenate string in Terraform
Iterate over map and concatenate string in Terraform

Time:05-12

I'd like to iterate over a map variable and concatenate individual strings to a single string for an S3 subfolder.

My expected output is:

  • s3://my-bucket/name1/k1/abc/
  • s3://my-bucket/name1/k2/def/
  • s3://my-bucket/name2/k3/xyz/

But with the current setup I get a Cannot include the given value in a string template: string required. Is there another way to loop over my source_names variable or should I set it up differently?

main.tf s3_buckets modules

resource "aws_s3_bucket" "bucket" {
  bucket = var.bucket_name
  acl    = "private"

  tags = {
    Name = var.tag
  }
}

resource "aws_s3_object" "new_folders" {
  for_each = var.source_names
  bucket = aws_s3_bucket.bucket.id
  acl = "private"
  key = "${each.key}/${each.value.key_name}/${each.value.key_value}"
}


resource "aws_s3_bucket_public_access_block" "example" {
  bucket              = aws_s3_bucket.bucket.id
  block_public_acls   = true
  block_public_policy = true

}

variables.tf s3_buckets_module

variable "bucket_name" {
    description = "Name of the bucket"
    type = string
}

variable "tag" {
    description = "Resource tag"
    type = string
}

variable "source_names" {
    description = "key"
    type = map(object({
        key_name = list(string)
        key_value = list(string)
    }))
}

main.tf pipeline module

module "s3_bucket" {
    source = "../s3_buckets"
    bucket_name  = "tf-list-keys-bucket"
    tag          = "tf"
    source_names = {"name1" = {"key_name" = ["k1", "k2"], "key_value" = ["abc/", "def/"]},
                    "name2" = {"key_name" = ["k3"], "key_value" = ["xyz/"]}
    }
}

main.tf

module "pipeline" {
  
  source      = "../modules/pipeline"
}

CodePudding user response:

Iterating over your source_names requires you to have 2 embedder for loops:

resource "aws_s3_object" "new_folders" {
  for_each = toset(flatten([
    for key, value in var.source_names : [
      for key_name, key_values in value : [
        for key_value in key_values :
        "${key}/${key_name}/${key_value}"
    ]]
  ]))
  bucket = aws_s3_bucket.bucket.id
  acl    = "private"
  key    = each.value
}

You have to flatten out your data structure, otherwise Terraform wont know how many resources to provision. You cannot just put an array in a string and expecting Terraform to iterate over it (example: key = "${each.key}/${each.value.key_name}/${each.value.key_value}" - each.value.key_value is an array).

  • Related