🧑‍🍳

Amazon Bedrock + GitHub Actions でClaude Codeを本格運用。複数リポジトリ対応の共通化も解説

フィッツプラスシステム開発部の高山です。

話題のClaude Codeを、GitHub Actions上で直接活用できる環境を構築しました。フィッツプラスではすでにAmazon BedrockでClaudeをAPI利用できる体制が整っているため、これを活かしたセットアップに挑戦してみました。

複数のリポジトリから共通のワークフローを呼び出せるようにする方法も含めて、手順をまとめています。同じようにAmazon Bedrockで課金を統一しつつ、GitHub ActionsでClaude Codeを使いたい方の参考になれば嬉しいです。

セットアップ手順

1. Bedrockでモデルを有効化

もしまだモデルの環境が整っていなければ、AWSコンソールからClaude Codeで使用するモデルを有効にします。モデルを選んでリクエストするだけなので簡単です。今回はClaude Sonnet 4を使う前提で設定を書いていますが、お好きなものをお使いください。ちなみにフィッツプラスではus-east-1で動かしています。

2. IAMロールの作成

GitHub ActionsからBedrockのAPIを呼び出すため、OIDC(OpenID Connect)を利用してGitHubリポジトリとAWSアカウントを安全に連携させるIAMロールを作成します。

権限ポリシー:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "bedrock:InvokeModel",
        "bedrock:InvokeModelWithResponseStream"
      ],
      "Resource": "*"
    }
  ]
}

