【アドベントカレンダー2025】「cdk diffが長すぎる」問題をGitHub Modelsで攻略する!変更内容を自動要約するCI/CD構築

こんにちは。ぐるなびでバックエンド開発を担当している小林です。

この記事では、AWS CDKの変更差分(cdk diff)をGitHub Modelsを用いて自動的に要約し、プルリクエストにコメントとして通知するCI/CDパイプラインの構築方法を解説します。

IaC(Infrastructure as Code)運用において、cdk diff は変更内容を確認するための生命線です。しかし、大規模な変更やIAMポリシーの修正が含まれる場合、出力される生の差分(JSON/YAML)は膨大で読みづらく、レビューの心理的ハードルを上げる原因になります。

今回紹介する方法は、この「diffが長すぎて辛い」という課題に対し、AIを活用してレビューを効率化する一つのアプローチです。

ただし、あらかじめお伝えしておきたい重要な点があります。この方法は「銀の弾丸」ではありません。

AIモデルには入力できる文字数(トークン数)に制限があるため、非常に巨大なdiffの場合、途中で内容をカットしてAIに渡す必要があります。その結果、diffの後半に含まれる重要な変更がAIの要約から漏れてしまうリスクがあります。

本記事で構築するパイプラインは、あくまで人間によるレビューを「補助」し、効率化するためのツールです。最終的な確認と判断は、必ずエンジニア自身が行う必要があるという前提で読み進めていただければと思います。

今回は、GitHub Models と、公式アクション actions/ai-inference を組み合わせ、このレビュープロセスを効率化する方法を紹介します。

目次

1. アーキテクチャと処理の流れ

今回構築するCI/CDのフローは以下の通りです。

  1. エンジニアがプルリクエストを作成/更新する。
  2. GitHub Actions が起動し、AWSに対して cdk diff を実行する。
  3. diffの結果(テキスト)を抽出し、AIモデルが処理できるように文字数制限(カット)を行う。
  4. actions/ai-inference を使用して、GitHub Models(GPT-4o等)にdiffを送信・要約させる。
  5. 要約結果をプルリクエストにコメントとして投稿する(既存のコメントがあれば更新する)。

2. 実装:GitHub Actionsワークフローの構築

特別なスクリプトコードを書かず、GitHub ActionsのYAML定義だけで完結させます。AIの呼び出しにはGitHub公式の actions/ai-inferenceを使用します。これが最も手軽な方法です。

ワークフローファイル (.github/workflows/cdk-diff-summary.yml)

以下のファイルをリポジトリに追加してください。

name: CDK Diff Summary

on:
  pull_request:

permissions:
  id-token: write       # AWS OIDC認証用
  contents: read        # コードチェックアウト用
  pull-requests: write  # PRへのコメント投稿用
  models: read          # 【重要】GitHub Modelsへのアクセス権限

