Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
48ee0ac
fix(infra): Address workshop security findings
Aug 20, 2026
e5a86df
chore(infra): Regenerate java-on-aws template
Aug 20, 2026
5897e80
fix(infra): Add ECS service principal to IAM trust policy
Aug 20, 2026
e631f47
feat(test): Add workshop script generator
Aug 21, 2026
76a6363
feat(infra): Add safe workshop cleanup
Aug 21, 2026
8c34f10
chore(infra): Update template references and CDK synthesis output
Aug 21, 2026
c351069
fix(scripts): Disable stack deletion and increase deployment timeouts
Aug 21, 2026
0cb86cf
feat(scripts): Enable stack deletion and enhance ECS deployment testing
Aug 22, 2026
8fd6669
fix(scripts): Add service stability check and improve deployment veri…
Aug 22, 2026
814e8ff
fix(scripts): Improve ECS task definition queries and update line num…
Aug 22, 2026
ff1de2e
fix(scripts): Improve SOCI index creation and add credential handling
Aug 22, 2026
7d0cf8d
fix(scripts): Add force flag to SOCI index creation
Aug 22, 2026
7294803
fix(scripts): Update ECS DaemonSet node selector and improve test output
Aug 22, 2026
6a0e5d6
feat(scripts): Add block progress tracking to workshop script generator
Aug 22, 2026
dd10d4b
fix(scripts): Update line number references in on-demand analysis blocks
Aug 22, 2026
0a0c78f
fix(scripts): Update line number references in workshop test script
Aug 22, 2026
d81b87d
fix(scripts): Refactor alert rule creation into reusable helper function
Aug 22, 2026
ece6a3f
fix(scripts): Replace read loop with awk for log tailing and update l…
Aug 22, 2026
fdf3551
fix(scripts): Add profiling config cleanup and update line numbers in…
Aug 22, 2026
fb05091
fix(scripts): Update Prometheus config to prevent duplicate scrape jobs
Aug 22, 2026
64ce89b
fix(scripts): Reorder ECS service detection and improve cluster filte…
Aug 22, 2026
bb15d69
fix(infra): Refine CloudFormation IAM policies and S3 bucket cleanup
Aug 23, 2026
dcae6b2
fix(scripts): Refine Grafana Pod Identity injection and CloudWatch da…
Aug 23, 2026
6b53b04
fix(scripts): Add analysis result polling and baseline tracking to wo…
Aug 23, 2026
178df7f
fix(scripts): Simplify analysis polling and baseline tracking logic
Aug 23, 2026
c60afe4
fix(scripts): Update holidays API endpoint and add schema validation
Aug 24, 2026
fac08b8
test(ws-test): Add Java AI agents runner
Aug 24, 2026
ea9bec8
fix(iam): Allow AgentCore identity secrets
Aug 24, 2026
a44563a
fix(iam): Refactor AgentCore IAM policies with granular resource scoping
Aug 25, 2026
732d32c
feat(java-spring-ai-agents): Restructure deployment scripts and updat…
Aug 25, 2026
b75f9e4
fix(infra): Refactor IAM policies and consolidate role management
Aug 25, 2026
f4e2795
fix(infra): Resolve AWS SDK version conflicts and enhance CodeBuild w…
Aug 25, 2026
b1cdf03
fix(infra): Add Checkov skip annotations for CodeBuild IAM policies
Aug 25, 2026
24824a0
fix(java-spring-ai-agents): Simplify deployment logging and enhance E…
Aug 25, 2026
604849d
fix(java-spring-ai-agents): Refactor ingress and DNS readiness checks
Aug 25, 2026
fb6256f
fix(java-spring-ai-agents): Standardize Docker image tags and runtime…
Aug 25, 2026
f78c3b4
fix(java-spring-ai-agents): Refactor variable declaration in test inv…
Aug 25, 2026
d8d41a7
fix(java-spring-ai-agents): Enhance test invocation with session IDs …
Aug 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,12 @@ build/

### Kiro ###
.kiro/debug/
semantic-review/

### Other

infrastructure/cdk/output*
dependency-reduced-pom.xml
infra/scripts/ws-test/reports/