信頼ポリシー:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::xxxxxxxxxxxx:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        },
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:armg/*:*"
        }
      }
    }
  ]
}

xxxxxxxxxxxxはご自身のAWSアカウントID、armg/*は対象とするOrganization/リポジトリ名に適宜変更してください。

フィッツプラスではTerraformを使ってIAMロールを作成しています。以下のコードスニペットは、上記の設定をTerraformで実装したものです。

terraform
resource "aws_iam_role" "claude" {
  name               = "claude-action"
  assume_role_policy = data.aws_iam_policy_document.assume_claude.json
}

data "aws_iam_policy_document" "assume_claude" {
  statement {
    actions = ["sts:AssumeRoleWithWebIdentity"]

    principals {
      type        = "Federated"
      identifiers = [aws_iam_openid_connect_provider.github.arn]
    }

    condition {
      test     = "StringEquals"
      variable = "token.actions.githubusercontent.com:aud"
      values = [
        "sts.amazonaws.com",
      ]
    }

    condition {
      test     = "StringLike"
      variable = "token.actions.githubusercontent.com:sub"
      values = [
        "repo:armg/*:*",
      ]
    }
  }
}

resource "aws_iam_policy" "claude" {
  name   = "claude-action"
  path   = "/"
  policy = data.aws_iam_policy_document.claude.json
}

resource "aws_iam_role_policy_attachment" "claude" {
  role       = aws_iam_role.claude.name
  policy_arn = aws_iam_policy.claude.arn
}

# tfsec:ignore:aws-iam-no-policy-wildcards
data "aws_iam_policy_document" "claude" {
  statement {
    effect = "Allow"
    actions = [
      "bedrock:InvokeModel",
      "bedrock:InvokeModelWithResponseStream",
    ]
    resources = ["*"]
  }
}

3. カスタムGitHub Appの作成

Anthropic公式のAppもありますが、組織内の権限をより細かく管理するため、カスタムGitHub Appを作成します。これはAnthropic自身も推奨している方法で、企業利用におけるセキュリティ統制の観点からも重要だと考えて設定しました。

主要な設定項目:

  • GitHub App name: armg-claude-botなど、お好きな名前
  • Webhook: Activeのチェックを外す

Repository permissions:

  • Contents: Read & Write
  • Issues: Read & Write
  • Pull requests: Read & Write
  • Workflows: Read & write

作成後、「generate a private key」をクリックして秘密鍵を生成・ダウンロードしておきます。この秘密鍵はあとでGitHub ActionsのSecretsに登録するのに使います。

ちなみにGitHub App nameは、Issueのコメントやコミット履歴の名前として出現します。

4. GitHub Appのインストールと設定

作成したGitHub Appを対象のリポジトリまたはOrganization全体にインストールします。

その後、GitHubリポジトリの Settings > Secrets and variables > Actions から、以下の3つのシークレットを登録します:

  • CLAUDE_APP_ID: 作成したカスタムGitHub AppのID
  • CLAUDE_APP_PRIVATE_KEY: ダウンロードした秘密鍵
  • CLAUDE_AWS_ROLE_TO_ASSUME: 作成したIAMロールのARN

ワークフローの作成

.github/workflows/claude-code.ymlとして、以下のワークフローファイルを作成します:

.github/workflows/claude-code.yml
name: Claude Code Action

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

on:
  issue_comment:
    types: [created]
  pull_request_review_comment:
    types: [created]
  issues:
    types: [opened, assigned]

jobs:
  claude-response:
    if: |
      (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'issues' && contains(github.event.issue.body, '@claude'))
    runs-on: ubuntu-latest
    env:
      AWS_REGION: us-east-1
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Generate GitHub App token
        id: app-token
        uses: actions/create-github-app-token@v2
        with:
          app-id: ${{ secrets.CLAUDE_APP_ID }}
          private-key: ${{ secrets.CLAUDE_APP_PRIVATE_KEY }}

      - name: Configure AWS Credentials (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.CLAUDE_AWS_ROLE_TO_ASSUME }}
          aws-region: us-east-1

      - uses: anthropics/claude-code-action@beta
        with:
          trigger_phrase: "@claude"
          timeout_minutes: "60"
          github_token: ${{ steps.app-token.outputs.token }}
          use_bedrock: "true"
          model: "us.anthropic.claude-sonnet-4-20250514-v1:0"

modelの指定は、利用したいモデルのIDに適宜変更してください。

設定がうまくいっていると、@claudeを付けて依頼すればPRのレビューやIssueを読んで開発作業をしてくれます。

ワークフローの共通化(応用編)

フィッツプラスはリポジトリがものすごく多いです。各リポジトリで設定をしてもいいのですが、モデルの変更やアクション自体のアップデートをそれぞれのリポジトリで管理するのはいささか面倒に感じました。

というわけなので、メンテナンス性を考慮してワークフローを共通化しました。GitHub Actionsのworkflow_callを利用すると、メインの処理を1つのリポジトリに集約し、他のリポジトリから簡単に呼び出せるようになります。

共通ワークフローの作成

まず、共通ワークフローを配置するリポジトリを決めます。フィッツプラスでは専用の共通ワークフローリポジトリがあるので、そこに以下の修正を加えたファイルを配置しました。

元々のワークフローにworkflow_callトリガーと、必要なinputsとsecretsを追加します:

claude-code.yml
@@ -13,13 +13,33 @@ on:
     types: [created]
   issues:
     types: [opened, assigned]
+  workflow_call:
+    inputs:
+      trigger_phrase:
+        description: 'Phrase to trigger Claude action'
+        required: false
+        type: string
+        default: '@claude'
+      timeout_minutes:
+        description: 'Timeout in minutes'
+        required: false
+        type: string
+        default: '60'
+    secrets:
+      CLAUDE_APP_ID:
+        required: true
+      CLAUDE_APP_PRIVATE_KEY:
+        required: true
+      CLAUDE_AWS_ROLE_TO_ASSUME:
+        required: true
 
 jobs:
   claude-response:
     if: |
       (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
       (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
-      (github.event_name == 'issues' && contains(github.event.issue.body, '@claude'))
+      (github.event_name == 'issues' && contains(github.event.issue.body, '@claude')) ||
+      (github.event_name == 'workflow_call')
     runs-on: ubuntu-latest
     env:
       AWS_REGION: us-east-1
@@ -42,8 +62,8 @@ jobs:
 
       - uses: anthropics/claude-code-action@beta
         with:
-          trigger_phrase: "@claude"
-          timeout_minutes: "60"
+          trigger_phrase: ${{ inputs.trigger_phrase || '@claude' }}
+          timeout_minutes: ${{ inputs.timeout_minutes || '60' }}
           github_token: ${{ steps.app-token.outputs.token }}
           use_bedrock: "true"
           model: "us.anthropic.claude-sonnet-4-20250514-v1:0"
共通ワークフロー(完全版)
.github/workflows/claude-code.yml
name: Claude Code Action

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

on:
  issue_comment:
    types: [created]
  pull_request_review_comment:
    types: [created]
  issues:
    types: [opened, assigned]
  workflow_call:
    inputs:
      trigger_phrase:
        description: 'Phrase to trigger Claude action'
        required: false
        type: string
        default: '@claude'
      timeout_minutes:
        description: 'Timeout in minutes'
        required: false
        type: string
        default: '60'
    secrets:
      CLAUDE_APP_ID:
        required: true
      CLAUDE_APP_PRIVATE_KEY:
        required: true
      CLAUDE_AWS_ROLE_TO_ASSUME:
        required: true

jobs:
  claude-response:
    if: |
      (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'issues' && contains(github.event.issue.body, '@claude')) ||
      (github.event_name == 'workflow_call')
    runs-on: ubuntu-latest
    env:
      AWS_REGION: us-east-1
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Generate GitHub App token
        id: app-token
        uses: actions/create-github-app-token@v2
        with:
          app-id: ${{ secrets.CLAUDE_APP_ID }}
          private-key: ${{ secrets.CLAUDE_APP_PRIVATE_KEY }}

      - name: Configure AWS Credentials (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.CLAUDE_AWS_ROLE_TO_ASSUME }}
          aws-region: us-east-1

      - uses: anthropics/claude-code-action@beta
        with:
          trigger_phrase: ${{ inputs.trigger_phrase || '@claude' }}
          timeout_minutes: ${{ inputs.timeout_minutes || '60' }}
          github_token: ${{ steps.app-token.outputs.token }}
          use_bedrock: "true"
          model: "us.anthropic.claude-sonnet-4-20250514-v1:0"

各リポジトリでの利用

各リポジトリでは、この共通ワークフローを呼び出すシンプルなファイルを配置するだけで済みます:

.github/workflows/claude-code.yml
name: Claude Code Action

on:
  issue_comment:
    types: [created]
  pull_request_review_comment:
    types: [created]
  issues:
    types: [opened, assigned]

jobs:
  claude-response:
    if: |
      (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'issues' && contains(github.event.issue.body, '@claude'))
    uses: armg/common-workflows/.github/workflows/claude-code.yml@main
    permissions:
      contents: write
      pull-requests: write
      issues: write
      id-token: write
    secrets:
      CLAUDE_APP_ID: ${{ secrets.CLAUDE_APP_ID }}
      CLAUDE_APP_PRIVATE_KEY: ${{ secrets.CLAUDE_APP_PRIVATE_KEY }}
      CLAUDE_AWS_ROLE_TO_ASSUME: ${{ secrets.CLAUDE_AWS_ROLE_TO_ASSUME }}

uses句のパス(armg/common-workflows/...)は、共通ワークフローを置いたリポジトリに合わせて変更してください。

共通化のメリット

各リポジトリ側のコードも決して少ないとは言えませんが、メンテナンスは楽になりそうだと感じます。

また、リポジトリ固有の設定が必要な場合は、inputsパラメータを追加することで柔軟に対応できます。例えば、特定のリポジトリでは異なるモデルを使用するなども可能です。

まとめ

Amazon Bedrockを利用したClaude Code GitHub Actionsの環境を構築し、さらに共通化によってメンテナンス性を高める手順をご紹介しました。

まだベータ版という位置づけのようですが、Issueの内容に基づいたコードの自動修正提案など、開発の様々な場面で活躍してくれそうです。 共通化の仕組みを整えることで、複数のリポジトリでも運用しやすくなり、組織全体でのClaude Code活用が現実的になりました。

参考リンク

今回の設定をするにあたり参考にした記事です。ありがとうございました!

https://zenn.dev/team_zenn/articles/claude-code-vertex-ai

https://zenn.dev/339/articles/d297f2f7dd8619

https://zenn.dev/kou_pg_0131/articles/gh-actions-oidc-aws

https://zenn.dev/aeonpeople/articles/1ec37f3ae91995

ARMテックブログ

Discussion