CloudFormation StackSetsでOUのアカウントに対してリソースを自動デプロイ
Terraformを仲介し、CloudFormation StackSetsのSERVICE_MANAGEDモードを使うことで、OU配下の各アカウントへ自動的にS3バケットをデプロイする例。OUに新規アカウントを追加してもスタックが自動生成され、IAMロールの管理も不要となるため運用負荷が大幅に軽減される。
目次
はじめに
- こちらの記事で個人用Organizationsを導入できたので、特定のOUに対してCloudFormation Stacksetsを使ってリソースを自動デプロイしてみる
- CloudFormation StacksetsはCloudFormationの機能の一つ。シングルアカウントに適用するのはStackと呼ばれるが、複数アカウントのユースケースを想定されていて、複数アカウントに対してStackを生み出せるのがStackSets。
- CloudFormationのデプロイは相変わらずネイティブのAWS CLIで行うと取り回しが悪いので、こちらの記事のように、Terraformを仲介させる。
- StackSetsのように、複数アカウントに対してリソースを自動的に生み出すためのリソースは、おそらくTerraformのネイティブ機能だとできないので(CI/CDやTerragruntなどの他のツールを併用すればおそらく可能)、AWS公式の機能でこれができるというとややCloudFormationの優位性が出てくるところ。
AWS Organizationsの信頼されたアクセスの有効化
管理側・子側のIAMロールを作成せずにCloudFormation StacksetができるSERVICE_MANAGED
モードがある。これを使うとIAMロールの管理の煩雑さがなくなる。
SERVICE_MANAGED
モードを使うには、AWS OrganizationsでCloudFormation Stacksetsの信頼されたアクセスを有効化しておく必要がある。管理側のアカウントのマネジメントコンソールで、AWS Organizations→サービスで、「CloudFormation Stacksets」を有効化しておく。
有効化されていないと以下のようなエラーが出るはず。
api error ValidationError: You must enable organizations access to operate a service managed stack set
特定OUの全アカウントに対しS3バケットを作るStackSets
以下のようなOrganizationの構成になっているとしたときに、「dev OU」に対してS3バケットをデプロイするStackSetsを考える
AWS Organization (ルート)
├── management アカウント
└── OU: dev
└── develop アカウント
Terraform
CloudFormationを取り回しやすくするために、デプロイ用にTerraformを使う。ポイントは、
- 管理アカウント側だけのプロバイダで良い。子のアカウントの認証情報や承認はいらない
- StackSetsは
SERVICE_MANAGED
でデプロイする。SELF_MANAGED
だとIAMロールが面倒なことになる
###############################
# プロバイダー定義
###############################
# 管理アカウント用プロバイダー (profile: management)
provider "aws" {
profile = "management"
region = "ap-northeast-1"
}
###############################
# 変数定義 (variables.tf)
###############################
variable "bucket_suffix" {
description = "CloudFormation テンプレートへ渡す S3 バケット名のサフィックス(ユニークにするための値)"
type = string
default = "sample"
}
# 管理アカウント側から Organizations 情報を取得
data "aws_organizations_organization" "org" {
}
# ルート OU 配下の「dev」OU を取得
data "aws_organizations_organizational_unit" "dev" {
name = "dev"
parent_id = data.aws_organizations_organization.org.roots[0].id
}
# CloudFormation StackSet の作成 (SERVICE_MANAGED モード)
resource "aws_cloudformation_stack_set" "example" {
name = "example-stackset"
template_body = file("${path.module}/template.yaml")
permission_model = "SERVICE_MANAGED" # OU 単位のターゲット指定が利用可能
parameters = {
BucketSuffix = var.bucket_suffix
}
capabilities = ["CAPABILITY_NAMED_IAM"]
# AutoDeployment を有効化(新規アカウント加入時に自動展開)
auto_deployment {
enabled = true
retain_stacks_on_account_removal = false
}
}
# StackSet インスタンスの作成 (OU をデプロイ対象)
resource "aws_cloudformation_stack_set_instance" "example_instance" {
stack_set_name = aws_cloudformation_stack_set.example.name
region = "ap-northeast-1"
deployment_targets {
organizational_unit_ids = [data.aws_organizations_organizational_unit.dev.id]
}
}
aws_cloudformation_stack_set リソースで StackSet を作成
name
で StackSet 名を指定template_body
に読み込む CloudFormation テンプレート (template.yaml) を指定permission_model
= “SERVICE_MANAGED” により、AWS が StackSet の操作権限を管理し、OU 単位でのデプロイが可能SERVICE_MANAGED
にすれば、AWS Organizationsと連携してOU単位でのデプロイが可能になる。SELF_MANAGED
モードで利用していた administration_role_arn や execution_role_name は不要になり、AWS側で権限管理が行われるようになる。
auto_deployment
を enabled = true にすることで、新規アカウントが OU に追加されたときに自動的にスタックが作成される
aws_cloudformation_stack_set_instance リソースで StackSet の実際のデプロイ先を指定
stack_set_name
に上で作成した StackSet 名を指定region
は “ap-northeast-1” として、東京リージョンにデプロイdeployment_targets.organizational_unit_ids
に、対象となる OU の ID を指定 (data.aws_organizations_organizational_unit.dev.id)
CloudFormationのコード(template.yaml)
S3バケットをデプロイするだけ。ただ、S3バケット名はグローバルでユニークでないといけないので、最後にアカウントIDをつけてStackSets間で差を出すようにする。ここはデプロイされる側(子アカウント)のIDが入る。AWS::AccountId
で取れる。
AWSTemplateFormatVersion: '2010-09-09'
Description: >
シンプルな S3 バケットを作成する CloudFormation テンプレート
Parameters:
BucketSuffix:
Type: String
Description: "バケット名のサフィックス(ユニークになるよう設定)"
Resources:
MyS3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub 'stack-sets-bucket-${BucketSuffix}-${AWS::AccountId}'
Outputs:
BucketName:
Description: "作成された S3 バケットの名前"
Value: !Ref MyS3Bucket
結果
子アカウント側
- S3バケットができている
- スタックはできているが、StackSetsはできていない
親アカウント側
- StackSetsはできているが、スタックはできていない
ということで、いずれも想定通りの挙動。StackSets→スタックを生んでいる
所感
- これのすごいところは、OUにあとからアカウントを追加してもよしなにStackSets側でデプロイしてくれること。この機能はさすがにTerraform単体だと再現できなそう。
- ただ、CloudFormation単体のメンテンスコストが異様に高いので、それで打ち消し合ってイーブンなのかなという印象。Terraformでこのへんがよしなにできたら自分はTerraform使うかな。
- デプロイするリソースはそこまで面倒ではないが、アカウント数が異様に多くて数百とかだとこの手法は有効そう
Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内
技術書コーナー
北海道の駅巡りコーナー