@mavogel/cdk-vscode-server 0.0.62 → 0.0.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/.jsii +124 -178
  2. package/API.md +105 -200
  3. package/CLAUDE.md +288 -58
  4. package/README.md +40 -2
  5. package/assets/idle-monitor-enabler/idle-monitor-enabler.lambda/index.js +67 -0
  6. package/assets/installer/installer.lambda/index.js +67 -30
  7. package/awslint.json +5 -0
  8. package/examples/auto-stop/main.ts +1 -1
  9. package/examples/custom/main.ts +1 -1
  10. package/examples/git-repo/main.ts +30 -0
  11. package/integ-tests/integ.al2023.ts.snapshot/IntegSetupVSCodeOnAl2023DefaultTestDeployAssert74D8F645.assets.json +2 -2
  12. package/integ-tests/integ.al2023.ts.snapshot/IntegSetupVSCodeOnAl2023DefaultTestDeployAssert74D8F645.template.json +1 -1
  13. package/integ-tests/integ.al2023.ts.snapshot/IntegTestStackAl2023.assets.json +8 -8
  14. package/integ-tests/integ.al2023.ts.snapshot/IntegTestStackAl2023.template.json +257 -94
  15. package/integ-tests/{integ.stop-on-idle.ts.snapshot/asset.33da23274e25bd9f43638c5d83dad26e3931cbe78d462ffd9a9f565e948b4f5f.lambda → integ.al2023.ts.snapshot/asset.2f99f38311da357eaaea1284d67c759759324dec4a1cd11621d9c59eea9e81df.lambda}/index.js +67 -30
  16. package/integ-tests/{integ.ubuntu.ts.snapshot/asset.0ad50fc42afd768c3d0bfdd4701e43284fb077a25f19eea1e8c51a5ca36ebfe4 → integ.al2023.ts.snapshot/asset.efac30c7091c58fed492058fa6403c14f7e58aab8cf4fd595d838b8d5eeec2b9}/index.js +50 -25
  17. package/integ-tests/integ.al2023.ts.snapshot/integ.json +1 -1
  18. package/integ-tests/integ.al2023.ts.snapshot/manifest.json +19 -3
  19. package/integ-tests/integ.al2023.ts.snapshot/tree.json +1 -1
  20. package/integ-tests/integ.custom-domain.ts.snapshot/IntegSetupVSCodeOnCustomDomainDefaultTestDeployAssert6982D514.assets.json +2 -2
  21. package/integ-tests/integ.custom-domain.ts.snapshot/IntegSetupVSCodeOnCustomDomainDefaultTestDeployAssert6982D514.template.json +1 -1
  22. package/integ-tests/integ.custom-domain.ts.snapshot/IntegTestStackCustomDomain.assets.json +8 -8
  23. package/integ-tests/integ.custom-domain.ts.snapshot/IntegTestStackCustomDomain.template.json +273 -97
  24. package/integ-tests/{integ.ubuntu.ts.snapshot/asset.33da23274e25bd9f43638c5d83dad26e3931cbe78d462ffd9a9f565e948b4f5f.lambda → integ.custom-domain.ts.snapshot/asset.2f99f38311da357eaaea1284d67c759759324dec4a1cd11621d9c59eea9e81df.lambda}/index.js +67 -30
  25. package/integ-tests/{integ.al2023.ts.snapshot/asset.0ad50fc42afd768c3d0bfdd4701e43284fb077a25f19eea1e8c51a5ca36ebfe4 → integ.custom-domain.ts.snapshot/asset.efac30c7091c58fed492058fa6403c14f7e58aab8cf4fd595d838b8d5eeec2b9}/index.js +50 -25
  26. package/integ-tests/integ.custom-domain.ts.snapshot/integ.json +1 -1
  27. package/integ-tests/integ.custom-domain.ts.snapshot/manifest.json +25 -6
  28. package/integ-tests/integ.custom-domain.ts.snapshot/tree.json +1 -1
  29. package/integ-tests/integ.stop-on-idle.ts +1 -4
  30. package/integ-tests/integ.stop-on-idle.ts.snapshot/IntegStopOnIdleFunctionalityDefaultTestDeployAssertEECF3FC0.assets.json +2 -2
  31. package/integ-tests/integ.stop-on-idle.ts.snapshot/IntegStopOnIdleFunctionalityDefaultTestDeployAssertEECF3FC0.template.json +4 -4
  32. package/integ-tests/integ.stop-on-idle.ts.snapshot/IntegTestStackStopOnIdle.assets.json +23 -9
  33. package/integ-tests/integ.stop-on-idle.ts.snapshot/IntegTestStackStopOnIdle.template.json +758 -197
  34. package/integ-tests/integ.stop-on-idle.ts.snapshot/asset.22c8a6c357b704e370bef317ae1b52c59f684aa7640422a3d1dfe813d1f77853.lambda/index.js +67 -0
  35. package/integ-tests/{integ.custom-domain.ts.snapshot/asset.33da23274e25bd9f43638c5d83dad26e3931cbe78d462ffd9a9f565e948b4f5f.lambda → integ.stop-on-idle.ts.snapshot/asset.2f99f38311da357eaaea1284d67c759759324dec4a1cd11621d9c59eea9e81df.lambda}/index.js +67 -30
  36. package/integ-tests/integ.stop-on-idle.ts.snapshot/manifest.json +363 -76
  37. package/integ-tests/integ.stop-on-idle.ts.snapshot/tree.json +1 -1
  38. package/integ-tests/integ.ubuntu.ts.snapshot/IntegSetupVSCodeOnUbuntuDefaultTestDeployAssertFF8DF2C5.assets.json +2 -2
  39. package/integ-tests/integ.ubuntu.ts.snapshot/IntegSetupVSCodeOnUbuntuDefaultTestDeployAssertFF8DF2C5.template.json +1 -1
  40. package/integ-tests/integ.ubuntu.ts.snapshot/IntegTestStackUbuntu22.assets.json +8 -8
  41. package/integ-tests/integ.ubuntu.ts.snapshot/IntegTestStackUbuntu22.template.json +273 -97
  42. package/integ-tests/{integ.al2023.ts.snapshot/asset.33da23274e25bd9f43638c5d83dad26e3931cbe78d462ffd9a9f565e948b4f5f.lambda → integ.ubuntu.ts.snapshot/asset.2f99f38311da357eaaea1284d67c759759324dec4a1cd11621d9c59eea9e81df.lambda}/index.js +67 -30
  43. package/integ-tests/{integ.custom-domain.ts.snapshot/asset.0ad50fc42afd768c3d0bfdd4701e43284fb077a25f19eea1e8c51a5ca36ebfe4 → integ.ubuntu.ts.snapshot/asset.efac30c7091c58fed492058fa6403c14f7e58aab8cf4fd595d838b8d5eeec2b9}/index.js +50 -25
  44. package/integ-tests/integ.ubuntu.ts.snapshot/integ.json +1 -1
  45. package/integ-tests/integ.ubuntu.ts.snapshot/manifest.json +25 -6
  46. package/integ-tests/integ.ubuntu.ts.snapshot/tree.json +1 -1
  47. package/integ-tests/integ.ubuntu24.ts +69 -0
  48. package/integ-tests/integ.ubuntu24.ts.snapshot/IntegSetupVSCodeOnUbuntuDefaultTestDeployAssertFF8DF2C5.assets.json +33 -0
  49. package/integ-tests/integ.ubuntu24.ts.snapshot/IntegSetupVSCodeOnUbuntuDefaultTestDeployAssertFF8DF2C5.template.json +337 -0
  50. package/integ-tests/integ.ubuntu24.ts.snapshot/IntegTestStackUbuntu24.assets.json +118 -0
  51. package/integ-tests/integ.ubuntu24.ts.snapshot/IntegTestStackUbuntu24.template.json +2725 -0
  52. package/integ-tests/integ.ubuntu24.ts.snapshot/asset.2819175352ad1ce0dae768e83fc328fb70fb5f10b4a8ff0ccbcb791f02b0716d/index.js +1 -0
  53. package/integ-tests/integ.ubuntu24.ts.snapshot/asset.2f99f38311da357eaaea1284d67c759759324dec4a1cd11621d9c59eea9e81df.lambda/index.js +180 -0
  54. package/integ-tests/integ.ubuntu24.ts.snapshot/asset.530055f7515b3f0a47900f5df37e729ba40ca977b2d07b952bdefa2b8f883f42.bundle/index.js +30676 -0
  55. package/integ-tests/integ.ubuntu24.ts.snapshot/asset.781ab0ab74634cdaf61539ab208ab777829ef07097ac21f95b9e15a3b1eedc1b.lambda/index.js +57 -0
  56. package/integ-tests/integ.ubuntu24.ts.snapshot/asset.7fa1e366ee8a9ded01fc355f704cff92bfd179574e6f9cfee800a3541df1b200/__entrypoint__.js +1 -0
  57. package/integ-tests/integ.ubuntu24.ts.snapshot/asset.7fa1e366ee8a9ded01fc355f704cff92bfd179574e6f9cfee800a3541df1b200/index.js +1 -0
  58. package/integ-tests/integ.ubuntu24.ts.snapshot/asset.9d043014be736e8162bcc7ec5590cc6d2ff24fd0d9c73a5c5d595151c5fdad00/index.js +1 -0
  59. package/integ-tests/integ.ubuntu24.ts.snapshot/asset.bdc104ed9cab1b5b6421713c8155f0b753380595356f710400609664d3635eca/cfn-response.js +1 -0
  60. package/integ-tests/integ.ubuntu24.ts.snapshot/asset.bdc104ed9cab1b5b6421713c8155f0b753380595356f710400609664d3635eca/consts.js +1 -0
  61. package/integ-tests/integ.ubuntu24.ts.snapshot/asset.bdc104ed9cab1b5b6421713c8155f0b753380595356f710400609664d3635eca/framework.js +3 -0
  62. package/integ-tests/integ.ubuntu24.ts.snapshot/asset.bdc104ed9cab1b5b6421713c8155f0b753380595356f710400609664d3635eca/outbound.js +1 -0
  63. package/integ-tests/integ.ubuntu24.ts.snapshot/asset.bdc104ed9cab1b5b6421713c8155f0b753380595356f710400609664d3635eca/util.js +1 -0
  64. package/integ-tests/integ.ubuntu24.ts.snapshot/asset.efac30c7091c58fed492058fa6403c14f7e58aab8cf4fd595d838b8d5eeec2b9/index.js +6017 -0
  65. package/integ-tests/integ.ubuntu24.ts.snapshot/integ.json +23 -0
  66. package/integ-tests/integ.ubuntu24.ts.snapshot/manifest.json +1473 -0
  67. package/integ-tests/integ.ubuntu24.ts.snapshot/tree.json +1 -0
  68. package/integ-tests/integ.ubuntu25.ts +69 -0
  69. package/integ-tests/integ.ubuntu25.ts.snapshot/IntegSetupVSCodeOnUbuntu25DefaultTestDeployAssert48DBCF35.assets.json +33 -0
  70. package/integ-tests/integ.ubuntu25.ts.snapshot/IntegSetupVSCodeOnUbuntu25DefaultTestDeployAssert48DBCF35.template.json +337 -0
  71. package/integ-tests/integ.ubuntu25.ts.snapshot/IntegTestStackUbuntu25.assets.json +118 -0
  72. package/integ-tests/integ.ubuntu25.ts.snapshot/IntegTestStackUbuntu25.template.json +2725 -0
  73. package/integ-tests/integ.ubuntu25.ts.snapshot/asset.2819175352ad1ce0dae768e83fc328fb70fb5f10b4a8ff0ccbcb791f02b0716d/index.js +1 -0
  74. package/integ-tests/integ.ubuntu25.ts.snapshot/asset.2f99f38311da357eaaea1284d67c759759324dec4a1cd11621d9c59eea9e81df.lambda/index.js +180 -0
  75. package/integ-tests/integ.ubuntu25.ts.snapshot/asset.530055f7515b3f0a47900f5df37e729ba40ca977b2d07b952bdefa2b8f883f42.bundle/index.js +30676 -0
  76. package/integ-tests/integ.ubuntu25.ts.snapshot/asset.781ab0ab74634cdaf61539ab208ab777829ef07097ac21f95b9e15a3b1eedc1b.lambda/index.js +57 -0
  77. package/integ-tests/integ.ubuntu25.ts.snapshot/asset.7fa1e366ee8a9ded01fc355f704cff92bfd179574e6f9cfee800a3541df1b200/__entrypoint__.js +1 -0
  78. package/integ-tests/integ.ubuntu25.ts.snapshot/asset.7fa1e366ee8a9ded01fc355f704cff92bfd179574e6f9cfee800a3541df1b200/index.js +1 -0
  79. package/integ-tests/integ.ubuntu25.ts.snapshot/asset.9d043014be736e8162bcc7ec5590cc6d2ff24fd0d9c73a5c5d595151c5fdad00/index.js +1 -0
  80. package/integ-tests/integ.ubuntu25.ts.snapshot/asset.bdc104ed9cab1b5b6421713c8155f0b753380595356f710400609664d3635eca/cfn-response.js +1 -0
  81. package/integ-tests/integ.ubuntu25.ts.snapshot/asset.bdc104ed9cab1b5b6421713c8155f0b753380595356f710400609664d3635eca/consts.js +1 -0
  82. package/integ-tests/integ.ubuntu25.ts.snapshot/asset.bdc104ed9cab1b5b6421713c8155f0b753380595356f710400609664d3635eca/framework.js +3 -0
  83. package/integ-tests/integ.ubuntu25.ts.snapshot/asset.bdc104ed9cab1b5b6421713c8155f0b753380595356f710400609664d3635eca/outbound.js +1 -0
  84. package/integ-tests/integ.ubuntu25.ts.snapshot/asset.bdc104ed9cab1b5b6421713c8155f0b753380595356f710400609664d3635eca/util.js +1 -0
  85. package/integ-tests/integ.ubuntu25.ts.snapshot/asset.efac30c7091c58fed492058fa6403c14f7e58aab8cf4fd595d838b8d5eeec2b9/index.js +6017 -0
  86. package/integ-tests/integ.ubuntu25.ts.snapshot/integ.json +23 -0
  87. package/integ-tests/integ.ubuntu25.ts.snapshot/manifest.json +1473 -0
  88. package/integ-tests/integ.ubuntu25.ts.snapshot/tree.json +1 -0
  89. package/lib/idle-monitor/idle-monitor-function.js +2 -2
  90. package/lib/idle-monitor/idle-monitor.js +5 -2
  91. package/lib/idle-monitor-enabler/idle-monitor-enabler-function.d.ts +13 -0
  92. package/lib/idle-monitor-enabler/idle-monitor-enabler-function.js +22 -0
  93. package/lib/idle-monitor-enabler/idle-monitor-enabler.d.ts +25 -0
  94. package/lib/idle-monitor-enabler/idle-monitor-enabler.js +76 -0
  95. package/lib/idle-monitor-enabler/idle-monitor-enabler.lambda.d.ts +9 -0
  96. package/lib/idle-monitor-enabler/idle-monitor-enabler.lambda.js +48 -0
  97. package/lib/index.d.ts +0 -1
  98. package/lib/index.js +1 -2
  99. package/lib/installer/installer-function.js +2 -2
  100. package/lib/installer/installer.d.ts +105 -0
  101. package/lib/installer/installer.js +659 -301
  102. package/lib/installer/installer.lambda.js +64 -30
  103. package/lib/mappings.js +11 -3
  104. package/lib/secret-retriever/secret-retriever-function.js +2 -2
  105. package/lib/vscode-server.d.ts +45 -1
  106. package/lib/vscode-server.js +35 -5
  107. package/package.json +12 -12
  108. package/.claude/hooks/file_checker.sh +0 -178
  109. package/.qlty/.gitignore +0 -7
  110. package/.qlty/configs/.yamllint.yaml +0 -21
  111. package/.qlty/qlty.toml +0 -115
  112. package/assets/status-check/status-check.lambda/index.js +0 -123
  113. package/integ-tests/integ.al2023.ts.snapshot/cdk.out +0 -1
  114. package/integ-tests/integ.al2023.ts.snapshot/read.13497.1.lock +0 -1
  115. package/integ-tests/integ.custom-domain.ts.snapshot/read.13497.1.lock +0 -1
  116. package/integ-tests/integ.ubuntu.ts.snapshot/cdk.out +0 -1
  117. package/integ-tests/integ.ubuntu.ts.snapshot/read.13497.1.lock +0 -1
  118. package/lib/status-check/status-check-function.d.ts +0 -13
  119. package/lib/status-check/status-check-function.js +0 -22
  120. package/lib/status-check/status-check.d.ts +0 -36
  121. package/lib/status-check/status-check.js +0 -109
  122. package/lib/status-check/status-check.lambda.d.ts +0 -2
  123. package/lib/status-check/status-check.lambda.js +0 -104
