ForgeVision Engineer Blog

フォージビジョン エンジニア ブログ

Agent Plugins for AWS を安全に使うための Claude Code 制御レイヤー設計 (第1回:Guide編)

こんにちは、AWSグループの藤岡です。コード実装もインフラ構築も、AIに任せる範囲が日々増えていくのを体感しています。

2026年2月にAWSから「Agent Plugins for AWS」が発表されました。これによってAIエージェントが、AWSが推奨するプラクティスに沿って、IaCを実装したりデプロイを実行できるようになりました。

aws.amazon.com

AIが強力になることは嬉しい一方で、安全な範囲で作業させるための制御はより重要になっています。特にAWSリソースの操作は、一つのミスが既存環境への影響や、セキュリティリスク、予期せぬコスト増大につながる可能性があります。

そこで本記事では、Agent Plugins for AWS を活用しつつ、Claude Codeのプロジェクト指示やHooksを使って、AIエージェントの判断・生成・実行をどこまで制御できるかを検証します。

なお、この検証は全2回シリーズとします。第1回の本記事では、以下を扱います:

  1. Agent Plugins for AWS のセットアップ手順
  2. プラグイン単体での挙動検証
  3. Guide Layerの導入と効果測定

第2回では、Hooks から cdk-nagcdk diff を呼び出して、Sensorレイヤを扱う予定です。


前提

Agent Plugins for AWS とは

Agent Plugins for AWS は、Claude CodeやCodex、Cursorでの利用をサポートする(2026年4月時点)、Skillsやスクリプトといったパッケージです。

GitHubリポジトリに登録される、aws-amplifydeploy-on-aws といったユースケースごとのプラグインをコーディングエージェントに設定します。これによってAIは、AWSの提供するプラクティスに沿ってIaC生成やCLI実行を判断・実行することができます。

github.com

日本語ドキュメント(みのるんさん提供)もあってありがたいね!

プラグイン導入によって、コーディングエージェントを介して作業することでAWSのお作法に沿ったインフラ整備ができ、AWSに精通してないユーザーの敷居を下げることにつながります。

本記事で使う制御レイヤー

