05. GitHub Actions × Terraform
PR を出したら自動で terraform plan、main にマージされたら自動で terraform apply。これが Terraform の標準 CI/CD ワークフローです。
この章の目次
なぜ CI/CD が必要か
- レビューと自動チェックを同時に: PR を見るレビュアーが、構文 OK・plan の差分も同じ画面で確認できる
- 本番 apply を「人間の手元」から外す: 個人 PC のキー漏洩・うっかり apply を防ぐ
- 誰がいつ apply したかが GitHub に永久に残る: 監査の起点になる
標準フロー
[開発者] 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@v3 | Terraform CLI を入れる。バージョンを env で固定 |
aws-actions/configure-aws-credentials@v4 | OIDC でロールを assume、一時クレデンシャルを env に流し込む |
terraform fmt -check -recursive | 整形違反があれば赤。手元の pre-commit を回避してきた人をここで止める |
terraform init -input=false | provider と 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 ジョブに「人間の承認待ち」を挟めます。