package/CLAUDE.md CHANGED
@@ -4,18 +4,25 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
4
4
 
5
5
  ## Repository Overview
6
6
 
7
- This is a JSII-enabled CDK construct library published to npm, PyPI, and Go that deploys VS Code Server on AWS. It's designed for workshop/development purposes and creates infrastructure for running a cloud-based VS Code instance accessible through CloudFront.
7
+ A JSII-enabled CDK construct library that deploys VS Code Server on AWS. Published to npm and PyPI. Designed for workshop/development environments with intentionally permissive security for ease of use.
8
8
 
9
- **Key Characteristics**:
10
- - **Projen-managed**: All project configuration is in `.projenrc.ts` - run `npx projen` after modifying it
11
- - **Multi-language**: TypeScript source published to Python and Go via JSII
12
- - **Not production-ready**: Intentionally permissive for workshop scenarios
9
+ **Key Characteristic**: This is **NOT production-ready** by design - it's optimized for quick workshop setup and learning environments.
10
+
11
+ ## Projen-Managed Project
12
+
13
+ **Critical**: ALL project configuration is in `.projenrc.ts`. After modifying it, run `npx projen` to regenerate managed files.
14
+
15
+ **Never manually edit**:
16
+ - `package.json`
17
+ - Task definitions
18
+ - GitHub workflows
19
+ - Any file with "~~ Generated by projen" header
13
20
 