jobs:
  diff-and-comment:
    runs-on: ubuntu-latest
    # 必要に応じて環境名を指定(承認フローなどに関わります)
    environment: test
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      # AWS認証(OIDC)の設定
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          # IAMロールのARNはSecretsから読み込むことを推奨
          role-to-assume: ${{ secrets.AWS_GITHUB_ACTIONS_ROLE_ARN }}
          aws-region: ap-northeast-1

      # Step 1: CDK Diff を実行し、結果を変数に格納
      - name: Run CDK Diff
        id: diff
        env:
          CDK_NOTICES: false
        run: |
          # diffを実行してファイルに出力 (exit code対策で || true を付与)
          # 【重要】マルチスタック構成の場合は、状況に応じて `--all` やスタック名(例: `npx cdk diff MyStack`)を指定してください。
          # 引数なしだと対話モードになりCIが停止する可能性があります。
          npx cdk diff --no-color > diff.txt 2>&1 || true
          
          # デバッグ用にログにも出力
          # 【重要なお知らせ】
          # 実際の運用では、diff内にデータベースの接続情報やAPIキーなどの機密情報が含まれる可能性があります。
          # その場合、以下の `cat diff.txt` によってGitHub Actionsの実行ログに機密情報が残ってしまうリスクがあります。
          # 本番環境で利用する際は、このデバッグ出力を削除するか、専用のツールで機密情報をマスクする等の対策を強く推奨します。 
          # echo "=== CDK Diff Output ==="
          # cat diff.txt
          # echo "======================="
          
          # 差分の中身を GitHub Actions の出力変数にセット (改行対応)
          # diffが長すぎる場合、AIモデルのトークン制限エラーを防ぐために先頭20,000文字でカットします。
          # ※注意: 末尾に重要な変更が含まれている場合、AIの要約対象から漏れる可能性があります。
          DIFF_CONTENT=$(cat diff.txt | cut -c 1-20000)
          
          echo "content<<EOF" >> $GITHUB_OUTPUT
          echo "$DIFF_CONTENT" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

      # Step 2: actions/ai-inference で要約を生成
      - name: Summarize with GitHub Models
        id: ai_summary
        continue-on-error: true
        uses: actions/ai-inference@v2
        with:
          # 使用するモデルを指定 (例: gpt-4o, Llama-3-70bなど)
          model: 'gpt-4o'
          # 【重要】レスポンスが途中で切れないように十分なトークン数を確保
          max-tokens: 4096
          system-prompt: |
            あなたは経験豊富なAWSインフラエンジニアです。
            提供された `cdk diff` の出力結果をレビューし、開発チーム向けに簡潔かつ重要なポイントを要約してください。
            特にセキュリティリスクとリソースの破壊的な変更には最大の注意を払ってください。

            # 【重要】事実に基づかない推測の禁止
            - 提供されたdiffテキスト**のみ**を情報の源泉としてください。
            - テキストが途中で切れている(cutされている)、または必要なパラメータ情報がdiffに含まれていない場合は、自身のAWSの知識で勝手に値を補完・推測しないでください。
            - 情報が不足していて判断できない項目については、正直に「**情報なし(提供されたテキストから判断不可)**」と出力してください。嘘をつくよりも「分からない」と答える方が安全です。

            # 出力フォーマット
            以下のフォーマットに従って出力してください。

            ## 変更の概要
            (1行で何が変わるかを記述)

            ## 主な変更点
            - (Resourcesセクションに基づき、主要なリソースの追加・削除・変更を箇条書き。詳細すぎるパラメータは省略し、何がどう変わるかに焦点を当てる)
            - (※もしdiffが途中で切れていると思われる場合は、「※diffが長すぎるため、以降の変更点は要約に含まれていない可能性があります」と明記すること)

            ## ⚠️ 重要な注意点とセキュリティリスク
            以下の項目について確認し、リスクがある場合は警告してください。判断材料がない場合は「情報なし」としてください。

            - **IAMポリシーの変更**: 
              - `IAM Policy Changes` および `IAM Statement Changes` を確認。
              - 過剰な権限付与(例: `Action: *`, `Resource: *`, `AdministratorAccess`)や信頼関係の緩和があれば警告。
            - **セキュリティグループの変更**: 
              - `Security Group Changes` を確認。
              - 危険なルール(例: `0.0.0.0/0` からの SSH/RDP 許可)があれば警告。
            - **リソースの置換 (Replacement)**: 
              - `Resources` セクションで `(requires replacement)` となっているリソースがあれば、データ消失リスクとして必ず明記。
            - **リスクなし / 不明**: 
              - 明確なリスクが見当たらない場合は「特になし」と記述。
              - diffが切れていて確認できない場合は「情報なし(テキスト範囲外の可能性あり)」と記述。
          prompt: |
            以下の diff を要約してください:
            
            ${{ steps.diff.outputs.content }}

      # Step 3: PRにコメントとして投稿(既存コメントがあれば更新)
      - name: Post CDK Diff Summary as Comment
        uses: actions/github-script@v7
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            // actions/ai-inferenceの出力キーの揺れに対応
            const summary = process.env.SUMMARY_RESPONSE || 
                          process.env.SUMMARY_CONTENT || 
                          process.env.SUMMARY_ANSWER;
            
            if (!summary) {
              console.log('AI summary is empty. Skipping comment post.');
              return;
            }

            // Botコメントを識別するためのマーカー(HTMLコメント)
            const marker = '<!-- cdk-diff-summary -->';
            const header = '## 🤖 AWS CDK Diff Summary by GitHub Models\n\n';
            const footer = '\n\n---\n*This summary was automatically generated by GitHub Models*';
            const commentBody = marker + '\n' + header + summary + footer;

            // PRの既存コメントを取得
            const { data: comments } = await github.rest.issues.listComments({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              per_page: 100,
            });

            // マーカーを含む自身のコメントを探す
            const existingComment = comments.find(comment => 
              comment.body && comment.body.includes(marker)
            );

            if (existingComment) {
              // 既存コメントがあれば内容を更新する
              console.log(`Updating existing comment ID: ${existingComment.id}`);
              await github.rest.issues.updateComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: existingComment.id,
                body: commentBody
              });
            } else {
              // なければ新規にコメントを作成する
              console.log('Creating a new comment.');
              await github.rest.issues.createComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.issue.number,
                body: commentBody
              });
            }
        env:
          # 出力結果を受け取る(フォールバック用)
          SUMMARY_RESPONSE: ${{ steps.ai_summary.outputs.response }}
          SUMMARY_CONTENT: ${{ steps.ai_summary.outputs.content }}
          SUMMARY_ANSWER: ${{ steps.ai_summary.outputs.answer }}

