Terraform + GitHub Actions連携
ストーリー
「CI/CDとIaCを組み合わせる。これが今月の最終目標だ」
木村先輩がアーキテクチャ図を更新した。
「PRを出したら自動でterraform planが走り、 変更内容がPRにコメントされる。 マージしたら自動でterraform applyが走る。 インフラの変更もアプリケーションと同じように、 コードレビューとCI/CDで管理できるんだ」
「GitOps......ですか?」
「そうだ。インフラの全ての変更がGitを通じて行われる。 誰が、いつ、何を変更したかが全て記録される。 これが本当の意味でのInfrastructure as Codeだ」
Terraform CI/CDパイプラインの全体像
Terraform CI/CD フロー
PR作成時:
.tf ファイル変更 → PR → CI
│
┌────┴────┐
│ fmt │
│ validate │
│ plan │
└────┬────┘
│
PRにplan結果を
コメントとして投稿
│
レビュー & 承認
マージ後:
main ← マージ → CD
│
terraform apply
│
インフラ更新完了
GitHub Actions ワークフロー
PR時のplanワークフロー
yaml
# .github/workflows/terraform-plan.yml
name: Terraform Plan
on:
pull_request:
branches: [ main ]
paths:
- 'infrastructure/**'
permissions:
contents: read
pull-requests: write
env:
TF_VERSION: '1.7.0'
WORKING_DIR: 'infrastructure/environments/staging'
jobs:
terraform-plan:
name: Terraform Plan
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
terraform_wrapper: true
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ap-northeast-1
- name: Terraform Format Check
id: fmt
run: terraform fmt -check -recursive
working-directory: ${{ env.WORKING_DIR }}
continue-on-error: true
- name: Terraform Init
id: init
run: terraform init
working-directory: ${{ env.WORKING_DIR }}
- name: Terraform Validate
id: validate
run: terraform validate -no-color
working-directory: ${{ env.WORKING_DIR }}
- name: Terraform Plan
id: plan
run: terraform plan -no-color -out=plan.tfplan
working-directory: ${{ env.WORKING_DIR }}
continue-on-error: true
- name: Comment Plan on PR
uses: actions/github-script@v7
with:
script: |
const output = `#### Terraform Format: \`${{ steps.fmt.outcome }}\`
#### Terraform Init: \`${{ steps.init.outcome }}\`
#### Terraform Validate: \`${{ steps.validate.outcome }}\`
#### Terraform Plan: \`${{ steps.plan.outcome }}\`
<details><summary>Plan Output</summary>
\`\`\`
${{ steps.plan.outputs.stdout }}
\`\`\`
</details>
*Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
- name: Fail if plan failed
if: steps.plan.outcome == 'failure'
run: exit 1マージ後のapplyワークフロー
yaml
# .github/workflows/terraform-apply.yml
name: Terraform Apply
on:
push:
branches: [ main ]
paths:
- 'infrastructure/**'
permissions:
contents: read
id-token: write
env:
TF_VERSION: '1.7.0'
WORKING_DIR: 'infrastructure/environments/staging'
jobs:
terraform-apply:
name: Terraform Apply
runs-on: ubuntu-latest
timeout-minutes: 30
environment: staging
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ap-northeast-1
- name: Terraform Init
run: terraform init
working-directory: ${{ env.WORKING_DIR }}
- name: Terraform Plan
run: terraform plan -no-color -out=plan.tfplan
working-directory: ${{ env.WORKING_DIR }}
- name: Terraform Apply
run: terraform apply -auto-approve plan.tfplan
working-directory: ${{ env.WORKING_DIR }}
- name: Terraform Output
run: terraform output -json
working-directory: ${{ env.WORKING_DIR }}複数環境のデプロイ
staging → production の段階デプロイ
yaml
name: Terraform Deploy
on:
push:
branches: [ main ]
paths:
- 'infrastructure/**'
jobs:
deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
environment: staging
env:
WORKING_DIR: 'infrastructure/environments/staging'
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: '1.7.0'
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN_STAGING }}
aws-region: ap-northeast-1
- run: terraform init
working-directory: ${{ env.WORKING_DIR }}
- run: terraform apply -auto-approve
working-directory: ${{ env.WORKING_DIR }}
deploy-production:
name: Deploy to Production
needs: deploy-staging
runs-on: ubuntu-latest
environment: production # 手動承認が必要
env:
WORKING_DIR: 'infrastructure/environments/production'
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: '1.7.0'
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN_PRODUCTION }}
aws-region: ap-northeast-1
- run: terraform init
working-directory: ${{ env.WORKING_DIR }}
- run: terraform apply -auto-approve
working-directory: ${{ env.WORKING_DIR }}段階デプロイのフロー
main ← マージ
│
▼
[staging apply] ← 自動実行
│
▼
[承認ゲート] ← 手動承認(Environment protection rule)
│
▼
[production apply] ← 承認後に自動実行
AWS OIDC設定
GitHub Actionsから安全にAWSにアクセスするためのOIDC設定です。
hcl
# oidc/main.tf
# GitHub OIDC Provider
resource "aws_iam_openid_connect_provider" "github" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]
}
# GitHub Actions用IAMロール
resource "aws_iam_role" "github_actions" {
name = "github-actions-terraform"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Federated = aws_iam_openid_connect_provider.github.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
}
StringLike = {
"token.actions.githubusercontent.com:sub" = "repo:myorg/myrepo:*"
}
}
}
]
})
}
# 必要な権限を付与
resource "aws_iam_role_policy_attachment" "terraform" {
role = aws_iam_role.github_actions.name
policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess" # 本番では最小権限に
}まとめ
| ポイント | 内容 |
|---|---|
| PR時のplan | 変更プレビューをPRコメントとして投稿 |
| マージ後のapply | 自動的にインフラ変更を適用 |
| 段階デプロイ | staging → 承認 → production |
| OIDC | シークレットレスのAWS認証 |
チェックリスト
- PR時にterraform planを実行するワークフローを書ける
- plan結果をPRコメントに投稿する方法を理解した
- マージ後のterraform applyワークフローを構成できる
- OIDC認証の仕組みを理解した
次のステップへ
次のセクションでは、IaCにおけるセキュリティとベストプラクティスを学びます。
推定読了時間: 40分