14
21
  ## Development Commands
15
22
 
16
23
  ### Build and Test
17
24
  ```bash
18
- # Build project (compiles TS, bundles Lambdas, generates API docs)
25
+ # Full build (compiles TS, bundles Lambdas, generates API docs)
19
26
  npx projen build
20
27
 
21
28
  # Run unit tests
@@ -24,10 +31,10 @@ npx projen test
24
31
  # Run single test file
25
32
  npx jest test/vscode-server.test.ts
26
33
 
27
- # Run tests in watch mode
34
+ # Watch mode for tests
28
35
  npx projen test:watch
29
36
 
30
- # Integration tests (deploys to eu-west-1, eu-west-2, eu-north-1)
37
+ # Integration tests (deploys to eu-west-1, eu-west-2, eu-north-1, eu-west-3)
31
38
  npm run integ-test
32
39
  ```
33
40
 
@@ -44,82 +51,305 @@ npm run awslint
44
51
  ```bash
45
52
  # Bundle specific Lambda
46
53
  npx projen bundle:installer/installer.lambda
54
+ npx projen bundle:idle-monitor/idle-monitor.lambda
55
+ npx projen bundle:idle-monitor-enabler/idle-monitor-enabler.lambda
47
56
  npx projen bundle:secret-retriever/secret-retriever.lambda
48
57
 
49
- # Watch mode for development
58
+ # Watch mode for Lambda development
50
59
  npx projen bundle:installer/installer.lambda:watch
51
60
  ```
