@@ -8,8 +8,7 @@ import * as dynamodb from "aws-cdk-lib/aws-dynamodb"
88import * as apigateway from "aws-cdk-lib/aws-apigateway"
99import * as logs from "aws-cdk-lib/aws-logs"
1010import * as s3 from "aws-cdk-lib/aws-s3"
11- import * as agentcore from "@aws-cdk/aws-bedrock-agentcore-alpha"
12- import * as bedrockagentcore from "aws-cdk-lib/aws-bedrockagentcore"
11+ import * as agentcore from "aws-cdk-lib/aws-bedrockagentcore"
1312import { PythonFunction } from "@aws-cdk/aws-lambda-python-alpha"
1413import * as lambda from "aws-cdk-lib/aws-lambda"
1514import * as ecr_assets from "aws-cdk-lib/aws-ecr-assets"
@@ -20,30 +19,35 @@ import { AgentCoreRole } from "./utils/agentcore-role"
2019import * as path from "path"
2120import * as fs from "fs"
2221
23- export interface BackendStackProps extends cdk . NestedStackProps {
22+ export interface BackendConstructProps {
2423 config : AppConfig
2524 userPoolId : string
2625 userPoolClientId : string
2726 userPoolDomain : cognito . UserPoolDomain
2827 frontendUrl : string
2928}
3029
31- export class BackendStack extends cdk . NestedStack {
30+ export class BackendConstruct extends Construct {
3231 public readonly userPoolId : string
3332 public readonly userPoolClientId : string
3433 public readonly userPoolDomain : cognito . UserPoolDomain
3534 public feedbackApiUrl : string
3635 public runtimeArn : string
3736 public memoryArn : string
38- private agentName : cdk . CfnParameter
37+ private readonly region : string
38+ private readonly account : string
3939 private userPool : cognito . IUserPool
4040 private machineClient : cognito . UserPoolClient
4141 private machineClientSecret : secretsmanager . Secret
4242 private runtimeCredentialProvider : cdk . CustomResource
4343 private agentRuntime : agentcore . Runtime
4444
45- constructor ( scope : Construct , id : string , props : BackendStackProps ) {
46- super ( scope , id , props )
45+ constructor ( scope : Construct , id : string , props : BackendConstructProps ) {
46+ super ( scope , id )
47+
48+ const stack = cdk . Stack . of ( this )
49+ this . region = stack . region
50+ this . account = stack . account
4751
4852 // Store the Cognito values
4953 this . userPoolId = props . userPoolId
@@ -99,13 +103,6 @@ export class BackendStack extends cdk.NestedStack {
99103 private createAgentCoreRuntime ( config : AppConfig ) : void {
100104 const pattern = config . backend ?. pattern || "strands-single-agent"
101105
102- // Parameters
103- this . agentName = new cdk . CfnParameter ( this , "AgentName" , {
104- type : "String" ,
105- default : "FASTAgent" ,
106- description : "Name for the agent runtime" ,
107- } )
108-
109106 const stack = cdk . Stack . of ( this )
110107 const deploymentType = config . backend . deployment_type
111108
@@ -268,31 +265,26 @@ export class BackendStack extends cdk.NestedStack {
268265
269266 // Create memory resource with short-term memory (conversation history) as default
270267 // To enable long-term strategies (summaries, preferences, facts), see docs/MEMORY_INTEGRATION.md
271- const memory = new cdk . CfnResource ( this , "AgentMemory" , {
272- type : "AWS::BedrockAgentCore::Memory" ,
273- properties : {
274- Name : cdk . Names . uniqueResourceName ( this , { maxLength : 48 } ) ,
275- EventExpiryDuration : 30 ,
276- Description : `Short-term memory for ${ config . stack_name_base } agent` ,
277- MemoryStrategies : [
278- {
279- // Extracts and stores factual information shared by the user across sessions.
280- // Stored under /facts/{actorId} — retrieved on each turn to personalise responses.
281- SemanticMemoryStrategy : {
282- Name : "FactExtractor" ,
283- Namespaces : [ "/facts/{actorId}" ] ,
284- } ,
285- } ,
286- ] ,
287- MemoryExecutionRoleArn : agentRole . roleArn ,
288- Tags : {
289- Name : `${ config . stack_name_base } _Memory` ,
290- ManagedBy : "CDK" ,
291- } ,
268+ const memory = new agentcore . Memory ( this , "AgentMemory" , {
269+ memoryName : cdk . Names . uniqueResourceName ( this , { maxLength : 48 } ) ,
270+ expirationDuration : cdk . Duration . days ( 30 ) ,
271+ description : `Short-term memory for ${ config . stack_name_base } agent` ,
272+ memoryStrategies : [
273+ // Extracts and stores factual information shared by the user across sessions.
274+ // Stored under /facts/{actorId} — retrieved on each turn to personalise responses.
275+ agentcore . MemoryStrategy . usingSemantic ( {
276+ strategyName : "FactExtractor" ,
277+ namespaces : [ "/facts/{actorId}" ] ,
278+ } ) ,
279+ ] ,
280+ executionRole : agentRole ,
281+ tags : {
282+ Name : `${ config . stack_name_base } _Memory` ,
283+ ManagedBy : "CDK" ,
292284 } ,
293285 } )
294- const memoryId = memory . getAtt ( "MemoryId" ) . toString ( )
295- const memoryArn = memory . getAtt ( "MemoryArn" ) . toString ( )
286+ const memoryId = memory . memoryId
287+ const memoryArn = memory . memoryArn
296288
297289 // Store the memory ARN for access from main stack
298290 this . memoryArn = memoryArn
@@ -401,7 +393,7 @@ export class BackendStack extends cdk.NestedStack {
401393 // from RequestContext.request_headers, which is needed to securely extract the
402394 // user ID from the validated JWT token (sub claim) instead of trusting the payload body.
403395 this . agentRuntime = new agentcore . Runtime ( this , "Runtime" , {
404- runtimeName : `${ config . stack_name_base . replace ( / - / g, "_" ) } _${ this . agentName . valueAsString } ` ,
396+ runtimeName : `${ config . stack_name_base . replace ( / - / g, "_" ) } _${ config . backend . agent_name } ` ,
405397 agentRuntimeArtifact : agentRuntimeArtifact ,
406398 executionRole : agentRole ,
407399 networkConfiguration : networkConfiguration ,
@@ -739,7 +731,6 @@ export class BackendStack extends cdk.NestedStack {
739731
740732 // Load tool specification from JSON file
741733 const toolSpecPath = path . join ( __dirname , "../../gateway/tools/sample_tool/tool_spec.json" ) // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
742- const apiSpec = JSON . parse ( require ( "fs" ) . readFileSync ( toolSpecPath , "utf8" ) )
743734
744735 // Cognito OAuth2 configuration for gateway
745736 const cognitoIssuer = `https://cognito-idp.${ this . region } .amazonaws.com/${ this . userPool . userPoolId } `
@@ -837,54 +828,32 @@ export class BackendStack extends cdk.NestedStack {
837828 // Store for use in createAgentCoreRuntime()
838829 this . runtimeCredentialProvider = runtimeCredentialProvider
839830
840- // Create Gateway using L1 construct (CfnGateway)
841- // This replaces the Custom Resource approach with native CloudFormation support
842- const gateway = new bedrockagentcore . CfnGateway ( this , "AgentCoreGateway" , {
843- name : `${ config . stack_name_base } -gateway` ,
844- roleArn : gatewayRole . roleArn ,
845- protocolType : "MCP" ,
846- protocolConfiguration : {
847- mcp : {
848- supportedVersions : [ "2025-03-26" ] ,
849- // Optional: Enable semantic search for tools
850- // searchType: "SEMANTIC",
851- } ,
852- } ,
853- authorizerType : "CUSTOM_JWT" ,
854- authorizerConfiguration : {
855- customJwtAuthorizer : {
856- allowedClients : [ this . machineClient . userPoolClientId ] ,
857- discoveryUrl : cognitoDiscoveryUrl ,
858- } ,
859- } ,
831+ // Create Gateway using L2 construct
832+ const gateway = new agentcore . Gateway ( this , "AgentCoreGateway" , {
833+ gatewayName : `${ config . stack_name_base } -gateway` ,
834+ role : gatewayRole ,
835+ protocolConfiguration : new agentcore . McpProtocolConfiguration ( {
836+ supportedVersions : [ agentcore . MCPProtocolVersion . MCP_2025_03_26 ] ,
837+ } ) ,
838+ authorizerConfiguration : agentcore . GatewayAuthorizer . usingCustomJwt ( {
839+ discoveryUrl : cognitoDiscoveryUrl ,
840+ allowedClients : [ this . machineClient . userPoolClientId ] ,
841+ } ) ,
860842 description : "AgentCore Gateway with MCP protocol and JWT authentication" ,
861843 } )
862844
863- // Create Gateway Target using L1 construct (CfnGatewayTarget)
864- const gatewayTarget = new bedrockagentcore . CfnGatewayTarget ( this , "GatewayTarget" , {
865- gatewayIdentifier : gateway . attrGatewayIdentifier ,
866- name : "sample-tool-target" ,
845+ // Create Gateway Target using L2 addLambdaTarget().
846+ // This grants the gateway role invoke permission and adds the resource-based
847+ // Lambda permission the CreateGatewayTarget dry-run validation requires.
848+ const gatewayTarget = gateway . addLambdaTarget ( "GatewayTarget" , {
849+ gatewayTargetName : "sample-tool-target" ,
867850 description : "Sample tool Lambda target" ,
868- targetConfiguration : {
869- mcp : {
870- lambda : {
871- lambdaArn : toolLambda . functionArn ,
872- toolSchema : {
873- inlinePayload : apiSpec ,
874- } ,
875- } ,
876- } ,
877- } ,
878- credentialProviderConfigurations : [
879- {
880- credentialProviderType : "GATEWAY_IAM_ROLE" ,
881- } ,
882- ] ,
851+ lambdaFunction : toolLambda ,
852+ toolSchema : agentcore . ToolSchema . fromLocalAsset ( toolSpecPath ) ,
853+ // credentialProviderConfigurations defaults to [GatewayCredentialProvider.iamRole()]
883854 } )
884855
885856 // Ensure proper creation order
886- gatewayTarget . addDependency ( gateway )
887- gateway . node . addDependency ( toolLambda )
888857 gateway . node . addDependency ( this . machineClient )
889858 gateway . node . addDependency ( gatewayRole )
890859
@@ -965,15 +934,17 @@ export class BackendStack extends cdk.NestedStack {
965934 // Grant Lambda permissions to update the Gateway (attach/detach policy engine)
966935 // and read Gateway configuration for the update_gateway call.
967936 // iam:PassRole is required because update_gateway re-associates the Gateway's IAM role.
937+ // InvokeGateway is required for CreatePolicy/UpdatePolicy: policy validation calls.
968938 cedarPolicyLambda . addToRolePolicy (
969939 new iam . PolicyStatement ( {
970940 actions : [
971941 "bedrock-agentcore:UpdateGateway" ,
972942 "bedrock-agentcore:GetGateway" ,
943+ "bedrock-agentcore:InvokeGateway" ,
973944 "bedrock-agentcore:ManageResourceScopedPolicy" ,
974945 "bedrock-agentcore:ListGatewayTargets" ,
975946 ] ,
976- resources : [ gateway . attrGatewayArn ] ,
947+ resources : [ gateway . gatewayArn ] ,
977948 } )
978949 )
979950
@@ -1000,12 +971,12 @@ export class BackendStack extends cdk.NestedStack {
1000971 . filter ( ( line : string ) => ! line . trimStart ( ) . startsWith ( "//" ) )
1001972 . join ( "\n" )
1002973 . trim ( )
1003- . replaceAll ( "{{GATEWAY_ARN}}" , gateway . attrGatewayArn )
974+ . replaceAll ( "{{GATEWAY_ARN}}" , gateway . gatewayArn )
1004975
1005976 const cedarPolicy = new cdk . CustomResource ( this , "GatewayPolicy" , {
1006977 serviceToken : cedarPolicyProvider . serviceToken ,
1007978 properties : {
1008- GatewayIdentifier : gateway . attrGatewayIdentifier ,
979+ GatewayIdentifier : gateway . gatewayId ,
1009980 PolicyDocument : policyDocument ,
1010981 // Policy name format: {PolicyEngineName}_cp_{timestamp}
1011982 // The AgentCore API enforces a 48-character limit on policy names.
@@ -1020,28 +991,28 @@ export class BackendStack extends cdk.NestedStack {
1020991 // Store AgentCore Gateway URL in SSM for AgentCore Runtime access
1021992 new ssm . StringParameter ( this , "GatewayUrlParam" , {
1022993 parameterName : `/${ config . stack_name_base } /gateway_url` ,
1023- stringValue : gateway . attrGatewayUrl ,
994+ stringValue : gateway . gatewayUrl ! ,
1024995 description : "AgentCore Gateway URL" ,
1025996 } )
1026997
1027998 // Output gateway information
1028999 new cdk . CfnOutput ( this , "GatewayId" , {
1029- value : gateway . attrGatewayIdentifier ,
1000+ value : gateway . gatewayId ,
10301001 description : "AgentCore Gateway ID" ,
10311002 } )
10321003
10331004 new cdk . CfnOutput ( this , "GatewayUrl" , {
1034- value : gateway . attrGatewayUrl ,
1005+ value : gateway . gatewayUrl ! ,
10351006 description : "AgentCore Gateway URL" ,
10361007 } )
10371008
10381009 new cdk . CfnOutput ( this , "GatewayArn" , {
1039- value : gateway . attrGatewayArn ,
1010+ value : gateway . gatewayArn ,
10401011 description : "AgentCore Gateway ARN" ,
10411012 } )
10421013
10431014 new cdk . CfnOutput ( this , "GatewayTargetId" , {
1044- value : gatewayTarget . ref ,
1015+ value : gatewayTarget . targetId ,
10451016 description : "AgentCore Gateway Target ID" ,
10461017 } )
10471018
0 commit comments