ついにControl Towerのアカウント発行からカスタマイズまでIaC対応!Account Factory for Terraform (AFT)が新登場 #reinvent

Terraformユーザー待望の、Terraform x Control Tower の扉がついに開かれた
2021.12.01

ちゃだいん(@chazuke4649)です。

Control Towerのアカウント発行・プロビジョニングがIaC対応しました!その名も AWS Control Tower Account Factory for Terraform (AFT) の登場です。

2社それぞれのリリース記事と、ツイートが以下です。

AWS Control Tower introduces Terraform account provisioning and customization

HashiCorp Teams with AWS on New Control Tower Account Factory for Terraform

最初にまとめ(個人の感想等も含む)

  • Control TowerのアカウントファクトリーによるAWSアカウント発行がついにコードで実行できるようになった
  • アカウントファクトリーの課題である並列実行NG(1つずつ作らなきゃいけない仕様)を補完し、一度に複数登録しても順番にやってくれるようになった
  • AWSアカウントごとの環境の初期設定・独自ガードレール・アカウントベースライン設定も可能。まだ試してないけど期待大
  • マスターアカウントのTerraformリソース量が増えplan/apply実行時間が大幅に増えるので注意 (場合によって.tfstateファイル分けて管理するのもあり)
  • 暫定で、Control TowerによるAWSアカウントの発行から独自ガードレール設定まで最もIaCでカバーしている選択肢
  • ただし、現時点で Control Towerの全ての機能をTerraformで実行できるわけではない

2022.1.11更新: コストにご注意ください。本構成はセキュリティ強化のためVPC-Lambdaによる関数実行と他サービス通信を行っており、現時点で14点とインターフェース型VPCエンドポイントと、2点のNATGWが作成されます。(東京リージョンにて 約$400/月 程度の料金が発生します。)これを避けたい場合は次回記事の終わりの方をご覧ください。

構成図

AFTの実体であるTerraformモジュールを実行すると、ざっと下図のような構成が敷かれることになります。

上図はTerraformチュートリアルより引用:Manage AWS Accounts Using Control Tower Account Factory for Terraform | Terraform - HashiCorp Learn

どういうこと?

前段

近年の傾向からマルチアカウント管理においてControl Towerの利用やニーズ・期待が高まる一方であり、AWSとしてもControl Tower自体のアップデートやサポートツールの開発が大変盛んな状況です。サポートツールとしては、CFnと定義ファイルYAMLによってCTをカスタマイズできるCfCT や CDKによるセキュリティベースラインを簡単作成するBLEA など 様々な選択肢が登場しました。そんな中でTerraformユーザーはどうしていたでしょうか。

今まではControl Towerとのインテグレーションの決定打がなく、Visional社のようにTerrafomでControl Towerライクな独自ランディングゾーンを構築するパターンや、Hashicorp社によるランディングゾーンGroutwork社によるランディングゾーンなどのソリューションを検討したこともあったと思います。そんな中でついに、TerraformユーザーがTerraformでControl Towerを実行・管理するための最有力選択肢となりうるのが今回ご紹介する AFT です。

何が嬉しい?

AFTの実体はTerraform Registryで公開されているパブリックモジュールです。なので、Terraformユーザーはこのモジュールを呼び出し、基本的にはTerraformのコードを書くだけで、Control TowerのAWSアカウントのプロビジョニングを自動化することができます。

メリットや特徴は先ほどのまとめでも挙げましたが、何より目玉となるのは、ついにアカウントファクトリーによるAWSアカウント発行がコードで実行できるようになった点です。Control TowerにはAWS CLIも現時点でサポートしていなく、アカウントファクトリーによるアカウント発行はコンソール操作による実行のみとなっていました。これが今回このソリューションを使うと、git pushでアカウントファクトリーを実行できます。

また、もう1点アカウントファクトリーにてControl Tower管理者がよく持つ課題として、並列実行ができず1つずつ作らなきゃいけない課題がありました。このソリューションはここも補完し、一度に複数登録しても順番にやってくれるようになっています。

他にもいろんなことができますが、今回はまずここに焦点を絞って実際にやってみたいと思います。

参考情報

関連するドキュメントが色々あるので参考にしたものをリストアップしています。

AWSブログ

基本的には本内容に沿って進めています。全体理解などもまずはここを読むのが良いです。
New – AWS Control Tower Account Factory for Terraform | AWS News Blog

AWSドキュメント