52
61
 
53
62
  ### Publishing
54
63
  ```bash
55
- # Package for all targets (JS, Python, Go)
64
+ # Package for all targets (npm, Python)
56
65
  npx projen package-all
57
66
 
58
67
  # Package specific target
59
68
  npx projen package:js
60
69
  npx projen package:python
61
- npx projen package:go
62
70
  ```
63
71
 
64
- ## Architecture
72
+ ## Architecture Overview
65
73
 
66
- ### Main Construct (`VSCodeServer`)
67
- Located in `src/vscode-server.ts`, orchestrates:
74
+ ### Main Construct: `VSCodeServer` (src/vscode-server.ts)
75
+
76
+ Orchestrates the entire infrastructure:
68
77
  - VPC with single public subnet
69
78
  - EC2 instance (default: m7g.xlarge Graviton3 ARM)
70
- - CloudFront distribution with custom cache policies for VS Code Server
79
+ - CloudFront distribution with custom cache policies
71
80
  - Security groups (CloudFront prefix list restriction only)
72
- - IAM role with CDK permissions for workshop use
73
- - Optional Route53 + ACM certificate integration via `domainName` prop
81
+ - IAM role with broad CDK permissions (workshop-friendly)
82
+ - Optional: Route53 + ACM certificate integration
83
+ - Optional: Auto-stop feature with idle monitoring
84
+
85
+ ### Custom Resource Pattern
74
86
 
75
- ### Custom Resources Pattern
76
87
  Uses Lambda-backed custom resources via CDK Provider construct:
77
- - **Installer** (`src/installer/`): OS-specific VS Code Server installation via SSM documents
78
- - **SecretRetriever** (`src/secret-retriever/`): Extracts generated password from Secrets Manager
79
- - **PrefixListRetriever** (`src/prefixlist-retriever/`): Fetches AWS-managed CloudFront prefix lists
80
88
 
