IAM Identity CenterのGoogle Workspace連携における制限をTerraformでいい感じに管理する
概要
SRE Advent Calendar 2024の12日目の記事です。
この記事では、IAM Identity Center の概要と、 Terraform による管理方法の一例を紹介します。
prineNumber では Google Workspace を IAM Identity Center の IdP として利用しています。
特定の Google Group のみを Identity Center の User として provisioning する設定にしています。[1]
一方で、Google Workspace を IdP として利用する場合の制限として、Google Workspace 側で Group を作成しても、Identity Center 側では Group が作成されません。
Google Workspace からの SCIM 自動同期は、現在、ユーザープロビジョニングに限定されています。現在、自動グループプロビジョニングはサポートされていません
Google Workspace および IAM アイデンティティセンターによる SAML と SCIM の設定 - AWS IAM Identity Center
この Google Workspace 利用時の制限を Terraform で吸収しましょう、というのがこの記事の主題です。
この記事では触れないこと
初期連携周り、Google WorkspaceとIAM Identity Centerの連携のセットアップについてはこの記事では触れません。
少なくとも弊社の環境では公式ドキュメント通りに設定すればOKでした。
IAM Identity Center とは
- 旧 AWS Single Sign-On の名の通りSSOをAWS上で行うためのサービス。
- AWS Organization と組み合わせて、Organization 配下のアカウントに対する権限を制御します。
- 詳しくは What is IAM Identity Center? - AWS IAM Identity Center を参照してください。
IAM Identity Center の登場人物
Application は使ってないので省略しています。
Organization Instance (Managing Instance)
- Organization 単位で1つ作成される IAM Identity Center のインスタンス
- おそらく1つの Organization で複数インスタンスは持てないと思うので、普段の運用で気にする必要はないですが、APIレベル(≒ Terraform)では紐づけが必要になってきます。
Identity Source
- User と Group を持ってくる Source
- ざっくりと以下の選択肢があります
- Identity Center 組み込みの
Identity Center directory
Azure AD
-
External identity provider
(今回はこちら)
- Identity Center 組み込みの
User
- Identity Center 上のエンティティの最小単位
- Identity Source からの provisioning 対象
Group
- User の集合
- 外部 IdP を利用している場合、コンソールから作成することはできません。
- Identity Source からの provisioning 対象ですが、仕様上、Google Workspace を IdP として利用している場合、provisioning されません
Permission Set
- 認可を行うためのリソース
- AWS Managed Policy と Customer Managed Policy をアタッチできます。
- AWS Managed Policy の場合は ARN を指定します。
- Customer Managed Policy の場合はポリシー名を指定します。
- このとき、Permission Set を紐づける AWS アカウント上に名前が完全一致する IAM Policy が存在している必要があります。
- Permission Set は単体では AWS アカウントに紐づけることはできず、User または Group とセットで紐づける必要があります。
Terraform で IAM Identity Center のリソースを管理しよう
Group については IdP 側からの provisioning もできず、マネジメントコンソールからも作成できないようになっていますが、APIからは作成可能です。そのため、 Group 管理部分を Terraform で巻き取ります。
ディレクトリの概観としては、以下のような形を取っています。
それぞれについて、コード例を交えて説明していきます。
identity-center
├── iam.tf # CI/CD向けの権限設定 (今回の記事のスコープ外)
├── organizations.tf # Organizationの設定まわり(今回の記事の記事のスコープ外)
├── permission_set.tf # 複数アカウントに対して適用するPermissionSetを定義
├── users.tf # provisiningされたUserをGroupにする定義
├── account_A.tf # 各アカウントごとのPermissionSetとGroupの紐づけを定義
├── account_B.tf
├── account_C.tf
(snip)
locals
頻繁に参照する必要のある値は data ソースからローカル変数に入れて参照するようにしています。
data "aws_ssoadmin_instances" "instance" {}
locals {
identity_store_instance_arn = tolist(data.aws_ssoadmin_instances.instance.arns)[0]
identity_store_id = tolist(data.aws_ssoadmin_instances.instance.identity_store_ids)[0]
}
permission_set.tf
複数アカウントに対して適用する Permission Set を定義しています。
例えば、 AWSマネージドポリシーである ReadOnlyAccess
をアタッチした Permission Set はどのアカウントでも利用したいのでこのファイルで定義しています。
resource "aws_ssoadmin_permission_set" "aws_readonly_access" {
name = "AWSReadOnlyAccess"
description = "This policy grants permissions to view resources and basic metadata across all AWS services"
instance_arn = local.identity_store_instance_arn
session_duration = "PT1H"
}
resource "aws_ssoadmin_managed_policy_attachment" "readonly_access" {
instance_arn = local.identity_store_instance_arn
managed_policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
permission_set_arn = aws_ssoadmin_permission_set.aws_readonly_access.arn
}
特定のアカウントのみに適用するPermission Setに関しては、それぞれのアカウント名のファイルに定義するようにしています。
users.tf
チームなど権限をまとめて付与したい対象を local 変数で定義し、Group を作成しています。
例えば、新しい人に権限追加するだけであれば local 変数だけ修正して Pull Request を出してもらえば OK な形にしています。[2]
locals {
team_cat = toset([
"kuro@example.com",
"shiro@example.com",
"tama@example.com",
])
team_dog = toset([
"pochi@example.com",
"taro@example.com",
])
team_owl = toset([
"omame@example.com",
])
all_users = setunion([
local.team_cat,
local.team_dog,
local.team_owl,
])
groups = {
"team_cat" = {
users = local.team_cat,
}
"team_dog" = {
users = local.team_dog,
}
"team_owl" = {
users = local.team_owl,
}
}
flatten_groups = flatten([
for group, users in local.groups : [
for user in users.users : {
group_name = group
user_name = user
}
]
])
}
data "aws_identitystore_user" "users" {
for_each = local.all_users
identity_store_id = local.identity_store_id
alternate_identifier {
unique_attribute {
attribute_path = "UserName"
attribute_value = each.value
}
}
}
resource "aws_identitystore_group" "groups" {
for_each = local.groups
identity_store_id = local.identity_store_id
display_name = each.key
}
resource "aws_identitystore_group_membership" "group_memberships" {
for_each = { for f in local.flatten_groups : "${f.group_name}_${f.user_name}" => f }
identity_store_id = local.identity_store_id
group_id = aws_identitystore_group.groups[each.value.group_name].group_id
member_id = data.aws_identitystore_user.users[each.value.user_name].id
}
account_*.tf
AWS アカウントと Permission Set と Group を紐づけます。
全アカウントで利用する (permission_set.tf
で定義した) Permission Set を使う場合は以下のような感じで定義します。
locals {
account_A_account_id = aws_organizations_account.production_account["account-A"].id
account_A_administrator_access_groups = toset([
"team_cat"
])
account_A_readonly_access_groups = toset(flatten([
local.account_A_administrator_access_groups,
"team_dog",
]))
}
resource "aws_ssoadmin_account_assignment" "account_A_administrator_access_groups" {
for_each = local.account_A_administrator_access_groups
instance_arn = local.identity_store_instance_arn
permission_set_arn = aws_ssoadmin_permission_set.aws_administrator_access.arn
principal_id = aws_identitystore_group.groups[each.value].group_id
principal_type = "GROUP"
target_id = local.account_A_account_id
target_type = "AWS_ACCOUNT"
}
resource "aws_ssoadmin_account_assignment" "account_A_readonly_access_groups" {
for_each = local.account_A_readonly_access_groups
instance_arn = local.identity_store_instance_arn
permission_set_arn = aws_ssoadmin_permission_set.aws_readonly_access.arn
principal_id = aws_identitystore_group.groups[each.value].group_id
principal_type = "GROUP"
target_id = local.account_A_account_id
target_type = "AWS_ACCOUNT"
}
特定のアカウントのみで使いたい Permission Set を定義する場合は account_*.tf
上で定義しています。
以下の例では、 CloudWatchLogsReadOnlyAccess
と、ユーザー定義の Policy (super-readonly-policy
) が付与された Permission Set を定義しています。
注意点として、AWS マネージドポリシーとカスタマーマネージドポリシーで Terraform Resource が別で用意されていること、Permission Set を紐づける AWS アカウント上に名前が完全一致する IAM Policy が存在している必要があることです。
locals {
account_A_cwlogs_readonly_groups = toset([
"team_owl",
])
account_A_cwlogs_readonly_managed_policies = [
"arn:aws:iam::aws:policy/CloudWatchLogsReadOnlyAccess",
]
account_A_cwlogs_readonly_customer_managed_policies = [
"super-readonly-policy", # この名前の IAM Policy がアカウントに存在する必要がある
]
}
resource "aws_ssoadmin_permission_set" "account_A_cwlogs_readonly" {
name = "CWLogsReadOnly"
description = "This policy grants permissions to use CW Logs"
instance_arn = local.identity_store_instance_arn
session_duration = "PT1H"
}
resource "aws_ssoadmin_managed_policy_attachment" "account_A_cwlogs_readonly_managed_policies" {
for_each = { for i, v in local.account_A_cwlogs_readonly_managed_policies : i => v }
instance_arn = local.identity_store_instance_arn
managed_policy_arn = each.value
permission_set_arn = aws_ssoadmin_permission_set.account_A_cwlogs_readonly.arn
}
resource "aws_ssoadmin_customer_managed_policy_attachment" "account_A_cwlogs_readonly_cosutomer_policy" {
for_each = { for i, v in local.account_A_cwlogs_readonly_customer_managed_policies : i => v }
instance_arn = local.identity_store_instance_arn
permission_set_arn = aws_ssoadmin_permission_set.account_A_cwlogs_readonly.arn
customer_managed_policy_reference {
name = each.value
path = "/"
}
}
resource "aws_ssoadmin_account_assignment" "account_A_cwlogs_readonly_groups" {
for_each = local.account_A_cwlogs_readonly_groups
instance_arn = local.identity_store_instance_arn
permission_set_arn = aws_ssoadmin_permission_set.account_A_cwlogs_readonly.arn
principal_id = aws_identitystore_group.groups[each.value].group_id
principal_type = "GROUP"
target_id = local.account_A_account_id
target_type = "AWS_ACCOUNT"
}
まとめ
IAM Identity Center の概要と、 Terraform による管理方法の一例を紹介しました。
Google Workspace 連携を利用した場合の仕様上の制限を Terraform で吸収することでいい感じに運用できることを目指しています。
We are hiring 🐈
TROCCO/COMETA を開発する primeNumber では、一緒に信頼性向上を実現してくれる SRE を絶賛募集中です! カジュアル面談もお待ちしています🙏
信頼性向上が選ばれる理由になる、裁量の大きいSRE【データ分析基盤総合支援SaaS TROCCO®/地方フルリモート有】 - 株式会社primeNumber
Discussion