Search code examples
terraform

Iterate through map of lists as value in Terraform resource


I have a map that looks something like this:

apps = {
  team_1 = ["app_1", "app_2"],
  team_2 = ["app_3"]
}

I am trying to create a AWS role and policy document for each app, but each app needs to know which team it belongs to when trying to create the policy document because I want to create a service account in the 'team' namespace with the service account name using the app name, something like this:

data "aws_iam_policy_document" "application_trust_policy" {
  statement{
    condition {
      test     = "StringEquals"
      variable = "${local.op_url}:sub"
      values = [
        flatten([for team, apps in var.managed_apps:
        [ for app in apps:
            "system:serviceaccount:${team}:${app}-sa"
        ]
        ])
      ]
    }
  }
}

The role will have the trust policy attached:

resource "aws_iam_role" "application_role" {
  for_each = { for team, app in var.managed_apps : team => app}
  name_prefix = "${each.value}_role"
  assume_role_policy = data.aws_iam_policy_document.application_trust_policy.json
  tags               = var.tags
}

How can I create a role for each app while still being able to reference the team for which the app belongs to?


Solution

  • To create an AWS IAM role for each app and reference the corresponding team for which the app belongs, you could iterate over both the teams and their associated apps.

    For that you could Flatten the Team-App Map: Use a flatten operation to convert the nested map structure into a list of key-value pairs that associates each app with its corresponding team.

    variable "managed_apps" {
      type = map(list(string))
      description = "Mapping of teams to their respective apps"
      default = {
        team_1 = ["app_1", "app_2"]
        team_2 = ["app_3"]
      }
    }
    
    locals {
      # Create a flattened map of app-to-team relationships
      app_to_team_map = merge([
        for team, apps in var.managed_apps : {
          for app in apps : app => team
        }
      ]...)
    }
    

    In the above the ... is being used as a way to spread or unpack elements from a list into individual arguments inside the merge()

    Now you can create the IAM policy document using the flattened map and create the IAM Role for Each App

    data "aws_iam_policy_document" "application_trust_policy" {
      statement {
        effect = "Allow"
        actions = ["sts:AssumeRoleWithWebIdentity"]
    
        condition {
          test     = "StringEquals"
          variable = "${local.op_url}:sub"
          values = flatten([
            for app, team in local.app_to_team_map :
            "system:serviceaccount:${team}:${app}-sa"
          ])
        }
      }
    }
    
    resource "aws_iam_role" "application_role" {
      for_each = local.app_to_team_map
      
      # each.key is the app name, and each.value is the team name
      name_prefix          = "${each.key}_role"
      assume_role_policy   = data.aws_iam_policy_document.application_trust_policy.json
      tags                 = var.tags
    }