Need help with nested JSON file using locals block

It seems like you are experiencing a very common confusing situation in Terraform - the need to loop over two nested levels of data to produce resources. Here’s a previous time I answered this: How to deal with nested for_each loops in dependent ressources - #2 by maxb

There are many slight variations in how this can be written, but fundamentally you always need:

  • To loop over the outer collection:
[for security_group in local.config : 
  • To loop over the inner data:
[for rule in security_group.IpPermissions :
  • To assemble an object that contains information from both levels of loop:
{
  security_group = security_group
  rule           = rule
}
  • To wrap all of the code above in flatten()
  • To loop over the flattened data again, picking out a string value that identifies each item, and will be used as the resource name:
{ for item in flatten( ... ) : "${some kind of expression}" => item }

That last one is particularly hard in this case, as security group rules don’t have a simple field works well to use as their name.

You could just decide to use their numeric index within the input JSON array, but that exposes you to Terraform potentially adding/deleting/modifying more than it needs to on future updates, if additions/deletions cause other rules to shift position in the array.

You could jsonencode the entire input element from IpPermissions, but that would lead to some particularly long and unreadable resource keys.

Or, you could write a custom expression that attempts to capture just enough of the fields to ensure uniqueness.

Anyway, putting the previous parts together:

resource "aws_security_group_rule" "something" {
  for_each = { for item in flatten(
    [for security_group in local.config :
      [for rule in security_group.IpPermissions :
        {
          security_group = security_group
          rule           = rule
    }]]
  ) : "${some_kind_of_expression}" => item }
}