2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GitHub IssueからTerraformコードを自動生成!Claude Code × GitHub Actionsで実現するIaC自動化

Last updated at Posted at 2026-01-07

はじめに

「Terraformコードを書くのは時間がかかる」「毎回同じようなボイラープレートを書いている」「レビューで指摘される品質問題を事前に防ぎたい」

こんな課題を抱えているインフラエンジニアの皆さんに朗報です。本記事では、GitHub IssueにTerraform要件を書くだけで、コード生成から検証、エラー修正、デプロイまでを自動化するワークフローを詳しく解説します。

この記事で実現すること

この仕組みを導入すると、以下のようなフローが完全自動で実行されます。

  1. GitHub Issueを作成(Terraformの要件を記載)
  2. Claude CodeがIssue内容をもとにTerraformコードを生成・修正
  3. 自動で terraform plan まで実行し、結果をIssueとPRにコメント
  4. PRを自動作成
  5. PRをマージすると、対象環境へ自動で terraform apply

📝 Issue作成 → 自動でTerraform検証(plan)まで実行

Issueを作成すると、Claude Codeが内容を理解してTerraformコードを生成・修正し、
terraform plan の結果までを Issueにコメントとして自動投稿します。

image.png

image.png


🔀 PR自動作成 → マージで環境適用

Issue処理が完了すると、修正内容を含んだPRが自動で作成されます。
PR上では、Terraformの各種チェック結果(fmt / validate / tflint / plan)も確認できます。

image.png
このPRをマージするだけで、対象環境へ terraform apply が自動実行されます。
image.png


このように、
「Issueを書く」→「レビューしてマージする」
という最小限の操作だけで、Terraformの実装からデプロイまでを安全に自動化できます。

  • 📝 Issue作成だけでコード自動生成: Terraformのボイラープレートを書く必要なし
  • 🤖 品質ガイドラインに準拠: CLAUDE.mdに記載したルールに従って生成
  • CI/CDで自動検証: fmt/init/validate/tflint/planを自動実行
  • 🔧 エラーを自動修正: 最大3回まで自動でエラー修正を試行
  • 💬 PRコメントで追加修正: 作成されたPRにコメントで修正を依頼可能
  • 🚀 mainマージで自動デプロイ: 承認されたコードを環境へ自動適用

対象読者

  • Terraformでインフラをコード化しているチーム
  • Claude Code(LLM)を活用した開発自動化に興味がある方
  • CI/CDパイプラインで品質を担保したい方

使用技術

技術 役割
Claude Code Anthropic社のLLM(GitHub Actions対応)
GitHub Actions CI/CDプラットフォーム
Terraform Infrastructure as Code
TFLint Terraformのベストプラクティスチェッカー
AWS S3バックエンド、OIDC認証

全体アーキテクチャ

システム構成図

本システムは4つのGitHub Actionsワークフローで構成されています:

1. Issue経由のコード生成フロー

2. PRコメント経由の修正フロー

ワークフローの役割

ワークフロー ファイル 役割 トリガー
オーケストレーター caller.yaml 全体を統括し、他のワークフローを呼び出す issues, issue_comment
Claude Code実行 claude-automation.yaml コード生成、検証、自動修正を実行 workflow_call
Terraform検証・デプロイ terraform-workflow.yaml PR時の検証とmainマージ時のデプロイ workflow_call, pull_request, push
PRコメント自動修正 pr-comment-automation.yaml PRコメントからの修正依頼を処理 issue_comment (PRコメント)

ワークフローの関係性

フロー1: Issue経由のコード生成(caller.yaml が統括)

Issue作成 → caller.yaml → claude-automation.yaml → terraform-workflow.yaml
  • Issue作成時に自動的に caller.yaml が起動
  • claude-automation.yaml でコード生成・検証・自動修正
  • terraform-workflow.yaml でPRへの検証結果コメント
  • mainマージ後、terraform-workflow.yaml が自動デプロイ

フロー2: PRコメント経由の修正(独立したワークフロー)

PRコメント(@claude付き) → pr-comment-automation.yaml
  • 作成されたPRに対して、コメントで追加修正を依頼可能
  • caller.yaml を経由せず、独立して動作
  • PRが更新されると terraform-workflow.yaml が自動的に再実行

補足:

  • pr-comment-automation.yamlcaller.yaml とは独立したワークフローです
  • PRコメントに @claude を含めることで起動します
  • PRに変更がプッシュされると、terraform-workflow.yaml の pull_request トリガーで検証が再実行されます

ワークフロー詳細解説

1. caller.yaml - オーケストレーター

完全なコード

caller.yaml の全コード(クリックして展開)
name: 'Caller Workflow'

on:
  issues:
    types: [opened]
  issue_comment:
    types: [created]

permissions:
  contents: write
  pull-requests: write
  issues: write
  actions: read
  id-token: write

