From 7385de861cca0b2155f79260f4a2c6b13a042c6e Mon Sep 17 00:00:00 2001 From: Yuki Nakashima Date: Thu, 20 Nov 2025 17:27:32 +0900 Subject: [PATCH 1/7] Deploy VPC environment with AgentCore and AgentBuilder enabled --- packages/cdk/cdk.json | 3 + packages/cdk/lib/agent-core-stack.ts | 12 + .../cdk/lib/construct/generic-agent-core.ts | 118 +++- packages/cdk/lib/stack-input.ts | 47 +- .../generative-ai-use-cases.test.ts.snap | 519 ++++++++++++++++++ .../cdk/test/generative-ai-use-cases.test.ts | 38 ++ 6 files changed, 718 insertions(+), 19 deletions(-) diff --git a/packages/cdk/cdk.json b/packages/cdk/cdk.json index a73e7dcf1..0b7467e85 100644 --- a/packages/cdk/cdk.json +++ b/packages/cdk/cdk.json @@ -68,6 +68,9 @@ "createGenericAgentCoreRuntime": false, "agentCoreRegion": null, "agentCoreExternalRuntimes": [], + "agentCoreNetworkType": "PUBLIC", + "agentCoreVpcId": null, + "agentCoreSubnetIds": null, "allowedIpV4AddressRanges": null, "allowedIpV6AddressRanges": null, "allowedCountryCodes": null, diff --git a/packages/cdk/lib/agent-core-stack.ts b/packages/cdk/lib/agent-core-stack.ts index e319c50c5..93d97ad99 100644 --- a/packages/cdk/lib/agent-core-stack.ts +++ b/packages/cdk/lib/agent-core-stack.ts @@ -22,6 +22,9 @@ export class AgentCoreStack extends Stack { env: params.env, createGenericRuntime: params.createGenericAgentCoreRuntime, createAgentBuilderRuntime: params.agentBuilderEnabled, + agentCoreNetworkType: params.agentCoreNetworkType, + agentCoreVpcId: params.agentCoreVpcId, + agentCoreSubnetIds: params.agentCoreSubnetIds, }); // Export runtime info for cross-region access via cdk-remote-stack (only if values exist) @@ -40,6 +43,15 @@ export class AgentCoreStack extends Stack { }); } + // Output retained security group ID for manual cleanup + if (this.genericAgentCore.retainedSecurityGroupId) { + new CfnOutput(this, 'RetainedSecurityGroupId', { + value: this.genericAgentCore.retainedSecurityGroupId, + description: + 'MANUAL CLEANUP REQUIRED: Security Group ID to delete after AgentCore ENI cleanup (check tags: ManualCleanupRequired=true)', + }); + } + if ( params.agentBuilderEnabled && this.genericAgentCore.deployedAgentBuilderRuntimeArn diff --git a/packages/cdk/lib/construct/generic-agent-core.ts b/packages/cdk/lib/construct/generic-agent-core.ts index 8077d5b61..1a971070b 100644 --- a/packages/cdk/lib/construct/generic-agent-core.ts +++ b/packages/cdk/lib/construct/generic-agent-core.ts @@ -5,12 +5,13 @@ import { Role, ServicePrincipal, } from 'aws-cdk-lib/aws-iam'; -import { Stack, RemovalPolicy } from 'aws-cdk-lib'; +import { Stack, RemovalPolicy, Tags } from 'aws-cdk-lib'; import { Bucket, BlockPublicAccess, BucketEncryption, } from 'aws-cdk-lib/aws-s3'; +import { Subnet, Vpc, SecurityGroup, IVpc, ISubnet } from 'aws-cdk-lib/aws-ec2'; import { Runtime, RuntimeNetworkConfiguration, @@ -37,6 +38,10 @@ export interface GenericAgentCoreProps { env: string; createGenericRuntime?: boolean; createAgentBuilderRuntime?: boolean; + agentCoreNetworkType: 'PUBLIC' | 'PRIVATE'; + agentCoreVpcId?: string | null; + agentCoreSubnetIds?: string[] | null; + agentCoreEnvironmentVariables?: Record; } interface RuntimeResources { @@ -51,6 +56,10 @@ export class GenericAgentCore extends Construct { private readonly agentBuilderRuntimeConfig: AgentCoreRuntimeConfig; private readonly resources: RuntimeResources; + // Security Group ID that requires manual cleanup after AgentCore Runtime deletion + // Used for CloudFormation Output to remind users of manual cleanup tasks + public readonly retainedSecurityGroupId?: string; + constructor(scope: Construct, id: string, props: GenericAgentCoreProps) { super(scope, id); @@ -58,6 +67,9 @@ export class GenericAgentCore extends Construct { env, createGenericRuntime = false, createAgentBuilderRuntime = false, + agentCoreNetworkType = 'PUBLIC', + agentCoreVpcId = null, + agentCoreSubnetIds = null, } = props; // Create bucket first @@ -68,10 +80,56 @@ export class GenericAgentCore extends Construct { this.genericRuntimeConfig = configs.generic; this.agentBuilderRuntimeConfig = configs.agentBuilder; + // Create security group if VPC mode + let securityGroup: SecurityGroup | undefined; + let vpc: IVpc | undefined; + let subnets: ISubnet[] | undefined; + + if ( + agentCoreNetworkType === 'PRIVATE' && + agentCoreVpcId && + agentCoreSubnetIds + ) { + vpc = Vpc.fromLookup(this, 'AgentCoreVpc', { vpcId: agentCoreVpcId }); + subnets = agentCoreSubnetIds.map((subnetId, index) => + Subnet.fromSubnetId(this, `AgentCoreSubnet${index}`, subnetId) + ); + securityGroup = new SecurityGroup(this, 'AgentCoreSecurityGroup', { + vpc, + description: 'Security group for AgentCore Runtime', + allowAllOutbound: true, + }); + + // Add tags for manual cleanup identification + Tags.of(securityGroup).add('ManualCleanupRequired', 'true'); + Tags.of(securityGroup).add( + 'CleanupReason', + 'AgentCore-Managed-ENI-Dependency' + ); + Tags.of(securityGroup).add('CreatedBy', `GenU-${env}`); + + // Retain security group to prevent deletion errors when changing PRIVATE->PUBLIC or removing AgentCore + // AgentCore Runtime creates managed ENIs that reference this security group + // CloudFormation cannot delete the SG while managed ENIs are using it (even though they're not manually deletable) + // The managed ENIs are automatically cleaned up after AgentCore Runtime deletion, but with a time delay + // Therefore, this SG must be retained and manually deleted after the managed ENIs are cleaned up + // + // Note: Custom Resource with ENI monitoring could solve this, but deletion can take up to 1 hour + // Since security groups incur no cost, RETAIN is the practical solution for better user experience + securityGroup.applyRemovalPolicy(RemovalPolicy.RETAIN); + + // Store SG ID for output + this.retainedSecurityGroupId = securityGroup.securityGroupId; + } + // Create all resources atomically this.resources = this.createResources( createGenericRuntime, - createAgentBuilderRuntime + createAgentBuilderRuntime, + agentCoreNetworkType, + vpc, + subnets, + securityGroup ); } @@ -125,7 +183,11 @@ export class GenericAgentCore extends Construct { private createResources( createGeneric: boolean, - createAgentBuilder: boolean + createAgentBuilder: boolean, + agentCoreNetworkType: 'PUBLIC' | 'PRIVATE', + vpc?: IVpc, + subnets?: ISubnet[], + securityGroup?: SecurityGroup ): RuntimeResources { if (!createGeneric && !createAgentBuilder) { return { role: this.createExecutionRole() }; @@ -138,7 +200,11 @@ export class GenericAgentCore extends Construct { resources.genericRuntime = this.createRuntime( 'Generic', this.genericRuntimeConfig, - role + role, + agentCoreNetworkType, + vpc, + subnets, + securityGroup ); } @@ -146,7 +212,11 @@ export class GenericAgentCore extends Construct { resources.agentBuilderRuntime = this.createRuntime( 'AgentBuilder', this.agentBuilderRuntimeConfig, - role + role, + agentCoreNetworkType, + vpc, + subnets, + securityGroup ); } @@ -157,20 +227,54 @@ export class GenericAgentCore extends Construct { private createRuntime( type: string, config: AgentCoreRuntimeConfig, - role: Role + role: Role, + agentCoreNetworkType: 'PUBLIC' | 'PRIVATE', + vpc?: IVpc, + subnets?: ISubnet[], + securityGroup?: SecurityGroup ): Runtime { + const networkConfig = this.createNetworkConfiguration( + agentCoreNetworkType, + vpc, + subnets, + securityGroup + ); + return new Runtime(this, `${type}AgentCoreRuntime`, { runtimeName: config.name, agentRuntimeArtifact: AgentRuntimeArtifact.fromAsset( path.join(__dirname, `../../${config.dockerPath}`) ), executionRole: role, - networkConfiguration: RuntimeNetworkConfiguration.usingPublicNetwork(), + networkConfiguration: networkConfig, protocolConfiguration: ProtocolType.HTTP, environmentVariables: config.environmentVariables, }); } + private createNetworkConfiguration( + agentCoreNetworkType: 'PUBLIC' | 'PRIVATE', + vpc?: IVpc, + subnets?: ISubnet[], + securityGroup?: SecurityGroup + ): RuntimeNetworkConfiguration { + if (agentCoreNetworkType === 'PRIVATE') { + if (!vpc || !subnets) { + throw new Error( + 'VPC and Subnets are required for PRIVATE network type' + ); + } + + return RuntimeNetworkConfiguration.usingVpc(this, { + vpc, + vpcSubnets: { subnets }, + securityGroups: securityGroup ? [securityGroup] : undefined, + }); + } else { + return RuntimeNetworkConfiguration.usingPublicNetwork(); + } + } + private createExecutionRole(): Role { const region = Stack.of(this).region; const accountId = Stack.of(this).account; diff --git a/packages/cdk/lib/stack-input.ts b/packages/cdk/lib/stack-input.ts index 3c066d1eb..1ef6fa9cc 100644 --- a/packages/cdk/lib/stack-input.ts +++ b/packages/cdk/lib/stack-input.ts @@ -160,6 +160,10 @@ const baseStackInputSchema = z.object({ }) ) .default([]), + // Agent Core Network Configuration + agentCoreNetworkType: z.enum(['PUBLIC', 'PRIVATE']).default('PUBLIC'), + agentCoreVpcId: z.string().nullish(), + agentCoreSubnetIds: z.array(z.string()).nullish(), // MCP mcpEnabled: z.boolean().default(false), // Guardrail @@ -202,19 +206,38 @@ const baseStackInputSchema = z.object({ }); // Common Validator with refine -export const stackInputSchema = baseStackInputSchema.refine( - (data) => { - // If searchApiKey is provided, searchEngine must also be provided - if (data.searchApiKey && !data.searchEngine) { - return false; +export const stackInputSchema = baseStackInputSchema + .refine( + (data) => { + // If searchApiKey is provided, searchEngine must also be provided + if (data.searchApiKey && !data.searchEngine) { + return false; + } + return true; + }, + { + message: 'searchEngine is required when searchApiKey is provided', + path: ['searchEngine'], } - return true; - }, - { - message: 'searchEngine is required when searchApiKey is provided', - path: ['searchEngine'], - } -); + ) + .refine( + (data) => { + // Validate AgentCore network configuration + if (data.agentCoreNetworkType === 'PRIVATE') { + return ( + data.agentCoreVpcId && + data.agentCoreSubnetIds && + data.agentCoreSubnetIds.length > 0 + ); + } + return true; + }, + { + message: + 'VPC ID and Subnet IDs are required when agentCoreNetworkType is PRIVATE', + path: ['agentCoreNetworkType'], + } + ); // schema after conversion export const processedStackInputSchema = baseStackInputSchema.extend({ diff --git a/packages/cdk/test/__snapshots__/generative-ai-use-cases.test.ts.snap b/packages/cdk/test/__snapshots__/generative-ai-use-cases.test.ts.snap index a163de685..308bc2404 100644 --- a/packages/cdk/test/__snapshots__/generative-ai-use-cases.test.ts.snap +++ b/packages/cdk/test/__snapshots__/generative-ai-use-cases.test.ts.snap @@ -1,5 +1,524 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`GenerativeAiUseCases matches the snapshot (AgentCore with VPC) 1`] = ` +{ + "Outputs": { + "AgentBuilderAgentCoreRuntimeArn": { + "Export": { + "Name": "AgentCoreStack-AgentBuilderAgentCoreRuntimeArn", + }, + "Value": { + "Fn::GetAtt": [ + "GenericAgentCoreAgentBuilderAgentCoreRuntimeFEFD60E0", + "AgentRuntimeArn", + ], + }, + }, + "AgentBuilderAgentCoreRuntimeName": { + "Export": { + "Name": "AgentCoreStack-AgentBuilderAgentCoreRuntimeName", + }, + "Value": "GenUAgentBuilderRuntime", + }, + "ExportsOutputRefGenericAgentCoreAgentCoreFileBucket0430DA423298A79F": { + "Export": { + "Name": "AgentCoreStack:ExportsOutputRefGenericAgentCoreAgentCoreFileBucket0430DA423298A79F", + }, + "Value": { + "Ref": "GenericAgentCoreAgentCoreFileBucket0430DA42", + }, + }, + "FileBucketName": { + "Export": { + "Name": "AgentCoreStack-FileBucketName", + }, + "Value": { + "Ref": "GenericAgentCoreAgentCoreFileBucket0430DA42", + }, + }, + "GenericAgentCoreRuntimeArn": { + "Export": { + "Name": "AgentCoreStack-GenericAgentCoreRuntimeArn", + }, + "Value": { + "Fn::GetAtt": [ + "GenericAgentCoreGenericAgentCoreRuntime041F933D", + "AgentRuntimeArn", + ], + }, + }, + "GenericAgentCoreRuntimeName": { + "Export": { + "Name": "AgentCoreStack-GenericAgentCoreRuntimeName", + }, + "Value": "GenUGenericRuntime", + }, + "RetainedSecurityGroupId": { + "Description": "MANUAL CLEANUP REQUIRED: Security Group ID to delete after AgentCore ENI cleanup (check tags: ManualCleanupRequired=true)", + "Value": { + "Fn::GetAtt": [ + "GenericAgentCoreAgentCoreSecurityGroup288CC66C", + "GroupId", + ], + }, + }, + }, + "Parameters": { + "BootstrapVersion": { + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]", + "Type": "AWS::SSM::Parameter::Value", + }, + }, + "Resources": { + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": { + "DependsOn": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + ], + "Properties": { + "Code": { + "S3Bucket": "cdk-hnb659fds-assets-123456890123-us-east-1", + "S3Key": "HASH-REPLACED.zip", + }, + "Description": { + "Fn::Join": [ + "", + [ + "Lambda function for auto-deleting objects in ", + { + "Ref": "GenericAgentCoreAgentCoreFileBucket0430DA42", + }, + " S3 bucket.", + ], + ], + }, + "Handler": "index.handler", + "MemorySize": 128, + "Role": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn", + ], + }, + "Runtime": "nodejs22.x", + "Timeout": 900, + }, + "Type": "AWS::Lambda::Function", + }, + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com", + }, + }, + ], + "Version": "2012-10-17", + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:\${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + }, + ], + }, + "Type": "AWS::IAM::Role", + }, + "GenericAgentCoreAgentBuilderAgentCoreRuntimeFEFD60E0": { + "DependsOn": [ + "GenericAgentCoreAgentCoreRuntimeRoleDefaultPolicy4E847841", + "GenericAgentCoreAgentCoreRuntimeRoleF34B80EA", + ], + "Properties": { + "AgentRuntimeArtifact": { + "ContainerConfiguration": { + "ContainerUri": { + "Fn::Sub": "123456890123.dkr.ecr.us-east-1.\${AWS::URLSuffix}/cdk-hnb659fds-container-assets-123456890123-us-east-1:69eb434ee242daa7224dd9fb0f5cf9142a71fe81dac805352a11509e5ecff870", + }, + }, + }, + "AgentRuntimeName": "GenUAgentBuilderRuntime", + "EnvironmentVariables": { + "FILE_BUCKET": { + "Ref": "GenericAgentCoreAgentCoreFileBucket0430DA42", + }, + "MCP_SERVERS": "{"time":{"command":"uvx","args":["mcp-server-time"],"metadata":{"category":"Utility","description":"Provides current time and date functionality"}},"aws-knowledge-mcp-server":{"command":"npx","args":["mcp-remote","https://knowledge-mcp.global.api.aws"],"metadata":{"category":"AWS","description":"AWS Knowledge Base MCP server for enterprise knowledge access"}},"awslabs.aws-documentation-mcp-server":{"command":"uvx","args":["awslabs.aws-documentation-mcp-server@latest"],"metadata":{"category":"AWS","description":"Access AWS documentation and guides"}},"awslabs.cdk-mcp-server":{"command":"uvx","args":["awslabs.cdk-mcp-server@latest"],"metadata":{"category":"AWS","description":"AWS CDK code generation and assistance"}},"awslabs.aws-diagram-mcp-server":{"command":"uvx","args":["awslabs.aws-diagram-mcp-server@latest"],"metadata":{"category":"AWS","description":"Generate AWS architecture diagrams"}},"awslabs.nova-canvas-mcp-server":{"command":"uvx","args":["awslabs.nova-canvas-mcp-server@latest"],"env":{"AWS_REGION":"us-east-1"},"metadata":{"category":"AI/ML","description":"Amazon Nova Canvas image generation"}},"tavily-search":{"command":"npx","args":["-y","mcp-remote","https://mcp.tavily.com/mcp/?tavilyApiKey="],"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}}}", + "SUPPORTED_CACHE_FIELDS": "{"anthropic.claude-sonnet-4-5-20250929-v1:0":["messages","system","tools"],"anthropic.claude-haiku-4-5-20251001-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-1-20250805-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-sonnet-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-3-7-sonnet-20250219-v1:0":["messages","system","tools"],"anthropic.claude-3-5-haiku-20241022-v1:0":["messages","system","tools"],"amazon.nova-premier-v1:0":["messages","system"],"amazon.nova-pro-v1:0":["messages","system"],"amazon.nova-lite-v1:0":["messages","system"],"amazon.nova-micro-v1:0":["messages","system"]}", + }, + "NetworkConfiguration": { + "NetworkMode": "VPC", + "NetworkModeConfig": { + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "GenericAgentCoreAgentCoreSecurityGroup288CC66C", + "GroupId", + ], + }, + ], + "Subnets": [ + "subnet-12345678", + "subnet-87654321", + ], + }, + }, + "ProtocolConfiguration": "HTTP", + "RoleArn": { + "Fn::GetAtt": [ + "GenericAgentCoreAgentCoreRuntimeRoleF34B80EA", + "Arn", + ], + }, + }, + "Type": "AWS::BedrockAgentCore::Runtime", + }, + "GenericAgentCoreAgentCoreFileBucket0430DA42": { + "DeletionPolicy": "Delete", + "Properties": { + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256", + }, + }, + ], + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true, + }, + "Tags": [ + { + "Key": "aws-cdk:auto-delete-objects", + "Value": "true", + }, + ], + }, + "Type": "AWS::S3::Bucket", + "UpdateReplacePolicy": "Delete", + }, + "GenericAgentCoreAgentCoreFileBucketAutoDeleteObjectsCustomResourceC1B4B54D": { + "DeletionPolicy": "Delete", + "DependsOn": [ + "GenericAgentCoreAgentCoreFileBucketPolicyB659200A", + ], + "Properties": { + "BucketName": { + "Ref": "GenericAgentCoreAgentCoreFileBucket0430DA42", + }, + "ServiceToken": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F", + "Arn", + ], + }, + }, + "Type": "Custom::S3AutoDeleteObjects", + "UpdateReplacePolicy": "Delete", + }, + "GenericAgentCoreAgentCoreFileBucketPolicyB659200A": { + "Properties": { + "Bucket": { + "Ref": "GenericAgentCoreAgentCoreFileBucket0430DA42", + }, + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:PutBucketPolicy", + "s3:GetBucket*", + "s3:List*", + "s3:DeleteObject*", + ], + "Effect": "Allow", + "Principal": { + "AWS": { + "Fn::GetAtt": [ + "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092", + "Arn", + ], + }, + }, + "Resource": [ + { + "Fn::GetAtt": [ + "GenericAgentCoreAgentCoreFileBucket0430DA42", + "Arn", + ], + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "GenericAgentCoreAgentCoreFileBucket0430DA42", + "Arn", + ], + }, + "/*", + ], + ], + }, + ], + }, + ], + "Version": "2012-10-17", + }, + }, + "Type": "AWS::S3::BucketPolicy", + }, + "GenericAgentCoreAgentCoreRuntimeRoleDefaultPolicy4E847841": { + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "bedrock:InvokeModel", + "bedrock:InvokeModelWithResponseStream", + ], + "Effect": "Allow", + "Resource": "*", + "Sid": "BedrockModelInvocation", + }, + { + "Action": "iam:CreateServiceLinkedRole", + "Condition": { + "StringEquals": { + "iam:AWSServiceName": "runtime-identity.bedrock-agentcore.amazonaws.com", + }, + }, + "Effect": "Allow", + "Resource": "arn:aws:iam::*:role/aws-service-role/runtime-identity.bedrock-agentcore.amazonaws.com/AWSServiceRoleForBedrockAgentCoreRuntimeIdentity", + "Sid": "CreateServiceLinkedRole", + }, + { + "Action": [ + "bedrock-agentcore:CreateCodeInterpreter", + "bedrock-agentcore:StartCodeInterpreterSession", + "bedrock-agentcore:InvokeCodeInterpreter", + "bedrock-agentcore:StopCodeInterpreterSession", + "bedrock-agentcore:DeleteCodeInterpreter", + "bedrock-agentcore:ListCodeInterpreters", + "bedrock-agentcore:GetCodeInterpreter", + "bedrock-agentcore:GetCodeInterpreterSession", + "bedrock-agentcore:ListCodeInterpreterSessions", + ], + "Effect": "Allow", + "Resource": "*", + "Sid": "Tools", + }, + { + "Action": [ + "s3:DeleteObject*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging", + "s3:Abort*", + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "GenericAgentCoreAgentCoreFileBucket0430DA42", + "Arn", + ], + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "GenericAgentCoreAgentCoreFileBucket0430DA42", + "Arn", + ], + }, + "/*", + ], + ], + }, + ], + }, + { + "Action": [ + "ecr:BatchCheckLayerAvailability", + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage", + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":ecr:us-east-1:123456890123:repository/cdk-hnb659fds-container-assets-123456890123-us-east-1", + ], + ], + }, + }, + { + "Action": "ecr:GetAuthorizationToken", + "Effect": "Allow", + "Resource": "*", + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "GenericAgentCoreAgentCoreRuntimeRoleDefaultPolicy4E847841", + "Roles": [ + { + "Ref": "GenericAgentCoreAgentCoreRuntimeRoleF34B80EA", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, + "GenericAgentCoreAgentCoreRuntimeRoleF34B80EA": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Condition": { + "ArnLike": { + "aws:SourceArn": "arn:aws:bedrock-agentcore:us-east-1:123456890123:*", + }, + "StringEquals": { + "aws:SourceAccount": "123456890123", + }, + }, + "Effect": "Allow", + "Principal": { + "Service": "bedrock-agentcore.amazonaws.com", + }, + }, + ], + "Version": "2012-10-17", + }, + }, + "Type": "AWS::IAM::Role", + }, + "GenericAgentCoreAgentCoreSecurityGroup288CC66C": { + "DeletionPolicy": "Retain", + "Properties": { + "GroupDescription": "Security group for AgentCore Runtime", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1", + }, + ], + "Tags": [ + { + "Key": "CleanupReason", + "Value": "AgentCore-Managed-ENI-Dependency", + }, + { + "Key": "CreatedBy", + "Value": "GenU-", + }, + { + "Key": "ManualCleanupRequired", + "Value": "true", + }, + ], + "VpcId": "vpc-12345", + }, + "Type": "AWS::EC2::SecurityGroup", + "UpdateReplacePolicy": "Retain", + }, + "GenericAgentCoreGenericAgentCoreRuntime041F933D": { + "DependsOn": [ + "GenericAgentCoreAgentCoreRuntimeRoleDefaultPolicy4E847841", + "GenericAgentCoreAgentCoreRuntimeRoleF34B80EA", + ], + "Properties": { + "AgentRuntimeArtifact": { + "ContainerConfiguration": { + "ContainerUri": { + "Fn::Sub": "123456890123.dkr.ecr.us-east-1.\${AWS::URLSuffix}/cdk-hnb659fds-container-assets-123456890123-us-east-1:69eb434ee242daa7224dd9fb0f5cf9142a71fe81dac805352a11509e5ecff870", + }, + }, + }, + "AgentRuntimeName": "GenUGenericRuntime", + "EnvironmentVariables": { + "FILE_BUCKET": { + "Ref": "GenericAgentCoreAgentCoreFileBucket0430DA42", + }, + "MCP_SERVERS": "{"time":{"command":"uvx","args":["mcp-server-time"],"metadata":{"category":"Utility","description":"Provides current time and date functionality"}},"aws-knowledge-mcp-server":{"command":"npx","args":["mcp-remote","https://knowledge-mcp.global.api.aws"],"metadata":{"category":"AWS","description":"AWS Knowledge Base MCP server for enterprise knowledge access"}},"awslabs.aws-documentation-mcp-server":{"command":"uvx","args":["awslabs.aws-documentation-mcp-server@latest"],"metadata":{"category":"AWS","description":"Access AWS documentation and guides"}},"awslabs.cdk-mcp-server":{"command":"uvx","args":["awslabs.cdk-mcp-server@latest"],"metadata":{"category":"AWS","description":"AWS CDK code generation and assistance"}},"awslabs.aws-diagram-mcp-server":{"command":"uvx","args":["awslabs.aws-diagram-mcp-server@latest"],"metadata":{"category":"AWS","description":"Generate AWS architecture diagrams"}},"awslabs.nova-canvas-mcp-server":{"command":"uvx","args":["awslabs.nova-canvas-mcp-server@latest"],"env":{"AWS_REGION":"us-east-1"},"metadata":{"category":"AI/ML","description":"Amazon Nova Canvas image generation"}},"tavily-search":{"command":"npx","args":["-y","mcp-remote","https://mcp.tavily.com/mcp/?tavilyApiKey="],"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}}}", + "SUPPORTED_CACHE_FIELDS": "{"anthropic.claude-sonnet-4-5-20250929-v1:0":["messages","system","tools"],"anthropic.claude-haiku-4-5-20251001-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-1-20250805-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-sonnet-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-3-7-sonnet-20250219-v1:0":["messages","system","tools"],"anthropic.claude-3-5-haiku-20241022-v1:0":["messages","system","tools"],"amazon.nova-premier-v1:0":["messages","system"],"amazon.nova-pro-v1:0":["messages","system"],"amazon.nova-lite-v1:0":["messages","system"],"amazon.nova-micro-v1:0":["messages","system"]}", + }, + "NetworkConfiguration": { + "NetworkMode": "VPC", + "NetworkModeConfig": { + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "GenericAgentCoreAgentCoreSecurityGroup288CC66C", + "GroupId", + ], + }, + ], + "Subnets": [ + "subnet-12345678", + "subnet-87654321", + ], + }, + }, + "ProtocolConfiguration": "HTTP", + "RoleArn": { + "Fn::GetAtt": [ + "GenericAgentCoreAgentCoreRuntimeRoleF34B80EA", + "Arn", + ], + }, + }, + "Type": "AWS::BedrockAgentCore::Runtime", + }, + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5", + ], + { + "Ref": "BootstrapVersion", + }, + ], + }, + ], + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI.", + }, + ], + }, + }, +} +`; + exports[`GenerativeAiUseCases matches the snapshot (closed network mode) 1`] = ` { "Outputs": { diff --git a/packages/cdk/test/generative-ai-use-cases.test.ts b/packages/cdk/test/generative-ai-use-cases.test.ts index 7c90a802f..3b3e2c2ca 100644 --- a/packages/cdk/test/generative-ai-use-cases.test.ts +++ b/packages/cdk/test/generative-ai-use-cases.test.ts @@ -209,4 +209,42 @@ describe('GenerativeAiUseCases', () => { }); } }); + + test('matches the snapshot (AgentCore with VPC)', () => { + const app = new cdk.App(); + + const params = processedStackInputSchema.parse({ + ...stackInput, + agentCoreNetworkType: 'PRIVATE', + agentCoreVpcId: 'vpc-12345678', + agentCoreSubnetIds: ['subnet-12345678', 'subnet-87654321'], + }); + + const { + cloudFrontWafStack, + ragKnowledgeBaseStack, + agentStack, + agentCoreStack, + guardrail, + generativeAiUseCasesStack, + dashboardStack, + } = createStacks(app, params); + + // Create Templates + if ( + !cloudFrontWafStack || + !ragKnowledgeBaseStack || + !agentStack || + !agentCoreStack || + !guardrail || + !generativeAiUseCasesStack || + !dashboardStack + ) { + throw new Error('Not all stacks are created'); + } + const agentCoreTemplate = Template.fromStack(agentCoreStack); + + // Assert + expect(agentCoreTemplate.toJSON()).toMatchSnapshot(); + }); }); From e4e44f98ccd63fa49f2d234566cef9f1af717c4c Mon Sep 17 00:00:00 2001 From: Yuki Nakashima Date: Thu, 20 Nov 2025 19:11:33 +0900 Subject: [PATCH 2/7] Add AgentCore Runtime VPC configuration documentation - Add VPC network configuration section to DEPLOY_OPTION.md - Include AZ support requirements with AWS official documentation link - Add NAT Gateway requirements for MCP server installation - Add manual stack deletion procedure for AgentCore - Include both Generic Runtime and AgentBuilder Runtime settings --- docs/ja/DEPLOY_OPTION.md | 61 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/docs/ja/DEPLOY_OPTION.md b/docs/ja/DEPLOY_OPTION.md index fff669479..6f0f5ffe9 100644 --- a/docs/ja/DEPLOY_OPTION.md +++ b/docs/ja/DEPLOY_OPTION.md @@ -769,6 +769,67 @@ const envs: Record> = { } ``` +> [!NOTE] +> AgentCore ユースケースの設定を有効後に、再度無効化する場合は、`createGenericAgentCoreRuntime: false` にして再デプロイすればAgentCore ユースケースは無効化されますが、`AgentCoreStack` 自体は残ります。マネージメントコンソールを開き、`agentCoreRegion` の CloudFormation から `AgentCoreStack` というスタックを削除することで完全に消去ができます。 + +#### AgentCore Runtime のネットワーク設定 + +AgentCore Runtime は以下のネットワークモードで動作できます: + +- `PUBLIC` (デフォルト): パブリックネットワークで動作 +- `PRIVATE`: VPC内のプライベートネットワークで動作 + +ネットワーク設定は、Generic Runtime と AgentBuilder Runtime の両方に適用されます。 + +**VPCモードの使用場面**: + +- AgentCore Runtime から社内システムやプライベートデータベースにアクセスする必要がある場合 +- 例えば、VPC内の他のAWSサービス(RDS、ElastiCache等)と直接通信したい場合 + +VPC モードを使用する場合は、以下のパラメータを設定してください: + +- `agentCoreNetworkType`: `PRIVATE` に設定 +- `agentCoreVpcId`: 使用するVPCのID +- `agentCoreSubnetIds`: 使用するサブネットのIDリスト + +> [!IMPORTANT] +> **Availability Zone (AZ) サポート**: AgentCore RuntimeはリージョンごとにサポートされているAZが限定されています。サブネットは必ずサポートされているAZ内に配置してください。詳細は[AWS公式ドキュメント](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agentcore-vpc.html#agentcore-supported-azs)をご確認ください。 +> +> **インターネットアクセス**: AgentCore Runtime で MCP サーバーのインストールにはインターネットアクセスが必要です。プライベートサブネットが接続先の場合には NAT Gateway への経路を設定してください。 + +**[parameter.ts](/packages/cdk/parameter.ts) を編集** + +```typescript +// parameter.ts +const envs: Record> = { + dev: { + createGenericAgentCoreRuntime: true, + agentBuilderEnabled: true, + agentCoreNetworkType: 'PRIVATE', + agentCoreVpcId: 'vpc-xxxxxxxxx', + agentCoreSubnetIds: ['subnet-xxxxxxxxx', 'subnet-yyyyyyyyy'], + }, +}; +``` + +**[packages/cdk/cdk.json](/packages/cdk/cdk.json) を編集** + +```json +// cdk.json +{ + "context": { + "createGenericAgentCoreRuntime": true, + "agentBuilderEnabled": true, + "agentCoreNetworkType": "PRIVATE", + "agentCoreVpcId": "vpc-xxxxxxxxx", + "agentCoreSubnetIds": ["subnet-xxxxxxxxx", "subnet-yyyyyyyyy"] + } +} +``` + +> [!WARNING] +> VPCモードを使用する場合、AgentCore Runtime削除時にセキュリティグループが自動削除されません。AgentCore Runtimeが作成するマネージドENIがセキュリティグループを参照するため、CloudFormationでは削除できません。AgentCore Runtime削除後、マネージドENIが自動削除されるまで待ってから、手動でセキュリティグループを削除してください。削除が必要なセキュリティグループIDはCloudFormationの出力に表示されます。 + ### 音声チャットユースケースの有効化 > [!NOTE] From 8605cbd4f18ed243a8694d47b77765c80a0924d2 Mon Sep 17 00:00:00 2001 From: Yuki Nakashima Date: Thu, 20 Nov 2025 19:18:30 +0900 Subject: [PATCH 3/7] Update AgentCore Runtime VPC documentation - Add use case scenarios for VPC mode - Include agentBuilderEnabled in configuration examples - Add AZ support requirements with official documentation link - Add NAT Gateway requirements for MCP server installation --- docs/ja/DEPLOY_OPTION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ja/DEPLOY_OPTION.md b/docs/ja/DEPLOY_OPTION.md index 6f0f5ffe9..9bd97f9fb 100644 --- a/docs/ja/DEPLOY_OPTION.md +++ b/docs/ja/DEPLOY_OPTION.md @@ -828,7 +828,7 @@ const envs: Record> = { ``` > [!WARNING] -> VPCモードを使用する場合、AgentCore Runtime削除時にセキュリティグループが自動削除されません。AgentCore Runtimeが作成するマネージドENIがセキュリティグループを参照するため、CloudFormationでは削除できません。AgentCore Runtime削除後、マネージドENIが自動削除されるまで待ってから、手動でセキュリティグループを削除してください。削除が必要なセキュリティグループIDはCloudFormationの出力に表示されます。 +> VPC モードを使用する場合、例えば PRIVATE から PUBLIC への変更により AgentCore Runtime 削除時にセキュリティグループが自動削除されません。AgentCore Runtime が作成する AWS マネージドな ENI がセキュリティグループを参照するため、CloudFormation では削除できません。AgentCore Runtime 削除後、マネージド ENI が自動削除されるまで待ってから、手動でセキュリティグループを削除してください。削除が必要なセキュリティグループ ID は CloudFormation の出力に表示されます。 ### 音声チャットユースケースの有効化 From 32216e990bed3fed81b1a1e92f2984ab572fba1e Mon Sep 17 00:00:00 2001 From: Yuki Nakashima Date: Thu, 20 Nov 2025 19:26:51 +0900 Subject: [PATCH 4/7] Add AgentCore Runtime network configuration section to EN and KO docs --- docs/en/DEPLOY_OPTION.md | 61 ++++++++++++++++++++++++++++++++++++++++ docs/ko/DEPLOY_OPTION.md | 61 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/docs/en/DEPLOY_OPTION.md b/docs/en/DEPLOY_OPTION.md index c717fa429..58e5a17ed 100644 --- a/docs/en/DEPLOY_OPTION.md +++ b/docs/en/DEPLOY_OPTION.md @@ -754,6 +754,67 @@ const envs: Record> = { } ``` +> [!NOTE] +> After enabling AgentCore use case settings, if you want to disable them again, you can disable the AgentCore use case by setting `createGenericAgentCoreRuntime: false` and redeploying, but the `AgentCoreStack` itself will remain. You can completely remove it by opening the Management Console and deleting the `AgentCoreStack` stack from CloudFormation in the `agentCoreRegion`. + +#### AgentCore Runtime Network Configuration + +AgentCore Runtime can operate in the following network modes: + +- `PUBLIC` (default): Operates on public network +- `PRIVATE`: Operates on private network within VPC + +Network settings apply to both Generic Runtime and AgentBuilder Runtime. + +**Use cases for VPC mode**: + +- When AgentCore Runtime needs to access internal systems or private databases +- For example, when you want to communicate directly with other AWS services (RDS, ElastiCache, etc.) within the VPC + +When using VPC mode, configure the following parameters: + +- `agentCoreNetworkType`: Set to `PRIVATE` +- `agentCoreVpcId`: VPC ID to use +- `agentCoreSubnetIds`: List of subnet IDs to use + +> [!IMPORTANT] +> **Availability Zone (AZ) Support**: AgentCore Runtime has limited supported AZs per region. Subnets must be placed within supported AZs. For details, please refer to the [AWS official documentation](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agentcore-vpc.html#agentcore-supported-azs). +> +> **Internet Access**: AgentCore Runtime requires internet access for MCP server installation. If connecting to private subnets, configure routes to NAT Gateway. + +**Edit [parameter.ts](/packages/cdk/parameter.ts)** + +```typescript +// parameter.ts +const envs: Record> = { + dev: { + createGenericAgentCoreRuntime: true, + agentBuilderEnabled: true, + agentCoreNetworkType: 'PRIVATE', + agentCoreVpcId: 'vpc-xxxxxxxxx', + agentCoreSubnetIds: ['subnet-xxxxxxxxx', 'subnet-yyyyyyyyy'], + }, +}; +``` + +**Edit [packages/cdk/cdk.json](/packages/cdk/cdk.json)** + +```json +// cdk.json +{ + "context": { + "createGenericAgentCoreRuntime": true, + "agentBuilderEnabled": true, + "agentCoreNetworkType": "PRIVATE", + "agentCoreVpcId": "vpc-xxxxxxxxx", + "agentCoreSubnetIds": ["subnet-xxxxxxxxx", "subnet-yyyyyyyyy"] + } +} +``` + +> [!WARNING] +> When using VPC mode, security groups are not automatically deleted when AgentCore Runtime is deleted due to changes such as from PRIVATE to PUBLIC. This is because AWS-managed ENIs created by AgentCore Runtime reference the security groups, making them undeletable by CloudFormation. After deleting AgentCore Runtime, wait for the managed ENIs to be automatically deleted, then manually delete the security groups. The security group IDs that need to be deleted are displayed in the CloudFormation outputs. + ### Enabling Voice Chat Use Case > [!NOTE] diff --git a/docs/ko/DEPLOY_OPTION.md b/docs/ko/DEPLOY_OPTION.md index 06caa0b54..6f0516f9f 100644 --- a/docs/ko/DEPLOY_OPTION.md +++ b/docs/ko/DEPLOY_OPTION.md @@ -753,6 +753,67 @@ const envs: Record> = { } ``` +> [!NOTE] +> AgentCore 사용 사례 설정을 활성화한 후 다시 비활성화하려면 `createGenericAgentCoreRuntime: false`로 설정하고 재배포하면 AgentCore 사용 사례가 비활성화되지만 `AgentCoreStack` 자체는 남아있습니다. 관리 콘솔을 열고 `agentCoreRegion`의 CloudFormation에서 `AgentCoreStack` 스택을 삭제하여 완전히 제거할 수 있습니다. + +#### AgentCore Runtime 네트워크 설정 + +AgentCore Runtime은 다음 네트워크 모드에서 작동할 수 있습니다: + +- `PUBLIC` (기본값): 퍼블릭 네트워크에서 작동 +- `PRIVATE`: VPC 내 프라이빗 네트워크에서 작동 + +네트워크 설정은 Generic Runtime과 AgentBuilder Runtime 모두에 적용됩니다. + +**VPC 모드 사용 사례**: + +- AgentCore Runtime에서 사내 시스템이나 프라이빗 데이터베이스에 액세스해야 하는 경우 +- 예를 들어, VPC 내의 다른 AWS 서비스(RDS, ElastiCache 등)와 직접 통신하고 싶은 경우 + +VPC 모드를 사용할 때는 다음 매개변수를 설정하세요: + +- `agentCoreNetworkType`: `PRIVATE`로 설정 +- `agentCoreVpcId`: 사용할 VPC ID +- `agentCoreSubnetIds`: 사용할 서브넷 ID 목록 + +> [!IMPORTANT] +> **가용 영역(AZ) 지원**: AgentCore Runtime은 리전별로 지원되는 AZ가 제한되어 있습니다. 서브넷은 반드시 지원되는 AZ 내에 배치해야 합니다. 자세한 내용은 [AWS 공식 문서](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agentcore-vpc.html#agentcore-supported-azs)를 확인하세요. +> +> **인터넷 액세스**: AgentCore Runtime에서 MCP 서버 설치에는 인터넷 액세스가 필요합니다. 프라이빗 서브넷이 연결 대상인 경우 NAT Gateway로의 경로를 설정하세요. + +**[parameter.ts](/packages/cdk/parameter.ts) 편집** + +```typescript +// parameter.ts +const envs: Record> = { + dev: { + createGenericAgentCoreRuntime: true, + agentBuilderEnabled: true, + agentCoreNetworkType: 'PRIVATE', + agentCoreVpcId: 'vpc-xxxxxxxxx', + agentCoreSubnetIds: ['subnet-xxxxxxxxx', 'subnet-yyyyyyyyy'], + }, +}; +``` + +**[packages/cdk/cdk.json](/packages/cdk/cdk.json) 편집** + +```json +// cdk.json +{ + "context": { + "createGenericAgentCoreRuntime": true, + "agentBuilderEnabled": true, + "agentCoreNetworkType": "PRIVATE", + "agentCoreVpcId": "vpc-xxxxxxxxx", + "agentCoreSubnetIds": ["subnet-xxxxxxxxx", "subnet-yyyyyyyyy"] + } +} +``` + +> [!WARNING] +> VPC 모드를 사용할 때, 예를 들어 PRIVATE에서 PUBLIC으로의 변경으로 인해 AgentCore Runtime 삭제 시 보안 그룹이 자동 삭제되지 않습니다. AgentCore Runtime이 생성하는 AWS 관리형 ENI가 보안 그룹을 참조하기 때문에 CloudFormation에서는 삭제할 수 없습니다. AgentCore Runtime 삭제 후 관리형 ENI가 자동 삭제될 때까지 기다린 다음 수동으로 보안 그룹을 삭제하세요. 삭제가 필요한 보안 그룹 ID는 CloudFormation 출력에 표시됩니다. + ### Voice Chat 사용 사례 활성화 > [!NOTE] From 8823df9c1cd5b9c990103e0db1d04cfe8d2d0c2c Mon Sep 17 00:00:00 2001 From: Yuki Nakashima Date: Sat, 29 Nov 2025 08:37:56 +0900 Subject: [PATCH 5/7] refactor: Auto-detect AgentCore network type from VPC configuration - Remove agentCoreNetworkType parameter from user input - Add isAgentCoreNetworkPrivate computed in parameter.ts - Automatically set to true when both agentCoreVpcId and agentCoreSubnetIds are provided - Add validation to ensure VPC settings are provided together - Update GenericAgentCore to use boolean flag instead of enum - Applies to both Generic Runtime and Agent Builder Runtime --- packages/cdk/cdk.json | 1 - packages/cdk/lib/agent-core-stack.ts | 2 +- .../cdk/lib/construct/generic-agent-core.ts | 28 ++++++++----------- packages/cdk/lib/stack-input.ts | 23 ++++++++------- packages/cdk/parameter.ts | 6 ++++ .../cdk/test/generative-ai-use-cases.test.ts | 2 +- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/packages/cdk/cdk.json b/packages/cdk/cdk.json index 0b7467e85..3a3bb26bb 100644 --- a/packages/cdk/cdk.json +++ b/packages/cdk/cdk.json @@ -68,7 +68,6 @@ "createGenericAgentCoreRuntime": false, "agentCoreRegion": null, "agentCoreExternalRuntimes": [], - "agentCoreNetworkType": "PUBLIC", "agentCoreVpcId": null, "agentCoreSubnetIds": null, "allowedIpV4AddressRanges": null, diff --git a/packages/cdk/lib/agent-core-stack.ts b/packages/cdk/lib/agent-core-stack.ts index 93d97ad99..b1b43586d 100644 --- a/packages/cdk/lib/agent-core-stack.ts +++ b/packages/cdk/lib/agent-core-stack.ts @@ -22,7 +22,7 @@ export class AgentCoreStack extends Stack { env: params.env, createGenericRuntime: params.createGenericAgentCoreRuntime, createAgentBuilderRuntime: params.agentBuilderEnabled, - agentCoreNetworkType: params.agentCoreNetworkType, + isAgentCoreNetworkPrivate: params.isAgentCoreNetworkPrivate, agentCoreVpcId: params.agentCoreVpcId, agentCoreSubnetIds: params.agentCoreSubnetIds, }); diff --git a/packages/cdk/lib/construct/generic-agent-core.ts b/packages/cdk/lib/construct/generic-agent-core.ts index 1a971070b..0b169813b 100644 --- a/packages/cdk/lib/construct/generic-agent-core.ts +++ b/packages/cdk/lib/construct/generic-agent-core.ts @@ -38,7 +38,7 @@ export interface GenericAgentCoreProps { env: string; createGenericRuntime?: boolean; createAgentBuilderRuntime?: boolean; - agentCoreNetworkType: 'PUBLIC' | 'PRIVATE'; + isAgentCoreNetworkPrivate?: boolean; agentCoreVpcId?: string | null; agentCoreSubnetIds?: string[] | null; agentCoreEnvironmentVariables?: Record; @@ -67,7 +67,7 @@ export class GenericAgentCore extends Construct { env, createGenericRuntime = false, createAgentBuilderRuntime = false, - agentCoreNetworkType = 'PUBLIC', + isAgentCoreNetworkPrivate = false, agentCoreVpcId = null, agentCoreSubnetIds = null, } = props; @@ -85,11 +85,7 @@ export class GenericAgentCore extends Construct { let vpc: IVpc | undefined; let subnets: ISubnet[] | undefined; - if ( - agentCoreNetworkType === 'PRIVATE' && - agentCoreVpcId && - agentCoreSubnetIds - ) { + if (isAgentCoreNetworkPrivate && agentCoreVpcId && agentCoreSubnetIds) { vpc = Vpc.fromLookup(this, 'AgentCoreVpc', { vpcId: agentCoreVpcId }); subnets = agentCoreSubnetIds.map((subnetId, index) => Subnet.fromSubnetId(this, `AgentCoreSubnet${index}`, subnetId) @@ -126,7 +122,7 @@ export class GenericAgentCore extends Construct { this.resources = this.createResources( createGenericRuntime, createAgentBuilderRuntime, - agentCoreNetworkType, + isAgentCoreNetworkPrivate, vpc, subnets, securityGroup @@ -184,7 +180,7 @@ export class GenericAgentCore extends Construct { private createResources( createGeneric: boolean, createAgentBuilder: boolean, - agentCoreNetworkType: 'PUBLIC' | 'PRIVATE', + isAgentCoreNetworkPrivate: boolean, vpc?: IVpc, subnets?: ISubnet[], securityGroup?: SecurityGroup @@ -201,7 +197,7 @@ export class GenericAgentCore extends Construct { 'Generic', this.genericRuntimeConfig, role, - agentCoreNetworkType, + isAgentCoreNetworkPrivate, vpc, subnets, securityGroup @@ -213,7 +209,7 @@ export class GenericAgentCore extends Construct { 'AgentBuilder', this.agentBuilderRuntimeConfig, role, - agentCoreNetworkType, + isAgentCoreNetworkPrivate, vpc, subnets, securityGroup @@ -228,13 +224,13 @@ export class GenericAgentCore extends Construct { type: string, config: AgentCoreRuntimeConfig, role: Role, - agentCoreNetworkType: 'PUBLIC' | 'PRIVATE', + isAgentCoreNetworkPrivate: boolean, vpc?: IVpc, subnets?: ISubnet[], securityGroup?: SecurityGroup ): Runtime { const networkConfig = this.createNetworkConfiguration( - agentCoreNetworkType, + isAgentCoreNetworkPrivate, vpc, subnets, securityGroup @@ -253,15 +249,15 @@ export class GenericAgentCore extends Construct { } private createNetworkConfiguration( - agentCoreNetworkType: 'PUBLIC' | 'PRIVATE', + isAgentCoreNetworkPrivate: boolean, vpc?: IVpc, subnets?: ISubnet[], securityGroup?: SecurityGroup ): RuntimeNetworkConfiguration { - if (agentCoreNetworkType === 'PRIVATE') { + if (isAgentCoreNetworkPrivate) { if (!vpc || !subnets) { throw new Error( - 'VPC and Subnets are required for PRIVATE network type' + 'VPC and Subnets are required for private network configuration' ); } diff --git a/packages/cdk/lib/stack-input.ts b/packages/cdk/lib/stack-input.ts index 1ef6fa9cc..16ab071d3 100644 --- a/packages/cdk/lib/stack-input.ts +++ b/packages/cdk/lib/stack-input.ts @@ -161,7 +161,6 @@ const baseStackInputSchema = z.object({ ) .default([]), // Agent Core Network Configuration - agentCoreNetworkType: z.enum(['PUBLIC', 'PRIVATE']).default('PUBLIC'), agentCoreVpcId: z.string().nullish(), agentCoreSubnetIds: z.array(z.string()).nullish(), // MCP @@ -222,20 +221,18 @@ export const stackInputSchema = baseStackInputSchema ) .refine( (data) => { - // Validate AgentCore network configuration - if (data.agentCoreNetworkType === 'PRIVATE') { - return ( - data.agentCoreVpcId && - data.agentCoreSubnetIds && - data.agentCoreSubnetIds.length > 0 - ); - } - return true; + // Validate AgentCore VPC configuration consistency + const hasVpcId = !!data.agentCoreVpcId; + const hasSubnetIds = + data.agentCoreSubnetIds && data.agentCoreSubnetIds.length > 0; + + // Both must be provided or both must be empty + return hasVpcId === hasSubnetIds; }, { message: - 'VPC ID and Subnet IDs are required when agentCoreNetworkType is PRIVATE', - path: ['agentCoreNetworkType'], + 'Both VPC ID and Subnet IDs must be provided together for AgentCore network configuration', + path: ['agentCoreVpcId'], } ); @@ -277,6 +274,8 @@ export const processedStackInputSchema = baseStackInputSchema.extend({ ), // Processed agentCoreRegion (null -> modelRegion) agentCoreRegion: z.string(), + // Computed from VPC configuration (computed in parameter.ts) + isAgentCoreNetworkPrivate: z.boolean().optional(), // Branding configuration brandingConfig: z .object({ diff --git a/packages/cdk/parameter.ts b/packages/cdk/parameter.ts index 8000e662a..152cef363 100644 --- a/packages/cdk/parameter.ts +++ b/packages/cdk/parameter.ts @@ -78,6 +78,12 @@ export const getParams = (app: cdk.App): ProcessedStackInput => { ), // Process agentCoreRegion: null -> modelRegion agentCoreRegion: params.agentCoreRegion || params.modelRegion, + // Compute isAgentCoreNetworkPrivate from VPC configuration + isAgentCoreNetworkPrivate: !!( + params.agentCoreVpcId && + params.agentCoreSubnetIds && + params.agentCoreSubnetIds.length > 0 + ), // Load branding configuration brandingConfig: loadBrandingConfig(), }; diff --git a/packages/cdk/test/generative-ai-use-cases.test.ts b/packages/cdk/test/generative-ai-use-cases.test.ts index 3b3e2c2ca..0f5686035 100644 --- a/packages/cdk/test/generative-ai-use-cases.test.ts +++ b/packages/cdk/test/generative-ai-use-cases.test.ts @@ -215,9 +215,9 @@ describe('GenerativeAiUseCases', () => { const params = processedStackInputSchema.parse({ ...stackInput, - agentCoreNetworkType: 'PRIVATE', agentCoreVpcId: 'vpc-12345678', agentCoreSubnetIds: ['subnet-12345678', 'subnet-87654321'], + isAgentCoreNetworkPrivate: true, }); const { From ae3953f3d40267c5b1994096a4764873e2e6a16a Mon Sep 17 00:00:00 2001 From: Yuki Nakashima Date: Wed, 3 Dec 2025 14:49:16 +0900 Subject: [PATCH 6/7] fix: AgentCore VPC validation and remove agentCoreNetworkType parameter - Fix validation logic to properly handle null values for agentCoreSubnetIds - Remove agentCoreNetworkType parameter (network mode is now auto-detected) - Update documentation to reflect the simplified VPC configuration - Add isAgentCoreNetworkPrivate computed property --- docs/en/DEPLOY_OPTION.md | 6 +++--- docs/ja/DEPLOY_OPTION.md | 6 +++--- docs/ko/DEPLOY_OPTION.md | 6 +++--- packages/cdk/lib/stack-input.ts | 5 +++-- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/docs/en/DEPLOY_OPTION.md b/docs/en/DEPLOY_OPTION.md index 58e5a17ed..ade372295 100644 --- a/docs/en/DEPLOY_OPTION.md +++ b/docs/en/DEPLOY_OPTION.md @@ -773,10 +773,12 @@ Network settings apply to both Generic Runtime and AgentBuilder Runtime. When using VPC mode, configure the following parameters: -- `agentCoreNetworkType`: Set to `PRIVATE` - `agentCoreVpcId`: VPC ID to use - `agentCoreSubnetIds`: List of subnet IDs to use +> [!NOTE] +> When both `agentCoreVpcId` and `agentCoreSubnetIds` are configured, AgentCore Runtime will be deployed in private network mode. If both are left unset (`null`), it will be deployed in public network mode. + > [!IMPORTANT] > **Availability Zone (AZ) Support**: AgentCore Runtime has limited supported AZs per region. Subnets must be placed within supported AZs. For details, please refer to the [AWS official documentation](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agentcore-vpc.html#agentcore-supported-azs). > @@ -790,7 +792,6 @@ const envs: Record> = { dev: { createGenericAgentCoreRuntime: true, agentBuilderEnabled: true, - agentCoreNetworkType: 'PRIVATE', agentCoreVpcId: 'vpc-xxxxxxxxx', agentCoreSubnetIds: ['subnet-xxxxxxxxx', 'subnet-yyyyyyyyy'], }, @@ -805,7 +806,6 @@ const envs: Record> = { "context": { "createGenericAgentCoreRuntime": true, "agentBuilderEnabled": true, - "agentCoreNetworkType": "PRIVATE", "agentCoreVpcId": "vpc-xxxxxxxxx", "agentCoreSubnetIds": ["subnet-xxxxxxxxx", "subnet-yyyyyyyyy"] } diff --git a/docs/ja/DEPLOY_OPTION.md b/docs/ja/DEPLOY_OPTION.md index 9bd97f9fb..99ef5f450 100644 --- a/docs/ja/DEPLOY_OPTION.md +++ b/docs/ja/DEPLOY_OPTION.md @@ -788,10 +788,12 @@ AgentCore Runtime は以下のネットワークモードで動作できます VPC モードを使用する場合は、以下のパラメータを設定してください: -- `agentCoreNetworkType`: `PRIVATE` に設定 - `agentCoreVpcId`: 使用するVPCのID - `agentCoreSubnetIds`: 使用するサブネットのIDリスト +> [!NOTE] +> `agentCoreVpcId`と`agentCoreSubnetIds`を両方設定すると、AgentCore Runtimeはプライベートネットワークモードでデプロイされます。両方とも未設定(`null`)の場合は、パブリックネットワークモードでデプロイされます。 + > [!IMPORTANT] > **Availability Zone (AZ) サポート**: AgentCore RuntimeはリージョンごとにサポートされているAZが限定されています。サブネットは必ずサポートされているAZ内に配置してください。詳細は[AWS公式ドキュメント](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agentcore-vpc.html#agentcore-supported-azs)をご確認ください。 > @@ -805,7 +807,6 @@ const envs: Record> = { dev: { createGenericAgentCoreRuntime: true, agentBuilderEnabled: true, - agentCoreNetworkType: 'PRIVATE', agentCoreVpcId: 'vpc-xxxxxxxxx', agentCoreSubnetIds: ['subnet-xxxxxxxxx', 'subnet-yyyyyyyyy'], }, @@ -820,7 +821,6 @@ const envs: Record> = { "context": { "createGenericAgentCoreRuntime": true, "agentBuilderEnabled": true, - "agentCoreNetworkType": "PRIVATE", "agentCoreVpcId": "vpc-xxxxxxxxx", "agentCoreSubnetIds": ["subnet-xxxxxxxxx", "subnet-yyyyyyyyy"] } diff --git a/docs/ko/DEPLOY_OPTION.md b/docs/ko/DEPLOY_OPTION.md index 6f0516f9f..2e6a547cf 100644 --- a/docs/ko/DEPLOY_OPTION.md +++ b/docs/ko/DEPLOY_OPTION.md @@ -772,10 +772,12 @@ AgentCore Runtime은 다음 네트워크 모드에서 작동할 수 있습니다 VPC 모드를 사용할 때는 다음 매개변수를 설정하세요: -- `agentCoreNetworkType`: `PRIVATE`로 설정 - `agentCoreVpcId`: 사용할 VPC ID - `agentCoreSubnetIds`: 사용할 서브넷 ID 목록 +> [!NOTE] +> `agentCoreVpcId`와 `agentCoreSubnetIds`를 모두 설정하면 AgentCore Runtime이 프라이빗 네트워크 모드로 배포됩니다. 둘 다 미설정(`null`)인 경우 퍼블릭 네트워크 모드로 배포됩니다. + > [!IMPORTANT] > **가용 영역(AZ) 지원**: AgentCore Runtime은 리전별로 지원되는 AZ가 제한되어 있습니다. 서브넷은 반드시 지원되는 AZ 내에 배치해야 합니다. 자세한 내용은 [AWS 공식 문서](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agentcore-vpc.html#agentcore-supported-azs)를 확인하세요. > @@ -789,7 +791,6 @@ const envs: Record> = { dev: { createGenericAgentCoreRuntime: true, agentBuilderEnabled: true, - agentCoreNetworkType: 'PRIVATE', agentCoreVpcId: 'vpc-xxxxxxxxx', agentCoreSubnetIds: ['subnet-xxxxxxxxx', 'subnet-yyyyyyyyy'], }, @@ -804,7 +805,6 @@ const envs: Record> = { "context": { "createGenericAgentCoreRuntime": true, "agentBuilderEnabled": true, - "agentCoreNetworkType": "PRIVATE", "agentCoreVpcId": "vpc-xxxxxxxxx", "agentCoreSubnetIds": ["subnet-xxxxxxxxx", "subnet-yyyyyyyyy"] } diff --git a/packages/cdk/lib/stack-input.ts b/packages/cdk/lib/stack-input.ts index 16ab071d3..6946517a0 100644 --- a/packages/cdk/lib/stack-input.ts +++ b/packages/cdk/lib/stack-input.ts @@ -223,8 +223,9 @@ export const stackInputSchema = baseStackInputSchema (data) => { // Validate AgentCore VPC configuration consistency const hasVpcId = !!data.agentCoreVpcId; - const hasSubnetIds = - data.agentCoreSubnetIds && data.agentCoreSubnetIds.length > 0; + const hasSubnetIds = !!( + data.agentCoreSubnetIds && data.agentCoreSubnetIds.length > 0 + ); // Both must be provided or both must be empty return hasVpcId === hasSubnetIds; From ccd25c36f3ee7a29b6d01e3f71533531b55f8456 Mon Sep 17 00:00:00 2001 From: Yuki Nakashima Date: Wed, 3 Dec 2025 15:51:38 +0900 Subject: [PATCH 7/7] test: update snapshot for Claude Opus 4.5 model addition --- .../test/__snapshots__/generative-ai-use-cases.test.ts.snap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cdk/test/__snapshots__/generative-ai-use-cases.test.ts.snap b/packages/cdk/test/__snapshots__/generative-ai-use-cases.test.ts.snap index 308bc2404..e5c11b73d 100644 --- a/packages/cdk/test/__snapshots__/generative-ai-use-cases.test.ts.snap +++ b/packages/cdk/test/__snapshots__/generative-ai-use-cases.test.ts.snap @@ -146,7 +146,7 @@ exports[`GenerativeAiUseCases matches the snapshot (AgentCore with VPC) 1`] = ` "Ref": "GenericAgentCoreAgentCoreFileBucket0430DA42", }, "MCP_SERVERS": "{"time":{"command":"uvx","args":["mcp-server-time"],"metadata":{"category":"Utility","description":"Provides current time and date functionality"}},"aws-knowledge-mcp-server":{"command":"npx","args":["mcp-remote","https://knowledge-mcp.global.api.aws"],"metadata":{"category":"AWS","description":"AWS Knowledge Base MCP server for enterprise knowledge access"}},"awslabs.aws-documentation-mcp-server":{"command":"uvx","args":["awslabs.aws-documentation-mcp-server@latest"],"metadata":{"category":"AWS","description":"Access AWS documentation and guides"}},"awslabs.cdk-mcp-server":{"command":"uvx","args":["awslabs.cdk-mcp-server@latest"],"metadata":{"category":"AWS","description":"AWS CDK code generation and assistance"}},"awslabs.aws-diagram-mcp-server":{"command":"uvx","args":["awslabs.aws-diagram-mcp-server@latest"],"metadata":{"category":"AWS","description":"Generate AWS architecture diagrams"}},"awslabs.nova-canvas-mcp-server":{"command":"uvx","args":["awslabs.nova-canvas-mcp-server@latest"],"env":{"AWS_REGION":"us-east-1"},"metadata":{"category":"AI/ML","description":"Amazon Nova Canvas image generation"}},"tavily-search":{"command":"npx","args":["-y","mcp-remote","https://mcp.tavily.com/mcp/?tavilyApiKey="],"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}}}", - "SUPPORTED_CACHE_FIELDS": "{"anthropic.claude-sonnet-4-5-20250929-v1:0":["messages","system","tools"],"anthropic.claude-haiku-4-5-20251001-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-1-20250805-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-sonnet-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-3-7-sonnet-20250219-v1:0":["messages","system","tools"],"anthropic.claude-3-5-haiku-20241022-v1:0":["messages","system","tools"],"amazon.nova-premier-v1:0":["messages","system"],"amazon.nova-pro-v1:0":["messages","system"],"amazon.nova-lite-v1:0":["messages","system"],"amazon.nova-micro-v1:0":["messages","system"]}", + "SUPPORTED_CACHE_FIELDS": "{"anthropic.claude-opus-4-5-20251101-v1:0":["messages","system","tools"],"anthropic.claude-sonnet-4-5-20250929-v1:0":["messages","system","tools"],"anthropic.claude-haiku-4-5-20251001-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-1-20250805-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-sonnet-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-3-7-sonnet-20250219-v1:0":["messages","system","tools"],"anthropic.claude-3-5-haiku-20241022-v1:0":["messages","system","tools"],"amazon.nova-premier-v1:0":["messages","system"],"amazon.nova-pro-v1:0":["messages","system"],"amazon.nova-lite-v1:0":["messages","system"],"amazon.nova-micro-v1:0":["messages","system"]}", }, "NetworkConfiguration": { "NetworkMode": "VPC", @@ -459,7 +459,7 @@ exports[`GenerativeAiUseCases matches the snapshot (AgentCore with VPC) 1`] = ` "Ref": "GenericAgentCoreAgentCoreFileBucket0430DA42", }, "MCP_SERVERS": "{"time":{"command":"uvx","args":["mcp-server-time"],"metadata":{"category":"Utility","description":"Provides current time and date functionality"}},"aws-knowledge-mcp-server":{"command":"npx","args":["mcp-remote","https://knowledge-mcp.global.api.aws"],"metadata":{"category":"AWS","description":"AWS Knowledge Base MCP server for enterprise knowledge access"}},"awslabs.aws-documentation-mcp-server":{"command":"uvx","args":["awslabs.aws-documentation-mcp-server@latest"],"metadata":{"category":"AWS","description":"Access AWS documentation and guides"}},"awslabs.cdk-mcp-server":{"command":"uvx","args":["awslabs.cdk-mcp-server@latest"],"metadata":{"category":"AWS","description":"AWS CDK code generation and assistance"}},"awslabs.aws-diagram-mcp-server":{"command":"uvx","args":["awslabs.aws-diagram-mcp-server@latest"],"metadata":{"category":"AWS","description":"Generate AWS architecture diagrams"}},"awslabs.nova-canvas-mcp-server":{"command":"uvx","args":["awslabs.nova-canvas-mcp-server@latest"],"env":{"AWS_REGION":"us-east-1"},"metadata":{"category":"AI/ML","description":"Amazon Nova Canvas image generation"}},"tavily-search":{"command":"npx","args":["-y","mcp-remote","https://mcp.tavily.com/mcp/?tavilyApiKey="],"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}}}", - "SUPPORTED_CACHE_FIELDS": "{"anthropic.claude-sonnet-4-5-20250929-v1:0":["messages","system","tools"],"anthropic.claude-haiku-4-5-20251001-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-1-20250805-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-sonnet-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-3-7-sonnet-20250219-v1:0":["messages","system","tools"],"anthropic.claude-3-5-haiku-20241022-v1:0":["messages","system","tools"],"amazon.nova-premier-v1:0":["messages","system"],"amazon.nova-pro-v1:0":["messages","system"],"amazon.nova-lite-v1:0":["messages","system"],"amazon.nova-micro-v1:0":["messages","system"]}", + "SUPPORTED_CACHE_FIELDS": "{"anthropic.claude-opus-4-5-20251101-v1:0":["messages","system","tools"],"anthropic.claude-sonnet-4-5-20250929-v1:0":["messages","system","tools"],"anthropic.claude-haiku-4-5-20251001-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-1-20250805-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-sonnet-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-3-7-sonnet-20250219-v1:0":["messages","system","tools"],"anthropic.claude-3-5-haiku-20241022-v1:0":["messages","system","tools"],"amazon.nova-premier-v1:0":["messages","system"],"amazon.nova-pro-v1:0":["messages","system"],"amazon.nova-lite-v1:0":["messages","system"],"amazon.nova-micro-v1:0":["messages","system"]}", }, "NetworkConfiguration": { "NetworkMode": "VPC",