IAM Identity Centerの以下操作をやってみました。
- IAM Identity Centerの有効化
- IAM Identity Centerの管理の委任
- ユーザー/グループ/許可セットの作成
2024年1月時点では、以下は対応するTerraform Resourceがないため、手動で行なっています。その他の作業はTerraformを使いました。
- IAM Identity Centerの有効化
- IAM Identity Centerの管理の委任
Terraformコードは以下にあります。
msato0731/terraform-sample/iic
前提
この手順はOrganizationsを利用していることを前提にしています。
管理アカウントへのアクセスを最小限にするために、IAM Identity Center管理を他のアカウントに委任します。
そのため、管理アカウントの他にIAM Identity Center管理用のアカウントを用意する必要があります。
1. IAM Identity Centerを有効化する(管理アカウント)
Organizationの管理アカウントにログインし、マネジメントコンソールからIAM Identity Centerにアクセスします。
有効にする
を選択します。
有効化に成功すると、以下のようにダッシュボード画面が表示されます。
2. IAM Identity Centerの管理の委任(管理アカウント)
設定
-> 管理
-> 委任された管理者を登録
-> アカウントを登録
の順に選択します。
委任先AWSアカウントを選択し、`アカウントを登録`を選択します。
以降の作業は、委任先アカウントで行います。
3. ユーザー・グループ・許可セットの作成
AWSアカウントは以下の4つがあるものとします。
AWSアカウント | 説明 |
---|---|
Prd-HogeService | HogeService 本番環境 |
Stg-HogeService | HogeService STG環境 |
Prd-FugaService | FugaService 本番環境 |
Stg-FugaService | FugaService STG環境 |
ユーザーとグループは以下です。
ユーザー | 説明 | グループ |
---|---|---|
taro.test | インフラ管理責任者 | HogeAdministrator |
FugaAdministrator | ||
jiro.test | Hoge・Fuga Serviceの開発者 | HogeDevelopper |
FugaDevelopper | ||
saburo.test | Hoge Serviceの開発者 | HogeDevelopper |
グループに付与されている権限は以下です。
グループ | 説明 |
---|---|
*Administrator | 対象サービスの本番/STG: Admin |
*Developper | 対象サービスの本番:Read・STG:Admin |
AWSアカウント情報の設定
terraform.tfvarsを作成して、アカウントIDを記載します。
cp terraform.tfvars.sample terraform.tfvars
terraform.tfvars
account_ids = {
# アカウントIDを書き換える
hoge_prd : "123456789012",
hoge_stg : "555555555555",
fuga_prd : "999999999999",
fuga_stg : "012345678901",
}
ユーザー・グループ・ユーザーとグループの関連付け
ユーザーとグループの記述は以下になります。
locals.tf
################################################################################
# Groups
################################################################################
groups = {
hoge_admin = {
name = "HogeAdministrator"
description = "hoge service prd/stg:admin"
}
hoge_dev = {
name = "HogeDevelopperTeam"
description = "hoge servide prd:read, stg:admin"
}
fuga_admin = {
name = "FugaAdministrator"
description = "fuga service prd/stg:admin"
}
fuga_dev = {
name = "FugaDevelopperTeam"
description = "fuga servide prd:read, stg:admin"
}
}
################################################################################
# Users
################################################################################
users = {
"taro.test@example.com" = {
name = {
family_name = "Test"
given_name = "Taro"
}
groups = [
"hoge_admin",
"fuga_admin",
]
}
"jiro.test@example.com" = {
name = {
family_name = "Test"
given_name = "Jiro"
}
groups = [
"hoge_dev",
"fuga_dev"
]
}
"saburo.test@example.com" = {
name = {
family_name = "Test"
given_name = "Saburo"
}
groups = [
"hoge_dev"
]
}
}
################################################################################
# Membership
################################################################################
# ユーザーとユーザーが属するグループの組み合わせを作成
users_groups_combined = [
for user, user_data in local.users : {
for group in user_data.groups :
"${user}_${group}" => {
"user" = user
"group" = group
}
}
]
# ユーザーがブロックごとに分かれているため、ユーザーとグループの組み合わせを1つのブロックにまとめる
users_groups_membership = zipmap(
flatten(
[for item in local.users_groups_combined : keys(item)]
),
flatten(
[for item in local.users_groups_combined : values(item)]
)
)
users_groups.tf
################################################################################
# Group
################################################################################
resource "aws_identitystore_group" "this" {
for_each = local.groups
identity_store_id = local.identity_store_id
display_name = each.value["name"]
description = each.value["description"]
}
################################################################################
# User
################################################################################
resource "aws_identitystore_user" "this" {
for_each = local.users
identity_store_id = local.identity_store_id
display_name = join(" ", [each.value.name.given_name, each.value.name.family_name])
user_name = each.key
name {
family_name = each.value["name"]["family_name"]
given_name = each.value["name"]["given_name"]
}
}
################################################################################
# Membership
################################################################################
resource "aws_identitystore_group_membership" "this" {
for_each = local.users_groups_membership
identity_store_id = local.identity_store_id
group_id = aws_identitystore_group.this[each.value["group"]].group_id
member_id = aws_identitystore_user.this[each.value["user"]].user_id
}
ポイントは、aws_identitystore_group_membership
でユーザーとグループを関連づける必要があることです。
愚直に書くなら、このリソースはユーザーとグループの関連付けの数分書く必要があります。
local users_groups_membership
でユーザーとグループの関連付け用の配列を作っています。
users
配列から、group
とuser
だけの配列を作っています。
users_groups_combined_output
+ users_groups_combined = [
+ {
+ "jiro.test@example.com_fuga_dev" = {
+ group = "fuga_dev"
+ user = "jiro.test@example.com"
}
+ "jiro.test@example.com_hoge_dev" = {
+ group = "hoge_dev"
+ user = "jiro.test@example.com"
}
},
+ {
+ "saburo.test@example.com_hoge_dev" = {
+ group = "hoge_dev"
+ user = "saburo.test@example.com"
}
},
+ {
+ "taro.test@example.com_fuga_admin" = {
+ group = "fuga_admin"
+ user = "taro.test@example.com"
}
+ "taro.test@example.com_hoge_admin" = {
+ group = "hoge_admin"
+ user = "taro.test@example.com"
}
},
]
このままだと、user
ごとにブロックが分かれてしまっていて、for_each等を使いづらいです。
そのため、ユーザーとグループの組み合わせを1つのブロックにまとめます。
users_groups_membership-output
+ users_groups_membership = {
+ "jiro.test@example.com_fuga_dev" = {
+ group = "fuga_dev"
+ user = "jiro.test@example.com"
}
+ "jiro.test@example.com_hoge_dev" = {
+ group = "hoge_dev"
+ user = "jiro.test@example.com"
}
+ "saburo.test@example.com_hoge_dev" = {
+ group = "hoge_dev"
+ user = "saburo.test@example.com"
}
+ "taro.test@example.com_fuga_admin" = {
+ group = "fuga_admin"
+ user = "taro.test@example.com"
}
+ "taro.test@example.com_hoge_admin" = {
+ group = "hoge_admin"
+ user = "taro.test@example.com"
}
}
アクセス権限セット
アクセス権限セットの記述は以下になります。
locals.tf
########################
# Permissions
########################
permission_sets = {
"admin" = {
name = "AdministratorAccess"
description = "Provides full access to AWS services and resources."
managed_policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
},
"read_only" = {
name = "ReadOnlyAccess"
description = "Provides read-only access to AWS services and resources."
managed_policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}
}
################################################################################
# Account Assignments
################################################################################
account_assignments = [
# Hoge Service Production
{
account_id = var.account_ids.hoge_prd
group = "hoge_admin"
permission_set = "admin"
},
{
account_id = var.account_ids.hoge_prd
group = "hoge_dev"
permission_set = "read_only"
},
# Hoge Service Staging
{
account_id = var.account_ids.hoge_stg
group = "hoge_admin"
permission_set = "admin"
},
{
account_id = var.account_ids.hoge_stg
group = "hoge_dev"
permission_set = "admin"
},
# Fuga Service Production
{
account_id = var.account_ids.fuga_prd
group = "fuga_admin"
permission_set = "admin"
},
{
account_id = var.account_ids.fuga_prd
group = "fuga_dev"
permission_set = "read_only"
},
# Fuga Service Staging
{
account_id = var.account_ids.fuga_stg
group = "fuga_admin"
permission_set = "admin"
},
{
account_id = var.account_ids.fuga_stg
group = "fuga_dev"
permission_set = "admin"
}
]
# アカウントID-グループ名-PermissionSet名をキーに設定
assignment_map = {
for a in local.account_assignments :
format("%v-%v-%v", a.account_id, local.groups[a.group].name, local.permission_sets[a.permission_set].name) => a
}
}
permission_sets.tf
resource "aws_ssoadmin_permission_set" "this" {
for_each = local.permission_sets
name = each.value.name
description = each.value.description
instance_arn = local.instance_arn
}
resource "aws_ssoadmin_managed_policy_attachment" "this" {
for_each = local.permission_sets
instance_arn = local.instance_arn
managed_policy_arn = each.value.managed_policy_arn
permission_set_arn = aws_ssoadmin_permission_set.this[each.key].arn
}
account_assignments.tf
resource "aws_ssoadmin_account_assignment" "this" {
for_each = local.assignment_map
instance_arn = local.instance_arn
permission_set_arn = aws_ssoadmin_permission_set.this[each.value.permission_set].arn
principal_id = aws_identitystore_group.this[each.value.group].group_id
principal_type = "GROUP"
target_id = each.value.account_id
target_type = "AWS_ACCOUNT"
}
aws_ssoadmin_account_assignment
でアクセス許可セットとグループとアカウントIDを渡す必要があります。
上記の組み合わせをlocalaccount_assignments
で定義しています。
local変数account_assignments
を使ってfor_eachでリソースを作っても良いですが、以下の理由からlocal変数 user_group_membership
で加工しました。
- resource側のfor_eachの記述がシンプルになる
- リソースのキーが連番ではなく、ユニークな文字列になり分かりやすい
- 一部リソースの作成だけ失敗したときに、対象箇所を見つけやすい
resource "aws_ssoadmin_account_assignment" "this" {
# そのまm
for_each = {
for idx, assignment in local.account_assignments: idx => assignment
}
users_groups_membership
では、以下のjiro.test@example.com_fuga_dev
のようにユニークなIDが付きます。
users_groups_membership_output
+ users_groups_membership = {
+ "jiro.test@example.com_fuga_dev" = {
+ group = "fuga_dev"
+ user = "jiro.test@example.com"
}
+ "jiro.test@example.com_hoge_dev" = {
+ group = "hoge_dev"
+ user = "jiro.test@example.com"
}
+ "saburo.test@example.com_hoge_dev" = {
+ group = "hoge_dev"
+ user = "saburo.test@example.com"
}
+ "taro.test@example.com_fuga_admin" = {
+ group = "fuga_admin"
+ user = "taro.test@example.com"
}
+ "taro.test@example.com_hoge_admin" = {
+ group = "hoge_admin"
+ user = "taro.test@example.com"
}
}
おわりに
今回はカスタマー管理ポリシーやインラインポリシーは考慮せずに書きました。
追加したい場合は、Local変数permission_sets
に要素を足して、アタッチ用のresourceを定義すればできそうです。
以上、AWS事業本部の佐藤(@chari7311)でした。