Account Factory for Terraform (AFT) で AWSアカウントのカスタマイズを試してみた

PythonスクリプトでもTerrformコードでもアカウントの初期設定として自動反映できるぞ
2022.01.12

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

今回はAWS Control Tower Account Factory for Terraform (AFT) のアカウントカスタマイゼーション機能を触ってみます。

以下記事の続編です。合わせてご確認ください。

やること概要と構成図

前回も引用した構成図を再掲します。

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

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

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

今回は主に、アカウントカスタムリポジトリを使用して、指定したAWSアカウントに事前定義したリソースの作成などが可能か試してみます。

やってみる

前提

基本前回と同様ですが、下2つが追加となります。

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

手順

アカウントカスタムリポジトリ(およびグローバルカスタムリポジトリ)を使ってAWSアカウントをカスタマイズしたい場合、2つの状況が想定されます。

(1)新しいAWSアカウントに独自カスタマイズを適用したい

以下の通り、事前に準備しておけば、自動化されたフローで進められます。

  • 1.アカウントカスタムリポジトリとグローバルカスタムリポジトリにコードを記述しプッシュする(いずれか一方だけ行いたい場合でも、片方が空だと失敗します)
  • 2.リクエストリポジトリに新しく発行したいAWSアカウントの設定を記述しプッシュする
  • 3.(AFTパイプラインが動き出し、各カスタム設定が反映されたAWSアカウントが発行される)

(2)既に発行したAWSアカウントに独自カスタマイズを新たに適用、あるいは変更したい

遡って他のAWSアカウントへ勝手に反映されることはありません。現時点では手動でStepFunctionsステートマシンを実行する必要があります。

  • 1.アカウントカスタムリポジトリとグローバルカスタムリポジトリにコードを記述しプッシュする(いずれか一方だけ行いたい場合でも、片方が空だと失敗します)
  • 2.(アカウントカスタムリポジトリの場合のみ)リクエストリポジトリに既に発行したAWSアカウントの設定に、[account_customizations_name]パラメータを追加しプッシュする
  • 3.手動で[aft-invoke-customizations]Step Functionsステートマシンを実行する

詳細は以下ドキュメント当該箇所をご覧ください。

Re-invoke customizations  - Account customizations | AWS Control Tower

今回は、(2)の既に発行したAWSアカウントにアカウントカスタムリポジトリを使って、独自カスタマイズを新たに適用させたいと思います。

1.アカウントカスタムリポジトリにコードを追加

基本的には以下の各サンプルを流用していきます。

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

前回作成した"sampleOrg/aft-global-customizations"に、上記aft-account-customizations配下のファイルをコピペします。

今回はS3バケットを作ることになります。

% tree
.
└── sandbox
    ├── api_helpers
    │   ├── post-api-helpers.sh
    │   ├── pre-api-helpers.sh 
    │   └── python
    │       └── requirements.txt 
    └── terraform
        ├── aft-providers.jinja
        ├── backend.jinja
        └── s3.tf # ← これを作りたい

s3.tf

data "aws_caller_identity" "current" {}

resource "aws_s3_bucket" "sandbox_bucket" {
  bucket = "aft-sandbox-${data.aws_caller_identity.current.account_id}"
  acl    = "private"
}

上記内容でリポジトリにプッシュします。

ちなみに、今回はアカウントカスタムリポジトリのS3のみ反映させたい訳ですが、グローバルカスタムリポジトリが空のままだとコケるとことがわかりました。よって特に実行したいTFファイルやスクリプトがなくても、上記と同様にサンプルのフォルダ等をプッシュしておきます。

2.リクエストリポジトリにパラメータを追加

(アカウントカスタムリポジトリの場合のみ)

グローバルカスタムリポジトリ側で行う場合は、否応なしに全てのアカウントに適用するので、特にリクエストリポジトリ側で何か設定追加する必要はありません。今回のアカウントカスタムリポジトリは、指定したAWSアカウントのみに sampleOrg/aft-global-customizations/sandbox配下のスクリプトやTFファイルを実行させたいので、何かしらリクエストリポジトリ側にもそれを分からせる必要があります。

それに該当するのが[account_customizations_name]パラメータです。