jobs:
  # Claude Code Workflowの実行
  run-claude-automation:
    name: 'Run Claude Code Automation'
    if: |
      github.event.issue.pull_request == null &&
      (
        github.event_name == 'issues' ||
        (github.event_name == 'issue_comment' &&
         contains(github.event.comment.body, '@claude'))
      )
    uses: ./.github/workflows/claude-automation.yaml
    with:
      issue_number: ${{ github.event.issue.number }}
    secrets: inherit

  # Terraform Workflowの実行
  run-terraform-workflow:
    name: 'Run Terraform Workflow'
    if: |
      github.event.issue.pull_request == null &&
      (needs.run-claude-automation.result == 'success' || needs.run-claude-automation.result == 'skipped')

    needs: run-claude-automation
    uses: ./.github/workflows/terraform-workflow.yaml
    with:
      pr_number: ${{ needs.run-claude-automation.outputs.pr_number }}
      issue_number: ${{ github.event.issue.number }}
    secrets: inherit

  # 実行結果のサマリー
  summary:
    name: 'Workflow Summary'
    runs-on: ubuntu-latest
    needs: [ run-claude-automation, run-terraform-workflow ]
    if: |
      always() && github.event.issue.pull_request == null
    steps:
    - name: 'Generate Summary'
      run: |
        echo "## Workflow Execution Summary" >> $GITHUB_STEP_SUMMARY
        echo "" >> $GITHUB_STEP_SUMMARY

        echo "### Claude Code Automation" >> $GITHUB_STEP_SUMMARY
        if [ "${{ needs.run-claude-automation.result }}" = "success" ]; then
          echo "✅ Successfully completed" >> $GITHUB_STEP_SUMMARY
          echo "- Issue #${{ github.event.issue.number }} has been processed" >> $GITHUB_STEP_SUMMARY
          echo "- A Pull Request should have been created" >> $GITHUB_STEP_SUMMARY
        else
          echo "❌ Failed or skipped" >> $GITHUB_STEP_SUMMARY
          echo "- Status: ${{ needs.run-claude-automation.result }}" >> $GITHUB_STEP_SUMMARY
        fi

        echo "" >> $GITHUB_STEP_SUMMARY

        echo "### Terraform Workflow" >> $GITHUB_STEP_SUMMARY
        if [ "${{ needs.run-terraform-workflow.result }}" = "success" ]; then
          echo "✅ Successfully completed" >> $GITHUB_STEP_SUMMARY
        else
          echo "❌ Failed or skipped" >> $GITHUB_STEP_SUMMARY
          echo "- Status: ${{ needs.run-terraform-workflow.result }}" >> $GITHUB_STEP_SUMMARY
        fi

        echo "" >> $GITHUB_STEP_SUMMARY
        echo "---" >> $GITHUB_STEP_SUMMARY
        echo "🤖 Orchestrated by Caller Workflow" >> $GITHUB_STEP_SUMMARY

重要ポイント解説

1. トリガー条件の設計
on:
  issues:
    types: [opened]  # 新しいIssueが作成されたとき
  issue_comment:
    types: [created]  # Issueにコメントされたとき

設計意図:

  • issues: opened: Issueが作成された瞬間に自動でワークフロー開始
  • issue_comment: 既存Issueに @claude メンションで追加作業を依頼可能
2. ジョブ間の依存関係
jobs:
  run-claude-automation:
    if: |
      github.event.issue.pull_request == null &&
      (
        github.event_name == 'issues' ||
        (github.event_name == 'issue_comment' &&
         contains(github.event.comment.body, '@claude'))
      )
    uses: ./.github/workflows/claude-automation.yaml
    with:
      issue_number: ${{ github.event.issue.number }}
    secrets: inherit

  run-terraform-workflow:
    needs: run-claude-automation  # claude-automationの後に実行
    if: |
      github.event.issue.pull_request == null &&
      (needs.run-claude-automation.result == 'success' || needs.run-claude-automation.result == 'skipped')
    uses: ./.github/workflows/terraform-workflow.yaml
    with:
      pr_number: ${{ needs.run-claude-automation.outputs.pr_number }}  # PR番号を引き継ぐ
      issue_number: ${{ github.event.issue.number }}

  summary:
    needs: [ run-claude-automation, run-terraform-workflow ]
    if: |
      always() && github.event.issue.pull_request == null

ポイント:

  • secrets: inherit: 全てのSecretsを子ワークフローに引き継ぐ
  • needs.run-claude-automation.outputs.pr_number: 作成されたPR番号を次のジョブに渡す
  • if: |: YAML block style(複数行条件を改行を保持して記述)を使用
  • github.event.issue.pull_request == null: 全てのジョブでPRコメントを明示的に除外(Issue本体へのコメントのみを対象)
  • PRコメントは pr-comment-automation.yaml で独立して処理される
  • この設計により、PRコメントで @claude を使った場合の重複実行を防止

2. claude-automation.yaml - Claude Code実行と自動修正

このワークフローが本システムの核心部分です。

全体構成

jobs:
  claude-code-automation:    # Claude Codeでコード生成・PR作成
  set-matrix:                # 変更されたTerraform環境ディレクトリを検出
  terraform-validate:        # Terraform検証(Matrix並列実行)
  fix-terraform-errors:      # エラー時の自動修正

主要ステップの詳細解説

Step 1: Issue情報の取得
- name: 'Get Issue Information'
  id: get-issue
  env:
    GITHUB_TOKEN: ${{ github.token }}
    GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  run: |
    if [ "${{ github.event_name }}" = "workflow_call" ]; then
      issue_number="${{ inputs.issue_number }}"
    elif [ "${{ github.event_name }}" = "issue_comment" ]; then
      issue_number="${{ github.event.issue.number }}"
    else
      issue_number="${{ github.event.issue.number }}"
    fi

    echo "issue_number=${issue_number}" >> "$GITHUB_OUTPUT"
    issue_title="$(gh issue view "${issue_number}" --json title --jq '.title')"
    echo "issue_title=${issue_title}" >> "$GITHUB_OUTPUT"

    issue_body="$(gh issue view "${issue_number}" --json body --jq '.body')"
    echo "issue_body<<EOF" >> "$GITHUB_OUTPUT"
    echo "${issue_body}" >> "$GITHUB_OUTPUT"
    echo "EOF" >> "$GITHUB_OUTPUT"

    echo "Issue #${issue_number}: ${issue_title}"

設計ポイント:

  • gh CLIでIssueのタイトルと本文を取得
  • HEREDOCで複数行の本文を安全に環境変数に格納
  • イベントタイプに応じてIssue番号を動的に取得
Step 2: 作業ブランチの作成
- name: 'Create and Push Working Branch'
  id: create-branch
  env:
    ISSUE_NUMBER: ${{ steps.get-issue.outputs.issue_number }}
    GITHUB_RUN_ID: ${{ github.run_id }}
    GITHUB_RUN_ATTEMPT: ${{ github.run_attempt }}
  run: |
    branch_name="claude-automation/issue-${ISSUE_NUMBER}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"

    git config --global user.name "github-actions[bot]"
    git config --global user.email "github-actions[bot]@users.noreply.github.com"

    git checkout -b "${branch_name}"
    git push -u origin "${branch_name}"

    echo "branch_name=${branch_name}" >> "$GITHUB_OUTPUT"
    echo "Branch created and pushed: ${branch_name}"

ブランチ命名規則:

  • claude-automation/issue-{Issue番号}-{Run ID}-{試行回数}
  • 例: claude-automation/issue-42-1234567890-1
  • この命名により、Issue番号とワークフロー実行を紐付け可能