ポイントと注意点

このワークフローを構築する上での重要なポイントと、運用上の注意点です。

  • 出力先を「コメント」にした理由 当初はPRのSummary(説明文)への埋め込みを検討しましたが、PRのSummaryには文字数制限があります。大規模な変更でAIの要約が長文になった場合、制限を超えて表示が切れてしまうリスクを避けるため、より制限の緩い「コメント投稿」を採用しました。

  • コメントの更新ロジック actions/github-script を使い、ワークフローが実行されるたびに新しいコメントが増え続けないよう工夫しています。見えないマーカー (``) を埋め込んだコメントを探し、存在すればそのコメントを更新(上書き)し、なければ新規作成します。

  • max-tokens の設定 AIモデルが生成する回答の最大トークン数を指定する max-tokens: 4096 を追加しました。これがないと、要約が長くなった場合にレスポンスが途中で途切れてしまう可能性があります。使用するモデルや期待する要約量に応じて、この値は調整してください。

  • 必須の権限設定 permissions: models: read は必須です。これを忘れると、アクションがGitHub ModelsのAPIにアクセスできずエラーになります。また、コメント投稿のために pull-requests: write も必要です。

  • Diffのサイズ制限と文字化け対策 AIモデルへの入力トークン制限オーバーを防ぐため、diffのサイズを制限しています。 この際、head -c(バイト単位のカット)を使用すると、日本語などのマルチバイト文字の途中でカットされて末尾が文字化けする恐れがあります。そのため、上記の例では cut -c 1-20000 を使用し、安全に文字単位でカットしています。 なお、非常に長いdiffの場合、カットによって末尾の重要な変更がAIに伝わらないリスクは依然として残るため、必要に応じて専用の整形スクリプトを挟むなどの対策を検討してください。

  • APIエラーでCIを止めない GitHub ModelsのAPI障害やレート制限でCI全体が失敗するのを防ぐため、continue-on-error: true を設定しています。要約生成に失敗しても、テストやデプロイなどの重要なフローは継続させるための安全策です。

発展:プロンプトの外部ファイル化

上記の例ではYAMLファイル内に直接プロンプトを記述しましたが、プロンプトが長くなったり頻繁に調整したりする場合は、別ファイルに切り出すことで管理しやすくなります。

