44import software .amazon .awscdk .Stack ;
55import software .amazon .awscdk .StackProps ;
66import software .amazon .awscdk .services .ecr .Repository ;
7+ import software .amazon .awscdk .services .iam .ManagedPolicy ;
78import software .constructs .Construct ;
89import sample .com .constructs .*;
910import sample .com .constructs .Ide .IdeProps ;
@@ -69,19 +70,54 @@ public WorkshopStack(final Construct scope, final String id, final StackProps pr
6970 .build ();
7071 Ide ide = new Ide (this , "Ide" , ideProps );
7172
73+ // CodeBuild for workshop setup (service-linked role creation)
74+ new CodeBuild (this , "CodeBuild" ,
75+ CodeBuild .CodeBuildProps .builder ()
76+ .projectName (prefix + "-setup" )
77+ .vpc (vpc .getVpc ())
78+ .environmentVariables (Map .of (
79+ "TEMPLATE_TYPE" , templateType ,
80+ "GIT_BRANCH" , gitBranch ))
81+ .buildSpec (buildSpec )
82+ .build ());
83+
84+ // Shared workshop bucket
85+ WorkshopBucket workshopBucket = new WorkshopBucket (this , "WorkshopBucket" ,
86+ WorkshopBucket .WorkshopBucketProps .builder ()
87+ .prefix (prefix )
88+ .build ());
89+
90+ // ECR Registry settings (Repository Creation Template for create-on-push)
91+ new EcrRegistry (this , "EcrRegistry" ,
92+ EcrRegistry .EcrRegistryProps .builder ()
93+ .prefix (prefix )
94+ .build ());
95+
96+ // Bedrock logging role (for model invocation logging to CloudWatch)
97+ software .amazon .awscdk .services .iam .Role .Builder .create (this , "BedrockLoggingRole" )
98+ .roleName (prefix + "-bedrock-logging-role" )
99+ .assumedBy (software .amazon .awscdk .services .iam .ServicePrincipal .Builder .create ("bedrock.amazonaws.com" )
100+ .conditions (java .util .Map .of (
101+ "StringEquals" , java .util .Map .of ("aws:SourceAccount" , this .getAccount ()),
102+ "ArnLike" , java .util .Map .of ("aws:SourceArn" , "arn:aws:bedrock:" + this .getRegion () + ":" + this .getAccount () + ":*" )
103+ ))
104+ .build ())
105+ .description ("Role for Bedrock model invocation logging to CloudWatch" )
106+ .inlinePolicies (java .util .Map .of ("BedrockLogging" ,
107+ software .amazon .awscdk .services .iam .PolicyDocument .Builder .create ()
108+ .statements (java .util .List .of (
109+ software .amazon .awscdk .services .iam .PolicyStatement .Builder .create ()
110+ .effect (software .amazon .awscdk .services .iam .Effect .ALLOW )
111+ .actions (java .util .List .of ("logs:CreateLogStream" , "logs:PutLogEvents" ))
112+ .resources (java .util .List .of ("arn:aws:logs:" + this .getRegion () + ":" + this .getAccount () + ":log-group:/aws/bedrock/*" ))
113+ .build ()
114+ ))
115+ .build ()
116+ ))
117+ .build ();
118+
72119 // Full template resources (java-on-aws-immersion-day, java-on-amazon-eks, java-spring-ai-agents)
73120 if (isFullTemplate ) {
74- // CodeBuild for workshop setup (service-linked role creation)
75- new CodeBuild (this , "CodeBuild" ,
76- CodeBuild .CodeBuildProps .builder ()
77- .projectName (prefix + "-setup" )
78- .vpc (vpc .getVpc ())
79- .environmentVariables (Map .of (
80- "TEMPLATE_TYPE" , templateType ,
81- "GIT_BRANCH" , gitBranch ))
82- .buildSpec (buildSpec )
83- .build ());
84-
85121 // Database
86122 Database database = new Database (this , "Database" , Database .DatabaseProps .builder ()
87123 .prefix (prefix )
@@ -96,18 +132,6 @@ public WorkshopStack(final Construct scope, final String id, final StackProps pr
96132 .ideInternalSecurityGroup (ide .getIdeInternalSecurityGroup ())
97133 .build ());
98134
99- // Shared workshop bucket
100- WorkshopBucket workshopBucket = new WorkshopBucket (this , "WorkshopBucket" ,
101- WorkshopBucket .WorkshopBucketProps .builder ()
102- .prefix (prefix )
103- .build ());
104-
105- // ECR Registry settings (Repository Creation Template for create-on-push)
106- new EcrRegistry (this , "EcrRegistry" ,
107- EcrRegistry .EcrRegistryProps .builder ()
108- .prefix (prefix )
109- .build ());
110-
111135 // Unicorn construct: EventBus, Roles, DB Setup (uses unicorn* naming for workshop content compatibility)
112136 Unicorn unicorn = new Unicorn (this , "Unicorn" , Unicorn .UnicornProps .builder ()
113137 .vpc (vpc .getVpc ())
@@ -144,6 +168,40 @@ public WorkshopStack(final Construct scope, final String id, final StackProps pr
144168
145169 // java-spring-ai-agents specific resources
146170 if (isSpringAi ) {
171+ // AI Agent EKS Pod Identity role with Bedrock access
172+ software .amazon .awscdk .services .iam .Role aiAgentEksRole = software .amazon .awscdk .services .iam .Role .Builder .create (this , "AiAgentEksRole" )
173+ .roleName ("ai-agent-eks-pod-role" )
174+ .assumedBy (software .amazon .awscdk .services .iam .ServicePrincipal .Builder .create ("pods.eks.amazonaws.com" ).build ())
175+ .description ("EKS Pod Identity role for AI Agent with Bedrock access" )
176+ .managedPolicies (java .util .List .of (
177+ ManagedPolicy .fromAwsManagedPolicyName ("AmazonBedrockFullAccess" )
178+ ))
179+ .build ();
180+ // Add sts:TagSession for Pod Identity
181+ aiAgentEksRole .getAssumeRolePolicy ().addStatements (
182+ software .amazon .awscdk .services .iam .PolicyStatement .Builder .create ()
183+ .effect (software .amazon .awscdk .services .iam .Effect .ALLOW )
184+ .principals (java .util .List .of (software .amazon .awscdk .services .iam .ServicePrincipal .Builder .create ("pods.eks.amazonaws.com" ).build ()))
185+ .actions (java .util .List .of ("sts:TagSession" ))
186+ .build ()
187+ );
188+ // Grant DB secrets access (same as unicornstore-eks-pod-role)
189+ database .grantSecretsRead (aiAgentEksRole );
190+
191+ // AI Agent Lambda execution role with Bedrock access
192+ software .amazon .awscdk .services .iam .Role aiAgentLambdaRole = software .amazon .awscdk .services .iam .Role .Builder .create (this , "AiAgentLambdaRole" )
193+ .roleName ("ai-agent-lambda-role" )
194+ .assumedBy (software .amazon .awscdk .services .iam .ServicePrincipal .Builder .create ("lambda.amazonaws.com" ).build ())
195+ .description ("Lambda execution role for AI Agent with Bedrock access" )
196+ .managedPolicies (java .util .List .of (
197+ ManagedPolicy .fromAwsManagedPolicyName ("service-role/AWSLambdaBasicExecutionRole" ),
198+ ManagedPolicy .fromAwsManagedPolicyName ("service-role/AWSLambdaVPCAccessExecutionRole" ),
199+ ManagedPolicy .fromAwsManagedPolicyName ("AmazonBedrockFullAccess" )
200+ ))
201+ .build ();
202+ // Grant DB secrets access
203+ database .grantSecretsRead (aiAgentLambdaRole );
204+
147205 // CodeBuild to push placeholder images (ECS Express needs images at deploy time)
148206 // ECR repos are created automatically via create-on-push (EcrRegistry)
149207 String placeholderBuildSpec = """
@@ -168,10 +226,10 @@ public WorkshopStack(final Construct scope, final String id, final StackProps pr
168226
169227 # Build and push placeholder to both repos (create-on-push creates repos)
170228 docker build -t placeholder /tmp
171- docker tag placeholder $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/unicorn-spring- ai-agent:latest
172- docker tag placeholder $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/unicorn-store-spring :latest
173- docker push $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/unicorn-spring-ai-agent :latest
174- docker push $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/unicorn-store-spring:latest
229+ for REPO in ai-agent mcp-server1; do
230+ docker tag placeholder $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$REPO :latest
231+ docker push $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$REPO :latest
232+ done
175233 """ ;
176234
177235 CodeBuild placeholderImageBuild = new CodeBuild (this , "PlaceholderImageBuild" ,
@@ -184,23 +242,24 @@ public WorkshopStack(final Construct scope, final String id, final StackProps pr
184242 .buildSpec (placeholderBuildSpec )
185243 .build ());
186244
187- // ECS Express Service for Spring AI Agent
188- new EcsExpressService (this , "SpringAiAgent " ,
245+ // ECS Express Service for AI Agent
246+ new EcsExpressService (this , "AiAgent " ,
189247 EcsExpressService .EcsExpressServiceProps .builder ()
190- .appName ("unicorn-spring- ai-agent" )
248+ .appName ("ai-agent" )
191249 .vpc (vpc .getVpc ())
192250 .database (database )
193- .unicorn (unicorn )
251+ .configureTaskRole (role ->
252+ role .addManagedPolicy (ManagedPolicy .fromAwsManagedPolicyName ("AmazonBedrockFullAccess" )))
194253 .dependsOn (placeholderImageBuild .getCustomResource ())
195254 .build ());
196255
197256 // ECS Express Service for MCP Server
198257 new EcsExpressService (this , "McpServer" ,
199258 EcsExpressService .EcsExpressServiceProps .builder ()
200- .appName ("unicorn-store-spring " )
259+ .appName ("mcp-server1 " )
201260 .vpc (vpc .getVpc ())
202261 .database (database )
203- .unicorn ( unicorn )
262+ .configureTaskRole ( role -> unicorn . getEventBus (). grantPutEventsTo ( role ) )
204263 .dependsOn (placeholderImageBuild .getCustomResource ())
205264 .build ());
206265 }
0 commit comments