Step 3: Claude Code Action の実行
- name: 'Run Claude Code Action'
  id: claude
  uses: anthropics/claude-code-action@v1.0.22
  env:
    USE_AGENT_SDK: "false"
  with:
    claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
    prompt: |
      Issue #${{ steps.get-issue.outputs.issue_number }}: ${{ steps.get-issue.outputs.issue_title }}

      ${{ steps.get-issue.outputs.issue_body }}

      Please implement the changes described in this issue following the guidelines in CLAUDE.md.
    claude_args: |
      --mcp-config "${{ github.workspace }}/.claude/mcp.json"
      --permission-mode acceptEdits
      --allowedTools "Read,Write,Edit,Glob,Grep,TodoWrite,Bash(terraform fmt),Bash(terraform init),Bash(terraform validate),Bash(tflint --init),Bash(tflint),Bash(git status),Bash(git diff),mcp__terraform"
      --max-turns 30
    show_full_output: true

重要な設定項目:

項目 説明
claude_code_oauth_token Claude Code APIの認証トークン
prompt Issueの内容を渡す。最後に「CLAUDE.mdに従って実装せよ」と指示
--mcp-config Terraform MCP Server設定ファイル(公式ドキュメント参照用)
--permission-mode acceptEdits ファイル編集を自動承認
--allowedTools 使用を許可するツール(セキュリティのため制限)
--max-turns Claude Codeの最大思考ターン数

allowedToolsの設計意図:

  • Read,Write,Edit,Glob,Grep: ファイル操作
  • TodoWrite: タスク管理(Claude Codeが内部で使用)
  • Bash(terraform fmt): フォーマット
  • Bash(terraform init): 初期化
  • Bash(terraform validate): 検証
  • Bash(tflint --init), Bash(tflint): Lint
  • Bash(git status), Bash(git diff): Git状態確認・差分確認
  • mcp__terraform: Terraform MCP Server(公式ドキュメント参照)

セキュリティ上の理由で許可しないツール:

  • Bash(git push): Gitプッシュは別ステップで管理
  • Bash(rm -rf): 危険なコマンド
  • 無制限のBash(*): 予期しないコマンド実行を防ぐ
Step 4: 変更検出とMatrix設定
- name: 'Find changed terraform environment dirs'
  id: find-tfstate-dirs
  run: |
    set -euo pipefail

    git fetch origin main --prune

    # terraform/environments配下で変更された.tfファイルのディレクトリを検出
    changed_dirs=$(
      git diff --name-only origin/main...HEAD -- terraform/environments/ \
      | grep '\.tf$' \
      | xargs -r dirname \
      | sort -u \
      | jq -R -s -c 'split("\n")[:-1]'
    )

    if [ -z "$changed_dirs" ] || [ "$changed_dirs" = "[]" ]; then
      echo "No terraform files changed in terraform/environments/"
      dirs="[]"
    else
      dirs="$changed_dirs"
    fi

    echo "Changed directories: $dirs"
    echo "dirs=$dirs" >> "$GITHUB_OUTPUT"

設計ポイント:

  • terraform/environments/ 配下の.tfファイルに変更があったディレクトリを自動検出
  • JSON配列形式で出力してMatrix戦略に渡す
  • 例: ["terraform/environments/develop", "terraform/environments/staging"]
Step 5: Terraform検証(Matrix並列実行)
terraform-validate:
  needs: [claude-code-automation, set-matrix]
  if: needs.claude-code-automation.outputs.has_changes == 'true' && needs.set-matrix.outputs.dirs != '[]'
  runs-on: ubuntu-latest

  strategy:
    fail-fast: false
    max-parallel: 5
    matrix:
      TF_DIR: ${{ fromJson(needs.set-matrix.outputs.dirs) }}

  env:
    TF_DIR: ${{ matrix.TF_DIR }}

  defaults:
    run:
      shell: bash
      working-directory: ${{ env.TF_DIR }}

  steps:
    - name: 'Terraform Format'
      id: fmt
      run: terraform fmt -check
      continue-on-error: true

    - name: 'Terraform Init'
      id: init
      run: terraform init -upgrade

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

    - name: 'TFLint'
      id: tflint
      run: |
        tflint --init
        tflint --format compact --recursive
      continue-on-error: true

    - name: 'Terraform Plan'
      id: plan
      run: terraform plan --parallelism=50 -no-color -input=false
      continue-on-error: true

Matrix戦略の利点:

  • 複数の環境(develop/staging/production)を並列実行
  • max-parallel: 5: 最大5つのジョブを同時実行
  • fail-fast: false: 1つ失敗しても他のジョブは継続

continue-on-errorの意図:

  • 全てのチェックを実行してエラー情報を収集
  • 後続の自動修正ステップで一括修正
Step 6: エラー情報の保存
- name: 'Save Error Information'
  id: save-errors
  if: |
    steps.fmt.outcome == 'failure' ||
    steps.init.outcome == 'failure' ||
    steps.validate.outcome == 'failure' ||
    steps.tflint.outcome == 'failure' ||
    steps.plan.outcome == 'failure'
  run: |
    mkdir -p /tmp/terraform-errors

    # ディレクトリ名をサニタイズ(/を-に変換)
    sanitized_dir=$(echo "${{ matrix.TF_DIR }}" | tr '/' '-')

    # エラー情報をJSONで保存
    cat > /tmp/terraform-errors/${sanitized_dir}.json <<EOF
    {
      "directory": "${{ matrix.TF_DIR }}",
      "fmt_outcome": "${{ steps.fmt.outcome }}",
      "init_outcome": "${{ steps.init.outcome }}",
      "validate_outcome": "${{ steps.validate.outcome }}",
      "tflint_outcome": "${{ steps.tflint.outcome }}",
      "plan_outcome": "${{ steps.plan.outcome }}",
      "fmt_log": $(echo "${fmt_stdout}${fmt_stderr}" | jq -Rs .),
      "validate_log": $(echo "${validate_stdout}${validate_stderr}" | jq -Rs .),
      "tflint_log": $(echo "${tflint_stdout}${tflint_stderr}" | jq -Rs .),
      "plan_log": $(echo "${plan_stdout}${plan_stderr}" | jq -Rs .)
    }
    EOF

    echo "has_errors=true" >> "$GITHUB_OUTPUT"

