🏂
GitHub ActionsでTerraformの実行を自動化する
どんな感じに自動化したいか
- Pull Request作成時にterraform planを実行し、その結果をPRにコメントしたい。
- Pull Requestをマージしたときにterraform applyを実行したい。
- tfstateをいい感じに分けたい。
- 変更した部分だけterraformを実行したい。
実際のコードはこちら
- https://github.com/rinchsan/terraform-github-actions
- ECRのリポジトリを1つ作るだけのシンプルな例です。
- 公式が出している、GitHub ActionsでTerraformを自動化するチュートリアルは こちら にあります。
解説
plan.yml
- planはPR作成時や変更時にGitHub Actionsを実行しましょう。
name: Plan
on:
pull_request:
branches:
- master
-
strategy.matrix
に何か配列を指定すると、それぞれを使って並列にジョブを実行することができます。 - 1つの
tfstate
ファイルに対応するディレクトリごとにstrategy.matrix.dir
に追記していくイメージです。- Dev環境、Stg環境などの環境ごとに
resources/dev/
、resources/stg/
などに分割してstrategy.matrix.dir
に追加していくのがよくありそうです。 -
resources/dev/
配下でさらにresources/dev/api/
とかresources/dev/worker/
みたいな感じでドリルダウンしていくのも良さそうです。 -
resources/dev/ecs/
とかresources/dev/rds/
みたいにリソースの種類ごとに分けるのもアリかもです。
- Dev環境、Stg環境などの環境ごとに
jobs:
plan:
name: Plan
runs-on: ubuntu-latest
strategy:
matrix:
dir: [
resources/shd/ecs,
]
- とりあえずCheckoutしてきて、 technote-space/get-diff-action を使ってmasterとの差分を取得しましょう。
- 自分でgit diffするように実装してもいいと思うんですが、面倒ですよね。
- ここで取得した差分が存在するかどうかによって、
matrix.dir
ごとにterraform plan
を実行するかどうかを後続のstepで判断します。-
id
に何か文字列を指定しておくと、後続のstepで実行結果などを参照することができます。
-
- 先ほど指定していた
strategy.matrix.dir
は${{ matrix.dir }}
みたいな感じで参照することができます。
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Check diff
id: diff
uses: technote-space/get-diff-action@v4.0.2
with:
PATTERNS: |
modules/**/*.tf
${{ matrix.dir }}/**/*.tf
- 今回はAWSを想定しているのでAWSの認証を行いましょう。
- ここでは普通にIAM Userのキーペアを使っていますが、会社のセキュリティ事情によってAssumeRoleとかでSessionTokenを取得しないといけなかったりする場合は、頑張ってください。
- name: Configure aws credentials
if: steps.diff.outputs.diff
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-1
- Hashicorp公式が提供している hashicorp/setup-terraform を使って
terraform
コマンドをインストールしましょう。 -
if
でsteps.diff.outputs.diff
を指定することで、Check diff
stepで差分が得られたときのみ実行するようにします。
- name: Setup terraform
if: steps.diff.outputs.diff
uses: hashicorp/setup-terraform@v1.2.1
with:
terraform_version: 0.13.2
-
terraform fmt
でフォーマットをチェックしておきます。 - フォーマットはPRをマージするまでに直ればいいので、ここでは
continue-on-error: true
を指定して後続のstepは実行します。
- name: Check format
id: fmt
if: steps.diff.outputs.diff
run: terraform fmt -check -recursive
working-directory: ${{ matrix.dir }}
continue-on-error: true
-
${{ matrix.dir }}
でterraform init
を実行しましょう。 - 今回のサンプルではTerraformのBackend設定やProvider設定などを書いた
init.tf
をディレクトリごとに用意する想定ですが、このファイルを共通化したい場合はリポジトリルートにinit/init.tf
などを用意して、${{ matrix.dir }}
にコピーしてからterraform init
を実行したりするとよいでしょう。-
${{ matrix.dir }}
を元にtfstate
のkey
を指定することもお忘れなく。
-
- name: Initialize
id: init
if: steps.diff.outputs.diff
run: terraform init
working-directory: ${{ matrix.dir }}
-
terraform get
で依存モジュールのダウンロードなどを行い、terraform validate
で設定を検証しましょう。
- name: Download modules
if: steps.diff.outputs.diff
run: terraform get
working-directory: ${{ matrix.dir }}
- name: Validate
if: steps.diff.outputs.diff
run: terraform validate
working-directory: ${{ matrix.dir }}
-
terraform plan
を実行しましょう。 -
terraform plan
で-no-color
オプションを指定しているのは、後続のstepでPRにコメントを残す際に表示がおかしくなるのを防ぐためです。
- name: Plan
if: steps.diff.outputs.diff
id: plan
run: terraform plan -no-color
working-directory: ${{ matrix.dir }}
continue-on-error: true
-
actions/github-script を使って
terraform fmt
やterraform plan
の結果をPRにコメントしてもらいましょう。 - いちいちGitHub Actionsの実行結果ページへ行く必要がないので楽ですね。
- このスクリプトは上述の公式チュートリアルにあるものを拝借しています。
- name: Comment
if: steps.diff.outputs.diff
uses: actions/github-script@v3.0.0
env:
PLAN: "terraform\n${{ steps.plan.outputs.stdout }}"
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const output = `## \`${{ matrix.dir }}\`
#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\`
#### Terraform Plan 📖\`${{ steps.plan.outcome }}\`
<details><summary>Show Plan</summary>
\`\`\`${process.env.PLAN}\`\`\`
</details>`;
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
apply.yml
-
terraform apply
はmasterブランチへのPush時に実行しましょう。 -
strategy
まわりはplanと同様です。
name: Apply
on:
push:
branches:
- master
jobs:
apply:
name: Apply
runs-on: ubuntu-latest
strategy:
matrix:
dir: [
resources/shd/ecs,
]
- applyでは最新のPRの差分を見て実行するかどうかを判断したいので、
ref
とfetch-depth
を指定します。 -
actions/checkout@v2
はデフォルトでは最新のコミット、つまり今回の場合はPRのマージコミットしかfetchしてくれないため、HEAD^
とHEAD
の差分を取得するためにfetch-depth: 2
を指定します。
steps:
- name: Checkout
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 2
-
terraform
コマンドのインストールとAWSの認証はplanと同様に行います。
- name: Setup terraform
uses: hashicorp/setup-terraform@v1.2.1
with:
terraform_version: 0.13.2
- name: Configure aws credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-1
- 最後に
HEAD^
とのgit diff
を取って、resources/
もしくはmodules/
に変更があった場合のみ、terraform apply
を実行します。 - 小さいですがあまりスクリプトは書きたくないなと思うので、いい感じに差分を取得できるアクションを書いてみたいですね。
- この結果はGitHub Actionsの実行結果ページに行って確認しましょう。
- もし失敗していたら新たに修正ブランチをmasterから切ってそのPRをマージする形で対応しましょう。
- name: Apply
run: |
DIFF=$(git diff --name-only HEAD^ modules ${{ matrix.dir }})
if [[ ${DIFF} = *resources* || ${DIFF} = *modules* ]]; then
cd ${{ matrix.dir }}
terraform init
terraform get
terraform apply -auto-approve
fi
shell: bash
感想
Orb未対応のCircleCIとかで頑張ってスクリプトを書いて実現していたのをある程度キレイに書けていい感じです。
Discussion