詳細の確認に使用します。
Provision accounts with AWS Control Tower Account Factory for Terraform - AWS Control Tower

Terraformチュートリアル

Terraform側のチュートリアルもあります。手順を細かく紹介しているので、やってみる際はこっちベースでやるのもありです。
Manage AWS Accounts Using Control Tower Account Factory for Terraform | Terraform - HashiCorp Learn

Terraform Moduleページ

aws-ia/control_tower_account_factory/aws | Terraform Registry

Terraform Module GitHub リポジトリ

aws-ia/terraform-aws-control_tower_account_factory: AWS Control Tower Account Factory

やってみる

基本的には先述のAWSブログやAWSドキュメントの手順に従いますが、途中試行錯誤したりしてます。

前提

  • ControlTowerは有効化済み
  • 対象のVCSをGitHubとする
  • Terraformは OSS版を利用する(EnterpriseやTerraform Cloudではない)
  • ControlTowerの管理アカウントを従来の呼び名である"マスターアカウント"と呼称する
  • AFT管理用のアカウントをそのまま"AFT管理アカウント"と呼称する

手順

  • 展開前の準備
    • 1 AFT管理アカウントを用意する(マスターアカウント)
    • 2 OUを作成する(マスターアカウント)
  • 展開作業
    • 3 AFT Moduleをapplyする(マスターアカウント)
  • 展開後の手順
    • 4 GitHubとCodeSeriesを接続させる(AFT管理アカウント)
    • 5 Service Catalogに権限を追加する(マスターアカウント)
  • 本作業:AWSアカウントを発行する
    • 6 専用リポジトリにコードをプッシュする(AFT管理アカウント)

展開前の準備

1. AFT管理アカウントを用意する(マスターアカウント)

AFTを利用するには、AFT管理専用のアカウントを準備する必要があります。公式ブログでは新たに通常のアカウントファクトリーから発行していますが、今回は余剰のAWSアカウントがあったためそれを使用します。 よって、内容は割愛します。

2. OUを作成する(マスターアカウント)

AFT管理専用アカウントをAFT管理専用のOUに配置することが推奨されています。今回はマスターアカウントにて、 AFT-ManagementOU を作成しました。

展開作業

3. AFT Moduleをapplyする(マスターアカウント)

それでは、AFTのTerraformモジュールを構築します。

先に注意点です。

terraform applyを実行してから完了するまで30分程度時間がかかります。使用するクレデンシャルの有効期限切れに注意してください。さらにマスターアカウントにTerraformとして306のリソースを作成することになり、以後plan/applyにかかる時間が数十秒増えます。それが困る場合は、tfstateファイルを分けたり、処理時間軽減するオプションをコマンド実行時につけるなど検討した方が良いかもしれません。

すでにマスターアカウント用のTerraformリポジトリがあったので、リポジトリのexamplesを参考に以下tfファイルを追加しました。

aft.tf

module "aft" {
  source  = "aws-ia/control_tower_account_factory/aws" 
  version = "1.0.3" 

  # Required Parameters
  ct_management_account_id    = "111111111111"
  log_archive_account_id      = "222222222222"
  audit_account_id            = "333333333333"
  aft_management_account_id   = "444444444444"
  ct_home_region              = "ap-northeast-1"
  tf_backend_secondary_region = "ap-southeast-1"

  # Optional Parameters
  terraform_distribution = "oss"
  vcs_provider           = "github"
  account_request_repo_name                     = "sampleOrg/aft-account-request" 
  global_customizations_repo_name               = "sampleOrg/aft-global-customizations" 
  account_customizations_repo_name              = "sampleOrg/aft-account-customizations" 
  account_provisioning_customizations_repo_name = "sampleOrg/aft-account-provisioning-customizations" 

  # Optional Feature Flags
  aft_feature_delete_default_vpcs_enabled = false
  aft_feature_cloudtrail_data_events      = false
  aft_feature_enterprise_support          = false
}

補足事項

  • モジュールのバージョン指定を追加します
  • CTのホームリージョンは東京になっており、今回用のセカンダリリージョンとしてはシンガポールを指定します
  • terraform_distribution: デフォルトでOSSが選ばれますが、今回は明示的に指定します
  • vcs_provider: デフォルトはCodeCommitで、今回はGitHubを選択します
  • 各リポジトリの指定: 本来は会社のGitHub Organizationsに作成したリポジトリで試そうとしましたが、ちょっとうまくいかなかったので個人アカウントのリポジトリにしています。このあたりの詳細は 「4. GitHubとCodeSeriesを接続させる(AFT管理アカウント)」 にて説明しているので合わせてご参考ください。