- name: 'Upload Error Artifact'
  if: steps.save-errors.outputs.has_errors == 'true'
  uses: actions/upload-artifact@v4
  with:
    name: ${{ env.artifact_name }}
    path: /tmp/terraform-errors/
    retention-days: 1

エラー情報の形式:

{
  "directory": "terraform/environments/develop",
  "fmt_outcome": "failure",
  "validate_outcome": "failure",
  "validate_log": "Error: Missing required argument\n  on main.tf line 10..."
}
Step 7: 自動修正(最大3回リトライ)
fix-terraform-errors:
  needs: [claude-code-automation, set-matrix, terraform-validate]
  if: failure() && needs.terraform-validate.result == 'failure'
  runs-on: ubuntu-latest

  steps:
    - name: 'Check Auto-fix Retry Count'
      id: check-retry
      run: |
        set -euo pipefail

        # 最近10件のコミットメッセージから[terraform-auto-fix]の回数をカウント
        retry_count=$(git log -10 --oneline | grep -c '\[terraform-auto-fix\]' || true)

        echo "Current auto-fix retry count: ${retry_count}"

        if [ "${retry_count}" -ge 3 ]; then
          echo "Auto-fix retry limit (3) reached. Stopping automatic fixes."
          echo "should_fix=false" >> "$GITHUB_OUTPUT"
          exit 0
        else
          echo "should_fix=true" >> "$GITHUB_OUTPUT"
        fi

    - name: 'Download Error Artifacts'
      if: steps.check-retry.outputs.should_fix == 'true'
      uses: actions/download-artifact@v4
      with:
        path: /tmp/all-terraform-errors/
        pattern: terraform-errors-*

    - name: 'Aggregate Error Information'
      if: steps.check-retry.outputs.should_fix == 'true'
      id: aggregate-errors
      run: |
        # すべてのエラーファイルを読み込んで集約
        > /tmp/error_summary.txt

        for error_file in /tmp/all-terraform-errors/terraform-errors-*/*.json; do
          if [ -f "$error_file" ]; then
            directory=$(jq -r '.directory' "$error_file")
            validate_log=$(jq -r '.validate_log // ""' "$error_file")
            tflint_log=$(jq -r '.tflint_log // ""' "$error_file")

            cat >> /tmp/error_summary.txt <<ERRORSUMMARY

        ## Error in Directory: ${directory}

        ### Validate Log:
        \`\`\`
        ${validate_log}
        \`\`\`

        ### TFLint Log:
        \`\`\`
        ${tflint_log}
        \`\`\`

        ---
        ERRORSUMMARY
          fi
        done

    - name: 'Prepare Error Prompt'
      if: steps.check-retry.outputs.should_fix == 'true'
      run: |
        cat > /tmp/claude_fix_prompt.txt <<'FIXPROMPT'
        # Terraform Validation Errors Detected

        The following Terraform validation errors were detected in the automated CI/CD pipeline.
        Please fix these errors according to the guidelines in CLAUDE.md.

        FIXPROMPT

        cat /tmp/error_summary.txt >> /tmp/claude_fix_prompt.txt

        cat >> /tmp/claude_fix_prompt.txt <<'FIXPROMPT'

        ## Instructions:
        1. Read the error logs carefully
        2. Fix the issues in the affected Terraform files
        3. Run terraform fmt, terraform init, and terraform validate to ensure the fixes are correct
        4. Make sure all changes follow the guidelines in CLAUDE.md

        Please fix these errors now.
        FIXPROMPT

        # プロンプトを環境変数に保存
        echo "CLAUDE_FIX_PROMPT<<PROMPTEOF" >> "$GITHUB_ENV"
        cat /tmp/claude_fix_prompt.txt >> "$GITHUB_ENV"
        echo "PROMPTEOF" >> "$GITHUB_ENV"

    - name: 'Run Claude Code to Fix Errors'
      if: steps.check-retry.outputs.should_fix == 'true'
      uses: anthropics/claude-code-action@v1.0.22
      with:
        claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
        prompt: ${{ env.CLAUDE_FIX_PROMPT }}
        claude_args: |
          --mcp-config "${{ github.workspace }}/.claude/mcp.json"
          --permission-mode acceptEdits
          --allowedTools "Read,Write,Edit,Glob,Grep,TodoWrite,Bash(terraform fmt),Bash(terraform init),Bash(terraform validate),Bash(tflint --init),Bash(tflint),Bash(git status),Bash(git diff),mcp__terraform"
          --max-turns 30

    - name: 'Commit and Push Fixes'
      if: steps.check-retry.outputs.should_fix == 'true'
      run: |
        git add -A
        if ! git diff --staged --quiet; then
          git commit -m "[terraform-auto-fix] Fix Terraform validation errors" \
                     -m "Automated fix by Claude Code based on CI/CD validation errors"
          git push origin HEAD
          echo "fixes_applied=true" >> "$GITHUB_OUTPUT"
        fi

自動修正の仕組み:

  1. リトライ回数チェック: 最近10件のコミットから [terraform-auto-fix] を含むコミット数をカウント
  2. 3回以上なら停止: 無限ループを防ぐ
  3. エラー集約: 全環境のエラーログを1つのプロンプトに統合
  4. Claude Code実行: エラーログを渡して修正を依頼
  5. 自動コミット: [terraform-auto-fix] マーカー付きでコミット
  6. 再検証: PRが更新され、検証が自動再実行される

3. terraform-workflow.yaml - 検証とデプロイ

このワークフローは、PR作成時の検証とmainブランチマージ時の自動デプロイを担当します。

主要機能

1. 差分検出ロジック
- name: Check diff
  id: diff
  run: |
    set -euo pipefail

    git fetch origin main --prune

    # マージコミット検出: push to main の場合
    if { [ "${{ github.event_name }}" = "push" ] && [ "${{ github.ref }}" = "refs/heads/main" ]; }; then
      echo "Detecting changes from merge commit (HEAD^..HEAD)"

      if [ "${TF_DIR}" = "." ]; then
        # ルート直下の *.tf のみ(サブディレクトリは除外)
        changed=$(
          git diff --name-only HEAD^..HEAD -- . \
          | grep -E '^[^/]+\.tf$' || true
        )
      else
        changed=$(
          git diff --name-only HEAD^..HEAD -- . \
          | grep -E '\.tf$' || true
        )
      fi
    else
      # PR時は従来通りの差分検出
      echo "Detecting changes from branch (origin/main...HEAD)"

      if [ "${TF_DIR}" = "." ]; then
        changed=$(
          git diff --name-only origin/main...HEAD -- . \
          | grep -E '^[^/]+\.tf$' || true
        )
      else
        changed=$(
          git diff --name-only origin/main...HEAD -- . \
          | grep -E '\.tf$' || true
        )
      fi
    fi

    if [ -n "$changed" ]; then
      echo "diff=true" >> "$GITHUB_OUTPUT"
    else
      echo "diff=false" >> "$GITHUB_OUTPUT"
    fi

設計ポイント:

  • PR時: origin/main...HEAD で差分検出(ブランチ全体の変更)
  • mainマージ時: HEAD^..HEAD でマージコミットのみ検出
  • .tfファイルに変更がある場合のみ後続ステップを実行(コスト削減)
2. PRへのコメント投稿
- name: Adding Pull Request Comment
  uses: actions/github-script@v6
  if: steps.diff.outputs.diff == 'true'
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    script: |
      const output = `#### Terraform Format and Style 🖌 \`${{ steps.fmt.outcome }}\`
      #### Terraform Initialization ⚙️ \`${{ steps.init.outcome }}\`
      #### Terraform Validation 🤖 \`${{ steps.validate.outcome }}\`
      #### TFLint 🔍 \`${{ steps.tflint.outcome }}\`
      #### Terraform Plan 📖 \`${{ steps.plan.outcome }}\`

      <details><summary>Show Plan</summary>

      \`\`\`terraform
      ${process.env.PLAN}
      \`\`\`

      </details>

      *Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;

      const prNumber = ${{ inputs.pr_number || 'context.issue.number' }};

      if (prNumber) {
        await github.rest.issues.createComment({
          issue_number: prNumber,
          owner: context.repo.owner,
          repo: context.repo.repo,
          body: output
        });
      }

コメント例:

#### Terraform Format and Style 🖌 `success`
#### Terraform Initialization ⚙️ `success`
#### Terraform Validation 🤖 `success`
#### TFLint 🔍 `success`
#### Terraform Plan 📖 `success`

<details><summary>Show Plan</summary>

```terraform
Terraform will perform the following actions:

  # module.vpc.aws_vpc.this will be created
  + resource "aws_vpc" "this" {
      + cidr_block = "10.0.0.0/16"
      ...
    }

Plan: 5 to add, 0 to change, 0 to destroy.

Pusher: @github-actions[bot], Action: pull_request


##### 3. 自動Apply条件

```yaml
- name: Terraform Apply (${{ env.TF_DIR }})
  id: apply
  if: ( github.ref == 'refs/heads/main' && github.event_name == 'push' && steps.diff.outputs.diff == 'true' && !contains(steps.plan.outputs.stdout, 'No changes.') )
  run: terraform apply --parallelism=50 -auto-approve -input=false -no-color
  continue-on-error: true

条件の意味:

  • github.ref == 'refs/heads/main': mainブランチへのpush
  • github.event_name == 'push': pushイベント(PRマージ時)
  • steps.diff.outputs.diff == 'true': .tfファイルに変更がある
  • !contains(steps.plan.outputs.stdout, 'No changes.'): planで変更が検出された

これにより:

  • PRマージ時のみ自動デプロイ
  • 変更がない場合はスキップ(無駄な実行を防止)

4. pr-comment-automation.yaml - PRコメントからの自動修正

このワークフローは、作成されたPRに対してコメントで修正を依頼できる機能を提供します。

概要

Issue経由でClaude CodeがPRを作成した後、さらに修正が必要な場合、PRのコメント欄から直接Claude Codeに修正を依頼できます。

主な用途:

  • 生成されたコードの微調整
  • 追加のリソース設定
  • コードレビュー後の修正依頼

トリガー条件

on:
  issue_comment:
    types: [created]

jobs:
  claude-code-pr-fix:
    if: |
      github.event.issue.pull_request &&
      contains(github.event.comment.body, '@claude')

条件:

  • PRのコメント欄にコメントが投稿された
  • コメント本文に @claude が含まれている

主要ステップの詳細解説

Step 1: PR情報の取得
- name: 'Get PR Information'
  id: get-pr
  run: |
    pr_json=$(gh pr view "${PR_NUMBER}" --json headRefName,headRefOid,baseRefName,title)

    pr_branch=$(echo "${pr_json}" | jq -r '.headRefName')
    pr_head_sha=$(echo "${pr_json}" | jq -r '.headRefOid')
    pr_base=$(echo "${pr_json}" | jq -r '.baseRefName')
    pr_title=$(echo "${pr_json}" | jq -r '.title')

    echo "pr_branch=${pr_branch}" >> "$GITHUB_OUTPUT"
    echo "pr_head_sha=${pr_head_sha}" >> "$GITHUB_OUTPUT"
    echo "pr_base=${pr_base}" >> "$GITHUB_OUTPUT"
    echo "pr_title=${pr_title}" >> "$GITHUB_OUTPUT"

取得情報:

  • PRのブランチ名(headRefName)
  • PRのHEAD SHA(最新コミット)
  • ベースブランチ(通常はmain)
  • PRタイトル
Step 2: PRブランチのチェックアウト
- name: 'Checkout PR Branch'
  uses: actions/checkout@v4
  with:
    fetch-depth: 0
    ref: ${{ steps.get-pr.outputs.pr_branch }}

ポイント:

  • fetch-depth: 0: 全履歴を取得(必要に応じて差分確認可能)
  • ref: PRのブランチをチェックアウト(mainではない)
Step 3: Claude用プロンプトの準備
- name: 'Prepare Claude Prompt'
  id: prepare-prompt
  run: |
    # @claudeを除去したコメント本文を取得
    comment_cleaned=$(echo "${COMMENT_BODY}" | sed 's/@claude//g' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')

    cat > /tmp/claude_prompt.txt <<PROMPT
# PR Modification Request

**PR #${PR_NUMBER}**: ${PR_TITLE}

## User's Request:
${comment_cleaned}

## Instructions:
1. Read the user's request carefully
2. Modify the necessary Terraform files according to the request
3. Follow the guidelines in CLAUDE.md
4. Run terraform fmt, terraform init, and terraform validate to ensure correctness
5. Make sure all changes are appropriate and follow best practices

Please implement the requested changes now.
PROMPT

設計ポイント:

  • @claude メンションを除去してクリーンなリクエスト文を抽出
  • PR番号とタイトルをコンテキストとして提供
  • CLAUDE.mdのガイドラインに従うよう明示的に指示
  • 検証コマンド(fmt/init/validate)の実行を指示
Step 4: Claude Code Action の実行
- name: 'Run Claude Code Action'
  id: claude
  uses: anthropics/claude-code-action@v1.0.22
  with:
    claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
    prompt: ${{ env.CLAUDE_PROMPT }}
    claude_args: |
      --mcp-config "${{ github.workspace }}/.claude/mcp.json"
      --permission-mode acceptEdits
      --allowedTools "Read,Write,Edit,Glob,Grep,TodoWrite,Bash(terraform fmt),Bash(terraform init),Bash(terraform validate),Bash(tflint --init),Bash(tflint),Bash(git status),Bash(git diff),mcp__terraform"
      --max-turns 30

allowedToolsの特徴:

  • claude-automation.yaml とほぼ同じツールセット
  • 追加: Bash(git status), Bash(git diff) - 変更確認用
  • セキュリティ: git push は許可しない(別ステップで実行)
Step 5: 変更のコミット & プッシュ
- name: 'Commit and Push Changes'
  id: commit
  run: |
    git config --global user.name "github-actions[bot]"
    git config --global user.email "github-actions[bot]@users.noreply.github.com"

    git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git"

    # 変更があればコミット
    git add -A
    if ! git diff --staged --quiet; then
      git commit -m "Fix PR #${PR_NUMBER}: Changes requested by @${COMMENT_USER}" \
                 -m "Automated fix by Claude Code based on PR comment" \
                 -m "Co-authored-by: ${COMMENT_USER} <${COMMENT_USER}@users.noreply.github.com>"
      git push origin HEAD
      echo "has_changes=true" >> "$GITHUB_OUTPUT"
    else
      echo "has_changes=false" >> "$GITHUB_OUTPUT"
    fi

コミットメッセージの工夫:

  • 1行目: Fix PR #XXX: Changes requested by @user - 誰の依頼か明記
  • 2行目: 自動修正であることを明記
  • 3行目: Co-authored-by でコメント投稿者を共著者に追加
Step 6: フィードバックコメントの投稿
- name: 'Add Success Comment'
  if: steps.commit.outputs.has_changes == 'true'
  run: |
    cat > /tmp/success_comment.md <<'COMMENT'
✅ **Claude Code PR Fix Applied**

@${{ env.COMMENT_USER }}, I've processed your request and applied the changes to this PR.

The changes have been committed and pushed to the PR branch.
Terraform validation will run automatically to verify the changes.

Please review the changes and let me know if you need any further modifications.
COMMENT

    gh pr comment "${PR_NUMBER}" --body-file /tmp/success_comment.md

フィードバックの種類:

  1. 変更あり: ✅ Claude Code PR Fix Applied - 成功メッセージ
  2. 変更なし: ℹ️ No Changes Detected - 変更が不要だった場合の通知

使用例

例1: インスタンスタイプの変更

PRコメント:

@claude インスタンスタイプをt3.microからt3.smallに変更してください。
理由: メモリ不足のため

自動処理:

  1. Claude Codeが terraform/environments/develop/values.tf を検索
  2. instance_type = "t3.micro"instance_type = "t3.small" に変更
  3. terraform fmt, terraform init, terraform validate を実行
  4. コミット & プッシュ
  5. PRに成功コメントを投稿
例2: タグの追加

PRコメント:

@claude すべてのリソースに `Owner = "TeamA"` タグを追加してください

自動処理:

  1. Claude Codeが関連するTerraformファイルを検索
  2. tags ブロックに Owner = "TeamA" を追加
  3. 検証コマンドを実行
  4. コミット & プッシュ
例3: セキュリティグループルールの追加

PRコメント:

@claude セキュリティグループに以下のルールを追加してください:
- インバウンド: TCP 8080(アプリケーションポート)
- ソース: 10.0.0.0/16(VPC内のみ)

自動処理:

  1. セキュリティグループモジュールを特定
  2. インバウンドルールを追加
  3. 検証 & コミット

ワークフローの利点

利点 説明
高速な修正サイクル Issueを作成せず、PRコメントから直接修正依頼
コンテキストの維持 PRの文脈の中で修正を依頼できる
レビュー効率化 コードレビュー中に発見した問題をその場で修正依頼
トレーサビリティ 修正依頼と実装がPRコメント上で追跡可能
Co-authored-by コメント投稿者が共著者として記録される

注意事項

  1. PRコメント限定: Issue本文のコメントでは動作しません(PRに紐付いたコメントのみ)
  2. @claude必須: コメントに @claude を含める必要があります
  3. 並列実行: 複数のコメントを同時に投稿すると、複数のワークフローが並列実行されます
    • 推奨: 前の修正が完了してから次のコメントを投稿
  4. AWS認証: Terraform init/plan が必要な修正の場合、AWS認証が自動的に行われます
  5. 再検証: PRが更新されると terraform-workflow.yaml が自動的に再実行されます

CLAUDE.md - LLMへの指示設計(詳細解説)

CLAUDE.mdは、Claude Codeに対する「プロジェクト憲法」です。このファイルがあることで、LLMは一貫性のあるコードを生成できます。

設計思想

1. なぜCLAUDE.mdが必要なのか?

LLMは強力ですが、以下の課題があります:

課題 CLAUDE.mdによる解決
暗黙の前提を持たない デフォルト値を明示(例: リージョン=ap-northeast-1)
プロジェクト固有のルールを知らない ディレクトリ構成、命名規則を明記
幻覚(Hallucination) 「実際のリソースのみ記載せよ」と強調
一貫性のないコード コーディング規約、テンプレートを提供
品質基準が不明確 Definition of Doneを明記

2. CLAUDE.mdの構成

1. プロジェクト概要
   └─ デフォルトAWSリージョン(明示的指定)

2. ディレクトリ構成
   └─ ファイル配置ルール

3. コーディング規約
   ├─ 命名規則
   ├─ ファイル構成
   └─ セキュリティベストプラクティス

4. モジュール作成ガイドライン
   ├─ 設計原則
   ├─ README.md作成ルール
   └─ 作成手順

5. 環境別設定ガイドライン

6. 品質保証:CI/CD自動検証
   └─ Definition of Done

7. Claude Code利用時の注意事項
   ├─ Issue記述のベストプラクティス
   └─ 生成ファイルの説明

8. トラブルシューティング

重要セクションの詳細解説

セクション1: デフォルトAWSリージョン

### 🌏 デフォルトAWSリージョン

**このプロジェクトでは、基本的にAWS東京リージョン(ap-northeast-1)を使用します。**

- すべてのリソースは特別な理由がない限り `ap-northeast-1` にデプロイしてください
- プロバイダー設定では `region = "ap-northeast-1"` を指定してください
- リージョン固有の設定(AMI ID、アベイラビリティゾーンなど)も東京リージョンを基準としてください

設計意図:

  • LLMは暗黙の前提を持たないため、明示的にデフォルト値を指定
  • これがないと、LLMは米国リージョン(us-east-1)を使う可能性がある

セクション2: ディレクトリ構成

## ディレクトリ構成

\`\`\`
terraform/
├── environments/          # 環境別のTerraform実行ディレクトリ
│   ├── develop/
│   │   ├── main.tf       # モジュール呼び出し
│   │   ├── values.tf     # 環境固有のローカル値(locals)
│   │   ├── outputs.tf
│   │   ├── versions.tf
│   │   ├── provider.tf
│   │   └── backend.tf
│   ├── staging/
│   └── production/
└── modules/              # 再利用可能なモジュール
    ├── vpc/
    ├── ec2/
    └── rds/
\`\`\`

設計意図:

  • ファイル配置の一貫性を保証
  • LLMがファイルをどこに作成すべきか明確に理解できる

セクション3: モジュールREADME.md作成ルール

### モジュールのREADME.md作成ガイドライン

#### README.md作成時のポイント

1. **実際のリソースのみを記載(最重要)**:
   - README.md作成前に必ず `main.tf` を確認し、実際に定義されているリソースのみを「## リソース」セクションに記載すること
   - 例: VPCモジュールなのにEC2やRDSなど、そのモジュールに含まれていないリソースを記載しないこと
   - 他のモジュールで定義されるリソースは記載しないこと

設計意図:

  • LLMの幻覚(Hallucination)を防ぐ
  • 実際のリソースのみ記載せよ」と強調することで、ドキュメントの正確性を担保

具体例:

悪い例(幻覚):

# VPC Module

## リソース
- aws_vpc.this
- aws_subnet.public
- aws_instance.web_server  # ← VPCモジュールにEC2は含まれない!
- aws_rds_instance.db      # ← VPCモジュールにRDSは含まれない!

良い例:

# VPC Module

## リソース
- aws_vpc.this
- aws_subnet.public
- aws_subnet.private
- aws_internet_gateway.this
- aws_nat_gateway.this

セクション4: Definition of Done

### 📋 Definition of Done(DoD)

Claude Codeが生成したコードは、以下を満たす必要があります:

#### GitHub Actions環境での自動検証
- [ ] 変更された環境ディレクトリで `terraform init` が成功する
- [ ] `terraform fmt -check` が成功する
- [ ] `terraform validate` が成功する
- [ ] `tflint` が成功する(エラー0件)
- [ ] `terraform plan` が成功する(エラーなし)
- [ ] 既存の `versions.tf / provider.tf / backend.tf` の方針を壊していない
- [ ] 追加/変更したモジュールに `variables.tf / outputs.tf` が揃っている
- [ ] モジュールの README.md は **main.tf で定義しているリソースのみ**を記載している

設計意図:

  • 自動修正時にClaude Codeが何を目指すべきか明確化
  • チェックリスト形式で具体的に記載

セクション5: Issue記述のベストプラクティス

### Issue記述のベストプラクティス

#### ✅ 良い例

**Issueタイトル**: 「開発環境にEC2インスタンスとRDSを追加」

**Issue本文**:
\`\`\`markdown
## 要件
開発環境にWebサーバー用のEC2インスタンスとMySQLデータベース(RDS)を追加したい

## 詳細

### EC2インスタンス
- インスタンスタイプ: t3.micro
- AMI: Amazon Linux 2023 (最新)
- セキュリティグループ: HTTP(80), HTTPS(443), SSH(22)を許可
- サブネット: パブリックサブネット

### RDS (MySQL)
- エンジン: MySQL 8.0
- インスタンスクラス: db.t3.micro
- ストレージ: 20GB (gp3)
- マルチAZ: 無効(開発環境のため)

## 環境
- 対象: develop環境 (`terraform/environments/develop/`)
- リージョン: ap-northeast-1(東京)※デフォルトのため明示不要
\`\`\`

#### ❌ 悪い例

**Issueタイトル**: 「サーバー作って」

**Issue本文**:
\`\`\`markdown
サーバーが必要です
\`\`\`
→ 具体的な情報が不足しているため、Claude Codeが適切なコードを生成できません

設計意図:

  • ユーザーがどのようにIssueを記述すべきかガイダンス
  • 良い例と悪い例を対比させて理解を促進

CLAUDE.mdのカスタマイズ方法

プロジェクトに合わせてカスタマイズする際のポイント:

1. リージョンの変更

### 🌏 デフォルトAWSリージョン

**このプロジェクトでは、基本的にAWS東京リージョン(ap-northeast-1)を使用します。**

↓ 例: 米国東部リージョンを使う場合

### 🌏 デフォルトAWSリージョン

**このプロジェクトでは、基本的にAWS米国東部リージョン(us-east-1)を使用します。**

2. ディレクトリ構成の変更

terraform/
├── environments/
│   ├── develop/
│   ├── staging/
│   └── production/

↓ 例: 環境名を変更する場合

terraform/
├── envs/
│   ├── dev/
│   ├── stg/
│   └── prd/

重要: ディレクトリ構成を変更したら、ワークフローの terraform/environments/ パスも修正が必要

3. コーディング規約の追加

例: タグルールを厳格化

### 必須タグ

すべてのリソースに以下のタグを必ず付与すること:

\`\`\`hcl
tags = {
  Environment = local.environment   # 必須
  ManagedBy   = "Terraform"        # 必須
  Project     = "my-project"       # 必須
  Owner       = "team-name"        # 必須
  CostCenter  = "12345"            # 必須
}
\`\`\`

4. セキュリティポリシーの追加

例: S3バケットの暗号化必須

### S3セキュリティポリシー

すべてのS3バケットは以下を満たすこと:

\`\`\`hcl
resource "aws_s3_bucket" "example" {
  # ...

  # 必須: サーバーサイド暗号化
  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm = "AES256"
      }
    }
  }

  # 必須: バージョニング有効化
  versioning {
    enabled = true
  }

  # 必須: パブリックアクセスブロック
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}
\`\`\`

導入手順

前提条件

  • GitHubリポジトリ
  • AWSアカウント(S3バケットとIAM Role)
  • Claude Code OAuthトークン

Step 1: Claude Code OAuthトークンの取得

こちらの記事を参考に作成しました。

Step 2: AWS環境準備

S3バケット作成(Terraformバックエンド用)

image.png

IAM Role作成(GitHub Actions OIDC用)

こちらの記事を参考に作成しました。

Step 3: GitHub Secretsの設定

Settings > Secrets and variables > Actions で以下を設定:

Secret名
CLAUDE_CODE_OAUTH_TOKEN Step 1で取得したトークン
ASSUME_ROLE_ARN AWS IAM Role ARN(例: arn:aws:iam::123456789012:role/GitHubActionsRole

Step 4: ファイル配置

リポジトリに以下のファイルを配置:

.github/workflows/
├── caller.yaml
├── claude-automation.yaml
├── pr-comment-automation.yaml
└── terraform-workflow.yaml

.claude/
└── mcp.json

CLAUDE.md
.terraform-version

mcp.json:

{
  "mcpServers": {
    "terraform": {
      "command": "docker",
      "args": ["run", "-i", "--rm", "hashicorp/terraform-mcp-server:0.3.2"]
    }
  }
}

.terraform-version:

1.5.0

Step 5: 初回実行

  1. GitHub Issueを作成
  2. タイトル: 「開発環境にVPCモジュールを追加」
  3. 本文:
## 要件
開発環境用のVPCを作成したい

## 詳細
- CIDR: 10.0.0.0/16
- パブリックサブネット: 10.0.1.0/24
- プライベートサブネット: 10.0.10.0/24

## 環境
- 対象: develop環境
  1. Issue作成と同時にワークフローが起動
  2. 数分後にPRが作成される
  3. PRをレビュー
  4. 問題なければマージ → 本番デプロイ

運用Tips

Issue記述のコツ

1. 構造化された記述

## 要件
[何を実現したいか 1-2行で簡潔に]

## 詳細
### リソース1
- 設定項目1: 値
- 設定項目2: 値

### リソース2
- 設定項目1: 値

## 環境
- 対象: develop環境
- リージョン: ap-northeast-1(デフォルトなので省略可)

2. 具体的な値を記載

❌ 悪い例:

EC2インスタンスを作って

✅ 良い例:

### EC2インスタンス
- インスタンスタイプ: t3.micro
- AMI: Amazon Linux 2023 (最新)
- セキュリティグループ: HTTP(80), HTTPS(443)を許可

3. 環境を明示

## 環境
- 対象: develop環境 (`terraform/environments/develop/`)

トラブルシューティング

Q1: 自動修正が3回失敗した

A: PRにコメントで警告が投稿されます。以下の手順で対応:

  1. GitHub Actionsのログでterraform-validateジョブを確認
  2. エラー内容を特定
  3. ローカルで修正またはGitHub上で直接編集
  4. PRブランチにプッシュ
  5. 自動的に再検証される

Q2: Claude Codeが意図しないコードを生成した

A: 以下を確認:

  1. CLAUDE.mdを見直す: ガイドラインが明確か?
  2. Issueを詳細に: 具体的な値や設定を記載したか?
  3. PRで修正: 生成されたコードをレビューして修正

Q3: AWS認証エラーが発生する

A: 以下を確認:

  1. ASSUME_ROLE_ARN Secretが正しく設定されているか
  2. IAM RoleのTrust PolicyでGitHub ActionsのOIDCが許可されているか
  3. IAM Roleに適切な権限が付与されているか

まとめ

本記事では、Claude Code × GitHub Actionsを活用したTerraform完全自動化ワークフローを詳しく解説しました。

重要なポイント

ポイント 説明
CLAUDE.mdが最重要 LLMに明確な指針を与えることで一貫性のあるコードを生成
3つのワークフローの連携 caller → claude-automation → terraform-workflow の流れ
自動修正機能 エラー検出から修正まで最大3回自動試行
Matrix戦略 複数環境を並列実行して高速化
セキュリティ allowedToolsで使用可能なツールを制限

システムの利点

  • 📈 開発速度向上: Issueを書くだけでコード生成
  • 🎯 品質担保: CI/CDパイプラインで自動検証
  • 🔄 一貫性: CLAUDE.mdによる統一されたコードスタイル
  • 🤖 自動修正: 人的ミスを削減

今後の展開

  • マルチクラウド対応: GCP、Azureにも対応
  • 高度な自動修正: より複雑なエラーにも対応
  • コスト最適化: Claude Code実行回数の最適化

この仕組みを導入することで、Terraformの開発体験が劇的に向上します。ぜひ試してみてください!

質問や改善提案があれば、コメント欄でお聞かせください。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?