.env
4 changes: 2 additions & 2 deletions apps/java-spring-ai-agents/aiagent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
</scm>
<properties>
<java.version>25</java.version>
<spring-ai.version>2.0.0</spring-ai.version>
<spring-ai.version>2.0.1</spring-ai.version>
</properties>
<dependencies>
<!-- Security - OAuth2 Resource Server for JWT validation -->
Expand Down Expand Up @@ -134,7 +134,7 @@
<dependency>
<groupId>org.springaicommunity</groupId>
<artifactId>spring-ai-agentcore-bom</artifactId>
<version>1.0.0</version>
<version>2.1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Expand Down
2 changes: 1 addition & 1 deletion apps/java-spring-ai-agents/backoffice/tools/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/
public class CurrencyHandler implements RequestHandler<Map<String, Object>, Map<String, Object>> {

private static final String FRANKFURTER_API = "https://api.frankfurter.app";
private static final String FRANKFURTER_API = "https://api.frankfurter.dev/v1";
private static final HttpClient httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
Expand Down
2 changes: 1 addition & 1 deletion apps/java-spring-ai-agents/demo-scripts/01-create.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ cd ~/environment/aiagent
if ! grep -q "spring-ai-agentcore-bom" pom.xml; then
sed -i '/<artifactId>spring-ai-bom<\/artifactId>/,/<\/dependency>/{
/<\/dependency>/a \
\t\t\t<dependency>\n\t\t\t\t<groupId>org.springaicommunity</groupId>\n\t\t\t\t<artifactId>spring-ai-agentcore-bom</artifactId>\n\t\t\t\t<version>1.0.0</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>
\t\t\t<dependency>\n\t\t\t\t<groupId>org.springaicommunity</groupId>\n\t\t\t\t<artifactId>spring-ai-agentcore-bom</artifactId>\n\t\t\t\t<version>2.1.0</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>
}' pom.xml
fi

Expand Down
11 changes: 11 additions & 0 deletions apps/java-spring-ai-agents/demo-scripts/02-memory.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ if ! grep -q "spring-ai-agentcore-memory" pom.xml; then
}' pom.xml
fi

# AgentCore Memory 2.1.0 and Spring AI 2.0.1 otherwise resolve
# incompatible AWS SDK modules (2.49.4 and 2.51.2 respectively).
# Use the same coherent AWS SDK version as the full deployed application.
if ! grep -A2 '<groupId>software.amazon.awssdk</groupId>' pom.xml \
| grep -q '<artifactId>bom</artifactId>'; then
sed -i '/<artifactId>spring-ai-agentcore-bom<\/artifactId>/,/<\/dependency>/{
/<\/dependency>/a \
\t\t\t<dependency>\n\t\t\t\t<groupId>software.amazon.awssdk</groupId>\n\t\t\t\t<artifactId>bom</artifactId>\n\t\t\t\t<version>2.46.20</version>\n\t\t\t\t<type>pom</type>\n\t\t\t\t<scope>import</scope>\n\t\t\t</dependency>
}' pom.xml
fi

# --- Add memory properties ---

if ! grep -q "agentcore.memory.memory-id" src/main/resources/application.properties; then
Expand Down
2 changes: 1 addition & 1 deletion apps/java-spring-ai-agents/demo-scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ Follow **STYLE.md** for all content formatting decisions.
## Technology Stack

- Spring Boot 4.1.0, Java 25
- Spring AI 2.0.0
- Spring AI 2.0.1
- Amazon Bedrock (Claude Sonnet 4.6, Claude Opus 4.6, Nova 2 Lite)
- Amazon Bedrock AgentCore (Runtime, Memory, Browser, Code Interpreter, Gateway)
- Amazon Cognito (JWT authentication)
Expand Down
9 changes: 7 additions & 2 deletions apps/java-spring-ai-agents/scripts/06-mcp-gateway.sh
Original file line number Diff line number Diff line change
Expand Up @@ -294,9 +294,9 @@ fi
if [ "${EXISTING_HOLIDAYS_TARGET}" = "None" ] || [ -z "${EXISTING_HOLIDAYS_TARGET}" ]; then
echo "Creating holidays target"

