やりたいこと
GitHub ActionsでTerragruntを使う際に、plan結果をプルリクエストにコメントする方法を調べていました。
plan結果をそのままコメントすることはできたのですが、もっと見やすくできないかと思い、気になっていたtfcmtと組み合わせてみることにしました。
Terragruntとは?
Terragruntは、Terraformをより便利にしてくれる薄いラッパーツールです。
設定をDRYに保ち、複数のTerraformモジュールの管理や、リモートステートの管理をやりやすくしてくれます。
Terraformのステートが複数ディレクトリに分かれていて、かつ依存関係があるような場合だと、必須レベルで便利なツールだと思います。
色々と便利な機能があるので、使ったことがない方はぜひ一度試してみてほしいです。
tfcmtとは?
tfcmt は terraform plan, apply の結果を GitHub の Pull Request (以下 PR) にコメントとして通知する CLI ツールです。
態々 CI のログを見にいかなくても PR のページで結果を確認でき、
なおかつ素のログに比べて非常に分かりやすいのが特徴です。
tfcmtの使い方
terraform plan
, terraform apply
を以下のように置き換えます。
$ tfcmt plan -- terraform plan [terraform plan の引数...]
$ tfcmt apply -- terraform apply [terraform apply の引数...]
terraformコマンドをそのままterragruntコマンドに置き換えた場合、run-allを使うことができないので、少し工夫する必要があります。
Terragruntとtfcmtを組み合わせて使う方法
terragruntコマンドのオプションで--terragrunt-tfpath
を指定します。
このオプションによって、Terragrunt内部で実行されるterraformコマンドの代わりに実行されるバイナリを指定することができます。
以下のようなシェルスクリプトへのパスを指定することで、terraformコマンドの動作をカスタマイズすることができます。
terragrunt run-all plan --terragrunt-tfpath $GITHUB_WORKSPACE/.github/scripts/tfwrapper.sh
#!/bin/bash
set -euo pipefail
# コマンドの種類を取得(例: apply, plan, fmt...)
type=$(echo "$@" | awk '{print $1}')
# 実行しているディレクトリ名を取得
current_dir=$(pwd | sed 's/.*\///g')
if [ "$type" == "plan" ]; then
# planのときは-patchオプションを付ける
# 実行ディレクトリ名をターゲットとして指定
tfcmt -var "target:${current_dir}" plan -patch -- terraform "$@"
elif [ "$type" == "apply" ]; then
tfcmt -var "target:${current_dir}" apply -- terraform "$@"
else
terraform "$@"
fi
terraformコマンドの種類で条件分岐させています。
planとapplyの時はtfcmtを実行しますが、planのときは-patchオプションを付けてコメントの数を抑えています。
それ以外のコマンドはそのまま実行します。
また、tfcmtでは-var "target:foo"
のようにすることで、それぞれの結果をラベルのprefixなどで区別できるようになります。
これを利用して実行ディレクトリ名をターゲットとして指定しています。
サンプルコード
.
├── .github
│ ├── scripts
│ │ └── tfwrapper.sh
│ └── workflows
│ └── plan.yml
└── app
├── envs
│ ├── dev
│ │ ├── app
│ │ │ ├── 〇〇.tf
│ │ │ └── terragrunt.hcl
│ │ ├── cicd
│ │ │ ├── 〇〇.tf
│ │ │ └── terragrunt.hcl
│ │ ├── db
│ │ │ ├── 〇〇.tf
│ │ │ └── terragrunt.hcl
│ │ └── ...
│ └── prod
├── modules
│ ├── acm
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ └── variables.tf
│ ├── cloudfront
│ ├── cognito
│ └── ...
└── shared
├── provider.tf
├── variables.tf
└── version.tf
name: Terraform plan
on:
pull_request:
branches:
- main
- develop
types:
- opened
- synchronize
env:
AWS_REGION: ap-northeast-1
PROJECT: sample
TF_VERSION: 1.3.7
TG_VERSION: 0.43.0
TFCMT_VERSION: 4.0.1
permissions:
id-token: write
contents: read
pull-requests: write
jobs:
plan:
name: Terraform plan
runs-on: ubuntu-latest
steps:
- name: Set env vars for dev
if: github.base_ref == 'develop'
run: |
echo "ENVIRONMENT=dev" >> $GITHUB_ENV
echo "AWS_ACCOUNT_ID=123456789012" >> $GITHUB_ENV
echo "WORK_DIR=app/envs/dev" >> $GITHUB_ENV
- name: Set env vars for prod
if: github.base_ref == 'main'
run: |
echo "ENVIRONMENT=dev" >> $GITHUB_ENV
echo "AWS_ACCOUNT_ID=123456789012" >> $GITHUB_ENV
echo "WORK_DIR=app/envs/prod" >> $GITHUB_ENV
- name: Checkout
uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-Terraform@v1
with:
terraform_version: ${{ env.TF_VERSION }}
terraform_wrapper: false
- name: Setup Terragrunt
run: |
sudo wget -q -O /bin/terragrunt "https://github.com/gruntwork-io/terragrunt/releases/download/v${TG_VERSION}/terragrunt_linux_amd64"
sudo chmod +x /bin/terragrunt
terragrunt --version
- name: Setup tfcmt
run: |
wget "https://github.com/suzuki-shunsuke/tfcmt/releases/download/v${TFCMT_VERSION}/tfcmt_linux_amd64.tar.gz" -O /tmp/tfcmt.tar.gz
tar xzf /tmp/tfcmt.tar.gz -C /tmp
mv /tmp/tfcmt /usr/local/bin
tfcmt --version
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@master
with:
role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/${{ env.PROJECT }}-${{ env.ENVIRONMENT }}-github-actions-terraform-role
role-session-name: ${{ env.PROJECT }}-${{ env.ENVIRONMENT }}-github-actions-terraform-session
aws-region: ${{ env.AWS_REGION }}
- name: Terragrunt init
working-directory: ${{ env.WORK_DIR }}
run: terragrunt run-all init
- name: Check terraform fmt
working-directory: ${{ env.WORK_DIR }}
run: terraform fmt -check -recursive
- name: Terragrunt validate
working-directory: ${{ env.WORK_DIR }}
run: terragrunt run-all validate
- name: Terragrunt plan
working-directory: ${{ env.WORK_DIR }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
terragrunt run-all plan --terragrunt-tfpath $GITHUB_WORKSPACE/.github/scripts/tfwrapper.sh
参考