🤖

Bedrock AgentCore Runtime の CDK L1 Construct を試す

に公開

Amazon Bedrock AgentCore の CloudFormation サポートが実装され始めています。記載時点では AgentCore Runtime, AgentCore Code Interpreter, AgentCore Browser がサポートされています。

従来は Bedrock AgentCore Starter Toolkit で CLI などでのデプロイが中心でしたが、IaC 派としては CloudFormation (AWS CDK) サポートを待ち望んでいました。

AWS CDK の L1 Construct でも ver 2.217.0 でリリースがされました。 AgentCore Runtime の CDK L1 Construct を構築して AWS 上に Agent をデプロイしてみます。

なお L2 Construct の RFC も起票されており、L2 Construct 化が進行中です。

事前準備

ディレクトリ構成

以下で実装しています。cdk init の後、lib/app 配下に Agent の実装や資材を格納したのみで、特に変わったことはしていません。

.
├── bin
│   └── cdk-bedrock-agentcore.ts
├── cdk.json
├── lib
│   ├── app
│   │   ├── agent.py
│   │   ├── Dockerfile
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   └── cdk-bedrock-agentcore-stack.ts
├── package-lock.json
├── package.json
├── README.md
└── tsconfig.json

Agent の実装

Strands Agents のサンプル Option B: Custom Agent を使用しています。

手順通り FastAPI による Agent および Dockerfile などデプロイ資材を用意します。特に変更点はないので説明は割愛します。

CDK 実装内容

Stack (lib/cdk-bedrock-agentcore-stack.ts)の実装内容は以下です。

コンテナイメージ

DockerImageAsset を使用して、コンテナイメージのビルド、ECR へのプッシュまで行います。

コンテナイメージは ARM64 であることが必須なので、platform で指定します。

lib/cdk-bedrock-agentcore-stack.ts
import { DockerImageAsset, Platform } from 'aws-cdk-lib/aws-ecr-assets';

const dockerImageAsset = new DockerImageAsset(this, 'DockerImageAsset', {
    directory: path.join(__dirname, 'app'),
    platform: Platform.LINUX_ARM64, // ARM を指定
    file: 'Dockerfile',
});

AgentCore Runtime の Execution role

AgentCore Runtime の実行に必要な IAM Role を作成します。Docs の内容を元にロールを作成、権限を付与しています。

ここは L2 Construct 化された際は自動生成を期待したいポイントですね。

lib/cdk-bedrock-agentcore-stack.ts
import * as iam from 'aws-cdk-lib/aws-iam';

// Create IAM role for Bedrock AgentCore with required policies
const agentCoreRole = new iam.Role(this, 'BedrockAgentCoreRole', {
  assumedBy: new iam.ServicePrincipal('bedrock-agentcore.amazonaws.com'),
  description: 'IAM role for Bedrock AgentCore Runtime',
});

const region = cdk.Stack.of(this).region;
const accountId = cdk.Stack.of(this).account;

agentCoreRole.addToPolicy(
  new iam.PolicyStatement({
    sid: 'ECRImageAccess',
    effect: iam.Effect.ALLOW,
    actions: ['ecr:BatchGetImage', 'ecr:GetDownloadUrlForLayer'],
    resources: [`arn:aws:ecr:${region}:${accountId}:repository/*`],
  }),
);

agentCoreRole.addToPolicy(
  new iam.PolicyStatement({
    effect: iam.Effect.ALLOW,
    actions: ['logs:DescribeLogStreams', 'logs:CreateLogGroup'],
    resources: [`arn:aws:logs:${region}:${accountId}:log-group:/aws/bedrock-agentcore/runtimes/*`],
  }),
);

agentCoreRole.addToPolicy(
  new iam.PolicyStatement({
    effect: iam.Effect.ALLOW,
    actions: ['logs:DescribeLogGroups'],
    resources: [`arn:aws:logs:${region}:${accountId}:log-group:*`],
  }),
);

agentCoreRole.addToPolicy(
  new iam.PolicyStatement({
    effect: iam.Effect.ALLOW,
    actions: ['logs:CreateLogStream', 'logs:PutLogEvents'],
    resources: [`arn:aws:logs:${region}:${accountId}:log-group:/aws/bedrock-agentcore/runtimes/*:log-stream:*`],
  }),
);

agentCoreRole.addToPolicy(
  new iam.PolicyStatement({
    sid: 'ECRTokenAccess',
    effect: iam.Effect.ALLOW,
    actions: ['ecr:GetAuthorizationToken'],
    resources: ['*'],
  }),
);

agentCoreRole.addToPolicy(
  new iam.PolicyStatement({
    effect: iam.Effect.ALLOW,
    actions: [
      'xray:PutTraceSegments',
      'xray:PutTelemetryRecords',
      'xray:GetSamplingRules',
      'xray:GetSamplingTargets',
    ],
    resources: ['*'],
  }),
);

