kakakakakku blog

Weekly Tech Blog: Keep on Learning!

Branch Deploy Action で Terraform をデプロイする (Apply-Before-Merge)

IssueOps でブランチデプロイを実現できる GitHub 公式アクション Branch Deploy Action (branch-deploy) を使って,Terraform の Apply-Before-Merge デプロイ (plan / apply) を自動化してみた❗️

github.com

ちなみに Branch Deploy Action に関しては2023年7月(約2年前)にまとめたことがある😀

kakakakakku.hatenablog.com

Terraform サンプル

Branch Deploy Action のドキュメント docs/examples.md に Terraform のサンプルが載っている📝

name: branch-deploy

on:
  issue_comment:
    types: [created]

# The working directory where our Terraform files are located
env:
  WORKING_DIR: terraform/

# Permissions needed for reacting and adding comments for IssueOps commands
permissions:
  pull-requests: write
  deployments: write
  contents: write
  checks: read
  statuses: read

jobs:
  deploy:
    name: deploy
    runs-on: ubuntu-latest
    if: ${{ github.event.issue.pull_request }} # only run on pull request comments
    environment: production-secrets # the locked down environment we pull secrets from
    defaults:
      run:
        working-directory: ${{ env.WORKING_DIR }} # the directory we use where all our TF files are stored

    steps:
      # The branch-deploy Action
      - name: branch-deploy
        id: branch-deploy
        uses: github/branch-deploy@vX.X.X

        # If the branch-deploy Action was triggered, checkout our branch
      - name: Checkout
        if: steps.branch-deploy.outputs.continue == 'true'
        uses: actions/checkout@v4
        with:
          ref: ${{ steps.branch-deploy.outputs.sha }}

        # Setup Terraform on our Actions runner
      - uses: hashicorp/setup-terraform@ed3a0531877aca392eb870f440d9ae7aba83a6bd # pin@v1
        if: steps.branch-deploy.outputs.continue == 'true'
        with:
          terraform_version: 1.1.7 # use the version of Terraform your project uses here
          cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}

        # Run Terraform init in our working directory
      - name: Terraform init
        if: steps.branch-deploy.outputs.continue == 'true'
        run: terraform init

        # If '.noop' was used, run a Terraform plan
      - name: Terraform plan
        if: ${{ steps.branch-deploy.outputs.continue == 'true' && steps.branch-deploy.outputs.noop == 'true' }}
        id: plan
        run: terraform plan -no-color
        continue-on-error: true # continue on error as we will handle errors later on

        # If '.deploy' was used, run a Terraform apply
      - name: Terraform apply
        if: ${{ steps.branch-deploy.outputs.continue == 'true' && steps.branch-deploy.outputs.noop != 'true' }}
        id: apply
        run: terraform apply -no-color -auto-approve
        continue-on-error: true # continue on error as we will handle errors later on

        # This step writes the TF plan/apply output to $GITHUB_ENV which the branch-deploy Action will read from and post as a comment on the pull request
      - name: Terraform plan output
        if: ${{ steps.branch-deploy.outputs.continue == 'true' && steps.branch-deploy.outputs.noop == 'true' }}
        env:
          TF_STDOUT: ${{ steps.plan.outputs.stdout }}
        run: |
          TF_OUTPUT="\`\`\`terraform\n${TF_STDOUT}\n\`\`\`"
          echo 'DEPLOY_MESSAGE<<EOF' >> $GITHUB_ENV
          echo "$TF_OUTPUT" >> $GITHUB_ENV
          echo 'EOF' >> $GITHUB_ENV

      - name: Terraform apply output
        if: ${{ steps.branch-deploy.outputs.continue == 'true' && steps.branch-deploy.outputs.noop != 'true' }}
        env:
          TF_STDOUT: ${{ steps.apply.outputs.stdout }}
        run: |
          TF_OUTPUT="\`\`\`terraform\n${TF_STDOUT}\n\`\`\`"
          echo 'DEPLOY_MESSAGE<<EOF' >> $GITHUB_ENV
          echo "$TF_OUTPUT" >> $GITHUB_ENV
          echo 'EOF' >> $GITHUB_ENV

        # Here we handle any errors that might have occurred during the Terraform plan/apply and exit accordingly
      - name: Check Terraform plan output
        if: ${{ steps.branch-deploy.outputs.continue == 'true' && steps.branch-deploy.outputs.noop == 'true' && steps.plan.outcome == 'failure' }}
        run: exit 1
      - name: Check Terraform apply output
        if: ${{ steps.branch-deploy.outputs.continue == 'true' && steps.branch-deploy.outputs.noop != 'true' && steps.apply.outcome == 'failure' }}
        run: exit 1

