Terraform用のGitHub Actionsをterraform-github-actionsから後継のsetup-terraformに移行する - メドピア開発者ブログ

メドピア開発者ブログ

集合知により医療を再発明しようと邁進しているヘルステックカンパニーのエンジニアブログです。読者に有用な情報発信ができるよう心がけたいので応援のほどよろしくお願いします。

Terraform用のGitHub Actionsをterraform-github-actionsから後継のsetup-terraformに移行する

SREの侘美です。

最近はfirst call for オンライン診療の開発でRailsのコードを書いてました。

hashicorp/terraform-github-actions から後継である hashicorp/setup-terraformへ移行した際にいくつか設定でハマったので、そのことについて書いていきたいと思います。

背景

メドピアではterraformでAWSのインフラを管理しています。
terraformのリポジトリでは、レビューがスムーズに行えるようにGitHub Actions上で terraform planterraform applyterraform fmt 等を実行できる hashicorp/terraform-github-actions を利用し、下の画像のようにplan結果をPRに自動で投稿するようにしていました。

f:id:satoshitakumi:20200520154554p:plain

新サービスをリリースし仕事も一段落した先日、TerraformのGitHub Actionsに関するとあるドキュメントが更新されていることを発見しました。

Teraform GitHub Actions - Terraform by HashiCorp

なんと hashicorp/terraform-github-actions のメンテナンスが終了されていました!

メンテナンスされていないActionsを利用したままでは、最新のterraformのバージョンに対応できない日がやってきそうなので、さっそく後継の setup-terraform でGitHub Actionsの設定を書き換えることにしました。

準備

まずGitHub Actionsの設定を修正する前に、 terraform-github-actions と 後継である setup-terraform の特徴と terraform plan の実行例を比較してみました。

terraform-github-actions

リポジトリ

github.com

特徴

  • 各ステップで uses: hashicorp/terraform-github-actions@master を指定して、 initplan などのサブコマンドを指定して使う
  • tf_actions_comment: 'true' を指定することで、plan結果をPRに投稿してくれる
  • planの差分が無い場合はPRに投稿はしない

Actions上でterraform planを実行するサンプル

steps:
  - uses: actions/checkout@v2

  - name: Terraform Init
    uses: hashicorp/terraform-github-actions@master
    with:
      tf_actions_version: ${{ env.TF_VERSION }}
      tf_actions_subcommand: 'init'

  - name: Terraform plan
    uses: hashicorp/terraform-github-actions@master
    with:
      tf_actions_version: ${{ env.TF_VERSION }}
      tf_actions_subcommand: 'plan'
      tf_actions_comment: 'true' # PRへplan結果を投稿する設定

setup-terraform

リポジトリ

github.com

特徴

  • 文字通りterraformをsetupし、 terraform コマンドが利用できるようにする
  • GitHub Actionsのoutput等に対応するようにscriptでwrapされている
  • plan等は run: terraform plan で実行する
  • その他の機能は無く、plan結果のPRへの投稿などは自前で設定する必要がある

Actions上でterraform planを実行するサンプル

steps:
  - uses: actions/checkout@v2

  - uses: hashicorp/setup-terraform@v1
    with:
      terraform_version: ${{ env.TF_VERSION }}

  - run: terraform init

  - run: terraform plan -no-color

setup-terraformへの乗り換え

特徴が把握できたところで、setup-terraformを利用してPRにplan結果を投稿する設定をしていきます。 基本的には terraform-github-actions の仕様を再現する形としています。

具体的には下記の3点です。

  • PRへのplan結果の投稿
  • 差分が無い場合は投稿を抑制
  • 差分以外の余計な出力の削除

PRへのplan結果の投稿

setup-terraform の README に記載されている設定を参考にします。

下記のように actions/github-script を使い createComment 関数でコメントを投稿します。
secrets.GITHUB_TOKEN はActions上で自動で定義される変数です。

- uses: actions/github-script@v1
  env:
    # id: planのステップの出力を参照
    STDOUT: "```terraform\n${{ steps.plan.outputs.stdout }}```"
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    script: |
      const output = `<details><summary>tf plan:</summary>\n\n${process.env.STDOUT}\n\n</details>`;

      github.issues.createComment({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        body: output
      })

差分が無い場合は投稿を抑制

実装方法としては2通りあります。