81
- ### AMI Selection
82
- `src/mappings.ts` contains SSM parameter paths for:
83
- - Ubuntu 22/24 (ARM + x86_64)
89
+ 1. **Installer** (`src/installer/`)
90
+ - Runs SSM documents to install VS Code Server
91
+ - OS-specific installation for Ubuntu 22/24/25 and Amazon Linux 2023
92
+ - Returns SUCCESS when installation completes
93
+ - Custom resource name: `SSMInstallerCustomResource`
94
+ - **Critical**: Must pass `linuxFlavorType` parameter to ensure correct OS-specific SSM document is used
95
+
96
+ 2. **SecretRetriever** (`src/secret-retriever/`)
97
+ - Extracts generated password from Secrets Manager
98
+ - Returns password as CloudFormation output
99
+
100
+ 3. **PrefixListRetriever** (`src/prefixlist-retriever/`)
101
+ - Fetches AWS-managed CloudFront prefix lists
102
+ - Used to restrict security group ingress
103
+
104
+ 4. **IdleMonitorEnabler** (`src/idle-monitor-enabler/`)
105
+ - Enables EventBridge rule ONLY after installation completes
106
+ - Prevents race condition where IdleMonitor stops instance mid-installation
107
+ - Critical dependency chain: `Installer → IdleMonitorEnabler → IdleMonitor active`
108
+
109
+ ### Auto-Stop Feature Architecture
110
+
111
+ **Problem Solved**: Race condition where IdleMonitor could stop instance during installation.
112
+
113
+ **Solution Flow**:
114
+ 1. EventBridge rule created in **DISABLED** state (`src/idle-monitor/idle-monitor.ts:118`)
115
+ 2. Installer runs and completes VS Code Server setup
116
+ 3. IdleMonitorEnabler custom resource **enables** the rule after installation succeeds
117
+ 4. IdleMonitor now safely monitors CloudFront metrics for idle detection
118
+
119
+ **Key Files**:
120
+ - `src/idle-monitor/idle-monitor.ts` - EventBridge rule + Lambda for monitoring
121
+ - `src/idle-monitor/idle-monitor.lambda.ts` - Checks CloudWatch metrics, stops instance if idle
122
+ - `src/idle-monitor-enabler/idle-monitor-enabler.ts` - Custom resource construct
123
+ - `src/idle-monitor-enabler/idle-monitor-enabler.lambda.ts` - Enables EventBridge rule via AWS SDK
124
+
125
+ **Dependency Wiring** (`src/vscode-server.ts:984`):
126
+ ```typescript
127
+ const installerCustomResource = this.node.findChild('SSMInstallerCustomResource');
128
+ enabler.node.addDependency(installerCustomResource);
129
+ ```
130
+
131
+ ### AMI Selection (`src/mappings.ts`)
132
+
133
+ Contains SSM parameter paths for:
134
+ - Ubuntu 22/24/25 (ARM + x86_64)
84
135
  - Amazon Linux 2023 (ARM + x86_64)
85
136
 
137
+ **Ubuntu Codenames**:
138
+ - Ubuntu 22 = "jammy" (uses ebs-gp2)
139
+ - Ubuntu 24 = "noble" (uses ebs-gp3)
140
+ - Ubuntu 25 = "plucky" (uses ebs-gp3)
141
+
86
142
  Function `getAmiSSMParameterForLinuxArchitectureAndFlavor()` returns region-specific SSM parameter for latest AMI.
87
143
 
88
- ### Key Props
89
- `VSCodeServerProps` in `src/vscode-server.ts:27-155`:
90
- - **Instance**: `instanceClass`, `instanceSize`, `instanceVolumeSize`
91
- - **OS**: `instanceOperatingSystem` (LinuxFlavorType enum), `instanceCpuArchitecture` (LinuxArchitectureType enum)
92
- - **VS Code**: `vscodeUser`, `vscodePassword`, `homeFolder`, `devServerPort`, `devServerBasePath`
93
- - **Domain**: `domainName`, `hostedZoneId`, `certificateArn`, `autoCreateCertificate`
94
- - **Extensions**: `additionalInstanceRolePolicies`, `additionalTags`
144
+ **Verify SSM Parameters** (when adding new OS versions):
145
+ ```bash
146
+ # List available Ubuntu versions
147
+ aws ssm get-parameters-by-path --path "/aws/service/canonical/ubuntu/server/" --recursive --query "Parameters[*].Name" --region us-east-1
148
+
149
+ # Verify specific AMI paths exist
150
+ aws ssm get-parameters --names \
151
+ "/aws/service/canonical/ubuntu/server/plucky/stable/current/amd64/hvm/ebs-gp3/ami-id" \
152
+ "/aws/service/canonical/ubuntu/server/plucky/stable/current/arm64/hvm/ebs-gp3/ami-id" \
153
+ --region us-east-1
154
+ ```
155
+
156
+ ### Key Props (`src/vscode-server.ts:27-200`)
157
+
158
+ **Instance Configuration**:
159
+ - `instanceClass`, `instanceSize`, `instanceVolumeSize`
160
+ - `instanceOperatingSystem` (LinuxFlavorType enum)
161
+ - `instanceCpuArchitecture` (LinuxArchitectureType enum)
162
+
163
+ **VS Code Configuration**:
164
+ - `vscodeUser`, `vscodePassword`, `homeFolder`
165
+ - `devServerPort`, `devServerBasePath`
166
+
167
+ **Domain/Certificate**:
168
+ - `domainName`, `hostedZoneId`, `certificateArn`, `autoCreateCertificate`
95
169
 
96
- ## Important Patterns
170
+ **Auto-Stop**:
171
+ - `enableAutoStop` - Enable automatic instance stop when idle
172
+ - `idleTimeoutMinutes` - Minutes of inactivity before stopping (default: 30)
173
+ - `idleCheckIntervalMinutes` - How often to check (default: 5)
174
+ - `skipStatusChecks` - Skip EC2 status checks before stopping (for testing only)
97
175
 
