この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
ちゃだいん(@chazuke4649)です。
Terraformで複数リソースを少ないコード量で作成したい場合の選択肢の1つとしてfor_each
の活用があります。
今回はそのfor_each
を別のリソースにもさらに連鎖・連携させてより効率的にリソースを定義することができます。
Chaining for_each Between Resources - The for_each Meta-Argument | Terraform by HashiCorp
for_eachを使用したリソースは、他の場所で式に使用されるとオブジェクトのマップとして表示されるので、2つのオブジェクトのセットの間に一対一の関係がある状況では、あるリソースを他のリソースのfor_eachとして直接使用することができる。
上記を参考にやってみました。
やってみた
最も基本的なパターンは上記ドキュメントのサンプルがわかりやすいですが、今回は AWS SSO の許可セット(あるいは、アクセス権限セット、Permission sets)を作成してみました。
やりたいこと
- 4つのプロダクト(banana,melon,grape,orange)がある
- 開発者の権限を参照権限と、担当するプロダクトのEC2へのSession Managerでログインできる権限を付与したい
※こちらの詳細は以下で紹介しているものとほぼ同等です。
[AWS SSO版] Session Manager で特定の EC2 のみアクセスできるよう制限する | DevelopersIO
作成したいリソース
やりたいことを実現する手段として、以下のような構成にします。
- 許可セット x 4 (banana,melon,grape,orange) を作成したい
- それぞれの許可セットに対し、2つのアクセスポリシーを付与したい
- マネージドポリシーの
ReadOnlyAccess
- インラインポリシーの
AllowSessionManager
- インラインポリシーの記述の中に環境変数として、各プロダクト名を入れたい
- マネージドポリシーの
コードサンプル
作成したいリソースを for_each
の連鎖を使用して以下のように定義できます。
sso.tf
## AWS SSO インスタンス(コンソールでAWS SSOを有効化後、データソースとして引っ張ってくる)
data "aws_ssoadmin_instances" "main" {}
## プロダクト名
locals {
products = {
"banana" = "BananaDeveloper"
"melon" = "MelonDeveloper"
"grape" = "GrapeDeveloper"
"orange" = "OrangeDeveloper"
}
}
## 許可セット
resource "aws_ssoadmin_permission_set" "products" {
for_each = local.products
name = each.value
description = "Test product developer"
instance_arn = tolist(data.aws_ssoadmin_instances.main.arns)[0]
session_duration = "PT4H"
}
## 参照権限のマネージドポリシー
resource "aws_ssoadmin_managed_policy_attachment" "readonly_products" {
for_each = aws_ssoadmin_permission_set.products
instance_arn = tolist(data.aws_ssoadmin_instances.main.arns)[0]
permission_set_arn = each.value.arn
managed_policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}
## インラインポリシー
resource "aws_ssoadmin_permission_set_inline_policy" "products" {
for_each = aws_ssoadmin_permission_set.products
instance_arn = tolist(data.aws_ssoadmin_instances.main.arns)[0]
permission_set_arn = each.value.arn
inline_policy = templatefile(
"./policies/AllowSessionManager.json",
{
projectName = each.key
}
)
}
AllowSessionManager.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:StartSession"
],
"Resource": [
"arn:aws:ec2:*:*:instance/*"
],
"Condition": {
"StringEquals": {
"ssm:ResourceTag/Project": [
"${projectName}"
]
}
}
},
{
"Effect": "Allow",
"Action": [
"ssm:TerminateSession",
"ssm:ResumeSession"
],
"Resource": [
"arn:aws:ssm:*:*:session/$${aws:PrincipalTag/ssousername}-*"
]
}
]
}
解説
そもそもAWS SSOの書き方については、以下を参照ください。
aws_ssoadmin_permission_set | Data Sources | hashicorp/aws | Terraform Registry TerraformがAWS SSO(Single Sign-On)に対応してました | DevelopersIO
また、AllowSessionManager.jsonの解説は以下を参照ください。(本ブログでは割愛します)
[AWS SSO版] Session Manager で特定の EC2 のみアクセスできるよう制限する | DevelopersIO
許可セット部分
まずは、基本的なfor_each
の使い方として、ローカル変数のproductsを繰り返し処理の対象とし、その数だけ許可セットを作成しています。
## プロダクト名
locals {
products = {
"banana" = "BananaDeveloper"
"melon" = "MelonDeveloper"
"grape" = "GrapeDeveloper"
"orange" = "OrangeDeveloper"
}
}
## 許可セット
resource "aws_ssoadmin_permission_set" "products" {
for_each = local.products
name = each.value
description = "Test product developer"
instance_arn = tolist(data.aws_ssoadmin_instances.main.arns)[0]
session_duration = "PT4H"
}
ポリシー部分
ここが本題です。
今度は for_each で複数作成した許可セットを繰り返し処理の対象とし、その数だけマネージドポリシーやインラインポリシーを作成しています。
各許可セットのARNを下記のように指定することができます。
インラインポリシー側では、各許可セットのキー部分(例"banana"など)を設定することができます。
## 参照権限のマネージドポリシー
resource "aws_ssoadmin_managed_policy_attachment" "readonly_products" {
for_each = aws_ssoadmin_permission_set.products
instance_arn = tolist(data.aws_ssoadmin_instances.main.arns)[0]
permission_set_arn = each.value.arn
managed_policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}
## インラインポリシー
resource "aws_ssoadmin_permission_set_inline_policy" "products" {
for_each = aws_ssoadmin_permission_set.products
instance_arn = tolist(data.aws_ssoadmin_instances.main.arns)[0]
permission_set_arn = each.value.arn
inline_policy = templatefile(
"./policies/AllowSessionManager.json",
{
projectName = each.key
}
)
}
上記がfor_each
の連鎖(chaining)となります。
終わりに
Terraform のfor_each
を連鎖させる方法を紹介しました。お役に立てば幸いです。
それではこの辺で。ちゃだいん(@chazuke4649)でした。