本記事では、Agent Plugins for AWS を制御するために、Claude Codeに2つのレイヤーを用意します。

  1. Guide Layer: AIの判断を誘導するルールやコンテキストを提供する(例:CLAUDE.md / AGENTS.md
  2. Sensor Layer: AIが生成・実行しようとする内容を外部ツールで検証する(例:Hooks + cdk-nag / cdk diff

GuideSensor は、AIエージェントを単体で動かすのではなく、周辺に文脈・評価・実行ゲートを用意する Harness Engineering の考え方と近いものです。

martinfowler.com

Anthropic の記事でも、長時間動作するエージェントを扱うには、周辺アプリケーションや評価・観測の仕組みが重要であることが説明されています。

www.anthropic.com

ただし本記事で扱うのは、Harness Engineering 全体ではありません。その考え方のうち「判断を誘導する層(ガイド)」と「生成物・実行内容を検証する層(センサー)」に近い部分を、Claude Code の CLAUDE.md / AGENTS.md と Hooks に置き換えて、Agent Plugins for AWS を安全に扱いやすくなるかを検証します。

Claude Code の制御機能

Claude Codeでは、AIの挙動を制御するために以下の機能を利用できます。これらを使って、ガイドとセンサーを用意します。

機能 役割 強制力 区分
CLAUDE.md プロジェクト全体のコンテキスト・ルール定義 なし(ガイド) ガイド
AGENTS.md エージェント固有のルール定義 なし ガイド
Hooks 実行前後のスクリプト実行・ブロック あり(対象ツールに限る) センサー

本記事の検証設計

レイヤー構造

本検証では、下図のように実装・構築を主体的に行うClaude Codeを中心に、ガイド層とセンサー層を用意します。(細かい設定は後半と次回で説明します)

検証シナリオ

本検証では、サーバーレス構成のバックエンドシステムを作ることをゴールとして、CDK テンプレート生成 することで、AIエージェントの挙動を確認します。

このシナリオで、以下の観点を確認します。

  1. IAMポリシー: Lambda などに付与される DynamoDB 権限が最小権限になっているか
  2. ログ・運用設計: CloudWatch Logs の保持期間や API Gateway のアクセスログが考慮されるか
  3. CDKテンプレート生成: CDK TypeScript として実装・検証しやすい構成になっているか

セットアップ

1. バージョン・環境の確認

本検証では、各ツールを以下のバージョン・環境で実行します。

ツール バージョン
Claude Code 2.1.112
モデル Sonnet 4.5
AWS CLI 2.32.2
Node.js 22.x
npm 11.6.2
AWS CDK 2.1118.4
jq 1.8 以降

2. CDKプロジェクトの準備

mkdir cdk
cd cdk
npx cdk init app --language typescript

✅ All done! と表示されたら、CDKプロジェクトの初期化は完了です。


検証①: プラグイン単体での挙動検証

まずは、CLAUDE.md / AGENTS.md を使わず、Agent Plugins for AWS のみでCDKテンプレートの生成を行った場合の挙動を確認します。

Agent Plugins for AWS のインストール

作業ディレクトリで Claude Code を起動します。

claude

Claude Code の対話モード内で、AWS の plugin marketplace を追加します。

/plugin marketplace add awslabs/agent-plugins

成功すると、Successfully added marketplace: agent-plugins-for-aws と表示されます。

次に、対象の個別プラグイン aws-serverless をインストールします。Agent Plugins for AWS には複数のプラグインがありますが、今回は API Gateway / Lambda / DynamoDB を使うサーバーレスAPIのCDKテンプレート生成を検証するため、aws-serverless を選択しています。

/plugin install aws-serverless@agent-plugins-for-aws

プラグインのスコープ(Ex. ユーザー、プロジェクト、ローカル)を聞かれるので、適切なものを選択します。今回はローカルスコープでインストールします。

成功すると、✓ Installed aws-serverless. Run /reload-plugins to apply. と表示されます。

/reload-plugins
/plugin list

インストールが確認できました。

検証①: 実行

入力プロンプトは以下の通りです。

API経由で、DynamoDBテーブルにタスクの登録・更新・削除・取得を行うサーバーレスAPIを構築したいです。まずは、CDK(TypeScript)でテンプレートを作成お願いします。

するとClaude Codeがプロンプトに対してプラグインを利用するように判定され、以下のようにガイダンスを参照しつつ進み始めます。

そこからデプロイ直前までClaude Codeが自走してくれました。

出来上がったCDKテンプレートは以下の通りです。手動でデプロイすると、1発で成功しました。

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as path from 'path';
  
export class CdkStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
  
    // DynamoDB Table for Tasks
    const tasksTable = new dynamodb.Table(this, 'TasksTable', {
      partitionKey: { name: 'taskId', type: dynamodb.AttributeType.STRING },
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
      removalPolicy: cdk.RemovalPolicy.DESTROY, // 本番環境では RETAIN に変更
    });
  
    // Lambda Function for Task CRUD operations
    const taskHandler = new lambda.Function(this, 'TaskHandler', {
      runtime: lambda.Runtime.NODEJS_22_X,
      handler: 'index.handler',
      code: lambda.Code.fromAsset(path.join(__dirname, '../lambda')),
      environment: {
        TABLE_NAME: tasksTable.tableName,
      },
    });
  
    // Grant DynamoDB permissions to Lambda
    tasksTable.grantReadWriteData(taskHandler);
  
    // API Gateway REST API
    const api = new apigateway.RestApi(this, 'TasksApi', {
      restApiName: 'Tasks Service',
      description: 'API for managing tasks in DynamoDB',
      defaultCorsPreflightOptions: {
        allowOrigins: apigateway.Cors.ALL_ORIGINS,
        allowMethods: apigateway.Cors.ALL_METHODS,
      },
    });
  
    const lambdaIntegration = new apigateway.LambdaIntegration(taskHandler);
  
    // /tasks resource
    const tasks = api.root.addResource('tasks');
    tasks.addMethod('POST', lambdaIntegration);   // Create task
    tasks.addMethod('GET', lambdaIntegration);    // List all tasks
  
    // /tasks/{taskId} resource
    const taskById = tasks.addResource('{taskId}');
    taskById.addMethod('GET', lambdaIntegration);    // Get specific task
    taskById.addMethod('PUT', lambdaIntegration);    // Update task
    taskById.addMethod('DELETE', lambdaIntegration); // Delete task
  
    // Outputs
    new cdk.CfnOutput(this, 'ApiUrl', {
      value: api.url,
      description: 'API Gateway endpoint URL',
    });
  
    new cdk.CfnOutput(this, 'TableName', {
      value: tasksTable.tableName,
      description: 'DynamoDB Table Name',
    });
  }
}

