★★ 中級

05. GitHub Actions × Terraform

PR を出したら自動で terraform plan、main にマージされたら自動で terraform apply。これが Terraform の標準 CI/CD ワークフローです。

なぜ CI/CD が必要か

標準フロー

[開発者] feature ブランチで .tf を編集
   ↓ git push
[GitHub] PR を main 向けに作成
   ↓ pull_request トリガ
[Actions] fmt -check → init → validate → plan → PR にコメント
   ↓ レビュアー承認
[GitHub] main にマージ
   ↓ push トリガ
[Actions] apply(本番反映)

ワークフロー YAML 全文

.github/workflows/terraform.yml として置きます。OIDC で AWS 認証する前提(次章で設定)。

name: Terraform

on:
  pull_request:
    paths: ["**.tf", "**.tfvars", ".github/workflows/terraform.yml"]
  push:
    branches: [main]
    paths: ["**.tf", "**.tfvars"]

# OIDC で AWS の IAM ロールを assume するために必要
permissions:
  id-token: write    # OIDC トークン発行
  contents: read     # checkout
  pull-requests: write   # plan 結果を PR にコメント

env:
  TF_VERSION: "1.14.9"
  AWS_REGION: "ap-northeast-1"
  WORKDIR: "envs/dev"   # 環境ごとに変える

jobs:
  terraform:
    name: Terraform
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ${{ env.WORKDIR }}
    steps:
      - uses: actions/checkout@v4

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

      - name: Configure AWS credentials (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-actions-terraform
          aws-region: ${{ env.AWS_REGION }}

      - name: Terraform fmt
        run: terraform fmt -check -recursive
        working-directory: .

      - name: Terraform Init
        run: terraform init -input=false

      - name: Terraform Validate
        run: terraform validate -no-color

      - name: Terraform Plan
        if: github.event_name == 'pull_request'
        id: plan
        run: terraform plan -no-color -input=false -out=tfplan
        continue-on-error: true

      - name: Post Plan Comment
        if: github.event_name == 'pull_request'
        uses: marocchino/sticky-pull-request-comment@v2
        with:
          header: terraform-plan-${{ env.WORKDIR }}
          message: |
            #### Terraform Plan (`${{ env.WORKDIR }}`) — `${{ steps.plan.outcome }}`
            <details><summary>Show plan</summary>

            ```
            ${{ steps.plan.outputs.stdout }}
            ```
            </details>

      - name: Fail if plan errored
        if: github.event_name == 'pull_request' && steps.plan.outcome == 'failure'
        run: exit 1

      - name: Terraform Apply
        if: github.event_name == 'push' && github.ref == 'refs/heads/main'
        run: terraform apply -auto-approve -input=false

各ステップの解説

ステップ何をしているか
actions/checkout@v4このリポジトリのコードを runner に取得
hashicorp/setup-terraform@v3Terraform CLI を入れる。バージョンを env で固定
aws-actions/configure-aws-credentials@v4OIDC でロールを assume、一時クレデンシャルを env に流し込む
terraform fmt -check -recursive整形違反があれば赤。手元の pre-commit を回避してきた人をここで止める
terraform init -input=falseprovider と module を取得。-input=false で対話プロンプトを禁止(CI は対話できない)
terraform validate構文・型エラーをチェック
terraform plan差分を計算。PR 時のみ
terraform apply -auto-approve本番反映。push to main の時のみ

plan 結果を PR にコメント表示

marocchino/sticky-pull-request-comment を使うと、PR の同じコメントを 毎回上書き してくれます。push のたびに新しいコメントが積み上がる事故を防げます。

header をワークディレクトリごとに変えれば、複数環境の plan を 1 PR で並べて見られます。

apply は本番ブランチでだけ

YAML の最後にある条件:

if: github.event_name == 'push' && github.ref == 'refs/heads/main'

これがないと PR でも apply が動いてしまいます。絶対に外さない。さらに 07 章 の Environments と組み合わせると、apply ジョブに「人間の承認待ち」を挟めます。