2
votes

I wanted to create a for_each loop that loops only over objects in array that have some specific key : value pair.

My input variables are:

inputs = {
  names = ["first", "second"]
  lifecycle_rules = [
    {
      name = first
      condition = {
        age = "1"
      }
      action = {
        type = "Delete"
      }
    },{
      condition = {
        age = "2"
      }
      action = {
        type = "Delete"
      }
    },
    {
      name = second
      condition = {
        age = "3"
      }
      action = {
        type = "Delete"
      }
    },{
      condition = {
        age = "4"
      }
      action = {
        type = "Delete"
      }
    }
  ]

and in my main.tf (btw for deploying gcp bucket for reference), I wanted to separate the lifecycle per bucket and wanted to apply only the rules that have the buckets name in it.

So if anyone has idea how to modify for_each code below to work, I would highly appreciate. I believe only the for_each needs to be changed to loop over the right elements (let's say only objects in that list that have name = first). from the var.lifecycle_rules set

  resource "google_storage_bucket" "buckets" {
    count         = length(var.names)
    name          = "${lower(element(var.names, count.index))}"
...
    dynamic "lifecycle_rule" {
     #for_each = length(lookup(lifecycle_rules[lookup(element(var.names, count.index))])
     for_each = lifecycle_rules
     content {
       action {
         type          = lifecycle_rule.value.action.type
         storage_class = lookup(lifecycle_rule.value.action, "storage_class", null)
       }
       condition {
         #age = lifecycle_rule.value.name == element(var.names, count.index) ? lookup(lifecycle_rule.value.condition, "age", null) : null
         age = lookup(lifecycle_rule.value.condition, "age", null) : null
...
1
While this is completely possible (and there is an example in the docs on how to do it), why not just iterate over the lifecycle_rules Map instead of using a count on the length of the names variable? You can then use the iterated values of the Map for the logic in the dynamic block, and whether it should exist at all. That would be much easier and cleaner.Matt Schuchard
yea I wanted to do it, but the whole module(big one between the ... dots) is just one made in google predefined, and wasn't sure how double for_each work underneath itself - but will test - other than that could you give link to docs? I was sure to check all? methods available and didnt find one to do this issuepotatopotato

1 Answers

2
votes

I think that this "wierd" look can be obtained in two stages.

  1. Reorganize lifecycle_rules into a map based on names
variable "input" {

  default = {
  names = ["first", "second"],
  
  lifecycle_rules = [
    {
      name = "first",
      condition = {
        age = "1"
      },
      action = {
        type = "Delete"
      }
    },
    {
      condition = {
        age = "2"
      },
      action = {
        type = "Delete"
      }
    },
    {
      name = "second",
      condition = {
        age = "3"
      },
      action = {
        type = "Delete"
      }
    },
    {
      condition = {
        age = "4"
      },
      action = {
        type = "Delete"
      }
    }
  ]
  }

}



locals {

  new = {
    for name in var.input.names:  
       name => [for rule in var.input.lifecycle_rules: 
        contains(keys(rule), "name" ) ? 
          rule.name == name ? rule: null : 
          null ] 
  }    
}


which will give local.new in the form of:

{                                                        
  "first" = [            
    {                 
      "action" = {       
        "type" = "Delete"
      }              
      "condition" = { 
        "age" = "1"
      }                  
      "name" = "first"
    },                   
    null,          
    null,                
    null,                
  ]                  
  "second" = [           
    null,          
    null,                
    {                  
      "action" = {       
        "type" = "Delete"
      }              
      "condition" = {  
        "age" = "3"
      }                
      "name" = "second"
    },   
    null,
  ]
}  

  1. Perform the for_each
  resource "google_storage_bucket" "buckets" {

    for_each = toset(var.input.names)  

    name          = each.key

    dynamic "lifecycle_rule" {

      # iterate for each name skipping null values
      for_each = [for v in local.new[each.key]: v if v != null]
  
      content {
        action {
           type          = lifecycle_rule.value["action"].type
           storage_class = lookup(lifecycle_rule.value["action"], "storage_class", null)
         }

        condition {
            age = lookup( tag.value["condition"], "age", null) 
         }
      }
   }
}

I could only verify the first step and partial second (using aws_autoscaling_group and its multiple tag components). I don't access to google cloud to fully test the code.