検証としては十分なテンプレート構成です。ただし個人的にはいくつか気になる点があります。

気になるところ①

DynamoDBテーブルの removalPolicyDESTROY になっていたり、Lambda の CloudWatch Logs 保持期間がソースコード上で明示されていなかったりします。商用環境で利用するケースでは、削除ポリシーやログ保持期間を意図として明示したいところです。

気になるところ②

全体としては検証を想定した構成で生成されていますが、API Gateway は REST API で作成されています。今回の要件だけを見ると HTTP API で足りる可能性が高いため、REST API を使うなら API Key、Usage Plan、Request Validation、Private API など REST API 固有機能が必要な理由を明記してほしいところです。

上記の点について、ガイドを与えることでどのようにテンプレート生成結果が変わるか、次のステップで検証してみます。


検証②: Guide Layer の適用

ここからが本記事の本題です。CLAUDE.md / AGENTS.md を使って、AIの判断を適切にガイドします。

CLAUDE.md の作成

Claude Code が読み込む project memory として、CDKプロジェクトのルートに CLAUDE.mdAGENTS.md を作成します。今回は CLAUDE.md から @AGENTS.md を読み込ませ、共通ルールとエージェントの役割定義を分けて記載します。 (より複雑なプロジェクト構成では、対象ディレクトリごとに CLAUDE.md を置き、読み込ませるガイドを分けるのがオススメです。)

CLAUDE.md の内容:

# AWS操作ガイド
  
@AGENTS.md
  
このプロジェクトでは、AIによるIaC生成・デプロイ・操作において以下のルールを遵守する。
  
## 基本方針
- まずCDKテンプレートを生成し、デプロイは明示的な許可があるまで実行しない
  - 明示的な指示・許可がない限り、`cdk deploy` を実行しない
- 検証用の設定と商用利用できる設定を区別して説明する
- 商用利用できない設定を入れる場合は、検証用であることと理由を明記する
  
## IAMポリシー設計
### 必須原則

- **最小権限の原則**: 必要最小限の権限のみを付与する
- **ワイルドカード禁止**: `Resource: "*"``Action: "*"` は原則使用しない
- **明示的なリソース指定**: ARN を明示的に指定する
  
### 例外

ワイルドカードが必要な場合は、以下を明記する:
- 使用理由
- 代替案の検討結果
- リスク評価
  
## 削除・データ保持

### 必須手順

1. 削除対象リソースの確認
2. 依存関係の確認
3. バックアップの確認
4. 本番環境での削除は**事前承認必須**
5. DynamoDB など永続データを持つリソースは、削除ポリシーと復旧方針を確認する
  
### 禁止事項

- 確認なしでの削除実行
- 本番環境リソースの即座削除
- 商用利用を想定する構成で `RemovalPolicy.DESTROY` を無確認で使うこと
  
## CDKテンプレート生成

### 考慮事項

- **IAM**: DynamoDBアクセスは必要なアクションとテーブルARNに限定する
- **CloudWatch Logs**: ログ保持期間をソースコード上で明示する。未指定なら実装前に保持日数を確認する
- **API Gateway**: 外部公開APIではアクセスログ、メトリクス、トレースの要否を確認する
- **API種別**: REST API 固有機能が不要なら HTTP API を第一候補にする。REST API を使う場合は理由を明記する
- **CORS / 認証**: 公開APIでは、許可するOriginと認証・認可の要否を確認する
- **DynamoDB**: 永続データを扱う場合は point-in-time recovery と削除ポリシーを確認する
- **コスト**: 不要なリソースを作らず、検証用途に合う構成にする

