Zenn
👻

Terraformでネストしたloopを書く

2023/03/21に公開

環境

$ terraform -v
Terraform v1.4.2
on darwin_arm64

例題

aws_ssoadmin_account_assignmentを例に考えてみます。

aws_ssoadmin_account_assignmentリソースはAWSアカウントとSSOグループと許可セットを紐付けるものです。

あるAWSアカウントに複数のグループに複数の許可セットを紐付けようとすると単純にfor_eachで回せないことに気づきます。

locals {
  groups = {
    "GROUP_A" = {
      permission_set_arns = [
        "arn:aws:sso:::permissionSet/ssoins-12345e7cccc04b03/bs-6bdaf3a33d689a7c",
        "arn:aws:sso:::permissionSet/ssoins-12345e7cccc04b03/bs-6bdaf3a33d689a7b"
      ]
    }
    "GROUP_B" = {
      permission_set_arns = [
        "arn:aws:sso:::permissionSet/ssoins-12345e7cccc04b03/bs-6bdaf3a33d689a7c"
      ]
    }
  }
}

resource "aws_ssoadmin_account_assignment" "this" {
  for_each           = local.groups
  instance_arn       = "instance_arn"
  # ここは文字列を期待しているのでpermission_set_arnsを渡せない
  # かといってforやfor_eachを使えない...
  permission_set_arn = each.value.permission_sets
  principal_id       = each.key
  principal_type     = "GROUP"
  target_id          = "123456789012"
  target_type        = "AWS_ACCOUNT"
}

以上からわかるようにterraformのfor_eachでは1重のloopしか表現できないことがわかります。

# これは表現できる
for {
    # todo
}

# これは表現できない
for {
    for {
        # todo
    }
}

terraformでネストしたloopを表現するにはいくつか方法があります。

moduleを使う

リソースをmoduleに移動して、moduleとresourceでfor_eachを使うことでネストしたloopを表現します。

# main.tf
locals {
  groups = {
    "GROUP_A" = {
      permission_set_arns = [
        "arn:aws:sso:::permissionSet/ssoins-12345e7cccc04b03/bs-6bdaf3a33d689a7c",
        "arn:aws:sso:::permissionSet/ssoins-12345e7cccc04b03/bs-6bdaf3a33d689a7b"
      ]
    }
    "GROUP_B" = {
      permission_set_arns = [
        "arn:aws:sso:::permissionSet/ssoins-12345e7cccc04b03/bs-6bdaf3a33d689a7c"
      ]
    }
  }
}


module "group_permissions" {
  # ここでloop
  for_each            = local.groups
  source              = "./modules/group_permissions/"
  permission_set_arns = each.value.permission_set_arns
  group_id            = each.key
  aws_account_id      = "123456789012"
}
# module
variable "permission_set_arns" {
  type = set(string)
}

variable "group_id" {
  type = string
}

variable "aws_account_id" {
  type = string
}

resource "aws_ssoadmin_account_assignment" "this" {
  # ここでさらにloop
  for_each           = var.permission_set_arns
  instance_arn       = "instance_arn"
  permission_set_arn = each.value
  principal_id       = var.group_id
  principal_type     = "GROUP"
  target_id          = var.aws_account_id
  target_type        = "AWS_ACCOUNT"
}

moduleにわけるやり方が一番直感的にわかりやすいと思います。
module内でさらにmoduleをつくることで何重のloopにも対応可能です。

flattenする

変数を多次元からflattenで1次元化してあげてそれをfor_eachで回します。

locals {
  groups = {
    "GROUP_A" = {
      permission_set_arns = [
        "arn:aws:sso:::permissionSet/ssoins-12345e7cccc04b03/bs-6bdaf3a33d689a7c",
        "arn:aws:sso:::permissionSet/ssoins-12345e7cccc04b03/bs-6bdaf3a33d689a7b"
      ]
    }
    "GROUP_B" = {
      permission_set_arns = [
        "arn:aws:sso:::permissionSet/ssoins-12345e7cccc04b03/bs-6bdaf3a33d689a7c"
      ]
    }
  }
}

locals {
  flatten_groups = flatten([
    for group, ps in local.groups : [
      for p in ps.permission_set_arns : {
        group_id      = group
        permission_id = p
      }
    ]
  ])
}

resource "aws_ssoadmin_account_assignment" "this" {
  for_each           = { for f in local.flatten_groups : "${f.group_id}_${f.permission_id}" => f }
  instance_arn       = "instance_arn"
  permission_set_arn = each.value.permission_id
  principal_id       = each.value.group_id
  principal_type     = "GROUP"
  target_id          = "123456789012"
  target_type        = "AWS_ACCOUNT"
}

flatten_groupsの中身は以下のようになっています。

flatten_groups = [
    {
        group_id      = "GROUP_A"
        permission_id = "arn:aws:sso:::permissionSet/ssoins-12345e7cccc04b03/bs-6bdaf3a33d689a7c"
    },
    {
        group_id      = "GROUP_A"
        permission_id = "arn:aws:sso:::permissionSet/ssoins-12345e7cccc04b03/bs-6bdaf3a33d689a7b"
    },
    {
        group_id      = "GROUP_B"
        permission_id = "arn:aws:sso:::permissionSet/ssoins-12345e7cccc04b03/bs-6bdaf3a33d689a7c"
    },
]

Discussion

ログインするとコメントできます