Account Factory for Terraform (AFT) パイプラインのカスタマイズをローカル環境からテストしたい

AFTパイプライン処理、そこそこ時間かかるのでローカルからplanして確認しておきたいねん
2023.10.30

どうも、ちゃだいん(@chazuke4649)です。

Account Factory for Terraform (AFT) は、AWS Control Tower を拡張し、Terraformコードで定義したアカウントベースライン設定の配布を自動化できる強力なカスタマイズソリューションです。

AFTによってAWSアカウントのカスタマイズを行う際、いきなりパイプラインに乗せて対象のAWSアカウントに配布するのではなく、事前に ローカル環境からterraform planterraform validateなどで検証したい場合があります。それ以外でも何かしらローカルから試したいこと・やりたいことは出てきます。

このニーズに対して、実はしれっと公式ドキュメントの AWS Prescriptive Guidance にて紹介されていたので、実際にやってみます。

概要

今回は、AFTの「アカウントカスタマイズ」を利用し、既存のAWSアカウント sandbox へのカスタマイズを、あらかじめローカル環境からテスト(terraoform plan)します。

前提

  • ローカル環境: MacBook Pro
  • MacOS: 13.4.1
  • AFT構成パターン: GitHub x Terraform OSS
  • AFT version: 1.9.2
  • AWS Control Tower 環境と AFT モジュールはデプロイ済み、過去にカスタマイズ実績有り
  • その他、ドキュメントの「前提条件と制限事項」も参照ください

手順

基本的に先述の公式ドキュメントを参考にしています。

  1. リモートリポジトリをローカルにクローンする
  2. シェルスクリプトをローカルに保存する
  3. backend.tfとaft-providers.tfを作成する
  4. AFT管理アカウントの認証情報を試す
  5. シェルスクリプトを使ってinitする
  6. シェルスクリプトを使ってplanする
  7. .gitignoreを作成し、プッシュする

0. リモートリポジトリをローカルにクローンする

ローカル検証したいリモートのAFTリポジトリ(※今回は「アカウントカスタマイズ」)をクローンします。

自分の環境ではすでにクローン済みのため割愛します。

1. シェルスクリプトをローカルに保存する

ローカルのどこかか適切なディレクトリにドキュメントに記載されているシェルスクリプト ct_terraform.sh をローカル保存しておきます。

(参考までに以下にも転記しますが、実際はオリジナルを流用ください)

ct_terraform.sh

#! /bin/bash
# Version: 1.1 2022-06-24 Unsetting AWS_PROFILE since, when set, it interferes with script operation
#          1.0 2022-02-02 Initial Version
#
# Purpose: For use with AFT: This script runs the local copy of TF code as if it were running within AFT pipeline.
#        * Facilitates testing of what the AFT pipline will do 
#           * Provides the ability to run terraform with custom arguments (like 'plan' or 'move') which are currently not supported within the pipeline.
#
# © 2021 Amazon Web Services, Inc. or its affiliates. All Rights Reserved.
# This AWS Content is provided subject to the terms of the AWS Customer Agreement
# available at http://aws.amazon.com/agreement or other written agreement between
# Customer and either Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both.
#
# Note: Arguments to this script are passed directly to 'terraform' without parsing nor validation by this script.
#
# Prerequisites:
#    1. local copy of ct GIT repositories
#    2. local backend.tf and aft-providers.tf filled with data for the target account on which terraform is to be run
#       Hint: The contents of above files can be obtain from the logs of a previous execution of the AFT pipeline for the target account.
#    3. 'terraform' binary is available in local PATH
#    4. Recommended: .gitignore file containing 'backend.tf', 'aft_providers.tf' so the local copy of these files are not pushed back to git

readonly credentials=$(aws sts assume-role \
    --role-arn arn:aws:iam::$(aws sts get-caller-identity --query "Account" --output text ):role/AWSAFTAdmin \
    --role-session-name AWSAFT-Session \
    --query Credentials )

unset AWS_PROFILE
export AWS_ACCESS_KEY_ID=$(echo $credentials | jq -r '.AccessKeyId')
export AWS_SECRET_ACCESS_KEY=$(echo $credentials | jq -r '.SecretAccessKey')
export AWS_SESSION_TOKEN=$(echo $credentials | jq -r '.SessionToken')
terraform "$@"

2. backend.tfとaft-providers.tfを作成する

AFTパイプラインでTerrformが実行される際、CodeBuild内にてbackend.tfとaft-providers.tfが生成・利用されます。 これをローカル環境にコピペし利用すれば、AFTパイプライン内で実行するのと同等のstateファイルの参照やプロバイダー設定を利用できる格好となります。

AFT管理アカウントのマネジメントコンソールにログインし、CodePipelineコンソールを開きます。

パイプラインの一覧の中から、対象のAWSアカウントIDが含まれたパイプラインを選択します。