AGENTS.md の内容例:

# AWS Operation Agent

## Role

AWSリソース構築のための、IaC生成やデプロイ、リソース操作を、安全かつ効率的に行うエージェント。
  
## Constraints

- IAMポリシーは必ず最小権限で設計
- 削除操作は必ず確認を取る
- コスト影響を常に考慮
- IaC は AWS CDK TypeScript を使用する
- CloudWatch Logs の保持期間が未指定の場合は、構築前に確認する
- DynamoDB の削除ポリシーが `DESTROY` の場合は、検証用であることを明記し、商用利用では使わない
- REST API を使う場合は、HTTP API では足りない理由を明記する
- CORS を `ALL_ORIGINS` / `ALL_METHODS` にする場合は、検証用であることを明記する
- 更新・削除APIを公開する場合は、認証・認可の要否を確認する
  
## Tools

- Agent Plugins for AWS
- AWS CDK
- AWS CLI(補助的に使用)

検証②: 実行

CLAUDE.md / AGENTS.md を配置した状態で、検証①と同じプロンプトを再実行します。(比較しやすくするため、セッションを一度切断してcdk実装を削除した上で、プロンプト本文は変えません。)

claude
> API経由で、DynamoDBテーブルにタスクの登録・更新・削除・取得を行うサーバーレスAPIを構築したいです。まずは、CDK(TypeScript)でテンプレートを作成お願いします。

すると、生成前に以下の確認をされました。指示とガイドを突き合わせて、不足する点を適切に質問してくれています。

質問に回答すると、反映した上でテンプレートが生成されました。単に実装が変わっただけでなく、コメントもより詳しく書かれていて、意図が明確になりました。

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigatewayv2 from 'aws-cdk-lib/aws-apigatewayv2';
import * as apigatewayv2_integrations from 'aws-cdk-lib/aws-apigatewayv2-integrations';
import * as logs from 'aws-cdk-lib/aws-logs';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as path from 'path';
  
export class CdkStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
  
    // 【検証用】DynamoDBテーブル(商用利用時はRemovalPolicyをRETAINに変更すること)
    const tasksTable = new dynamodb.Table(this, 'TasksTable', {
      tableName: 'Tasks',
      partitionKey: {
        name: 'id',
        type: dynamodb.AttributeType.STRING,
      },
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
      removalPolicy: cdk.RemovalPolicy.DESTROY, // 検証用: スタック削除時にテーブルも削除
      pointInTimeRecovery: false, // 検証用: PITR無効
    });
  
    // Lambda関数(タスクCRUD操作)
    const taskHandlerFunction = new lambda.Function(this, 'TaskHandlerFunction', {
      runtime: lambda.Runtime.NODEJS_20_X,
      handler: 'index.handler',
      code: lambda.Code.fromAsset(path.join(__dirname, '../lambda')),
      environment: {
        TABLE_NAME: tasksTable.tableName,
      },
      timeout: cdk.Duration.seconds(10),
      logRetention: logs.RetentionDays.ONE_MONTH, // 30日保持
    });
  
    // IAMポリシー: 最小権限でDynamoDBアクセスを許可
    taskHandlerFunction.addToRolePolicy(
      new iam.PolicyStatement({
        effect: iam.Effect.ALLOW,
        actions: [
          'dynamodb:GetItem',
          'dynamodb:PutItem',
          'dynamodb:UpdateItem',
          'dynamodb:DeleteItem',
          'dynamodb:Query',
          'dynamodb:Scan',
        ],
        resources: [tasksTable.tableArn],
      })
    );
  
    // HTTP API(API Gateway v2)
    // REST API固有機能(API Key, Usage Plan, Request Validation)が不要なため、HTTP APIを使用
    const httpApi = new apigatewayv2.HttpApi(this, 'TasksHttpApi', {
      apiName: 'TasksAPI',
      description: 'Task management HTTP API',
      corsPreflight: {
        // 【検証用】全Originを許可(商用利用時は特定のOriginに制限すること)
        allowOrigins: ['*'],
        allowMethods: [
          apigatewayv2.CorsHttpMethod.GET,
          apigatewayv2.CorsHttpMethod.POST,
          apigatewayv2.CorsHttpMethod.PUT,
          apigatewayv2.CorsHttpMethod.DELETE,
          apigatewayv2.CorsHttpMethod.OPTIONS,
        ],
        allowHeaders: ['Content-Type', 'Authorization'],
        maxAge: cdk.Duration.days(1),
      },
    });
  
    // Lambda統合
    const lambdaIntegration = new apigatewayv2_integrations.HttpLambdaIntegration(
      'TaskHandlerIntegration',
      taskHandlerFunction
    );
  
    // ルート定義
    // 【注意】認証なしで公開(商用利用時は認証・認可を実装すること)
    httpApi.addRoutes({
      path: '/tasks',
      methods: [apigatewayv2.HttpMethod.POST, apigatewayv2.HttpMethod.GET],
      integration: lambdaIntegration,
    });
  
    httpApi.addRoutes({
      path: '/tasks/{id}',
      methods: [
        apigatewayv2.HttpMethod.GET,
        apigatewayv2.HttpMethod.PUT,
        apigatewayv2.HttpMethod.DELETE,
      ],
      integration: lambdaIntegration,
    });
  
    // 出力
    new cdk.CfnOutput(this, 'ApiEndpoint', {
      value: httpApi.apiEndpoint,
      description: 'HTTP API Endpoint URL',
    });
  
    new cdk.CfnOutput(this, 'TableName', {
      value: tasksTable.tableName,
      description: 'DynamoDB Table Name',
    });
  }
}