98
- ### Projen Workflow
99
- 1. Edit `.projenrc.ts` for project changes (dependencies, build config, etc.)
100
- 2. Run `npx projen` to regenerate managed files
101
- 3. Never manually edit `package.json`, task definitions, or GitHub workflows
176
+ **Extensions**:
177
+ - `additionalInstanceRolePolicies`, `additionalTags`
102
178
 
103
- ### JSII Constraints
179
+ ## JSII Constraints
180
+
181
+ **Critical for JSII compatibility**:
104
182
  - All public APIs must be JSII-compatible (no TS-specific types)
105
- - Bundled dependencies (like `node-html-parser`) in Lambda code must be listed in `.projenrc.ts` `bundledDeps`
106
- - Lambda functions use esbuild bundling configured via Projen
107
-
108
- ### CDK-nag Integration
109
- - `src/suppress-nags.ts` contains suppression patterns
110
- - Suppressions are applied via `NagSuppressions.addResourceSuppressions()` throughout construct code
111
- - Necessary because workshop design intentionally violates production best practices (e.g., broad IAM permissions)
112
-
113
- ### Integration Tests
114
- - Located in `integ-tests/`
115
- - Use `@aws-cdk/integ-tests-alpha` framework
116
- - Deploy actual stacks to 3 regions in parallel
117
- - Include assertion tests (e.g., login test via Lambda in `integ-tests/functions/`)
118
- - Run with `npm run integ-test` (requires AWS credentials)
119
-
120
- ## Domain/Certificate Feature (feat/route53-domain branch)
121
- When `domainName` prop is provided:
122
- - Creates Route53 A record pointing to CloudFront distribution
123
- - Supports `autoCreateCertificate` flag to create ACM cert in us-east-1 with DNS validation
124
- - Alternatively accepts existing `certificateArn`
125
- - Auto-discovers hosted zone if `hostedZoneId` not provided
183
+ - Bundled dependencies (like `node-html-parser`) must be in `.projenrc.ts` `bundledDeps`
184
+ - Lambda functions use esbuild bundling configured via Projen (auto-discovered in `src/**/*.lambda.ts`)
185
+
186
+ ## CDK-nag Integration
187
+
188
+ Suppressions defined in `src/suppress-nags.ts` and applied throughout construct code.
189
+
190
+ **Why suppressions are needed**: Workshop design intentionally violates production best practices:
191
+ - Broad IAM permissions for ease of use
192
+ - Permissive security groups
193
+ - Public subnet deployment
194
+ - No VPC endpoints
195
+
196
+ Apply suppressions via `NagSuppressions.addResourceSuppressions()`.
197
+
198
+ ## Integration Tests
199
+
200
+ Located in `integ-tests/`, using `@aws-cdk/integ-tests-alpha` framework.
201
+
202
+ **Test Files**:
203
+ - `integ.ubuntu.ts` - Basic Ubuntu 22 deployment + login test
204
+ - `integ.ubuntu24.ts` - Ubuntu 24 deployment + login test
205
+ - `integ.ubuntu25.ts` - Ubuntu 25 deployment + login test
206
+ - `integ.al2023.ts` - Amazon Linux 2023 deployment
207
+ - `integ.custom-domain.ts` - Custom domain + ACM certificate
208
+ - `integ.stop-on-idle.ts` - **4-phase auto-stop workflow test**
209
+
210
+ **Stop-on-Idle Test Phases** (`integ-tests/integ.stop-on-idle.ts`):
211
+ 1. `verify-auto-stop` - Wait for instance to stop after idle timeout
212
+ 2. `disable-idle-monitor` - Disable EventBridge rule to prevent re-stopping
213
+ 3. `start-instance` - Start instance and wait for running state
214
+ 4. `verify-login` - Check VS Code Server accessibility via CloudFront
215
+
216
+ **Run Integration Tests**:
217
+ ```bash
218
+ npm run integ-test # Deploys to 4 regions in parallel
219
+ ```
220
+
221
+ **Important**: Integration tests deploy actual stacks and incur AWS costs.
222
+
223
+ ## Lambda Bundling
224
+
225
+ Projen automatically discovers Lambda functions matching `src/**/*.lambda.ts` pattern.
226
+
227
+ **Auto-generated tasks** for each Lambda:
228
+ - `bundle:<path>/name.lambda` - Bundle Lambda code
229
+ - `bundle:<path>/name.lambda:watch` - Watch mode for development
230
+
231
+ **Bundled output**: `assets/<path>/name.lambda/index.js`
232
+
233
+ ## Common Workflows
234
+
235
+ ### Adding a New Lambda Function
236
+
237
+ 1. Create `src/my-feature/my-feature.lambda.ts`
238
+ 2. Create `src/my-feature/my-feature-function.ts` (CDK construct wrapper)
239
+ 3. Run `npx projen` - auto-discovers and creates bundle task
240
+ 4. Import in construct: `import { MyFeatureFunction } from './my-feature/my-feature-function'`
241
+
242
+ ### Modifying VSCodeServer Props
243
+
244
+ 1. Edit `src/vscode-server.ts` - update `VSCodeServerProps` interface
245
+ 2. Run `npx projen build` - regenerates JSII artifacts + API docs
246
+ 3. Update README.md with usage examples
247
+
248
+ ### Testing Changes
249
+
250
+ 1. Unit tests: `npx jest test/vscode-server.test.ts`
251
+ 2. Build validation: `npx projen build` (includes awslint checks)
252
+ 3. Integration test: `npm run integ-test` (full deployment test)
253
+
254
+ ### Adding Support for a New Ubuntu Version
255
+
256
+ When Ubuntu releases a new version (e.g., Ubuntu 26):
257
+
258
+ 1. **Add AMI mappings** in `src/mappings.ts`:
259
+ ```typescript
260
+ [
261
+ 'arm-ubuntu26',
262
+ '/aws/service/canonical/ubuntu/server/CODENAME/stable/current/arm64/hvm/ebs-gp3/ami-id',
263
+ ],
264
+ [
265
+ 'amd64-ubuntu26',
266
+ '/aws/service/canonical/ubuntu/server/CODENAME/stable/current/amd64/hvm/ebs-gp3/ami-id',
267
+ ],
268
+ ```
269
+ Replace `CODENAME` with Ubuntu's codename (verify via AWS SSM Parameter Store)
270
+
271
+ 2. **Add enum value** in `src/vscode-server.ts`:
272
+ ```typescript
273
+ export enum LinuxFlavorType {
274
+ // ...
275
+ UBUNTU_26 = 'ubuntu26',
276
+ }
277
+ ```
278
+
279
+ 3. **Update Installer** in `src/installer/installer.ts`:
280
+ - Add `LinuxFlavorType.UBUNTU_26` to the Ubuntu switch case in `createSSMDocument()` (line ~657)
281
+ - Update installer calls in `src/vscode-server.ts` to include new case (line ~930)
282
+
283
+ 4. **Pass linuxFlavorType** in `src/vscode-server.ts`:
284
+ ```typescript
285
+ installer = Installer.ubuntu({
286
+ // ... other options
287
+ linuxFlavorType: instanceOperatingSystem, // Critical!
288
+ })._bind(this);
289
+ ```
290
+
291
+ 5. **Create integration test**: Copy `integ-tests/integ.ubuntu25.ts` to `integ.ubuntu26.ts` and update OS version
292
+
293
+ 6. **Update documentation**:
294
+ - Update README.md examples with inline comments showing all supported versions
295
+ - Examples in `examples/` directory (optional - can keep as Ubuntu 24)
296
+
297
+ 7. **Verify and build**:
298
+ ```bash
299
+ # Verify SSM parameters exist
300
+ aws ssm get-parameters-by-path --path "/aws/service/canonical/ubuntu/server/CODENAME/" --recursive --region us-east-1
301
+
302
+ # Build and test
303
+ npx projen build
304
+ npm run integ-test
305
+ ```
306
+
307
+ ## Race Condition Prevention (Auto-Stop)
308
+
309
+ **Issue**: IdleMonitor EventBridge rule triggers immediately on stack creation, potentially stopping instance during installation (observed: instance stopped 72 seconds after installer started).
310
+
311
+ **Fix**: Three-stage dependency chain ensures safe operation:
312
+
313
+ ```
314
+ EC2 Instance
315
+
316
+ Installer Custom Resource (waits for VS Code Server installation)
317
+
318
+ IdleMonitorEnabler Custom Resource (enables EventBridge rule)
319
+
320
+ IdleMonitor Active (now safe to monitor)
321
+ ```
322
+
323
+ **Implementation Details**:
324
+ - EventBridge rule created with `enabled: false` (`src/idle-monitor/idle-monitor.ts:118`)
325
+ - IdleMonitorEnabler depends on `SSMInstallerCustomResource` via `node.addDependency()`
326
+ - CloudFormation enforces ordering automatically
327
+
328
+ This pattern can be applied to any future features requiring post-installation activation.
329
+
330
+ ## Multi-Language Publishing
331
+
332
+ Project publishes to:
333
+ - **npm**: `@mavogel/cdk-vscode-server`
334
+ - **PyPI**: `cdk-vscode-server`
335
+
336
+ JSII compiles TypeScript to:
337
+ - JavaScript (npm)
338
+ - Python (PyPI)
339
+
340
+ **Publishing** (requires credentials):
341
+ ```bash
342
+ npx projen release
343
+ ```
344
+
345
+ ## Workshop-Specific Considerations
346
+
347
+ **Remember**: This construct is intentionally designed for workshops, NOT production:
348
+
349
+ - IAM roles have broad permissions (all CDK operations)
350
+ - Security groups allow CloudFront only (but permissive within that constraint)
351
+ - No private subnets or VPC endpoints (cost optimization for workshops)
352
+ - Single public subnet (simplicity over high availability)
353
+ - Password-based authentication (ease of use over MFA/SSO)
354
+
355
+ When reviewing changes, prioritize **ease of use** and **quick setup** over security hardening.
package/README.md CHANGED
@@ -21,7 +21,9 @@ we implement new features. Therefore make sure you use an exact version in your
21
21
  - [Features](#features)
22
22
  - [Usage](#usage)
23
23
  - [Standard](#Standard)
24
+ - [Pre-populate with Git Repository](#pre-populate-with-git-repository)
24
25
  - [Custom Domain Configuration](#custom-domain-configuration)
26
+ - [Auto-Stop Configuration](#auto-stop-configuration)
25
27
  - [Solution Design](#solution-design)
26
28
  - [Inspiration](#inspiration)
27
29
 
@@ -31,7 +33,7 @@ we implement new features. Therefore make sure you use an exact version in your
31
33
  - 📏 **Best Practice Setup**: Set up with [projen](https://projen.io/) and a [single configuration file](./.projenrc.ts) to keep your changes centralized.
32
34
  - 🤹‍♂️ **Pre-installed packages**: Besides the [vscode](https://code.visualstudio.com/) server, other tools and software packages such as `git`, `docker`, `awscli` `nodejs` and `python` are pre-installed on the EC2 instance.
33
35
  - 🌐 **Custom Domain Support**: Use your own domain name with automatic ACM certificate creation and Route53 DNS configuration, or bring your existing certificate.
34
- - 💰 **Auto-Stop**: Automatically stop EC2 instances after inactivity with Elastic IP retention - save up to 75% on costs for development environments
36
+ - 💰 **Auto-Stop**: Automatically stop EC2 instances after inactivity with Elastic IP retention - save up to 75% on costs for development environments.
35
37
  - 🏗️ **Extensibility**: Pass in properties to the construct, which start with `additional*`. They allow you to extend the configuration to your needs. There are more to come...
36
38
 
37
39
  ## Usage
@@ -67,7 +69,7 @@ export class MyStack extends Stack {
67
69
  instanceVolumeSize: 8,
68
70
  instanceClass: ec2.InstanceClass.M7G,
69
71
  instanceSize: ec2.InstanceSize.LARGE,
70
- instanceOperatingSystem: LinuxFlavorType.UBUNTU_22,
72
+ instanceOperatingSystem: LinuxFlavorType.UBUNTU_24,
71
73
  instanceCpuArchitecture: LinuxArchitectureType.ARM,
72
74
 
73
75
  // 👇🏽 or if you want to give the InstanceRole more permissions
@@ -115,6 +117,41 @@ dev.vscodepassword64FBCA12 = foobarbaz
115
117
 
116
118
  See the [examples](./examples) folder for more inspiration.
117
119
 
120
+ ### Pre-populate with Git Repository
121
+
122
+ Clone a git repository into the VS Code Server's home folder during instance setup - perfect for workshops or development environments with starter code:
123
+
124
+ ```ts
125
+ new VSCodeServer(this, 'vscode', {
126
+ // Clone a git repository into the home folder
127
+ repoUrl: 'https://github.com/aws-samples/my-workshop-repo.git',
128
+
129
+ // Optional: customize the home folder path (default: /Workshop)
130
+ homeFolder: '/MyWorkshop',
131
+
132
+ // Optional: specify VS Code user (default: vscode-user)
133
+ vscodeUser: 'workshop-user',
134
+ });
135
+ ```
136
+
137
+ **What happens:**
138
+ 1. During instance setup, the specified git repository is cloned into the user's home folder
139
+ 2. VS Code Server opens with the repository already loaded and ready to use
140
+ 3. Participants can start coding immediately without manual git clone steps
141
+
142
+ **Use cases:**
143
+ - Workshop environments with pre-configured starter code
144
+ - Development environments with boilerplate projects
145
+ - Training sessions with example applications
146
+ - Code review sessions with pre-loaded repositories
147
+
148
+ **Repository requirements:**
149
+ - Must be publicly accessible (no authentication required)
150
+ - HTTPS URLs only (SSH git URLs are not supported)
151
+ - Repository will be cloned using `git clone` during instance initialization
152
+
153
+ For complete examples, see [examples/](./examples).
154
+
118
155
  ### Custom Domain Configuration
119
156
 
120
157
  You can configure your VS Code Server with a custom domain name instead of using the default CloudFront domain. The construct supports three different configuration options:
@@ -200,6 +237,7 @@ new VSCodeServer(this, 'vscode', {
200
237
  - Elastic IP for consistent public addressing
201
238
  - EventBridge rule triggering idle monitoring at configured intervals
202
239
  - IdleMonitor Lambda function checking CloudWatch metrics for request activity
240
+ - IdleMonitorEnabler custom resource ensuring monitoring only starts after installation completes
203
241
  - CloudWatch metrics from CloudFront distribution
204
242
 
205
243
  **Integration Testing:**
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/idle-monitor-enabler/idle-monitor-enabler.lambda.ts
21
+ var idle_monitor_enabler_lambda_exports = {};
22
+ __export(idle_monitor_enabler_lambda_exports, {
23
+ handler: () => handler
24
+ });
25
+ module.exports = __toCommonJS(idle_monitor_enabler_lambda_exports);
26
+ var import_client_eventbridge = require("@aws-sdk/client-eventbridge");
27
+ var eventBridge = new import_client_eventbridge.EventBridge();
28
+ var handler = async (event) => {
29
+ console.log("Event: %j", { ...event, ResponseURL: "..." });
30
+ const ruleName = event.ResourceProperties.RuleName;
31
+ if (!ruleName) {
32
+ throw new Error("RuleName is required in ResourceProperties");
33
+ }
34
+ switch (event.RequestType) {
35
+ case "Create":
36
+ case "Update":
37
+ console.log(`Enabling EventBridge rule: ${ruleName}`);
38
+ await eventBridge.send(
39
+ new import_client_eventbridge.EnableRuleCommand({
40
+ Name: ruleName
41
+ })
42
+ );
43
+ console.log(`Successfully enabled rule: ${ruleName}`);
44
+ return {
45
+ PhysicalResourceId: `idle-monitor-enabler-${ruleName}`
46
+ };
47
+ case "Delete":
48
+ console.log(`Disabling EventBridge rule on deletion: ${ruleName}`);
49
+ try {
50
+ await eventBridge.send(
51
+ new import_client_eventbridge.DisableRuleCommand({
52
+ Name: ruleName
53
+ })
54
+ );
55
+ console.log(`Successfully disabled rule: ${ruleName}`);
56
+ } catch (error) {
57
+ console.log(`Error disabling rule (ignoring): ${error.message}`);
58
+ }
59
+ return {};
60
+ default:
61
+ throw new Error(`Unsupported RequestType: ${event.RequestType}`);
62
+ }
63
+ };
64
+ // Annotate the CommonJS export names for ESM import in node:
65
+ 0 && (module.exports = {
66
+ handler
67
+ });