【AWS】Account Factory for Terraformを実装してみた - GLOBAL CUSTOMIZATIONSの書き方


# はじめに
こんにちは。

「[【AWS】Account Factory for Terraformを実施してみた](https://qiita.com/Soojin/items/a1240df60f555b8b3422)」 シリーズを書いています。
もし良かったら他の投稿も見てみてください。（＾＾）

本日の投稿は、**Account Factory for Terraform（以下、AFT）のグローバルカスタマイズ**について説明します。
AFTって何なの？、カスタマイズって何？っていう方は上のURLや[AFT公式ドキュメント](https://learn.hashicorp.com/tutorials/terraform/aws-control-tower-aft)をご参照ください！

さて、
アカウント発行の自動化自体はAFTの公式ドキュメントの手順そのままでいけますが、
以下のようなアカウント内の細かーい設定（カスタマイズ）まで自動化したかったら、自分でコードを書かないといけません。
- XXXっていうIAMロールとポリシーを作成したい
- 初期IAMユーザを作りたい
- Security Hubを開始させてマスターアカウントの管理下に設定しておきたい
- EC2インスタンスいくつかを作成して起動しておきたい
- 等々・・・（おそらくboto3とかで書けるものだったら何でもいけるような気はします。）

以下の公式ドキュメントを参照してみましたが、サンプルコードっていうかaft-global-customizationsリポジトリの構成は説明していますが具体的にどう書けば良いかよくわからなかったため、私の実装記録を残します。
- サンプルコードリポジトリ（aws-ia提供）：https://github.com/aws-ia/terraform-aws-control_tower_account_factory/tree/main/sources/aft-customizations-repos/aft-global-customizations
- サンプルコードリポジトリ（hashicorp提供）：https://github.com/hashicorp/learn-terraform-aft-global-customizations
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2203079/6cc94573-8ff2-4f9a-e961-befea25b878c.png)
↑ 手順も特にカスタマイズコードの書き方は説明していませんでした。

# 実装してみた
以下はaft-global-customizationsリポジトリで作業します。
（私は**Pythonとシェル**で実装しました。）

**1. ディレクトリを構成します。** 構成は、[こちら（aws-iaのAFTサンプルコードディレクトリ）](https://github.com/aws-ia/terraform-aws-control_tower_account_factory/tree/main/sources/aft-customizations-repos/aft-global-customizations)と全く同じくします。これが最低必要な構成らしいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2203079/1afec2ca-98e6-5ae0-3c34-598abcca4200.png)
↑ 私はこのカスタマイズコードでIAMユーザ・グループ・ロール・ポリシーを作りますしSecurity Hubを開始しますので、2つのPythonファイル（CreateIAMEntities.py、EnableSecurityHub.py）が追加しています。

各ファイルは以下の意味を持ちます。（詳しくは[こちら（aws-iaのAFTサンプルコードディレクトリ）](https://github.com/aws-ia/terraform-aws-control_tower_account_factory/tree/main/sources/aft-customizations-repos/aft-global-customizations)を参考）
- **api_helpers/pre-api-helpers.sh**：Terraform（アカウント発行）が走る前に実行するシェル
- **api_helpers/post-api-helpers.sh**: Terraform（アカウント発行）が走った後に実行するシェル
- **api_helpers/python/requirements.txt**: pythonコード実行に必要なライブラリ/パッケージをここに記載しておきます。Terraformが走ると自動的にそのライブラリ/パッケージをインストールしてくれます。（ex. boto3, awscli）
- **（例）api_helpers/python/CreateIAMEntities.py**: 普通にLambdaなどでboto3を使ってpythonコードを書くように書きます。
- **terraform/aft-providers.jinja**: Terraformのバックエンド実行のために必要らしいです。私は特にいじらなくサンプルコードをそのままコピペして使いました。
- **terraform/backend.jinja**: Terraformのバックエンド実行のために必要らしいです。私は特にいじらなくサンプルコードをそのままコピペして使いました。


**2．post-api-helpers.shの中身を書きます。** 
上でも話しましたが、私は新規アカウントが作られた**後**にその中にIAMエンティティを作ったり、組織のマスターアカウントからのSecurity Hub InvitationをAcceptしてSecurity Hubを開始したりする動きを作りたかったので、pre-api-helpers.shではなくpost-api-helpers.shを記述します。

```bash
#!/bin/bash
# Copyright Amazon.com, Inc. or its affiliates. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#

echo "Executing Post-API Helpers"

cd $DEFAULT_PATH/terraform
ACCOUNTID=$(aws sts get-caller-identity --query Account --output text)
echo $ACCOUNTID
python $DEFAULT_PATH/api_helpers/python/CreateIAMEntities.py $ACCOUNTID
python $DEFAULT_PATH/api_helpers/python/EnableSecurityHub.py
```
結構短いのでわかりやすいかなと思いますがざっくりこんな感じです。
AFTによって作られた新規アカウントIDを取得して、私が実行したいpythonファイルのパラメータで渡したりそのまま実行したりする感じです。

ちなみに、私は最初このTerraformがどこで実行されているのかがわからなく、pythonファイルのパスはどう設定すればいいんだろう・・・？と迷いましたが、何回か試してみてエラーログを読んでみたら **$DEFAULT_PATH** で設定すればよかったってわかったのでシェアします！



**3．pythonファイルを書きます。（例）**
例でSecurity Hubを開始する、そして、マスターアカウントからのInvitationをAcceptするコードを見てみましょう。
```python
import boto3
import botocore.exceptions

securityHub = boto3.client('securityhub')

print('Executing EnableSecurityHub...')

try:
    # Enable Security Hub in New Account
    securityHub.enable_security_hub(
        EnableDefaultStandards=False,
    )

    # Accept Invitation from JapanTechSales
    invitation = securityHub.list_invitations()['Invitations'][0]
        
    securityHub.accept_administrator_invitation(
        AdministratorId = invitation['AccountId'],
        InvitationId = invitation['InvitationId'],
    )   
except botocore.errorfactory.ResourceConflictException:
    print('Account is already subscribed to Security Hub.')

print('EnableSecurityHub Executed.')
```
みたいな感じです。
中身的にはLambdaなどで書くコードとあまり変わらないかと思います。
カスタマイズはこの例だけではなく、色々なAWSサービスに対して可能ですので色々自分で動きを試してみてください。


**4．あとはちゃんと保存してPushするだけです。以上です！**

# 最後に
この投稿では、Pushした後の動き（CodeCommitリポジトリにPushされてCodeBuildでビルド、CodePipeLineでパイプラインが作られコードが実行される）は説明範囲外ですので省略します。

このように、aft-global-customizationsリポジトリに自分が実装したいアカウントのカスタマイズコードを書いておくことで、新規アカウントの細かーい設定を自動設定することができますので是非試してみてください！

コメントとフィードバックは大歓迎です。よろしくお願いいたします。