for_each through a object in Terraform 0.12

393 views Asked by At

I want to call terraform module in the next way:

module "database_role" {
 source = "modules/roles"

 project_id = "testid"
 role_name = "testrole"

 actions = {
   action: ["ENABLE_PROFILER", "DROP_DATABASE"]
   database_name: "test_db"
 }

roles module definition i created is:

resource "mongodbatlas_custom_db_role" "custom_role" {
  project_id = var.project_id
  role_name  = var.role_name

  dynamic "actions" {
    for_each = [for item in [var.actions] : item]
      content {
        actions {
          action = lookup(actions.value, "action")

          resources {
            cluster = "false"
            database_name = lookup(actions.value, "database_name")
          }
        }
      }
    }
  }

as result i want to see actions properly generated:

 actions {
   action = "ENABLE_PROFILER"
   resources {
     cluster         = "false"
     database_name   = "test_db"
   }
 }


 actions {
   action = "DROP_DATABASE"
   resources {
     cluster         = "false"
     database_name   = "test_db"
   }
 }

I'm getting error: The given value is not suitable for child module variable "actions". What i'm doing wrong in module dynamic resource? Thanks

1

There are 1 answers

2
harshavmb On BEST ANSWER

The flatten function could be used here.

I've below actions local var::

locals {
  actions = {
    action = ["ENABLE_PROFILER", "DROP_DATABASE"]
    database_name = "test_db"
  }
}

Now, I'll flatten based on the size of local.actions["action"] to achieve the desired result. Once, I get the flattened list, I would loop through the list to create dynamic blocks.

resource "mongodbatlas_custom_db_role" "custom_role" {
  project_id = "xxx-xxx"
  role_name  = "yyy-yyy"

  dynamic "actions" {
    for_each = flatten([
      for item in range(length(local.actions["action"])): {
        act = local.actions["action"][item]
        db_name = local.actions.database_name
      }
     ])
     content {
       action = actions.value.act
       resources {
         cluster = "false"
         database_name = actions.value.db_name
       }
     }
   }
 }

This would produce the required number of blocks I like::

Harshas-MBP:mongo harshavmb$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.


------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # mongodbatlas_custom_db_role.custom_role will be created
  + resource "mongodbatlas_custom_db_role" "custom_role" {
      + id         = (known after apply)
      + project_id = "xxx-xxx"
      + role_name  = "yyy-yyy"

      + actions {
          + action = "ENABLE_PROFILER"

          + resources {
              + cluster       = false
              + database_name = "test_db"
            }
        }
      + actions {
          + action = "DROP_DATABASE"

          + resources {
              + cluster       = false
              + database_name = "test_db"
            }
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

This could be applied to create resources too. The key idea of working with nested or complicated loops is to flatten them into a list of resources or blocks, we create and then iterate through the flattened list with unique indexes as shown in above example.