Terraform modules & for_each

  • 2 min read

[WROTE THIS WITH A HEAVY COLD - I NEED TO CHECK IT]

I like for_each. I like modules that use for_each and create a bunch of resources based on an input map. But is this the best way?

What do I mean? A super basic module for parameter store might contain a single resource with a for_each iterating through the input map variable:

resource "aws_ssm_parameter" "store" {
  for_each = var.store

  name        = each.value.name
  description = each.value.description
  type        = each.value.type
  value       = each.value.value
}

And we’d call it as:

module "parameters" {
  source = "../..//modules/parameter-store"

  store = {
    beverley_hills_s3_bucket = {
      name        = "s3/buckets/beverley_hills_s3_bucket"
      description = "Bucket for 1990s TV show and more"
      type        = "String"
      value       = "s3a://bucket-90210/temp"
    }
    smokey_s3_bucket = {
      name        = "s3/buckets/smokey_s3_bucket"
      description = "An unusually mobile bucket"
      type        = "String"
      value       = "s3a://bucket-20252/temp"
    }
  }
}

And then we’d have a couple of parameters in SSM Parameter Store.

Why do I like this? I don’t have to call the module once per group of allied resources, which means my code is little DRYer. Why not a count through a list (instead of for_each through. map)? Because count really sucks.

What are the downsides though?

Simple modules are fine with this, but the more complex the set of resources, the larger and deeper the map has to get, and the muckier dynamic blocks can get. Instead, I’m looking at returning to the normal module method, assuming a single set of resources (which might include a map - for example, a map of ECS services and task definitions for the cluster that’s being managed).

The way forward will be to call the module once with a for_each inside that, so our single resource module goes back to a vanilla:

resource "aws_ssm_parameter" "store" {
  name        = var.name
  description = var.description
  type        = var.type
  value       = var.value
}

With the module call using a map and for_each like:

locals {
  store = {
    beverley_hills_s3_bucket = {
      name        = "s3/buckets/beverley_hills_s3_bucket"
      description = "Bucket for 1990s TV show and more"
      type        = "String"
      value       = "s3a://bucket-90210/temp"
    }
    smokey_s3_bucket = {
      name        = "s3/buckets/smokey_s3_bucket"
      description = "An unusually mobile bucket"
      type        = "String"
      value       = "s3a://bucket-20252/temp"
    }
  }
}

module "parameters" {
  source = "../..//modules/parameter-store"

  for_each = locals.store

  name        = each.value.name
  description = each.value.description
  type        = each.value.type
  value       = each.value.value
}