CloudFormation(CFn)のGit同期とは、GitリポジトリにPushしたらスタックが自動で更新される便利な機能です。プルリクエストに変更内容がコメントされる点も素晴らしいです。
ただし、公式では「マネジメントコンソールでスタックごとにGit同期を設定する方法」しか紹介されておらず、スタックが多い場合はかなり絶望的でした。
そこで本記事では「Gitリポジトリにファイルを追加したら、自動でスタックとGit同期が作成される状態を作る方法」を解説します。
TL;DR
先にゴールを紹介します。この記事では最終的に
aws cloudformation create-stack \
--stack-name "スタック名" \
--template-body "file://初期化用ファイル.yaml" # 何かファイルがないとスタックは作れない
で空のスタックを作成し、そのあと
aws codeconnections create-sync-configuration \
--branch "ブランチ名" \
--resource-name "スタック名" \
--config-file "デプロイファイル.yaml" \
--repository-link-id "REPOSITORY_LINK_ID" \
--role-arn "IAM RoleのARN" \
--pull-request-comment ENABLED \
--sync-type CFN_STACK_SYNC
でGit同期を作成します。
さらにGitHub Actionsを用いて、新規追加されたファイルに対して上記コマンドを実行します。
事前準備 GitHubとAWSを連携する
これだけはどうしても自動化できません
まずはGitHubとAWSを接続します(GitLabやBitbucketも接続できます)
AWSマネジメントコンソールで CodeBuild > 接続 > 接続を作成
画面に従ってGitHub Appをインストールします。接続名はGitHubのユーザー名(またはOrganization名)をおすすめします。
完了後に表示されるARNはこのあとすぐ使います。
次にAWS CLIでリポジトリリンクを作成します(マネジメントコンソールからは作れません1)
aws codeconnections create-repository-link \
--connection-arn "今作った接続のARN" \
--owner-id "GitHubのユーザー名(またはOrganization名)" \
--repository-name "GitHubのリポジトリ名"
出力されたリポジトリリンクIDは手元に保存してください。
f919f952-e050-4b24-XXXX-XXXX494d49d6
準備は以上です。
なお、今回は違いますが、Gitリポジトリが2つ以上ある場合は次のようにします。
- GitHub Appのインストール時に全てのリポジトリを指定する
- GitHub > Settings > Applications であとから追加することも可能
- リポジトリを制限せずにインストールするのもOK
- 1つのリポジトリにつき1つのリポジトリリンクを作成する
-
aws codeconnections list-repository-links
で一覧を見られる
-
1. テンプレートとデプロイファイルを作る
Git同期を使う場合、いつものテンプレートに加えてデプロイファイルを作ります
まずは「焼肉プロジェクト」の本番環境のIAMユーザーのファイルを作ります。
template-file-path: iam.yaml # テンプレートのパス
parameters:
env: prd
project: yakiniku
AWSTemplateFormatVersion: 2010-09-09
Parameters:
env:
Type: String
project:
Type: String
Resources:
IAMUser:
Type: AWS::IAM::User
Properties:
UserName: !Sub ${env}-${project}-user
次にテンプレートを使いまわして「寿司プロジェクト」のIAMユーザーのファイルを作ります。
template-file-path: iam.yaml
parameters:
env: prd
project: sushi # ここだけ変えました!
今回は本番環境(prd
)と事前環境(stg
)の2つのAWSアカウントがある想定で、次のようなディレクトリ構成にしてみました。
├── cfn-deployments # デプロイファイル
│ ├── prd
│ │ ├── sushi-iam.yaml
│ │ ├── sushi-log.yaml
│ │ ├── yakiniku-iam.yaml
│ │ └── yakiniku-log.yaml
│ └── stg
│ ├── yakiniku-iam.yaml
│ └── yakiniku-log.yaml
└── cfn-templates
├── iam.yaml # IAMユーザーのテンプレート
└── log.yaml # CloudWatchロググループのテンプレート
これらのファイルはこのあと使います。
2. 初期化用のCloudFormationテンプレートを作る
Git同期を設定するには先にスタックを作る必要があります2
空のスタックを作るために、次のようなテンプレートを準備しておきます(参考)
AWSTemplateFormatVersion: 2010-09-09
Resources:
NullResource:
Type: AWS::CloudFormation::WaitConditionHandle
3. スクリプトを作る
まずは「Gitリポジトリに新規追加されたファイルを一覧にする処理」が必要です。
作成だけに対応する場合(削除はしない場合)は次のように実装できます。
DIR="対象のディレクトリ"
# リモートのスタック一覧(削除済みは除く)(数が多い場合はページングが必要)
remote="$(aws cloudformation list-stacks --query 'StackSummaries[?StackStatus!=`DELETE_COMPLETE`].[StackName]' --output text | sort)"
# ローカルのファイル一覧
local="$(find "$DIR" -name '*.yaml' -exec basename {} .yaml \; | sort)"
# 差分(=新規作成されたファイル一覧)
diff="$(join -v2 <(echo "$remote") <(echo "$local"))"
これをfor文で回し、スタックとGit同期を作成します。なお、デプロイファイルのファイル名をスタック名にしています。
for stack_name in $diff; do
echo "NEW STACK: $stack_name"
# スタックの作成
aws cloudformation create-stack \
--stack-name "$stack_name" \
--template-body file://initial-template.yaml
# Git同期の作成
aws codeconnections create-sync-configuration \
--branch "ブランチ名" \
--resource-name "$stack_name" \
--config-file "$DIR/$stack_name.yaml" \
--repository-link-id "事前準備で作成したリポジトリリンクID" \
--role-arn "CloudFormationで使用するIAMロールのARN" \
--pull-request-comment ENABLED \
--sync-type CFN_STACK_SYNC
done
スクリプトの全体はGitHubでご確認ください。
4. スクリプトをGitHub Actionsで動かす
作成したスクリプトをGitHub Actions(GHA)で動かします。
今回はprd
とstg
の2つのAWSアカウントを使うので、Jobは2並列で動かします。
jobs:
create-git-sync:
runs-on: ubuntu-latest
strategy:
matrix:
conf:
- account: AWS_ACCOUNT_ID_PRD
branch: main
dir: cfn-deployments/prd # デプロイファイルのディレクトリ(prd)
link-id: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
- account: AWS_ACCOUNT_ID_STG
branch: main
dir: cfn-deployments/stg # デプロイファイルのディレクトリ(stg)
link-id: YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY
次のようなステップを作り、先ほどのスクリプトを動かします。
- name: create-git-sync
env:
BRANCH: ${{ matrix.conf.branch }}
DIR: ${{ matrix.conf.dir }}
LINK_ID: ${{ matrix.conf.link-id }}
ROLE_ARN: arn:aws:iam::${{ secrets[matrix.conf.account] }}:role/my-cloudformation-role
run: |
./create-git-sync.sh "$BRANCH" "$DIR" "$LINK_ID" "$ROLE_ARN"
ワークフローの全体はGitHubでご確認ください。
これで実装は完了です!
結果
リポジトリにファイルを追加すると、自動でスタックとGit同期が作られるようになりました
また、プルリクエストを作るとGitHub Appがスタックの変更内容をコメントしてくれます!(Git同期の作成時に--pull-request-comment ENABLED
を付けたからです)
ソースコード
作成したファイルは下記で公開しています。
最後までお読みいただきありがとうございました。よろしければ「いいね」もお願いします!