actions/ai-inference は、シンプルなテキストファイル (.txt) をプロンプトとして読み込む機能に対応しています。

例1: システムプロンプトをファイル化する場合

AIの役割(「あなたは経験豊富なエンジニアです...」)を記述したファイルを、system-prompt-file パラメータで指定します。

      - name: Summarize with GitHub Models
        uses: actions/ai-inference@v2
        with:
          model: 'gpt-4o'
          # システムプロンプトをファイルから読み込む
          system-prompt-file: .github/prompts/system-prompt.txt
          prompt: |
            以下の diff を要約してください:
            ${{ steps.diff.outputs.content }}

例2: ユーザープロンプト全体をファイル化する場合

AIへの具体的な命令(「以下のdiffを要約してください...」)自体をファイル化したい場合は、prompt-file パラメータを使用します。

      - name: Run AI Inference with Text File
        uses: actions/ai-inference@v2
        with:
          model: 'gpt-4o'
          # ユーザープロンプトをファイルから読み込む
          prompt-file: './path/to/user-prompt.txt'

このように、YAMLがスッキリし、プロンプトの管理も容易になるため、実運用ではファイルへの切り出しを検討してみてください。

3. 補足:公式アクションが対応していないモデルを使いたい場合

actions/ai-inference は手軽ですが、「将来出る新しいモデル」や「特定のオープンモデル」など、アクション側が未対応のモデルを使いたいケースもあるでしょう。

その場合は、公式アクションを使わず、Node.jsの標準ライブラリなどを使って、スクリプトからAPIを直接実行することで解決できます。

Node.js スクリプトの例 (scripts/summarize-diff.js)

GitHub ModelsのAPIは標準的なHTTP APIとして提供されています。以下のようにNode.jsの標準モジュール https を使ってリクエストを送ることで、追加のSDKをインストールすることなく、任意のモデルを呼び出せます。

const https = require('https');
const fs = require('fs');

// GitHub Actionsから渡されたトークンとdiffの内容を受け取る
const token = process.env.GITHUB_TOKEN;
const diffFilePath = process.argv[2];

if (!token) {
  console.error("Error: GITHUB_TOKEN environment variable is required.");
  process.exit(1);
}

if (!diffFilePath) {
  console.error("Error: Diff file path is required as first argument.");
  process.exit(1);
}

// diffファイルの内容を読み込む
let diffContent;
try {
  diffContent = fs.readFileSync(diffFilePath, 'utf8');
} catch (error) {
  console.error("Error reading diff file:", error);
  process.exit(1);
}

// 送信するデータ
const data = JSON.stringify({
  model: "gpt-4o",
  messages: [
    {
      role: "system",
      content: `あなたは経験豊富なAWSインフラエンジニアです。
提供された \`cdk diff\` の出力結果をレビューし、開発チーム向けに簡潔かつ重要なポイントを要約してください。
特にセキュリティリスクとリソースの破壊的な変更には最大の注意を払ってください。

以下のフォーマットに従って出力してください。

## 変更の概要
(1行で何が変わるかを記述)

## 主な変更点
- (Resourcesセクションに基づき、主要なリソースの追加・削除・変更を箇条書き。詳細すぎるパラメータは省略し、何がどう変わるかに焦点を当てる)

## ⚠️ 重要な注意点とセキュリティリスク
- **IAMポリシーの変更**: \`IAM Policy Changes\` および \`IAM Statement Changes\` を確認し、過剰な権限付与(例: \`Action: *\`, \`Resource: *\`, \`AdministratorAccess\` の付与)や、信頼関係の緩和(例: 意図しないPrincipalへの許可)がある場合は警告として明記してください。
- **セキュリティグループの変更**: \`Security Group Changes\` がある場合、危険なルール(例: \`0.0.0.0/0\` からの SSH/RDP 許可)がないか確認し、あれば警告してください。
- **リソースの置換 (Replacement)**: \`Resources\` セクションで \`(requires replacement)\` となっているリソースがある場合は、データ消失やダウンタイムのリスクがあるため、必ず明記してください。
- 特にリスクがない場合は「特になし」と記述してください。`
    },
    {
      role: "user",
      content: `以下の diff を要約してください:\n\n${diffContent}`
    }
  ],
  max_tokens: 4096,
});

