はじめに
AWS IAM Identity Center(AWS Single Sign-On)を使用して、ユーザー管理を考えていく上で、Terraformを使用して構成管理を実現しようと思います。
作成したコードはgithub上に上がっているので、ご参考ください。
(※*.tfstate
は.gitignore
に記載し、git管理外としています。)
構成図
構成管理にはStateファイルの管理をサーバレスに行える点を考慮してTerraform Cloudを使用します
なお、今回は複数環境へのデプロイ含むCI/CDの設計などは考慮していません。
実装
Terraform Cloud と Githubの接続
こちらを参考にVSC接続を行う
AWS SSO 有効化/ユーザー・グループの作成
最初に AWS SSO を有効にする必要がありますが、残念ながら 2023/6/21 時点で Terraformを使用したコードでの有効化設定は非対応です。 そのため、コンソール上から有効化したいリージョンを選択し、手動で有効にします
続いて、AWS IAM Identity Centerでユーザー/グループ
を作成しますが、こちらも Terraform で作成することはできません。 そのため、マネジメントコンソールで作成します。
また、グループにアタッチしたいユーザーもここでアタッチしておきます。
方針
今回は、Admin
,Developer
,Guest
,System
と4つのグループを用意し、それぞれのグループにアタッチするポリシーをあらかじめ決定。
Admin
: SREチームのユーザーが属するグループ。AdministratorAccess
の権限を付与。
Developer
: SREチーム以外の開発者用グループ。開発作業に必要最低限のアクセスを付与。
Guest
: 開発チーム以外のグループ。ReadOnly
が主となるアクセス。
System
: システム系で使用するIAMポリシーをまとめたもの。
例:Admin
グループにはAdmin
のアクセス権限セットをアタッチするように設定。
tarraform
実装したものが、以下。
※マネージドポリシーとカスタマー管理ポリシーでは作成に使用するterraform resourceが異なるので、variableを別々で定義した。
########################################
# Terraformできないこと
# 下記に示すものはTerraformではできないので、手動でコンソール上(IAM Identity Center)から設定する必要がある
# 1. グループの作成・削除
# 2. ユーザーの追加・削除
# 3. グループにユーザーを追加・削除
########################################
data "aws_ssoadmin_instances" "example" {}
locals {
groups = var.aws_sso_group
policies = {
Admin = {
managed = var.aws_sso_admin_managed_policies
customer = var.aws_sso_admin_customer_policies
}
Guest = {
managed = var.aws_sso_guest_managed_policies
customer = var.aws_sso_guest_customer_policies
}
Developer = {
managed = var.aws_sso_developer_managed_policies
customer = var.aws_sso_developer_customer_policies
}
System = {
managed = var.aws_sso_system_managed_policies
customer = var.aws_sso_system_customer_policies
}
}
managed_policy_attachments = flatten([
for group, policies in local.policies : [
for policy in policies.managed : {
group = group
policy = policy
}
]
])
customer_policy_attachments = flatten([
for group, policies in local.policies : [
for policy in policies.customer : {
group = group
policy = policy
}
]
])
}
########################################
# IAM Identity Center グループを取得
########################################
data "aws_identitystore_group" "group" {
for_each = toset(local.groups)
identity_store_id = tolist(data.aws_ssoadmin_instances.example.identity_store_ids)[0]
alternate_identifier {
unique_attribute {
attribute_path = "DisplayName"
attribute_value = each.value
}
}
}
########################################
# アクセス許可セット作成
########################################
resource "aws_ssoadmin_permission_set" "PermissionSet" {
for_each = toset(local.groups)
name = each.value
description = each.value
instance_arn = tolist(data.aws_ssoadmin_instances.example.arns)[0]
}
locals {
groups_to_permission_set = var.groups_to_permission_set
assignment = [
for tmp in setproduct(local.groups, values(data.aws_identitystore_group.group)) : {
group_name = tmp[0]
group_id = tmp[1].group_id
permission_set_name = local.groups_to_permission_set[tmp[0]]
}
]
}
########################################
# アクセス許可セットにポリシーをアタッチ
########################################
resource "aws_ssoadmin_managed_policy_attachment" "managed_policy_attachment" {
for_each = { for item in local.managed_policy_attachments : "${item.group}.${item.policy}" => item }
instance_arn = aws_ssoadmin_permission_set.PermissionSet[each.value.group].instance_arn
managed_policy_arn = each.value.policy
permission_set_arn = aws_ssoadmin_permission_set.PermissionSet[each.value.group].arn
}
resource "aws_ssoadmin_customer_managed_policy_attachment" "customer_policy_attachment" {
for_each = { for item in local.customer_policy_attachments : "${item.group}.${item.policy}" => item }
instance_arn = aws_ssoadmin_permission_set.PermissionSet[each.value.group].instance_arn
permission_set_arn = aws_ssoadmin_permission_set.PermissionSet[each.value.group].arn
customer_managed_policy_reference {
name = split("/", each.value.policy)[1]
path = "/"
}
}
########################################
# IAM Identity Center グループにアクセス許可セットを関連付け
########################################
resource "aws_ssoadmin_account_assignment" "example" {
for_each = data.aws_identitystore_group.group
instance_arn = aws_ssoadmin_permission_set.PermissionSet[each.key].instance_arn
permission_set_arn = aws_ssoadmin_permission_set.PermissionSet[each.key].arn
principal_id = each.value.group_id
principal_type = "GROUP"
target_id = var.account_id
target_type = "AWS_ACCOUNT"
}
# variables.tfはvariable(変数)の宣言を行うところです。
variable "region" {
description = "AWS region"
default = "ap-northeast-1"
}
# terraform cloudから取得
variable "AWS_ACCESS_KEY_ID" {}
variable "AWS_SECRET_ACCESS_KEY" {}
variable "account_id" {
description = "The ID of the AWS account"
type = string
# 12桁のアカウントID
default = "xxxxxxxxxxxx"
}
########################################
# IAM Identity Centerのグループ・許可セットを定義
# グループには同名の許可セットを関連付ける
########################################
variable "aws_sso_group" {
description = "The list of identity store group"
type = list(string)
default = ["Admin", "Guest", "Developer", "System"]
// Add more if needed
}
variable "groups_to_permission_set" {
type = map(any)
default = {
Admin = "Admin"
Guest = "Guest"
Developer = "Developer"
System = "System"
// Add more if needed
}
}
########################################
# グループごとに使用するポリシーのarnを指定
# managedポリシーとカスタマー管理ポリシーでは作成するresourceが異なるので、それぞれvariableを用意
########################################
## Admin
variable "aws_sso_admin_managed_policies" {
description = "The list of admin managed policies"
type = list(string)
default = [
"arn:aws:iam::aws:policy/AdministratorAccess"
]
}
variable "aws_sso_admin_customer_policies" {
description = "The list of admin customer policies"
type = list(string)
default = []
}
## Guest
variable "aws_sso_guest_managed_policies" {
description = "The list of guest managed policies"
type = list(string)
default = []
}
variable "aws_sso_guest_customer_policies" {
description = "The list of guest customer policies"
type = list(string)
default = []
}
## Developer
variable "aws_sso_developer_managed_policies" {
description = "The list of developer managed policies"
type = list(string)
default = []
}
variable "aws_sso_developer_customer_policies" {
description = "The list of developer customer policies"
type = list(string)
default = []
}
## System
variable "aws_sso_system_managed_policies" {
description = "The list of system managed policies"
type = list(string)
default = []
}
variable "aws_sso_system_customer_policies" {
description = "The list of system customer policies"
type = list(string)
default = []
}
git pushをすればmainブランチへのマージをトリガーにterraform plan
, terraform apply
が実行される
※ git push する前にterraform fmt
でフォーマットを整形しておくと良い(もしくはCI/CDに組み込む)
さいごに
今回の実装を通して感じた気づき・学びをシェア
- Terraform Cloudを使用するとstateファイルの管理がすごく楽
- Terraformで対応していないリソースのIaCをどのように管理・運用していくかチームで共通認識を作る必要がある
- どこまでを
variable
,local
といった変数として扱うかの判断が難しい - Terraformは楽しい😁
参考
【Terraform】
【Terraform Cloud】
【AWS SSO】