My Note

自己理解のためのブログ

terraformのコードをGitHub Actionsで構文チェックする

はじめに

terraformのコードをgithub-actionsでコードチェック(fmt, validate)する設定を書きました。 ワークフローは github/workflowsyamlファイルをセットすれば実行されます。

  • 今回の実行内容
    • mainブランチに対して "pull request" を作成したときに実行
    • 連携は "terraform-cloud"
    • terraform { fmt | init | validate } を実行
    • fmtの結果をPRのCommentに出力
    • 複数ディレクトリに対して実行

ソースコード

GitHub Actionsワークフローの構文については以下を参照。

GitHub Actionsのワークフロー構文 - GitHub Docs

■ 利用する "Terraform" の GitHub Actions

github.com

以前までは以下を利用していましたが、サポートが終了していたので変更しました。

github.com

(新) hashicorp/setup-terraform(旧) hashicorp/terraform-github-actions の違い

"hashicorp/terraform-github-actions" は step毎に hashicorp/terraform-github-actions@master を指定して "with" で "subcommand" 等を指定して実行していました。

     - name: Validate
       uses: hashicorp/terraform-github-actions@master
       with:
         tf_actions_version: ${{ env.TF_VERSION }}
         tf_actions_subcommand: validate
         tf_actions_cli_credentials_token: ${{ env.TF_ACTIONS_CLI_CREDENTIALS_TOKEN }}

"hashicorp/setup-terraform" は最初に terraformの実行環境をセットアップしてから実行コマンドを定義していく形に変更になっています。 こっちの方が直感的にわかりやすくて個人的に好きですね。

■ 利用する "checkout" の GItHub Actions

github.com

   steps:
   - name: Setup Terraform
     uses: hashicorp/setup-terraform@v1
     with:
       terraform_version: ${{ env.TF_VERSION }}
   - name: Configure TF Cloud
     uses: hashicorp/setup-terraform@v1
     with:
       cli_config_credentials_hostname: ${{ env.TF_HOSTNAME }}
       cli_config_credentials_token: ${{ env.TF_API_TOKEN }}  

    # terraform fmt
    - name: Terraform fmt
      id: fmt
      working-directory: ${{ matrix.workdir }}
      run: terraform fmt
      # run: terraform fmt -recursive -check
      continue-on-error: true

    # terraform init
    - name: Terraform init
      working-directory: ${{ matrix.workdir }}
      run: terraform init

■ PRにコメント投稿するために利用する GItHub Actions

github.com

  • terraform fmtの結果を環境変数にセットする
    • steps.${id}.outputs.stdout: stepで id を指定すると利用できる
    • "```" は markdown記法のコードで出力する
    • ${process.env.FMT} で内容を出力する
- uses: actions/github-script@v4
  if: ${{ contains(steps.fmt.outputs.stdout, '.tf') }}
  env:
    FMT: "```terraform\n${{steps.fmt.outputs.stdout}}```"
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    script: |
      const output = `<details><summary>terraform fmt:</summary>\n\n${process.env.FMT}\n\n</details>`;
      github.issues.createComment({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        body: output
      })

github actionを複数実行する

今回は workdirの 3ディレクトリをチェックするので、 jobs.<job_id>.strategy.matrix で単一のジョブの定義内の変数の置き換えを行い複数のジョブを作成する。

    strategy:
      fail-fast: false # default=true
      matrix:
        workdir: [
          "./terraform/aws",
          "./terraform/org_a",
          "./terraform/org_b"
        ]
  • jobs.<job_id>.strategy.matrix を使った結果

f:id:yhidetoshi:20220119140607p:plain

■ terraform fmtの結果に応じてコメントを入れる

  • .tf が含まれる場合にコメントを書き込む処理を実施する
    - uses: actions/github-script@v4
      if: ${{ contains(steps.fmt.outputs.stdout, '.tf') }}

f:id:yhidetoshi:20220119142207p:plain

ソースコード全体

  • .github/workflows/terraform.yaml
name: Terraform
on:
  pull_request:
    branches:
      - main
    paths:
      - "terraform/**.tf"
      - ".github/workflows/terraform.yaml"

env:
  TF_HOSTNAME: "app.terraform.io"
  TF_API_TOKEN: ${{ secrets.TF_API_TOKEN }}
  TF_VERSION: 1.0.11

jobs:
  terraform_check:
    runs-on: ubuntu-latest
    continue-on-error: true
    strategy:
      fail-fast: false # default=true
      matrix:
        workdir: [
          "./terraform/aws",
          "./terraform/org_a",
          "./terraform/org_b"
        ]
    steps:
    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v1
      with:
        terraform_version: ${{ env.TF_VERSION }}
    - name: Configure TF Cloud
      uses: hashicorp/setup-terraform@v1
      with:
        cli_config_credentials_hostname: ${{ env.TF_HOSTNAME }}
        cli_config_credentials_token: ${{ env.TF_API_TOKEN }}
    
    # git checkout   
    - name: Checkout
      uses: actions/checkout@v2

    # terraform fmt
    - name: Terraform fmt
      id: fmt
      working-directory: ${{ matrix.workdir }}
      run: terraform fmt
      # run: terraform fmt -recursive -check
      continue-on-error: true

    # terraform init
    - name: Terraform init
      working-directory: ${{ matrix.workdir }}
      run: terraform init

    # terraform validate
    - name: Terraform validate
      id: validate
      working-directory: ${{ matrix.workdir }}
      run: terraform validate

    # post fmt results
    - uses: actions/github-script@v4
      if: ${{ contains(steps.fmt.outputs.stdout, '.tf') }}
      env:
        FMT: "```terraform\n${{steps.fmt.outputs.stdout}}```"
      with:
        github-token: ${{ secrets.GITHUB_TOKEN }}
        script: |
          const output = `<details><summary>terraform fmt:</summary>\n\n${process.env.FMT}\n\n</details>`;

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