これでローカルから terraform plan します。

## 以上省略

306 to add, 0 to change, 0 to destroy.

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.

306ものリソースが作られます... ちょっとためらいましたが、せっかくなのでそのままGOします。 terraform apply -auto-approveします。

30分程度経過後

## 以上省略

Apply complete! Resources: 306 added, 0 changed, 0 destroyed.

完了しました。

マネジメントコンソールから確認するといろんなリソースが作られていました。ざっと各アカウントに以下リソースが作成されたようです。

Resource considerations for AWS Control Tower Account Factory for Terraform - AWS Control Tower

展開後の手順

4. GitHubとCodeSeriesを接続させる(AFT管理アカウント)

AFTモジュールのデプロイ自体は既存のマスターアカウントリポジトリにファイルを追加して実行しましたが、運用で使用する「どんなAWSアカウントを何個作るか?」や「それらにどんな初期設定・ベースラインを敷くか?」などは、別途専用のリポジトリにて行うことになります。これらのリモートリポジトリをCodeCommitか外部サービスかを選べました。今回はGitHubにしているため、GitHubとCodeSeriesを連携させる作業が追加となってます。

実作業で少し詰まった点をお話しすると、今回GitHubのリポジトリは個人アカウントに作成しました。本来は会社のGitHub Organizations内にて作成するつもりでしたが、この後の工程にある「対象のGitHubに GitHub Apps として AWS Connector for GitHub をインストールする」作業があります。この作業自体はGH管理者が行う必要があり、今回はスムーズにいかなかったので個人アカウントに切り替えた経緯があります。このインストールは組織に対して1度だけで良いようなのですでに自分の組織には当該Appがインストールされている、あるいは自分がGH管理者である場合は問題なく組織のGitHubにて進行して問題ないように思われます。

Create a connection to GitHub - Developer Tools console

話は戻って作業に入ります。CodeCommitなどで検索しCodeSeriesのコンソールを開き、Connectionsを開きます。

下図のようにGitHubとの接続設定がpendingになっています。 これをupdate pending connections します。

まだ対象のGitHubアカウント(組織or個人)に AWS Connector for GitHub がインストールされていない(下図検索窓で何も出てこない)場合は、Install a new app へ進みます。

その後、GitHub側のポップアップが表示され、対象のアカウントを選択します。その後に、All repositoriesか Only select repositories か選びますが、上記ドキュメント通り All repositories を選択し、Installすれば基本的には完了です。

5. Service Catalogに権限を追加する(マスターアカウント)

通常のアカウントファクトリーを使用したことがある人にはお馴染みの作業ですが、Service Catalogのアカウントファクトリー用のポートフォリオにて、AFTのIAMロールを許可します。

下図の通り、RolesからAWSAFTExcutionを選択し、Add accessします。

本作業:AWSアカウントを発行する

6. 専用リポジトリにコードをプッシュする(AFT管理アカウント)

さて、これでようやく準備が整いました。実際にコード経由でAWSアカウントを発行してみます。

その前にAFT管理アカウントでCodePipelineコンソールを開きます。 下図の通り、モジュールデプロイ時に作られたパイプラインが、もちろんリポジトリ接続前なのもありコケてます。

次に行うAFTリポジトリにコードをプッシュすることで、これらパイプラインが走りAWSアカウントが発行されるところを検証します。

各AFTリポジトリについては、ざっと以下のような役割です。

  1. aft-account-request: 発行したいAWSアカウントの必要情報を入力する(アカウントファクトリーの入力画面で入力する内容)
  2. aft-account-provisioning-customizations: Step Functionsを使用して、新しいアカウントのプロビジョニングプロセスをカスタマイズし、追加の環境との統合を簡素化する
  3. aft-global-customizations: プロビジョニングされたすべてのアカウントをカスタマイズできる
  4. aft-account-customizations: プロビジョニングされた任意のアカウントをカスタマイズできる

今回は、特段カスタマイズはしないので、1.と念のため2.だけコードを追加します。

元となるファイルはAFTリポジトリのsources/aft-customizations-repos配下のフォルダを使用します。

terraform-aws-control_tower_account_factory/sources/aft-customizations-repos at main · aws-ia/terraform-aws-control_tower_account_factory

