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
で指定します。
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 化された際は自動生成を期待したいポイントですね。
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 のイメージへのアクセスが権限不足で失敗しエラーとなるため入れています。
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
など)。
// 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