skip to Main Content

I need an if statement with for_each.
If it is non-prod create resource in a single subnet, if production create resources in both subnets:

locals {
  zone_data = {
    a = {
      subnet_id = data.aws_cloudformation_export.subnet1.value
    }
    b = {
      subnet_id = data.aws_cloudformation_export.subnet2.value
    }
  }
}

module "batch_server" {
     for_each = var.env_name == "non-prod" ? local.zone_data.a : local.zone_data
...

I get an error:

|----------------
    | local.zone_data is object with 2 attributes
    | local.zone_data.a is object with 1 attribute "subnet_id"

The true and false result expressions must have consistent types.
The given expressions are object and object, respectively.

I tried this:

for_each = var.env_name == "non-prod" ? [local.zone_data.a] : [local.zone_data]

and am getting similar error:

|----------------
    | local.zone_data is object with 2 attributes
    | local.zone_data.a is object with 1 attribute "subnet_id"

The true and false result expressions must have consistent types.
The given expressions are tuple and tuple, respectively.

Tried changing types but nothing seems to work

2

Answers


  1. How about a lookup with var.env_name as the key?

    variable "env_name" {
      type = string
      default = "non_prod"
    }
    
    locals {
      zone_data = {
        prod = {
          a = {
            subnet_id = data.aws_cloudformation_export.subnet1.value
          }
          b = {
            subnet_id = data.aws_cloudformation_export.subnet2.value
          }
        },
        non_prod = {
          a = {
            subnet_id = data.aws_cloudformation_export.subnet1.value
          }
        }
      }
    }
    
    module "batch_server" {
         for_each = lookup(local.zone_data, var.env_name)
    ...
    
    Login or Signup to reply.
  2. for_each expects a map with one element for each instance you want to declare. That means that if you want to declare only one instance then you need to produce a one-element map.

    Here’s one way to do that:

      for_each = var.env_name == "non-prod" ? tomap({a = local.zone_data.a}) : local.zone_data
    

    This can work because both arms of the conditional produce a map of objects. The true arm produces a single-element map while the second produces the whole original map.

    Another option to avoid the inline map construction would be to factor out a per-environment lookup table into a separate local value:

    locals {
      all_zones = tomap({
        a = {
          subnet_id = data.aws_cloudformation_export.subnet1.value
        }
        b = {
          subnet_id = data.aws_cloudformation_export.subnet2.value
        }
      })
      env_zones = tomap({
        non-prod = tomap({
          for k, z in local.all_zones : k => z
          if k == "a"
        })
      })
    }
    
    module "batch_server" {
      for_each = try(local.env_zones[var.env_name], local.all_zones)
    
      # ...
    }
    

    The try function call here means that local.all_zones is the fallback to use if var.env_name doesn’t match one of the keys in local.env_zones.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search