agentCoreRole.addToPolicy(
  new iam.PolicyStatement({
    effect: iam.Effect.ALLOW,
    actions: ['cloudwatch:PutMetricData'],
    resources: ['*'],
    conditions: {
      StringEquals: {
        'cloudwatch:namespace': 'bedrock-agentcore',
      },
    },
  }),
);

agentCoreRole.addToPolicy(
  new iam.PolicyStatement({
    sid: 'GetAgentAccessToken',
    effect: iam.Effect.ALLOW,
    actions: [
      'bedrock-agentcore:GetWorkloadAccessToken',
      'bedrock-agentcore:GetWorkloadAccessTokenForJWT',
      'bedrock-agentcore:GetWorkloadAccessTokenForUserId',
    ],
    resources: [
      `arn:aws:bedrock-agentcore:${region}:${accountId}:workload-identity-directory/default`,
      `arn:aws:bedrock-agentcore:${region}:${accountId}:workload-identity-directory/default/workload-identity/agentName-*`,
    ],
  }),
);

agentCoreRole.addToPolicy(
  new iam.PolicyStatement({
    sid: 'BedrockModelInvocation',
    effect: iam.Effect.ALLOW,
    actions: ['bedrock:InvokeModel', 'bedrock:InvokeModelWithResponseStream'],
    resources: ['arn:aws:bedrock:*::foundation-model/*', `arn:aws:bedrock:${region}:${accountId}:*`],
  }),
);

AgentCore Runtime

本題の AgentCore Runtime を実装します。以下は主要項目を実装したものです。
コンテナイメージ、IAM ロールは上記で作成したものを指定します。

Authorizer のプロパティも用意されていますが、こちらは AgentCore Identity が CloudFormation でサポートされたら試してみたいと思います。

また、Execution Role -> AgentCore Runtime の順で作成されるよう依存関係を定義しています。これがない場合は Execution Role の作成前に AgentCore Runtime が作成され、ECR のイメージへのアクセスが権限不足で失敗しエラーとなるため入れています。

lib/cdk-bedrock-agentcore-stack.ts
import * as agentcore from 'aws-cdk-lib/aws-bedrockagentcore';

// Bedrock AgentCore Runtime
const runtime = new agentcore.CfnRuntime(this, 'AgentCoreRuntime', {
  agentRuntimeName: 'MyAgentRuntime',
  agentRuntimeArtifact: {
    containerConfiguration: {
      containerUri: dockerImageAsset.imageUri,  // ECR Image URI を指定
    },
  },
  networkConfiguration: {
    networkMode: 'PUBLIC',  // 執筆時点では PUBLIC のみサポート
  },
  roleArn: agentCoreRole.roleArn,  // Execution Role ARN を指定
  protocolConfiguration: 'HTTP',  // 'HTTP' or 'MCP' を指定(任意項目)
});

// Execution Role -> AgentCore Runtime となるよう依存関係を定義
runtime.node.addDependency(agentCoreRole)

Runtime Endopoint

AgentCore Runtime までのデプロイだと DEFAULT のエンドポイントが作成されます。CfnRuntimeEndpoint を使用すると、特定バージョンを Pin したエンドポイントを立てることができます。

なお AgentCore Runtime のバージョンは CfnRuntime デプロイ時にイミュータブルなものが生成され、コードの変更などを行いデプロイするごとに新しいバージョンが作成されます。

以下の例のように agentRuntimeVersion に Runtime の Attribute を参照させると最新のデプロイのバージョンを使用することになります。具体的なバージョンを指定したい場合は直接値を指定します( 1 など)。

lib/cdk-bedrock-agentcore-stack.ts
// Runtime Endpoint
new agentcore.CfnRuntimeEndpoint(this, 'RuntimeEndpoint', {
  agentRuntimeId: runtime.attrAgentRuntimeId, // AgentCore Runtime の ID を指定
  agentRuntimeVersion: runtime.attrAgentRuntimeVersion,  // AgentCore Runtime のバージョンを指定。指定したバージョンのエンドポイントが作成される
  name: 'MyAgentRuntimeEndpoint',
});

動作確認

cdk deploy 後、マネジメントコンソールから Agent Runtime でテストを行ったところ、問題なく動作しました。簡単ですね。

終わりに

AgentCore Runtime を試してみましたが、AgentCore Gateway などもサポートされたら試してみたいと思います。また L2 Construct 化も待ち遠しいですね。

IaC での対応が進むと、使いやすくなるので個人的にも期待しています。

アマゾン ウェブ サービス ジャパン (有志)

Discussion