1つはterraformの -detailed-exitcode オプションを使うやり方です。
このオプションを付与することで、コマンドのexit codeが下記のようになります。

  • 0: 成功かつ差分なし
  • 1: エラー
  • 2: 成功かつ差分あり

GitHub Actionsではexit codeが0以外の場合はエラーとなりworkflowで利用するには continue-on-error: true をplanのステップに追加する必要があります。

steps:
  # 他のstepは省略
  - name: terraform plan
    id: plan
    run: terraform plan -detailed-exitcode
    continue-on-error: true # 0以外のexit codeでもworkflowを継続する
  
  - name: comment on PR
    if: ${{ steps.plan.outputs.exitcode == 2 }}
    # 以下PRにコメントする処理

2つめは単純に出力内容の文字列から取得する方法です。
こちらはあまりロバストではないですが、 continue-on-error: true を利用しなくてすむため、今回はこの方法を採用しています。

steps:
  # 他のstepは省略
  - name: terraform plan
    id: plan
    run: terraform plan
  
  - name: comment on PR
    if: ${{ !contains(steps.plan.outputs.stdout, 'No changes.') }}
    # 以下PRにコメントする処理

差分以外の余計な出力の削除

terraform-github-actions ではterraform planを実行した際に大量に出力される <resource_id>: Refreshing state... のような出力を削除した上でPRにコメントしてくれます。

f:id:satoshitakumi:20200520154554p:plain

terraform-github-actionsの実装 を確認してみると、 sed コマンドで ------(略 の区切り線を基準に行を削除していました。

terraform-github-actions 同様に sed コマンドで消しても良いのですが、PR投稿のgithub-script内でついでに整形する実装にします。

steps:
  - uses: actions/github-script@v1
    env:
      STDOUT: "${{ steps.plan.outputs.stdout }}"
    with:
      github-token: ${{ secrets.GITHUB_TOKEN }}
      # NOTE: 区切り文字で囲まれた範囲のみを出力する
      script: |
        const lines = process.env.STDOUT.split('\n')
        const separator = '-'.repeat(72)
        let index = lines.indexOf(separator)
        let outputLines = lines.slice(index + 1)
        index = outputLines.indexOf(separator)
        if (index) {
          outputLines = outputLines.slice(0, index)
        }
        const planOutput = '```' + outputLines.join('\n') + '```'
        const output = `<details><summary>plan:</summary>\n\n${planOutput}\n\n</details>`;

        github.issues.createComment({
          issue_number: context.issue.number,
          owner: context.repo.owner,
          repo: context.repo.repo,
          body: output
        })

最終的なGitHub Actionsの設定

最終的な設定は下記のようになりました。

steps:
  - name: Checkout Repo
    uses: actions/checkout@v2

  - name: setup Terraform
    uses: hashicorp/setup-terraform@v1
    with:
      terraform_version: ${{ env.TF_VERSION }}

  - name: terraform init
    run: terraform init

  - name: terraform plan
    id: plan
    run: terraform plan -no-color -lock=false

  - uses: actions/github-script@v1
    if: ${{ !contains(steps.plan.outputs.stdout, 'No changes.') }}
    env:
      STDOUT: "${{ steps.plan.outputs.stdout }}"
    with:
      github-token: ${{ secrets.GITHUB_TOKEN }}
      # NOTE: 区切り文字で囲まれた範囲のみを出力する
      script: |
        const lines = process.env.STDOUT.split('\n')
        const separator = '-'.repeat(72)
        let index = lines.indexOf(separator)
        let outputLines = lines.slice(index + 1)
        index = outputLines.indexOf(separator)
        if (index) {
          outputLines = outputLines.slice(0, index)
        }
        const planOutput = '```' + outputLines.join('\n') + '```'
        const output = `<details><summary>tf plan:</summary>\n\n${planOutput}\n\n</details>`;

        github.issues.createComment({
          issue_number: context.issue.number,
          owner: context.repo.owner,
          repo: context.repo.repo,
          body: output
        })

所感

terraform-github-actions から比べるとPRへplan結果を投稿する付近の処理を自前で用意しなければならず、難易度は上昇したように思えました。
ですが、無事に後継の setup-terraform へ移行することができたので、terraformの最新バージョンへの追従する際のActions関連の懸念を減らすことができました。


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html