考察

ガイドの導入の有用性

プラグイン単体の生成結果で気になった点に対して、方針を明示した CLAUDE.mdAGENTS.md を用意したことで、生成前に事前確認を挟みつつ、期待に近い形の生成結果を得ることができました。

ここまで直接的・限定的にガイドを用意できるケースは実務では少ないと思いますが、Agent Plugins for AWS で大枠を押さえたうえで、プロジェクト固有のガイドを手早く用意することで、効率的な上で生成物の品質を上げられることが期待できます。

想定される課題

今回の検証では、Guide Layer(CLAUDE.md / AGENTS.md)の導入によって、プラグイン単体で気になった点を生成前に確認させやすくなりました。一方で、これだけで全ての懸念が商用利用に耐える形で解消されたとは言い切れません。あくまでガイドは方針を示すだけであって、AIエージェントが確実に従うことを保証するものではありません。そのためより複雑なタスクをAIエージェントに依頼する場合には、以下の課題が想定されます。

  • 強制力がない: ガイドに書いても、AIが必ず従うとは限らない
  • 実行時チェックがない: 生成されたCDKコードが安全かどうかは、別途検査しないと分からない
  • ブロック機能がない: cdk deploy 直前の危険な差分を機械的に止められない

これらの課題は、第2回(Sensor Layer / Hooks編) で、Hooks と CDK 標準ツールを使ってどこまで補えるかを検証したいと思います。


今回の結果から、Agent Plugins はベストプラクティスへの“初期到達点”を提供する一方で、プロジェクト固有の要件や前提・制約は、ガイドによって別途定義する必要があることが分かりました。

まとめ

本記事では、Agent Plugins for AWS のセットアップから、プラグイン単体でのCDK生成、CLAUDE.md / AGENTS.md による Guide Layer の適用方針までを確認しました。

得られた知見

  1. プラグイン単体でも実用に近いCDKテンプレートは生成されるが、商用利用では確認したい設定が残る
  2. ガイドを明文化することで、生成前に確認してほしい観点を渡せる
  3. CLAUDE.md / AGENTS.md はAIの判断を誘導できるが、強制力はない

次回予告

第2回では、Hooks による検査・ブロック(Sensor Layer) を扱います。Hook 自体に独自の検知ロジックを書き込むのではなく、CDK 標準の開発フローに cdk-nagcdk diff を組み込み、ファイル生成後・保存後・デプロイ直前に検証を走らせます。

以上です。次回もよろしくお願いします!