tl;dr
- terraform Advent Calendar 2022の2日目の記事です
- 去年の記事では setup-terraform v1 を使用していましたが、2022年4月に v2 になり、GitHub Actions も composite action のバージョンアップがあったので、最新化してみました
- hashicorp/setup-terraform の個人用(兼業務用)ワークアラウンドです
注意
- (今のところ現職では)actions から apply はしてないので書いてない
- 一応動いてるけど、無保証です
- コードの手直し、質問等歓迎です
- CI/CD で apply や fmt の適用までやりたいケースは先日の Retty さんの記事 がよさげです
composite action yaml
.github/actions/terraform/action.yml
name: "setup-terraform"
description: 'terraform ci. fmt,init,validate,plan. not in apply'
# see: https://github.com/hashicorp/setup-terraform
inputs:
terraform_version:
description: 'use terraform version'
required: false
default: latest
secrets_github_token:
description: 'secrets.GITHUB_TOKEN'
required: true
default: ""
working-directory:
description: 'run directory'
required: true
default: ""
# cli_config_credentials_token: 必要なら。
runs:
using: "composite"
steps:
- uses: hashicorp/setup-terraform@v2
with:
terraform_version: ${{ inputs.terraform_version }}
- id: fmt
run: terraform fmt -check -diff
shell: bash
working-directory: ${{ inputs.working-directory }}
continue-on-error: true
# see: https://developer.hashicorp.com/terraform/cli/commands/fmt
- id: init
run: terraform init -no-color
shell: bash
working-directory: ${{ inputs.working-directory }}
# see: https://developer.hashicorp.com/terraform/cli/commands/init
- id: validate
run: terraform validate -no-color
shell: bash
working-directory: ${{ inputs.working-directory }}
# see: https://developer.hashicorp.com/terraform/cli/commands/validate
- id: plan
run: terraform plan -no-color -detailed-exitcode -input=false
shell: bash
working-directory: ${{ inputs.working-directory }}
# see: https://developer.hashicorp.com/terraform/cli/commands/plan
- uses: actions/github-script@v6
if: always()
# エラーについても PR に書き出したいので常に実行する
with:
github-token: ${{ inputs.secrets_github_token }}
script: |
// 見やすいようにアイコンを表示する
const icon_result_fmt = "${{ steps.fmt.outcome }}" == "success" ? ":blue_heart:" : ":broken_heart:";
const icon_result_init = "${{ steps.init.outcome }}" == "success" ? ":blue_heart:" : ":broken_heart:";
const icon_result_validate = "${{ steps.validate.outcome }}" == "success" ? ":blue_heart:" : ":broken_heart:";
// plan 差分を判断するため exitcode を使用する
const icon_result_plan = "${{ steps.plan.outputs.exitcode }}" == 0 ? ":blue_heart:" : ":broken_heart:";
const plan_exitcode_str = "${{ steps.plan.outputs.exitcode }}" == 0 ? "& No changes" : "but any changes !!";
// PR にコメントする内容
const output = `${{ matrix.dir }} ${ icon_result_fmt } ${ icon_result_init } ${ icon_result_validate } ${ icon_result_plan }
<details><summary>terraform fmt : ${{ steps.fmt.outcome }}</summary>
\`\`\`
${{ steps.fmt.outputs.stdout }}
${{ steps.fmt.outputs.stderr }}
\`\`\`
</details>
<details><summary>terraform init : ${{ steps.init.outcome }}</summary>
\`\`\`
${{ steps.init.outputs.stdout }}
${{ steps.init.outputs.stderr }}
\`\`\`
</details>
<details><summary>terraform validate : ${{ steps.validate.outcome }}</summary>
\`\`\`
${{ steps.validate.outputs.stdout }}
${{ steps.validate.outputs.stderr }}
\`\`\`
</details>
<details><summary>terraform plan : ${{ steps.plan.outcome }} ${ plan_exitcode_str }</summary>
\`\`\`
${{ steps.plan.outputs.stdout }}
${{ steps.plan.outputs.stderr }}
\`\`\`
</details>
*Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`, Workflow: \`${{ github.workflow }}\`*
`;
// PR 以外で呼ばれた場合は書き込みしない
if (!context.issue.number) { return }
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output.substring(0,60000)
})
- shell: bash
# success でない場合にワークフローをエラーにさせる
run: |
if [[ "${{steps.fmt.outcome}}" != "success" ]]; then exit 1 ; fi
if [[ "${{steps.init.outcome}}" != "success" ]]; then exit 1 ; fi
if [[ "${{steps.validate.outcome}}" != "success" ]]; then exit 1 ; fi
if [[ "${{steps.plan.outputs.exitcode}}" != "0" ]]; then exit 1 ; fi
# note:
# - step の continue-on-error: true でステップを継続する。付けてないところでエラーになると
# 後続のステップはスキップされる。なので fmt だけに適用している。
# fmt エラーでも他のチェックはできるが init や validate がエラーだと plan は実行できない。
# - if: always() でエラーやスキップがあっても常にコメント作成する処理をする
# - createComment の body 部は(ドキュメントに記載はないが)サイズ制限があるので
# 大きな出力結果の時にエラーになるため substring() で抑止
# - 最後の bash でワークフローのエラーを決めている。fmt がエラーでもチェックOKにするなら
# (すべきではないと思うが) exit 1 をコメントにすればよい
# - plan 差分ありでもワークフローをエラーにさせないならもう一工夫要る
workflow yaml
.github/workflows/pr.yml
name: workflow_test
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * SUN'
pull_request:
paths:
- ".github/workflows/pr.yml"
- "workflow_test/dir1/*.tf"
jobs:
on-pull-request:
name: terraform
runs-on: ubuntu-latest
timeout-minutes: 5
strategy:
# matrix のジョブのどれかがエラーになっても他のジョブをキャンセルしない(false)
fail-fast: false
matrix:
dir:
- "workflow_test/dir1"
steps:
- uses: actions/checkout@v3
# 必要なら各種クラウド等への接続はここで書く
- uses: ./.github/actions/terraform
with:
secrets_github_token: ${{ secrets.GITHUB_TOKEN }}
working-directory: ${{ matrix.dir }}
まとめ
- コメントと書いてあること自体でだいたいわかって欲しい。丸コピーしてディレクトリだけ書き換えて使える。
- 今年は PR へのコメント部分も記載した
- PR の push 毎に回してコメントとして記録したいので、正常時も異常時も処理している
- fmt や validate のエラー時に止めるかは任意
- 各ステップの結果を出力するかどうかは任意
- コメントの先頭にディレクトリと結果の emoji だけを出力しているのは、notification でメール、slack の github インテグレーション等で見やすくするため(やってみたらわかる)
- GitHub Actions のバージョンアップで composite action でも continue-on-error が使えるようになったの万歳
- continue-on-error:true にした step はエラーになっても check は正常ステータスにできる
- 以前は composite action では使えなくて不便だった
- setup-terraform の README.md にあるように、PR のコメントをアップデートするのって、履歴としてどうなの?って思ってるんだけど、実際どうなの?
- 使用する terraform のバージョンを固定するのはプラクティス的にいいのかもしれないんだけど、setup-terraform を使う場合は dependabot が使えない(この書き方ではバージョン処理できないから)のが気になる(とは言え書いてあるとおり latest 使ってて今のところ問題ない)