@@ -22,31 +22,28 @@ infra/
2222│ │ │ ├── Eks.java
2323│ │ │ ├── Database.java
2424│ │ │ ├── CodeBuild.java
25+ │ │ │ ├── Lambda.java
2526│ │ │ └── Roles.java
26- │ │ ├── stacks/
27- │ │ │ └── WorkshopStack.java
28- │ │ └── WorkshopApp.java # Main CDK application
27+ │ │ ├── WorkshopStack.java # Main stack
28+ │ │ └── WorkshopApp.java # Main CDK application
29+ │ ├── src/main/resources/
30+ │ │ └── ec2-userdata.sh # Minimal UserData script (embedded in CDK)
2931│ ├── pom.xml
3032│ └── cdk.json
3133├── workshop-template.yaml # Generated unified CloudFormation template
3234├── scripts/
33- │ ├── workshops/ # Workshop-specific orchestration scripts
34- │ │ ├── ide.sh
35- │ │ ├── java-on-aws.sh
36- │ │ ├── java-on-eks.sh
37- │ │ ├── java-ai-agents.sh
38- │ │ └── java-spring-ai-agents.sh
39- │ ├── setup/ # Modular setup scripts
40- │ │ ├── base.sh
41- │ │ ├── eks.sh
42- │ │ ├── app.sh
43- │ │ ├── monitoring.sh
44- │ │ └── ai-agents.sh
35+ │ ├── ide/ # Modular IDE and workshop scripts
36+ │ │ ├── vscode.sh # VS Code installation and configuration
37+ │ │ ├── base.sh # Base development tools
38+ │ │ ├── java-on-aws.sh # Java-on-AWS workshop setup
39+ │ │ ├── java-on-eks.sh # Java-on-EKS workshop setup
40+ │ │ └── java-ai-agents.sh # Java AI Agents workshop setup
4541│ ├── lib/ # Common utilities
4642│ │ ├── common.sh
4743│ │ └── wait-for-resources.sh
48- │ ├── deploy/ # Deployment utilities
49- │ ├── test/ # Testing scripts
44+ │ ├── cfn/ # CloudFormation utilities
45+ │ │ ├── generate.sh
46+ │ │ └── sync.sh
5047│ └── cleanup/ # Cleanup scripts
5148└── package.json # Build automation
5249```
@@ -56,35 +53,30 @@ infra/
5653### CDK Components
5754
5855#### WorkshopStack
59- The main CDK stack that conditionally creates resources based on workshop type:
56+ The main CDK stack that conditionally creates resources based on template type:
6057
6158``` java
6259public class WorkshopStack extends Stack {
6360 public WorkshopStack (final Construct scope , final String id , final StackProps props ) {
6461 super (scope, id, props);
6562
66- String workshopType = System . getenv(" WORKSHOP_TYPE " );
67- if (workshopType == null ) {
68- workshopType = " ide " ; // default
63+ String templateType = System . getenv(" TEMPLATE_TYPE " );
64+ if (templateType == null ) {
65+ templateType = " base " ; // default
6966 }
7067
7168 // Core infrastructure (always created)
72- var roles = new Roles (this , " Roles" );
7369 var vpc = new Vpc (this , " Vpc" );
74- var ide = new Ide (this , " Ide" , vpc. getVpc(), roles );
70+ var ide = new Ide (this , " Ide" , vpc. getVpc());
7571
76- // Conditional resources based on workshop type
77- if (! " ide " . equals(workshopType) && ! " java-ai-agents " . equals(workshopType )) {
78- new Eks (this , " Eks " , vpc . getVpc(), roles );
72+ // Custom roles only for non-base templates
73+ if (! " base " . equals(templateType )) {
74+ var roles = new Roles (this , " Roles " );
7975 }
8076
81- if (! " ide" . equals(workshopType)) {
82- new Database (this , " Database" , vpc. getVpc());
83- }
84-
85- // CodeBuild for workshop setup
77+ // CodeBuild for service-linked role creation
8678 new CodeBuild (this , " CodeBuild" ,
87- Map . of(" STACK_NAME" , Aws . STACK_NAME , " WORKSHOP_TYPE " , workshopType ));
79+ Map . of(" STACK_NAME" , Aws . STACK_NAME , " TEMPLATE_TYPE " , templateType ));
8880 }
8981}
9082```
@@ -95,7 +87,7 @@ public class WorkshopStack extends Stack {
9587** Ide** : Creates VS Code IDE environment with necessary permissions
9688** Eks** : Creates EKS cluster with AutoMode
9789** Database** : Configures RDS instances and database schemas
98- ** CodeBuild** : Creates CodeBuild project for workshop setup automation
90+ ** CodeBuild** : Creates CodeBuild project for AWS service-linked role creation
9991** Roles** : Creates IAM roles and policies for workshop resources
10092
10193### Lambda Function Architecture
@@ -128,60 +120,148 @@ Instead of multiple Lambda functions, the new design uses:
128120| ` unicornstore-db-setup-lambda ` | ** Setup Scripts** | N/A | N/A | Moved to workshop setup scripts |
129121
130122#### External Resource Approach
131- The new design uses ** external files** for all complex scripts and code, loaded via CDK for better maintainability while preserving CloudFormation template compatibility through inline code generation. This approach eliminates hard-to-maintain inline code blocks.
123+ The new design uses ** external files** for all complex scripts and code, loaded via CDK for better maintainability while preserving CloudFormation template compatibility through inline code generation. This approach eliminates hard-to-maintain inline code blocks and provides a ** reusable Lambda construct ** for consistent function creation .
132124
133125#### External Resource Organization
134126```
135127infra/cdk/src/main/resources/
136- ├── launcher.py # EC2 instance launching with multi-AZ/instance-type failover
137- └── bootstrap.sh # EC2 User Data bootstrap script with CloudWatch logging
128+ ├── lambda/
129+ │ ├── ec2-launcher.py # EC2 instance launching with multi-AZ/instance-type failover
130+ │ ├── codebuild-start.py # CodeBuild project starter for workshop setup
131+ │ └── codebuild-report.py # CodeBuild completion reporter via EventBridge
132+ └── ec2-userdata.sh # Minimal UserData script (2.4KB) with CloudWatch logging
133+ ```
134+
135+ #### Reusable Lambda Construct
136+ ``` java
137+ public class Lambda extends Construct {
138+ public Lambda (final Construct scope , final String id , final LambdaProps props ) {
139+ super (scope, id);
140+
141+ Function . Builder . create(this , " Function" )
142+ .runtime(Runtime . PYTHON_3_13 )
143+ .handler(" index.lambda_handler" )
144+ .code(Code . fromInline(loadFile(props. getSourceFile())))
145+ .timeout(props. getTimeout())
146+ .functionName(props. getFunctionName())
147+ .role(props. getRole())
148+ .build();
149+ }
150+ }
138151```
139152
140153#### Usage in IDE Construct
141154``` java
142- // Create instance launcher Lambda loading from external file
143- var instanceLauncherFunction = Function . Builder . create(this , " IdeInstanceLauncherFunction" )
144- .runtime(Runtime . PYTHON_3_13 )
145- .handler(" index.lambda_handler" )
146- .code(Code . fromInline(loadFile(" /launcher.py" )))
147- .timeout(Duration . minutes(5 ))
148- .functionName(instanceName + " -launcher" )
149- .role(props. getLambdaRole())
150- .build();
155+ // Create EC2 launcher Lambda using reusable construct
156+ var launcherLambda = new Lambda (this , " LauncherLambda" ,
157+ Lambda . LambdaProps . builder()
158+ .sourceFile(" /lambda/ec2-launcher.py" )
159+ .functionName(instanceName + " -launcher" )
160+ .timeout(Duration . minutes(5 ))
161+ .role(props. getLambdaRole())
162+ .build());
151163
152164// Create User Data from external script with variable substitution
153165var userData = UserData . forLinux();
154- String bootstrapScript = loadFile(" /bootstrap .sh" )
166+ String bootstrapScript = loadFile(" /ec2-userdata .sh" )
155167 .replace(" ${stackName}" , Aws . STACK_NAME )
156168 .replace(" ${awsRegion}" , Aws . REGION )
157169 .replace(" ${idePassword}" , ideSecretsManagerPassword. secretValueFromJson(" password" ). unsafeUnwrap());
158170userData. addCommands(bootstrapScript. split(" \n " ));
159-
160- // Helper method for loading files
161- private String loadFile(String filePath) {
162- try {
163- return Files . readString(Path . of(getClass(). getResource(filePath). getPath()));
164- } catch (IOException e) {
165- throw new RuntimeException (" Failed to load file " + filePath, e);
166- }
167- }
168171```
169172
170173### Script Organization
171174
172- #### Convention-Based Script Discovery
173- Scripts are organized using a naming convention where the script name matches the stack name:
174- - ` ide.sh ` → executed for ide workshop type
175- - ` java-on-aws.sh ` → executed for java-on-aws workshop type
176- - ` java-on-eks.sh ` → executed for java-on-eks workshop type
177-
178- #### Modular Setup Scripts
179- Common functionality is extracted into reusable modules:
180- - ` base.sh ` : Common tools and AWS CLI configuration
181- - ` eks.sh ` : EKS cluster configuration and kubectl setup
182- - ` app.sh ` : Application deployment and configuration
183- - ` monitoring.sh ` : Observability stack setup
184- - ` ai-agents.sh ` : AI-specific setup for agent workshops
175+ #### Minimal UserData Architecture
176+ The new architecture uses minimal UserData (2.4KB) that downloads and executes a full bootstrap script, avoiding AWS UserData size limits:
177+
178+ ```
179+ infra/cdk/src/main/resources/
180+ └── ec2-userdata.sh # Minimal UserData script (2.4KB)
181+
182+ infra/scripts/ide/
183+ ├── bootstrap.sh # Full bootstrap script (3.8KB)
184+ ├── vscode.sh # VS Code installation and configuration
185+ ├── base.sh # Base development tools (foundational for all workshops)
186+ ├── java-on-aws.sh # calls base.sh + EKS/DB setup
187+ ├── java-on-eks.sh # calls base.sh + EKS setup
188+ └── java-ai-agents.sh # calls base.sh + AI setup
189+ ```
190+
191+ #### Bootstrap Flow
192+ ```
193+ ec2-userdata.sh → bootstrap.sh → vscode.sh → {workshop}.sh
194+ ```
195+
196+ Where:
197+ - ` ec2-userdata.sh ` : Minimal UserData script that downloads and runs bootstrap.sh with fallback URLs
198+ - ` bootstrap.sh ` : Full system setup, CloudWatch, environment variables, git clone, calls vscode.sh and template script
199+ - ` vscode.sh ` : Complete VS Code IDE setup (code-server, Caddy, configuration)
200+ - ` base.sh ` : Base development tools (for base template type)
201+ - Future template scripts will be added to ` /ide ` folder as needed
202+
203+ #### Configuration
204+ - ** Template Type** : Configurable via ` TEMPLATE_TYPE ` environment variable (defaults to ` base ` )
205+ - ** Git Branch** : Defined in code as ` "main" `
206+ - ** VS Code Version** : Uses latest version by default
207+
208+ #### Environment Variables
209+ ** Input (CDK reads):**
210+ - ` TEMPLATE_TYPE ` - determines template type (defaults to "base")
211+
212+ ** Output (CDK passes to scripts):**
213+ - ` STACK_NAME ` - AWS stack name
214+ - ` TEMPLATE_TYPE ` - template type
215+ - ` GIT_BRANCH ` - git branch (hardcoded to "main")
216+
217+ #### Tool Version Management
218+ The system uses a hybrid approach for tool versions:
219+
220+ ** Pinned Versions (Renovate Managed):**
221+ - Java: 25 (default, installs 8,17,21,25)
222+ - Node.js: 20 (LTS)
223+ - Maven: 3.9.11
224+ - kubectl: 1.34.2
225+ - Helm: 3.19.3 (v3.x for chart compatibility)
226+ - eksctl: 0.220.0
227+ - eks-node-viewer: 0.7.4
228+ - Docker Compose: 2.40.2
229+ - SOCI: 0.12.0
230+ - yq: 4.49.2
231+
232+ ** Latest Versions (Auto-updating):**
233+ - VS Code: latest
234+ - AWS SAM CLI: latest
235+ - Session Manager Plugin: latest
236+ - AWS CLI: latest
237+ - CDK: latest (npm global)
238+ - Artillery: latest (npm global)
239+ - k9s: latest (webinstall.dev)
240+ - e1s: latest (GitHub script)
241+
242+ ** System Packages (Repository Latest):**
243+ - jq, Docker, git, Caddy: latest available in package repositories
244+
245+ #### Script Architecture
246+ Scripts are organized with helper functions and consistent error handling:
247+
248+ ** Bootstrap Script (` ide-bootstrap.sh ` ):**
249+ - Standardized on ` dnf ` package manager
250+ - Added error handling for critical operations (AWS CLI, git clone, CloudFront)
251+ - Improved logging and comments
252+
253+ ** VS Code Script (` vscode.sh ` ):**
254+ - Helper functions eliminate repetitive ` sudo -u ec2-user ` patterns
255+ - ` setup_user_file() ` function for clean file creation
256+ - ` run_as_user() ` function for user command execution
257+ - Uses latest VS Code version by default
258+
259+ ** IDE Script (` ide.sh ` ):**
260+ - Function-based organization by tool category
261+ - Comprehensive logging with timestamps (` log_info() ` )
262+ - Error handling and download verification (` handle_error() ` , ` download_and_verify() ` )
263+ - Consistent output handling and cleanup
264+ - Removed redundant operations (multiple ` java -version ` calls)
185265
186266### Build Automation
187267
0 commit comments