OPENAPI_SPEC=$(curl -s "https://date.nager.at/openapi/v3.json" | jq -c '
OPENAPI_SPEC=$(curl -fsSL "https://nagerholidays.com/openapi/community-v4.json" | jq -ce '
.openapi = "3.0.0" |
. + {servers: [{url: "https://date.nager.at"}]} |
. + {servers: [{url: "https://nagerholidays.com"}]} |
.paths |= with_entries(
.value |= with_entries(
.value.operationId = (.value.tags[0] // "api") + "_" + (.key | ascii_upcase) + "_" + (.value.summary | gsub("[^a-zA-Z0-9]"; "_") | .[0:30])
Expand All @@ -308,6 +308,11 @@ if [ "${EXISTING_HOLIDAYS_TARGET}" = "None" ] || [ -z "${EXISTING_HOLIDAYS_TARGE
else . end)
')

if ! jq -e '.openapi == "3.0.0" and (.paths | type == "object" and length > 0)' >/dev/null <<<"${OPENAPI_SPEC}"; then
echo "Failed to download a valid holidays OpenAPI schema" >&2
exit 1
fi

TARGET_CONFIG=$(jq -n --arg spec "${OPENAPI_SPEC}" \
'{mcp: {openApiSchema: {inlinePayload: $spec}}}')

Expand Down
139 changes: 136 additions & 3 deletions infra/cdk/src/main/java/sample/com/WorkshopStack.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
import software.amazon.awscdk.RemovalPolicy;
import software.amazon.awscdk.Stack;
import software.amazon.awscdk.StackProps;
import software.amazon.awscdk.Tags;
import software.amazon.awscdk.services.ecr.Repository;
import software.amazon.awscdk.services.iam.Effect;
import software.amazon.awscdk.services.iam.ManagedPolicy;
import software.amazon.awscdk.services.iam.PolicyStatement;
import software.constructs.Construct;
import sample.com.constructs.*;
import sample.com.constructs.Ide.IdeProps;
import java.util.List;
import java.util.Map;

public class WorkshopStack extends Stack {
Expand Down Expand Up @@ -46,6 +50,11 @@ public WorkshopStack(final Construct scope, final String id, final StackProps pr
templateType = "base"; // default
}

String workshopId = templateType;
Tags.of(this).add("WorkshopId", workshopId);
Tags.of(this).add("WorkshopDeploymentId", this.getStackId());
Tags.of(this).add("WorkshopOwner", "cloudformation");

// Configuration values - get current git branch from CDK context
String gitBranch = (String) this.getNode().tryGetContext("git.branch");
if (gitBranch == null) {
Expand All @@ -72,6 +81,7 @@ public WorkshopStack(final Construct scope, final String id, final StackProps pr
.vpc(vpc.getVpc())
.gitBranch(gitBranch)
.templateType(templateType)
.workshopId(workshopId)
.ideArch((isAiAgents || isAiAgentsAdvanced) ? Ide.IdeArch.ARM64 : Ide.IdeArch.X86_64_AMD)
.build();
Ide ide = new Ide(this, "Ide", ideProps);
Expand All @@ -84,6 +94,17 @@ public WorkshopStack(final Construct scope, final String id, final StackProps pr
.environmentVariables(Map.of(
"TEMPLATE_TYPE", templateType,
"GIT_BRANCH", gitBranch))
.rolePolicyStatements(List.of(PolicyStatement.Builder.create()
.effect(Effect.ALLOW)
.actions(List.of("iam:CreateServiceLinkedRole"))
.resources(List.of("arn:aws:iam::*:role/aws-service-role/*"))
.conditions(Map.of("StringEquals", Map.of("iam:AWSServiceName", List.of(
"ecs.amazonaws.com",
"elasticloadbalancing.amazonaws.com",
"network.bedrock-agentcore.amazonaws.com",
"runtime-identity.bedrock-agentcore.amazonaws.com"
))))
.build()))
.buildSpec(buildSpec)
.build());

Expand All @@ -94,9 +115,18 @@ public WorkshopStack(final Construct scope, final String id, final StackProps pr
.build());

// ECR Registry settings (Repository Creation Template for create-on-push)
List<String> ecrRepositoryNames = (isJavaOnAws || isEks)
? List.of("ai-jvm-analyzer", "perf-analyzer", "perf-collector")
: isSpringAi
? List.of("aiagent", "mcpserver")
: (isAiAgents || isAiAgentsAdvanced)
? List.of("aiagent", "backoffice")
: List.of();
EcrRegistry ecrRegistry = new EcrRegistry(this, "EcrRegistry",
EcrRegistry.EcrRegistryProps.builder()
.prefix(prefix)
.workshopId(workshopId)
.repositoryNames(ecrRepositoryNames)
.build());

// Bedrock logging role (for model invocation logging to CloudWatch)
Expand Down Expand Up @@ -200,18 +230,101 @@ public WorkshopStack(final Construct scope, final String id, final StackProps pr
.statements(java.util.List.of(
software.amazon.awscdk.services.iam.PolicyStatement.Builder.create()
.effect(software.amazon.awscdk.services.iam.Effect.ALLOW)
.actions(java.util.List.of("bedrock:*", "bedrock-agentcore:*"))
.actions(java.util.List.of(
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream"
))
.resources(java.util.List.of(
"arn:aws:bedrock:*::foundation-model/*",
"arn:aws:bedrock:*:" + this.getAccount() + ":inference-profile/*"
))
.build(),
software.amazon.awscdk.services.iam.PolicyStatement.Builder.create()
.effect(software.amazon.awscdk.services.iam.Effect.ALLOW)
.actions(java.util.List.of("bedrock:Retrieve", "bedrock:RetrieveAndGenerate"))
.resources(java.util.List.of("arn:aws:bedrock:*:" + this.getAccount() + ":knowledge-base/*"))
.build(),
software.amazon.awscdk.services.iam.PolicyStatement.Builder.create()
.effect(software.amazon.awscdk.services.iam.Effect.ALLOW)
.actions(java.util.List.of(
"bedrock-agentcore:CreateEvent",
"bedrock-agentcore:GetEvent",
"bedrock-agentcore:ListEvents",
"bedrock-agentcore:RetrieveMemoryRecords",
"bedrock-agentcore:GetWorkloadAccessToken",
"bedrock-agentcore:GetWorkloadAccessTokenForJWT",
"bedrock-agentcore:GetWorkloadAccessTokenForUserId",
"bedrock-agentcore:InvokeAgentRuntime",
"bedrock-agentcore:InvokeGateway"
))
.resources(java.util.List.of("arn:aws:bedrock-agentcore:*:" + this.getAccount() + ":*"))
.build(),
software.amazon.awscdk.services.iam.PolicyStatement.Builder.create()
.effect(software.amazon.awscdk.services.iam.Effect.ALLOW)
.actions(java.util.List.of(
"bedrock-agentcore:ConnectBrowserAutomationStream",
"bedrock-agentcore:ConnectBrowserLiveViewStream"
))
.resources(java.util.List.of("*"))
.build(),
software.amazon.awscdk.services.iam.PolicyStatement.Builder.create()
.effect(software.amazon.awscdk.services.iam.Effect.ALLOW)
.actions(java.util.List.of("ecr:*", "logs:*", "xray:*", "cloudwatch:*"))
.actions(java.util.List.of(
"bedrock-agentcore:GetBrowserSession",
"bedrock-agentcore:StartBrowserSession",
"bedrock-agentcore:StopBrowserSession",
"bedrock-agentcore:UpdateBrowserStream"
))
.resources(java.util.List.of("arn:aws:bedrock-agentcore:*:aws:browser/aws.browser.v1"))
.build(),
software.amazon.awscdk.services.iam.PolicyStatement.Builder.create()
.effect(software.amazon.awscdk.services.iam.Effect.ALLOW)
.actions(java.util.List.of(
"bedrock-agentcore:GetCodeInterpreterSession",
"bedrock-agentcore:InvokeCodeInterpreter",
"bedrock-agentcore:StartCodeInterpreterSession",
"bedrock-agentcore:StopCodeInterpreterSession"
))
.resources(java.util.List.of("arn:aws:bedrock-agentcore:*:aws:code-interpreter/aws.codeinterpreter.v1"))
.build(),
software.amazon.awscdk.services.iam.PolicyStatement.Builder.create()
.effect(software.amazon.awscdk.services.iam.Effect.ALLOW)
.actions(java.util.List.of("ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer"))
.resources(java.util.List.of("arn:aws:ecr:*:" + this.getAccount() + ":repository/aiagent"))
.build(),
software.amazon.awscdk.services.iam.PolicyStatement.Builder.create()
.effect(software.amazon.awscdk.services.iam.Effect.ALLOW)
.actions(java.util.List.of("ecr:GetAuthorizationToken"))
.resources(java.util.List.of("*"))
.build(),
software.amazon.awscdk.services.iam.PolicyStatement.Builder.create()
.effect(software.amazon.awscdk.services.iam.Effect.ALLOW)
.actions(java.util.List.of("aws-marketplace:Subscribe", "aws-marketplace:Unsubscribe", "aws-marketplace:ViewSubscriptions"))
.actions(java.util.List.of(
"logs:DescribeLogStreams",
"logs:CreateLogGroup",
"logs:PutResourcePolicy",
"logs:CreateLogStream",
"logs:PutLogEvents"
))
.resources(java.util.List.of("arn:aws:logs:*:" + this.getAccount() + ":log-group:/aws/bedrock-agentcore/runtimes/*"))
.build(),
software.amazon.awscdk.services.iam.PolicyStatement.Builder.create()
.effect(software.amazon.awscdk.services.iam.Effect.ALLOW)
.actions(java.util.List.of(
"logs:DescribeLogGroups",
"xray:PutTraceSegments",
"xray:PutTelemetryRecords",
"xray:GetSamplingRules",
"xray:GetSamplingTargets"
))
.resources(java.util.List.of("*"))
.build(),
software.amazon.awscdk.services.iam.PolicyStatement.Builder.create()
.effect(software.amazon.awscdk.services.iam.Effect.ALLOW)
.actions(java.util.List.of("cloudwatch:PutMetricData"))
.resources(java.util.List.of("*"))
.conditions(java.util.Map.of("StringEquals", java.util.Map.of(
"cloudwatch:namespace", "bedrock-agentcore")))
.build()
))
.build()
Expand Down Expand Up @@ -302,6 +415,25 @@ public WorkshopStack(final Construct scope, final String id, final StackProps pr
.privilegedMode(true)
.environmentVariables(Map.of(
"TEMPLATE_TYPE", templateType))
.rolePolicyStatements(List.of(
PolicyStatement.Builder.create()
.effect(Effect.ALLOW)
.actions(List.of("sts:GetCallerIdentity", "ecr:GetAuthorizationToken"))
.resources(List.of("*"))
.build(),
PolicyStatement.Builder.create()
.effect(Effect.ALLOW)
.actions(List.of(
"ecr:BatchCheckLayerAvailability",
"ecr:CompleteLayerUpload",
"ecr:GetDownloadUrlForLayer",
"ecr:InitiateLayerUpload",
"ecr:PutImage",
"ecr:UploadLayerPart"
))
.resources(List.of("arn:aws:ecr:" + this.getRegion() + ":" + this.getAccount() + ":repository/aiagent"))
.build()
))
.buildSpec(placeholderBuildSpec)
.dependencies(java.util.List.of(
vpc.getConcreteVpc(), // Ensures NAT Gateway is ready
Expand All @@ -328,6 +460,7 @@ public WorkshopStack(final Construct scope, final String id, final StackProps pr
CfnPreDeleteCleanup.CfnPreDeleteCleanupProps.builder()
.prefix(prefix)
.vpc(vpc.getVpc())
.buckets(java.util.List.of(workshopBucket.getBucket(), workshopBucket.getAccessLogBucket()))
.build());
}
}
Loading
Loading