Duplicated for_each in resources

Hi,

Although the example below works, it feels like I am duplicating for_each in two different resources. I looked at Chaining for_each Between Resources but not sure if I can apply to mine. The question is, am I using for_each right here or could I improve the whole thing?

Thanks

locals {
  people = [
    {
      name : "developer-1",
      pgp_key : "mQEN......qpBmjUPkzW"
    }
  ]
}

resource "aws_iam_user" "user" {
  for_each = { for item in local.people : item.name => item }

  name = each.value.name
}

resource "aws_iam_access_key" "user" {
  for_each = { for item in local.people : item.name => item }

  user    = each.value.name
  pgp_key = each.value.pgp_key

  depends_on = [
    aws_iam_user.user
  ]
}

output "access_keys" {
  value = {
    for name, user in aws_iam_access_key.user : name => "ID: ${user.id} => SECRET: ${user.encrypted_secret}"
  }
}

Hi @wevovib720,

This is indeed a more complicated case of chaining because the downstream resource aws_iam_access_key needs to access both attributes of the upstream resource and the map the upstream resource was using for its for_each.

I think there are a few different ways to reorganize this, and the one I’m about to show is just the one that seems the smallest change from what you’re already doing, but hopefully it also illustrates some new language features that you can combine in some different ways if you prefer to think about the problem a little differently.

First, I think it will make things easier to factor out the construction of the map of “people” in terms of the list of people (which I assume in your real example is an input variable and so you can’t just change it inline to be a map) so that we can use it in multiple locations without recalculating it:

locals {
  people_map = { for item in local.people : item.name => item }
}

With that in place, local.people_map gives a way to look up a person definition given a username.

With that in place, we can change the definition of resource "aws_iam_access_key" "user" to chain from the aws_iam_user resource but to still refer to the original map to get the PGP key, which wouldn’t otherwise be available through the user object:

resource "aws_iam_user" "user" {
  for_each = local.people_map

  name = each.value.name
}

resource "aws_iam_access_key" "user" {
  for_each = aws_iam_user.user

  user    = each.value.name
  pgp_key = local.people_map[each.key].pgp_key
}

Note that it’s also no longer necessary to specify depends_on, because the for_each expression includes a reference to aws_iam_user.user which establishes that dependency automatically.

1 Like

This is an excellent explanation with a working example. Not only answers the question but also teaches why. Much appreciated.