まず、GitHub側で4つのリポジトリ作成し、ローカルにクローンしておきます。次に、上記AFTリポジトリをクローンして、ローカルに持ってきます。その後、今回追加するAFTリポジトリの1.と2.のフォルダ内のファイルを一式コピーし、事前準備した4つの空のリポジトリにそれぞれ複製します。注意点としては、ディレクトリ構造が変わらないよう(余計なフォルダが入らないよう)に注意します。(最終的なディレクトリ構造は下記を参照)

その次に、aft-account-requestのexamples/account-request.tfをコピーし、terraformフォルダ配下に複製します。

その複製したファイルを今回は以下の通り修正しました。

module1つに対して1つのAWSアカウントが発行されます。今回は sandbox-aft-01sandbox-aft-02 の2つを発行します。

account-request.tf

module "sandbox_aft_01" { ## module名は一意である必要がある
  source = "./modules/aft-account-request"

  control_tower_parameters = {
    AccountEmail              = "aws+sandbox-aft-01@example.jp" 
    AccountName               = "sandbox-aft-01"
    ManagedOrganizationalUnit = "Sandbox"
    SSOUserEmail              = "aws+master@example.jp" ## 既存SSOユーザーのメールアドレスを入れればSSOユーザーは作られない
    SSOUserFirstName          = "NOT" ## SSOユーザー作成が不要な場合、既存SSOユーザーのメールアドレス入力とセットで、適当な文字列を入力
    SSOUserLastName           = "USE" ## SSOユーザー作成が不要な場合、既存SSOユーザーのメールアドレス入力とセットで、適当な文字列を入力
  }

  account_tags = {
    "Project"     = "test"
    "Environment" = "Production"
    "AFT"         = true
  }

  change_management_parameters = {
    change_requested_by = "chadain"
    change_reason       = "Test AFT 1"
  }
}

module "sandbox_aft_02" { 
  source = "./modules/aft-account-request"

  control_tower_parameters = {
    AccountEmail              = "aws+sandbox-aft-02@example.jp"
    AccountName               = "sandbox-aft-02"
    ManagedOrganizationalUnit = "Sandbox"
    SSOUserEmail              = ""aws+master@example.jp""
    SSOUserFirstName          = "NOT"
    SSOUserLastName           = "USE"
  }

  account_tags = {
    "Project"     = "test"
    "Environment" = "Production"
    "AFT"         = true
  }

  change_management_parameters = {
    change_requested_by = "chadain"
    change_reason       = "Test AFT 2"
  }
}

他パラメータの詳細はAFTリポジトリのREADME.mdを参照ください。

最終的に以下のようなディレクトリ構造になります。

% tree aft-account-provisioning-customizations aft-account-request -L 3
aft-account-provisioning-customizations
├── README.md
└── terraform
    ├── aft-providers.jinja
    ├── backend.jinja
    ├── iam
    │   ├── role-policies
    │   └── trust-policies
    ├── iam.tf
    ├── states
    │   └── customizations.asl.json
    ├── states.tf
    └── versions.tf
aft-account-request
├── README.md
├── examples
│   └── account-request.tf
└── terraform
    ├── account-request.tf
    ├── aft-providers.jinja
    ├── backend.jinja
    └── modules
        └── aft-account-request

ではこの内容でコミットし、プッシュしてみます。

しばらくすると、CodePipelineは元々コケてた2つのパイプラインが成功し、新たに2つの Customizations-pipeline ができて失敗してました。(Customizationsの方は、残り2つのリポジトリには空のままなのと、そもそもカスタマイズまではするつもりがないのでOKとします)

最初2つのパイプライン成功後に数分待つと、以下の通りControl Tower側でもService Catalog側でも新しいAWSアカウントが発行リクエストが通ったことが確認できました!

20分程度待つと1つ目のAWSアカウント発行が完了しました。    続いてその後数分待つと、2つ目のリクエストが通ったことが確認できました!

これで、コード実行によりアカウントファクトリーにてアカウント発行ができることと、2つ同時に実行してもよしなに自動で順番にやってくれることが確認できました。

終わりに

Control TowerとTerraformのコラボレーション、個人的には結構驚きでしたが嬉しい限りです。まだ複数アカウント発行することしか試せてないので、続いてカスタマイズ周りの検証も進めていきたいと思います。

それではこの辺で。ちゃだいん(@chazuke4649)でした。