const options = {
  hostname: 'models.inference.ai.azure.com',
  path: '/chat/completions',
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${token}`,
  },
};

const req = https.request(options, (res) => {
  let responseBody = '';

  res.on('data', (chunk) => {
    responseBody += chunk;
  });

  res.on('end', () => {
    if (res.statusCode >= 200 && res.statusCode < 300) {
      try {
        const parsedData = JSON.parse(responseBody);
        // APIからの応答(要約テキスト)を標準出力に出力
        if (parsedData.choices && parsedData.choices.length > 0) {
          console.log(parsedData.choices[0].message.content);
        } else {
          console.error("Unexpected API response format:", parsedData);
          process.exit(1);
        }
      } catch (e) {
        console.error("Failed to parse JSON response:", e);
        process.exit(1);
      }
    } else {
      console.error(`API Error: StatusCode ${res.statusCode}, Body: ${responseBody}`);
      process.exit(1);
    }
  });
});

req.on('error', (error) => {
  console.error("Request Error:", error);
  process.exit(1);
});

req.write(data);
req.end();

APIを直接呼ぶスクリプト (scripts/summarize-diff.js) を作成した場合、それをGitHub Actionsワークフローから呼び出し、その結果を使ってPRのSummaryを更新するためのYAMLの書き方は以下のようになります。

ポイントは、スクリプトの標準出力を GITHUB_OUTPUT 経由で次のステップに渡す部分です。

ワークフローファイル例 (.github/workflows/cdk-diff-summary-direct.yml)

# ... (前半の定義は省略) ...

      # Step 1: CDK Diff を実行 (省略)
      # ...

      # Step 2: カスタムスクリプトで要約を生成
      - name: Summarize with GitHub Models
        id: ai_summary
        continue-on-error: true
        run: |
          SUMMARY=$(node .github/scripts/summarize-diff.js diff.txt)
          echo "content<<EOF" >> $GITHUB_OUTPUT
          echo "$SUMMARY" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      # Step 3: PRにコメントとして投稿
      - name: Post CDK Diff Summary as Comment
        uses: actions/github-script@v7
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const summary = process.env.SUMMARY_CONTENT;
            
            if (!summary) {
              console.error('No summary content found');
              return;
            }

            const marker = '<!-- cdk-diff-summary -->';
            const header = '## 🤖 AWS CDK Diff Summary by GitHub Models\n\n';
            const footer = '\n\n---\n*This summary was automatically generated by GitHub Models*';
            const commentBody = marker + '\n' + header + summary + footer;

            const { data: comments } = await github.rest.issues.listComments({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
            });

            const existingComment = comments.find(comment => 
              comment.body && comment.body.includes(marker)
            );

            if (existingComment) {
              await github.rest.issues.updateComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: existingComment.id,
                body: commentBody
              });
            } else {
              await github.rest.issues.createComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.issue.number,
                body: commentBody
              });
            }
        env:
          SUMMARY_CONTENT: ${{ steps.ai_summary.outputs.content }}

解説

  1. 環境変数の受け渡し: スクリプト内で process.env.GITHUB_TOKEN を使ってAPI認証を行うため、env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} でトークンを環境変数として渡します。

  2. スクリプト実行と出力のキャプチャ: SUMMARY=$(node .github/scripts/summarize-diff.js diff.txt) でスクリプトの標準出力をシェル変数に受け取ります。

  3. GITHUB_OUTPUT への出力: AIの要約は複数行になるため、ヒアドキュメント形式(<<EOF)を使って GITHUB_OUTPUT に書き込みます。ここではキー名を content としています(任意の名前でOKです)。

  4. 次のステップでの利用: PR更新ステップの env で、${{ steps.ai_summary.outputs.content }} のようにして、先ほど設定したキー名でスクリプトの出力結果を受け取ります。

  5. 追加のセットアップ不要:  このスクリプトはNode.jsの標準モジュール(https, fs)のみで記述されているため、実行前に npm install で外部ライブラリをインストールする必要がありません。CIの実行時間を短縮し、依存関係トラブルを防ぐことができます。

これで、公式アクションを使った場合と同じように、自作スクリプトの結果をPRに埋め込むことができます。

4. 実際に動かした結果(公式アクション使用)

実際に、「S3バケットを1つ追加」し、「IAMロールにAdmin権限相当を付与する(わざと危険な変更)」というコードを書いてプルリクエストを出してみました。

Before: 通常の cdk diff 出力

ログファイルには以下のような差分が出力されます。IAMポリシーやリソースの追加がテーブル形式で羅列されており、全体像やリスクを一目で把握するのは困難です。

Stack CdkSampleStack
IAM Statement Changes
┌───┬───────────────────────────────────────────────────────────────┬────────┬────────────────────┬───────────────────────────────────────────────────────────────────┬───────────┐
│   │ Resource                                                      │ Effect │ Action             │ Principal                                                         │ Condition │
├───┼───────────────────────────────────────────────────────────────┼────────┼────────────────────┼───────────────────────────────────────────────────────────────────┼───────────┤
│ + │ ${AdminRole.Arn}                                              │ Allow  │ sts:AssumeRole     │ Service:ec2.amazonaws.com                                         │           │
├───┼───────────────────────────────────────────────────────────────┼────────┼────────────────────┼───────────────────────────────────────────────────────────────────┼───────────┤
│ + │ ${Custom::S3AutoDeleteObjectsCustomResourceProvider/Role.Arn} │ Allow  │ sts:AssumeRole     │ Service:lambda.amazonaws.com                                      │           │
├───┼───────────────────────────────────────────────────────────────┼────────┼────────────────────┼───────────────────────────────────────────────────────────────────┼───────────┤
│ + │ ${SampleBucket.Arn}                                           │ Allow  │ s3:DeleteObject* │ AWS:${Custom::S3AutoDeleteObjectsCustomResourceProvider/Role.Arn} │           │
│   │ ${SampleBucket.Arn}/* │        │ s3:GetBucket* │                                                                   │           │
│   │                                                               │        │ s3:List* │                                                                   │           │
│   │                                                               │        │ s3:PutBucketPolicy │                                                                   │           │
└───┴───────────────────────────────────────────────────────────────┴────────┴────────────────────┴───────────────────────────────────────────────────────────────────┴───────────┘
IAM Policy Changes
┌───┬───────────────────────────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────┐
│   │ Resource                                                  │ Managed Policy ARN                                                                           │
├───┼───────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${AdminRole}                                              │ arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess                                    │
├───┼───────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${Custom::S3AutoDeleteObjectsCustomResourceProvider/Role} │ {"Fn::Sub":"arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"} │
└───┴───────────────────────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Parameters
[+] Parameter BootstrapVersion BootstrapVersion: {"Type":"AWS::SSM::Parameter::Value<String>","Default":"/cdk-bootstrap/hnb659fds/version","Description":"Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"}

Resources
[+] AWS::S3::Bucket SampleBucket SampleBucket7F6F8160
[+] AWS::S3::BucketPolicy SampleBucket/Policy SampleBucketPolicy07CB9459
[+] Custom::S3AutoDeleteObjects SampleBucket/AutoDeleteObjectsCustomResource SampleBucketAutoDeleteObjectsCustomResourceAC99DCF6
[+] AWS::IAM::Role Custom::S3AutoDeleteObjectsCustomResourceProvider/Role CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092
[+] AWS::Lambda::Function Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F
[+] AWS::IAM::Role AdminRole AdminRole38563C57

After: GitHub Models による要約コメント

ワークフローが完了すると、プルリクエストのタイムラインに、Botアカウントから以下のようなコメントが自動的に投稿されました。

🤖 AWS CDK Diff Summary by GitHub Models

## 変更の概要 新たにS3バケット、IAMロール、Lambda関数、バケットポリシーなどが追加され、特定のリソースに管理ポリシーが割り当てられる構成変更。

## 主な変更点

  • S3バケットの追加: SampleBucket を新規作成。バケットに対する操作(削除、取得、一覧表示、ポリシー設定)を特定リソースに許可。
  • S3バケットポリシーの追加: SampleBucket/Policy を作成、バケットにアクセス制御を適用。
  • カスタムリソースの追加: S3オブジェクトの自動削除を管理するためのカスタムリソース(SampleBucket/AutoDeleteObjectsCustomResource)が作成される。
  • 上記リソースに対応するIAMロール(CustomS3AutoDeleteObjectsCustomResourceProvider/Role)とLambda関数(CustomS3AutoDeleteObjectsCustomResourceProvider/Handler)を作成。
  • IAMロールの追加: AdminRole が新規作成され、AdministratorAccess 権限を付与。
  • EC2のsts:AssumeRoleが許可された。
  • LambdaにAWSLambdaBasicExecutionRole管理ポリシーが付与。
  • SSMパラメータの追加: CDK ブートストラップのバージョンを示すパラメータ(BootstrapVersion)を追加。

## ⚠️ 重要な注意点とセキュリティリスク

  • IAMポリシーの変更: AdminRoleAdministratorAccess が付与されています。すべてのAWSリソースへの完全アクセスが許可されるため、このロールを使用するエンティティを厳重に管理してください。
  • EC2インスタンスに対してsts:AssumeRoleが許可されています。意図しないアクセスを防ぐため、信頼ポリシーのPrincipal指定を確認してください。
  • Lambdaに必要最小限以上の権限が付与されていないことを確認してください。
  • セキュリティグループの変更: 該当なし。
  • リソースの置換 (Replacement): 既存のリソースの置換は確認されていません。
  • その他: カスタムリソースがバケット内のデータ(オブジェクトの削除)に自動操作を行います。この動作が意図したものであるか確認してください。

特に、AdministratorAccess の付与と、カスタムリソースによる自動削除動作の安全性を十分に確認する必要があります。また、不要な権限や変更を含んでいないか全体を再確認してください。


This summary was automatically generated by GitHub Models


このように、単純なリソースの増減だけでなく、プロンプトで指示した「セキュリティリスク」について重点的にチェックされ、明確な警告として出力されました。

コードを修正して再度プッシュすると、このコメントが新しい内容で上書き更新されるため、常に最新の要約を確認できます。

5. デメリットと注意点

本パイプラインはレビュー効率を大きく向上させますが、AIモデルの特性上、いくつかのデメリットやリスクも存在します。これらを理解した上で運用することが重要です。

1. AIモデルの特性と限界

  • 要約の精度が100%ではない: AIモデルは確率に基づいてテキストを生成するため、事実と異なる情報(ハルシネーション)を出力したり、重要な変更を見落としたりするリスクはゼロではありません。AIの要約はあくまで「補助」として扱い、最終的な確認は必ず人間が行う必要があります。

  • 出力のばらつき: 同じ入力(diff)に対しても、モデルの種類やバージョン、あるいは生成時のパラメータ(temperatureなど)によって、出力される要約の内容やニュアンスが変化する可能性があります。

  • 入力トークン数の制限による情報欠落: 使用するモデルには入力できるテキスト量に上限があります。本記事のワークフローでは、APIエラーを防ぐために cut コマンドで diff の先頭部分(約20,000文字)のみを抽出しています。

    • リスク: 非常に大規模な diff(20,000文字超)の場合、末尾の変更がAIに渡されず、要約に漏れる可能性があります。

    • 現実的な影響: ほとんどの通常プロジェクト(スタック数 1~3 程度の日常的な変更)では問題になりませんが、マイクロサービス構成や超大規模インフラ、あるいは大規模なリファクタリング時には注意が必要です。

    • 対策:

      • 小~中規模: 現在の設定のままで運用可能です。
      • 大規模: スタック単位で cdk diff StackName を実行し、個別に要約させる運用を推奨します。
      • 安全性重視: 専用スクリプトによるセクション抽出(IAM/Security優先)の導入を検討してください。

2. セキュリティとプライバシーのリスク

  • 機密情報の漏洩と入力データの取り扱い: cdk diff の出力に、誤ってデータベースの接続文字列、APIキー、個人情報などの機密情報が含まれてしまう可能性があります。この情報がそのままAIモデルに送信されることになるため、コードや環境変数の管理には細心の注意が必要です。また、ワークフロー内でデバッグ用に logs出力している場合、GitHub Actionsのログにも機密情報が残るリスクがあります。

  • 入力データの学習利用について: 執筆時点(2025年12月)の規約では、GitHub Modelsに入力されたデータ(プロンプト)は、GitHub側ではモデルの学習や改善には使用されません。また、OpenAIなどの主要なモデルプロバイダーも、API経由のデータをデフォルトで学習に使用しないポリシーを採っています。しかし、万全を期すため、機密情報の混入防止を徹底し、利用するモデルプロバイダーの最新の規約も併せて確認してください。

  • データの外部送信: GitHub Modelsは入力データを学習に使用しない方針を掲げていますが、データが外部のサービス(GitHubやモデルプロバイダー)に送信されること自体は事実です。組織のセキュリティポリシーによっては、これが許容されない場合があります。

3. 運用とコスト

  • 将来的なコストとレート制限: GitHub Modelsは現在、パブリックベータ版として一定の無料枠が提供されていますが、将来的に有料化されたり、利用制限が変更されたりする可能性があります。 また、アカウントのプラン(Free/Pro/Team/Enterprise)によって利用可能なレート制限(Tier)が異なります。

    【運用上の注意】 短時間に大量のプルリクエストが作成・更新されると、このレート制限に抵触し、要約生成が失敗する可能性があります。前述の continue-on-error 設定は、このリスクへの備えとしても重要です。

    参考: Rate limits - GitHub Docs
    参考: GitHub Models billing - GitHub Docs

  • メンテナンス負荷: AWS CDKのバージョンアップでdiffの出力形式が変わったり、より良い要約を得るためにプロンプトを調整したくなったりと、ワークフローやプロンプトの継続的なメンテナンスが必要になります。

4. 外部サービスへの依存

本パイプラインは、GitHub Actions、GitHub Models、AWSという複数の外部サービスに依存しています。これらのサービスで障害が発生した場合、自動要約機能は利用できなくなります。 ただし、GitHub Modelsの障害については、前述の continue-on-error 設定により、CI/CDパイプライン自体の停止(マージブロック等)は防げる設計としています。

6. まとめ

GitHub ModelsとGitHub Actions(特に公式アクション actions/ai-inference)を組み合わせることで、「読むのが辛い」インフラの変更差分を、人間が理解しやすい形に変換して届けるCI/CDパイプラインを簡単に構築できました。

  • メリット: レビュー時間の短縮、セキュリティリスクの見落とし防止。

  • デメリット: AIの精度限界(ハルシネーションのリスク)、トークン制限による情報欠落リスク、機密情報管理の重要性増加など。

デメリットは存在しますが、AIモデルの特性を理解し、適切なプロンプト設計や運用ルール(最終確認は人間が行う、機密情報をdiffに含めない等)を組み合わせることで、インフラレビューの負荷を大幅に軽減できる強力なツールとなります。

ぜひ、チームの運用に合わせて調整しながら試してみてください。


長年フリーのエンジニアと活動していましたが、2017年にぐるなびに入社。
サービス全体のアーキテクチャ設計やバックエンド開発をメインに担当をしています。