Terraform サンプルを拡張する

今回は Branch Deploy Action のドキュメントに載っている Terraform のサンプルを参考に以下を拡張する❗️

  • plan / apply 結果の出力は GITHUB_ENV 環境変数ではなく tfcmt を使う
  • トリガーコメントをデフォルトの .noop.deploy から .plan.apply に変更する

tfcmt に関しては最近まとめた📝

kakakakakku.hatenablog.com

なお issue_comment で GitHub Actions ワークフローをトリガーする場合はデフォルトブランチが GITHUB_REF に設定されてしまう仕様になっていた.そこで tfcmt plan コマンドと tfcmt apply コマンドを実行するときに --pr ${{ github.event.issue.number }} を指定して期待通りに動くようになった👌

docs.github.com

name: branch-deploy

on:
  issue_comment:
    types: [created]

permissions:
  id-token: write
  pull-requests: write
  deployments: write
  contents: write
  checks: read
  statuses: read
  issues: write

jobs:
  deploy:
    name: deploy
    runs-on: ubuntu-latest
    env:
      GITHUB_TOKEN: ${{ github.token }}
      GH_TOKEN: ${{ github.token }}
    if: ${{ github.event.issue.pull_request }}

    steps:
      - name: branch-deploy
        id: branch-deploy
        uses: github/branch-deploy@v10.4.1
        with:
          noop_trigger: ".plan"
          trigger: ".apply"

      - name: Checkout
        if: steps.branch-deploy.outputs.continue == 'true'
        uses: actions/checkout@v4
        with:
          ref: ${{ steps.branch-deploy.outputs.sha }}

      - uses: aws-actions/configure-aws-credentials@v4
        if: steps.branch-deploy.outputs.continue == 'true'
        with:
          role-to-assume: arn:aws:iam::000000000000:role/xxxxx
          aws-region: ap-northeast-1

      - uses: hashicorp/setup-terraform@v3
        if: steps.branch-deploy.outputs.continue == 'true'
        with:
          terraform_version: 1.10.3

      - name: Setup tfcmt
        if: steps.branch-deploy.outputs.continue == 'true'
        run: |
          gh release download -R suzuki-shunsuke/tfcmt v4.14.7 -p tfcmt_linux_amd64.tar.gz
          tar -xzf tfcmt_linux_amd64.tar.gz
          sudo mv tfcmt /usr/local/bin/
          tfcmt --version

      - name: Terraform init
        if: steps.branch-deploy.outputs.continue == 'true'
        run: terraform init

      - name: Terraform plan
        if: ${{ steps.branch-deploy.outputs.continue == 'true' && steps.branch-deploy.outputs.noop == 'true' }}
        id: plan
        run: tfcmt plan --pr ${{ github.event.issue.number }} --patch -- terraform plan -no-color

      - name: Terraform apply
        if: ${{ steps.branch-deploy.outputs.continue == 'true' && steps.branch-deploy.outputs.noop != 'true' }}
        id: apply
        run: tfcmt apply --pr ${{ github.event.issue.number }} -- terraform apply -no-color -auto-approve

動作確認

期待通りになった👌

.plan (.noop)

.apply (.deploy)

関連記事

同じように Terraform の Apply-Before-Merge デプロイができる Digger に関しては以下の記事にまとめてある📝

kakakakakku.hatenablog.com

kakakakakku.hatenablog.com