今回は、「グローバルカスタマイズ」ではなく「アカウントカスタマイズ」なので、アカウントカスタマイズのCodeBuildのログを開きます。

aft-providers.tf で検索すると 該当箇所がヒットしたので、こちらをローカルにコピペします。

backend.tf も同様です。

ディレクトリ構造を見ておくと、account-customizations 以下をクローンしたとして、今回は、アカウント単位でディレクトリを切っており、sandboxを作っています。その配下の terraform 配下に、全て必要なファイルを格納しています。

% pwd
/Users/chadain/Project/account-customizations/sandbox/terraform

% tree -a -L 1
.
├── .gitignore
├── .terraform
├── .terraform.lock.hcl
├── aft-providers.jinja
├── aft-providers.tf
├── backend.jinja
├── backend.tf
└── s3.tf
  • 冒頭のシェルスクリプトは、ここに入れてません
  • 元々、「アカウントカスタマイズ」のアカウント単位でコピペするテンプレートの中には、*.jinjaファイルしか入ってません

3. AFT管理アカウントの認証情報を試す

IAM Identiy Center を利用している場合は、Identity Centerユーザーポータルから、AFT管理アカウントへの管理者ユーザーなどの一時的クレデンシャルをコピーし、ターミナルの環境変数へセットします。

AFTのホームリージョンが東京である場合、AWS CLI側でも東京リージョンが設定されていることを確認します。

4. シェルスクリプトを使ってinitする

先ほどローカル保存したシェルスクリプトを呼び出して、terraform initを実行します。

% ../shellscript/ct_terraform.sh init

Initializing the backend...

Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.

Initializing provider plugins...
- Finding latest version of hashicorp/aws...
- Installing hashicorp/aws v5.23.1...
- Installed hashicorp/aws v5.23.1 (signed by HashiCorp)

Terraform has been successfully initialized!

問題なく、CodeBuild上のバックエンドとプロバイダー設定と、シェルスクリプトによるAFT系IAMロールの一時クレデンシャルを利用して、initすることができました。

5. シェルスクリプトを使ってplanする

ここでは、元々カスタマイズしていたS3にPublic Access Block設定を追加してみます。(ハイライト行)

s3.tf

data "aws_caller_identity" "current" {}

resource "aws_s3_bucket" "sandbox" {
  bucket = "aft-sandbox-${data.aws_caller_identity.current.account_id}"
  tags   = { AccountId = "aft-sandbox-${data.aws_caller_identity.current.account_id}" }
}

resource "aws_s3_bucket_versioning" "sandbox" {
  bucket = aws_s3_bucket.sandbox.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_public_access_block" "sandbox" {
  bucket                  = aws_s3_bucket.sandbox.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

シェルスクリプトを呼び出して、terraform planを実行します。

% ../shellscript/ct_terraform.sh plan

## 中略

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following
symbols:
  + create

Terraform will perform the following actions:

  # aws_s3_bucket_public_access_block.sandbox will be created
  + resource "aws_s3_bucket_public_access_block" "sandbox" {
      + block_public_acls       = true
      + block_public_policy     = true
      + bucket                  = "aft-sandbox-111111111111"
      + id                      = (known after apply)
      + ignore_public_acls      = true
      + restrict_public_buckets = true
    }

Plan: 1 to add, 0 to change, 0 to destroy.

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

問題なく追加できそうです。

これによって本検証の目的である、terraform planなどのコマンドをローカルから実行することができました。

6. .gitignoreを作成し、プッシュする

ローカルから実行するためにコピペしたaft-providers.tfbackend.tf、terraform init で生成された .terraform/* や .terraform.lock.hcl` はリモートリポジトリ側では不要なので、.gitignoreに登録します。

そして、S3.tfの変更をリモートリポジトリにプッシュします。

% echo aft-providers.tf >> .gitignore
% echo backend.tf >> .gitignore
% echo .terraform.lock.hcl >> .gitignore

% git add -A
% git commit -m"S3にブロックパブリックアクセス設定を追加する"
% git push origin main

あとは、本来のフローに戻り、AFTパイプライン上で既存のAWSアカウントへのカスタマイズの実行を行い、正しくAFTパイプラインによりカスタマイズを反映させれば完了です。

Re-invoke customizations

AFTパイプラインの手動実行によりSandboxアカウントへカスタマイズを実行したところ、問題なく成功したことが確認できました。

余談

Terraform バージョンを合わせる方法

AFTパイプラインで使用しているTerrformバージョンと、ローカル環境のTeraformバージョンを合わせる必要があります。

AFT側は、パブリックモジュールのパラメータにて指定が可能です。

terraform_version | aws-ia/terraform-aws-control_tower_account_factory

終わりに

AFTパイプラインのカスタマイズをローカルから検証する方法を試しました。

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