前回りクエストリポジトリにて作成したコードに、以下ハイライトの行を追加します。

account_request.tf

module "sandbox_aft_01" { 
  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" 
    SSOUserFirstName          = "NOT" 
    SSOUserLastName           = "USE" 
  }

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

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

  account_customizations_name = "sandbox" 
}

これでプッシュすればOKです。

3.手動でStep Functionsステートマシンを実行

Terraformモジュールにて作成された[aft-invoke-customizations]Step Functionsステートマシンを手動で実行します。

先述のドキュメントには、ステートマシン手動実行時のサンプルとなるイベントスキーマの例(JSON)が記載されています。

それを今回以下の通り修正し使用します。

{
  "include": [
    {
      "type": "accounts",
      "target_value": [ "111122223333"]
    }
  ],
  "exclude": [
    {
      "type": "accounts",
      "target_value": [ "444455556666"]
    }
  ]
}

111122223333は、適用させたいAWSアカウント(アカウント名sandbox-aft-01)、444455556666は適用させないAWSアカウントとします。

AFTではグローバル/アカウントのカスタマイズのために、リクエストリポジトリで発行したAWSアカウントの数だけ、CodePipelineを作成します。上記JSONにて、ステートマシンによるカスタマイズの呼び出しに含めたい(include)AWSアカウントIDと、除外したい(exclude)AWSアカウントIDを指定しました。これによって、対象としないAWSアカウントのCodePipelineを無駄に走らせずに済みます。(もっとも、今回の場合例え両方を走らせたとしても、2.の account_customizations_name = "sandbox"とパラメータを指定したのは、11112222333だけなので、444455556666へ意図せず反映されてしまうということは起きませんが。)

Step Functionsコンソールを開き、当該ステートマシンの実行時に、以下の通りJSONを貼り付けて、実行します。

結果

手動実行したステートマシンは以下の通り成功しました。

111122223333用のカスタマイズパイプラインのみがキックされました。

パイプラインの中身を見ます。

Sourceステージ、Global-Customizationsステージが成功します。(グローバルカスタムリポジトリが空のままだと、Sourceステージでエラーになり止まります)

Account-Customizationsステージにて、例のs3.tfが実行されます。

最終的に当該AWSアカウントにS3バケットが作成されていることが確認できました。

その他AFTについて気になったこと

コストは大丈夫?

利用料金にご注意ください。AFTモジュールはデフォルトでは、セキュリティ強化のためVPC-Lambdaによる関数実行と他サービス通信を行っており、現時点で14点とインターフェース型VPCエンドポイントと、2点のNATGWが作成されます。(東京リージョンにて 約$400/月 程度の料金が発生します。)

デフォルトでデプロイした際に作成されるVPCエンドポイントの一覧です。

上記を回避したい場合、最新のモジュールのバージョンを指定し、以下ハイライトのパラメータにてVPCエンドポイントを使用せず、NATGWのみの構成に変更できます。

aft.tf

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

  # 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
  aft_vpc_endpoints                       = false
}

aws-ia/control_tower_account_factory/aws | Terraform Registry

Stateファイルはどこに保管されている?

"AFT管理アカウント"のS3バケットに集約して保管されていました。今回のアカウントカスタムリポジトリのs3.tf分はaft-customizations-pipeline-xxxにあります。

account_customizations_nameパラメータは複数指定できそう?

できなさそうです。よって、全アカウント共通設定はグローバルカスタムリポジトリ、個別設定はアカウントカスタムリポジトリにて、AWSアカウント単位か、OU単位か、属性単位などでディレクトリを分けて管理する必要があります。

既存のAWSアカウントを、AFTに追加することは可能か?

現時点では簡単にはできなさそうです。今後のアップデートに期待です!

終わりに

AFTを追加調査しました。前回や今回紹介した機能をTerraformとAWSサービス等で独自実装しようとすると、かなり高度なスキルと継続したメンテが必要です。その点、AFTはTerraformモジュールを適用するだけでできてしまうので、とても簡単で、AWSによる継続したメンテが期待できます。まだリリースされたばかりで、細やかな配慮が行き届いている状態ではありませんが、ユーザーが活用しフィードバックしていくことで改善されることが予想されます。ぜひ興味のある方は一度試してみてください!

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