@aws/nx-plugin 1.0.0-rc.9 → 1.0.0-rc.90

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 (1592) hide show
  1. package/LICENSE-THIRD-PARTY +13999 -17963
  2. package/README.md +30 -110
  3. package/executors.json +5 -0
  4. package/generators.json +170 -10
  5. package/migrations.json +364 -0
  6. package/package.json +40 -21
  7. package/src/agentcore-gateway/__snapshots__/generator.spec.ts.snap +344 -0
  8. package/src/agentcore-gateway/agent-connection/generator.d.ts +19 -0
  9. package/src/agentcore-gateway/agent-connection/generator.js +95 -0
  10. package/src/agentcore-gateway/agent-connection/generator.js.map +1 -0
  11. package/src/agentcore-gateway/agent-connection/schema.d.js +6 -0
  12. package/src/agentcore-gateway/agent-connection/schema.d.js.map +1 -0
  13. package/src/agentcore-gateway/agent-connection/schema.d.ts +13 -0
  14. package/src/agentcore-gateway/agent-connection/schema.json +31 -0
  15. package/src/agentcore-gateway/attach-upstream.d.ts +49 -0
  16. package/src/agentcore-gateway/attach-upstream.js +59 -0
  17. package/src/agentcore-gateway/attach-upstream.js.map +1 -0
  18. package/src/agentcore-gateway/files/cedar/policies/README.md +61 -0
  19. package/src/agentcore-gateway/files/cedar/policies/permit-all.cedar +26 -0
  20. package/src/agentcore-gateway/files/project/http/local-dev.ts.template +77 -0
  21. package/src/agentcore-gateway/files/project/mcp/local-dev.ts.template +134 -0
  22. package/src/agentcore-gateway/gateway-connection/generator.d.ts +21 -0
  23. package/src/agentcore-gateway/gateway-connection/generator.js +103 -0
  24. package/src/agentcore-gateway/gateway-connection/generator.js.map +1 -0
  25. package/src/agentcore-gateway/gateway-connection/schema.d.js +6 -0
  26. package/src/agentcore-gateway/gateway-connection/schema.d.js.map +1 -0
  27. package/src/agentcore-gateway/gateway-connection/schema.d.ts +13 -0
  28. package/src/agentcore-gateway/gateway-connection/schema.json +31 -0
  29. package/src/agentcore-gateway/generator.d.ts +90 -0
  30. package/src/agentcore-gateway/generator.js +202 -0
  31. package/src/agentcore-gateway/generator.js.map +1 -0
  32. package/src/agentcore-gateway/mcp-connection/generator.d.ts +19 -0
  33. package/src/agentcore-gateway/mcp-connection/generator.js +61 -0
  34. package/src/agentcore-gateway/mcp-connection/generator.js.map +1 -0
  35. package/src/agentcore-gateway/mcp-connection/schema.d.js +6 -0
  36. package/src/agentcore-gateway/mcp-connection/schema.d.js.map +1 -0
  37. package/src/agentcore-gateway/mcp-connection/schema.d.ts +13 -0
  38. package/src/agentcore-gateway/mcp-connection/schema.json +31 -0
  39. package/src/agentcore-gateway/react-connection/__snapshots__/generator.spec.ts.snap +68 -0
  40. package/src/agentcore-gateway/react-connection/generator.d.ts +19 -0
  41. package/src/agentcore-gateway/react-connection/generator.js +119 -0
  42. package/src/agentcore-gateway/react-connection/generator.js.map +1 -0
  43. package/src/agentcore-gateway/react-connection/schema.d.js +6 -0
  44. package/src/agentcore-gateway/react-connection/schema.d.js.map +1 -0
  45. package/src/agentcore-gateway/react-connection/schema.d.ts +13 -0
  46. package/src/agentcore-gateway/react-connection/schema.json +31 -0
  47. package/src/agentcore-gateway/schema.d.js +6 -0
  48. package/src/agentcore-gateway/schema.d.js.map +1 -0
  49. package/src/agentcore-gateway/schema.d.ts +17 -0
  50. package/src/agentcore-gateway/schema.json +72 -0
  51. package/src/agentcore-harness/__snapshots__/generator.spec.ts.snap +237 -0
  52. package/src/agentcore-harness/files/project/README.md.template +82 -0
  53. package/src/agentcore-harness/files/project/scripts/chat.ts.template +99 -0
  54. package/src/agentcore-harness/files/project/src/PROMPT.md.template +16 -0
  55. package/src/agentcore-harness/generator.d.ts +62 -0
  56. package/src/agentcore-harness/generator.js +158 -0
  57. package/src/agentcore-harness/generator.js.map +1 -0
  58. package/src/agentcore-harness/schema.d.js +8 -0
  59. package/src/agentcore-harness/schema.d.js.map +1 -0
  60. package/src/agentcore-harness/schema.d.ts +17 -0
  61. package/src/agentcore-harness/schema.json +53 -0
  62. package/src/connection/agent-local-dev.d.ts +49 -0
  63. package/src/connection/agent-local-dev.js +84 -0
  64. package/src/connection/agent-local-dev.js.map +1 -0
  65. package/src/connection/agent-runtime-config.js +9 -14
  66. package/src/connection/agent-runtime-config.js.map +1 -1
  67. package/src/connection/gateway-runtime-config.d.ts +19 -0
  68. package/src/connection/gateway-runtime-config.js +47 -0
  69. package/src/connection/gateway-runtime-config.js.map +1 -0
  70. package/src/connection/generator.d.ts +4 -11
  71. package/src/connection/generator.js +172 -194
  72. package/src/connection/generator.js.map +1 -1
  73. package/src/connection/{serve-local.d.ts → local-dev.d.ts} +3 -3
  74. package/src/connection/local-dev.js +37 -0
  75. package/src/connection/local-dev.js.map +1 -0
  76. package/src/connection/scaffold-catalog.d.ts +267 -0
  77. package/src/connection/scaffold-catalog.js +347 -0
  78. package/src/connection/scaffold-catalog.js.map +1 -0
  79. package/src/connection/schema-resolver.d.ts +5 -0
  80. package/src/connection/schema-resolver.js +27 -0
  81. package/src/connection/schema-resolver.js.map +1 -0
  82. package/src/connection/schema.d.js +6 -0
  83. package/src/connection/schema.d.js.map +1 -0
  84. package/src/connection/schema.d.ts +1 -0
  85. package/src/connection/schema.json +5 -0
  86. package/src/connection/supported-connections.d.ts +116 -0
  87. package/src/connection/supported-connections.js +163 -0
  88. package/src/connection/supported-connections.js.map +1 -0
  89. package/src/index.d.ts +1 -1
  90. package/src/index.js +2 -5
  91. package/src/index.js.map +1 -1
  92. package/src/infra/app/__snapshots__/generator.spec.ts.snap +172 -87
  93. package/src/infra/app/files/app/README.md.template +22 -4
  94. package/src/infra/app/files/app/src/main.ts.template +5 -1
  95. package/src/infra/app/files/app/src/stages/application-stage.ts.template +1 -1
  96. package/src/infra/app/generator.d.ts +44 -1
  97. package/src/infra/app/generator.js +209 -128
  98. package/src/infra/app/generator.js.map +1 -1
  99. package/src/infra/app/schema.d.js +6 -0
  100. package/src/infra/app/schema.d.js.map +1 -0
  101. package/src/infra/app/schema.d.ts +1 -1
  102. package/src/infra/app/schema.json +5 -0
  103. package/src/init/generator.d.ts +31 -0
  104. package/src/init/generator.js +46 -0
  105. package/src/init/generator.js.map +1 -0
  106. package/src/init/schema.d.js +6 -0
  107. package/src/init/schema.d.js.map +1 -0
  108. package/src/init/schema.d.ts +16 -0
  109. package/src/init/schema.json +35 -0
  110. package/src/internal/test-matrix/generator.d.ts +29 -0
  111. package/src/internal/test-matrix/generator.js +729 -0
  112. package/src/internal/test-matrix/generator.js.map +1 -0
  113. package/src/internal/test-matrix/schema.d.js +6 -0
  114. package/src/internal/test-matrix/schema.d.js.map +1 -0
  115. package/src/internal/test-matrix/schema.d.ts +12 -0
  116. package/src/internal/test-matrix/schema.json +21 -0
  117. package/src/license/config-types.d.ts +2 -2
  118. package/src/license/config-types.js +5 -3
  119. package/src/license/config-types.js.map +1 -1
  120. package/src/license/config.d.ts +12 -3
  121. package/src/license/config.js +295 -242
  122. package/src/license/config.js.map +1 -1
  123. package/src/license/dependency-check/check.d.ts +2 -2
  124. package/src/license/dependency-check/check.js +51 -46
  125. package/src/license/dependency-check/check.js.map +1 -1
  126. package/src/license/dependency-check/collectors/collector.js +41 -71
  127. package/src/license/dependency-check/collectors/collector.js.map +1 -1
  128. package/src/license/dependency-check/collectors/npm-collector.js +34 -41
  129. package/src/license/dependency-check/collectors/npm-collector.js.map +1 -1
  130. package/src/license/dependency-check/collectors/python-collector.js +61 -62
  131. package/src/license/dependency-check/collectors/python-collector.js.map +1 -1
  132. package/src/license/dependency-check/evaluator.d.ts +1 -1
  133. package/src/license/dependency-check/evaluator.js +91 -74
  134. package/src/license/dependency-check/evaluator.js.map +1 -1
  135. package/src/license/dependency-check/executor.js +30 -22
  136. package/src/license/dependency-check/executor.js.map +1 -1
  137. package/src/license/dependency-check/pre-approved.d.ts +1 -1
  138. package/src/license/dependency-check/pre-approved.js +949 -336
  139. package/src/license/dependency-check/pre-approved.js.map +1 -1
  140. package/src/license/dependency-check/types.d.ts +1 -1
  141. package/src/license/dependency-check/types.js +2 -3
  142. package/src/license/dependency-check/types.js.map +1 -1
  143. package/src/license/generator.d.ts +1 -1
  144. package/src/license/generator.js +60 -77
  145. package/src/license/generator.js.map +1 -1
  146. package/src/license/index.d.ts +4 -4
  147. package/src/license/index.js +3 -9
  148. package/src/license/index.js.map +1 -1
  149. package/src/license/known-exceptions.d.ts +2 -3
  150. package/src/license/known-exceptions.js +12 -28
  151. package/src/license/known-exceptions.js.map +1 -1
  152. package/src/license/schema.d.js +6 -0
  153. package/src/license/schema.d.js.map +1 -0
  154. package/src/license/schema.d.ts +2 -0
  155. package/src/license/schema.json +5 -0
  156. package/src/license/sync/generator.js +94 -119
  157. package/src/license/sync/generator.js.map +1 -1
  158. package/src/license/sync/project-file-sync.d.ts +1 -1
  159. package/src/license/sync/project-file-sync.js +28 -35
  160. package/src/license/sync/project-file-sync.js.map +1 -1
  161. package/src/mcp-server/dev-server.d.ts +1 -0
  162. package/src/mcp-server/dev-server.js +18 -0
  163. package/src/mcp-server/dev-server.js.map +1 -0
  164. package/src/mcp-server/generator-info.d.ts +8 -3
  165. package/src/mcp-server/generator-info.js +155 -167
  166. package/src/mcp-server/generator-info.js.map +1 -1
  167. package/src/mcp-server/guide-pipeline.d.ts +1 -1
  168. package/src/mcp-server/guide-pipeline.js +199 -206
  169. package/src/mcp-server/guide-pipeline.js.map +1 -1
  170. package/src/mcp-server/guide-render.js +10 -25
  171. package/src/mcp-server/guide-render.js.map +1 -1
  172. package/src/mcp-server/index.js +6 -10
  173. package/src/mcp-server/index.js.map +1 -1
  174. package/src/mcp-server/mdx-ast.js +39 -57
  175. package/src/mcp-server/mdx-ast.js.map +1 -1
  176. package/src/mcp-server/option-filter.js +33 -47
  177. package/src/mcp-server/option-filter.js.map +1 -1
  178. package/src/mcp-server/schema-registry.d.ts +9 -3
  179. package/src/mcp-server/schema-registry.js +41 -38
  180. package/src/mcp-server/schema-registry.js.map +1 -1
  181. package/src/mcp-server/schema.d.ts +1 -1
  182. package/src/mcp-server/schema.js +9 -7
  183. package/src/mcp-server/schema.js.map +1 -1
  184. package/src/mcp-server/server.js +24 -24
  185. package/src/mcp-server/server.js.map +1 -1
  186. package/src/mcp-server/tools/add-to-existing-project.d.ts +12 -0
  187. package/src/mcp-server/tools/add-to-existing-project.js +36 -0
  188. package/src/mcp-server/tools/add-to-existing-project.js.map +1 -0
  189. package/src/mcp-server/tools/create-workspace-command.js +23 -24
  190. package/src/mcp-server/tools/create-workspace-command.js.map +1 -1
  191. package/src/mcp-server/tools/general-guidance.d.ts +2 -2
  192. package/src/mcp-server/tools/general-guidance.js +55 -32
  193. package/src/mcp-server/tools/general-guidance.js.map +1 -1
  194. package/src/mcp-server/tools/generator-guide.d.ts +1 -1
  195. package/src/mcp-server/tools/generator-guide.js +29 -37
  196. package/src/mcp-server/tools/generator-guide.js.map +1 -1
  197. package/src/mcp-server/tools/list-generators.d.ts +1 -1
  198. package/src/mcp-server/tools/list-generators.js +19 -25
  199. package/src/mcp-server/tools/list-generators.js.map +1 -1
  200. package/src/mcp-server/tools/upgrade-workspace.d.ts +11 -0
  201. package/src/mcp-server/tools/upgrade-workspace.js +32 -0
  202. package/src/mcp-server/tools/upgrade-workspace.js.map +1 -0
  203. package/src/migrations/latest/add-assemble-target/metadata.json +3 -0
  204. package/src/migrations/latest/add-assemble-target/migration.d.ts +6 -0
  205. package/src/migrations/latest/add-assemble-target/migration.js +241 -0
  206. package/src/migrations/latest/add-assemble-target/migration.js.map +1 -0
  207. package/src/migrations/latest/add-destroy-sandbox-target/metadata.json +3 -0
  208. package/src/migrations/latest/add-destroy-sandbox-target/migration.d.ts +6 -0
  209. package/src/migrations/latest/add-destroy-sandbox-target/migration.js +150 -0
  210. package/src/migrations/latest/add-destroy-sandbox-target/migration.js.map +1 -0
  211. package/src/migrations/latest/declare-cacheable-target-inputs/metadata.json +3 -0
  212. package/src/migrations/latest/declare-cacheable-target-inputs/migration.d.ts +6 -0
  213. package/src/migrations/latest/declare-cacheable-target-inputs/migration.js +94 -0
  214. package/src/migrations/latest/declare-cacheable-target-inputs/migration.js.map +1 -0
  215. package/src/migrations/latest/dev-target-depends-on-pull-image/metadata.json +3 -0
  216. package/src/migrations/latest/dev-target-depends-on-pull-image/migration.d.ts +6 -0
  217. package/src/migrations/latest/dev-target-depends-on-pull-image/migration.js +67 -0
  218. package/src/migrations/latest/dev-target-depends-on-pull-image/migration.js.map +1 -0
  219. package/src/migrations/latest/docker-target-cache-soundness/metadata.json +3 -0
  220. package/src/migrations/latest/docker-target-cache-soundness/migration.d.ts +6 -0
  221. package/src/migrations/latest/docker-target-cache-soundness/migration.js +95 -0
  222. package/src/migrations/latest/docker-target-cache-soundness/migration.js.map +1 -0
  223. package/src/migrations/latest/nx-parallel-default/metadata.json +3 -0
  224. package/src/migrations/latest/nx-parallel-default/migration.d.ts +14 -0
  225. package/src/migrations/latest/nx-parallel-default/migration.js +29 -0
  226. package/src/migrations/latest/nx-parallel-default/migration.js.map +1 -0
  227. package/src/migrations/latest/open-api-codegen-executor/metadata.json +3 -0
  228. package/src/migrations/latest/open-api-codegen-executor/migration.d.ts +6 -0
  229. package/src/migrations/latest/open-api-codegen-executor/migration.js +100 -0
  230. package/src/migrations/latest/open-api-codegen-executor/migration.js.map +1 -0
  231. package/src/migrations/latest/python-test-input-scoping/metadata.json +3 -0
  232. package/src/migrations/latest/python-test-input-scoping/migration.d.ts +6 -0
  233. package/src/migrations/latest/python-test-input-scoping/migration.js +142 -0
  234. package/src/migrations/latest/python-test-input-scoping/migration.js.map +1 -0
  235. package/src/migrations/latest/python-version-drop-exact-patch/metadata.json +3 -0
  236. package/src/migrations/latest/python-version-drop-exact-patch/migration.d.ts +6 -0
  237. package/src/migrations/latest/python-version-drop-exact-patch/migration.js +43 -0
  238. package/src/migrations/latest/python-version-drop-exact-patch/migration.js.map +1 -0
  239. package/src/migrations/latest/react-website-nx-react-express-override/metadata.json +3 -0
  240. package/src/migrations/latest/react-website-nx-react-express-override/migration.d.ts +31 -0
  241. package/src/migrations/latest/react-website-nx-react-express-override/migration.js +74 -0
  242. package/src/migrations/latest/react-website-nx-react-express-override/migration.js.map +1 -0
  243. package/src/migrations/latest/terraform-fmt-check/metadata.json +3 -0
  244. package/src/migrations/latest/terraform-fmt-check/migration.d.ts +6 -0
  245. package/src/migrations/latest/terraform-fmt-check/migration.js +165 -0
  246. package/src/migrations/latest/terraform-fmt-check/migration.js.map +1 -0
  247. package/src/migrations/latest/terraform-provider-plugin-cache/metadata.json +3 -0
  248. package/src/migrations/latest/terraform-provider-plugin-cache/migration.d.ts +6 -0
  249. package/src/migrations/latest/terraform-provider-plugin-cache/migration.js +164 -0
  250. package/src/migrations/latest/terraform-provider-plugin-cache/migration.js.map +1 -0
  251. package/src/migrations/latest/terraform-rdb-proxy-iam-connect-grant/__snapshots__/migration.spec.ts.snap +23 -0
  252. package/src/migrations/latest/terraform-rdb-proxy-iam-connect-grant/metadata.json +3 -0
  253. package/src/migrations/latest/terraform-rdb-proxy-iam-connect-grant/migration.d.ts +6 -0
  254. package/src/migrations/latest/terraform-rdb-proxy-iam-connect-grant/migration.js +88 -0
  255. package/src/migrations/latest/terraform-rdb-proxy-iam-connect-grant/migration.js.map +1 -0
  256. package/src/migrations/latest/vitest-coverage-output-dir/metadata.json +3 -0
  257. package/src/migrations/latest/vitest-coverage-output-dir/migration.d.ts +6 -0
  258. package/src/migrations/latest/vitest-coverage-output-dir/migration.js +177 -0
  259. package/src/migrations/latest/vitest-coverage-output-dir/migration.js.map +1 -0
  260. package/src/migrations/v1.0.0-rc.50/0001-modernize-function-props-cast/metadata.json +3 -0
  261. package/src/migrations/v1.0.0-rc.50/0001-modernize-function-props-cast/migration.d.ts +6 -0
  262. package/src/migrations/v1.0.0-rc.50/0001-modernize-function-props-cast/migration.js +65 -0
  263. package/src/migrations/v1.0.0-rc.50/0001-modernize-function-props-cast/migration.js.map +1 -0
  264. package/src/migrations/v1.0.0-rc.50/0002-restrict-cors-to-custom-domains/metadata.json +3 -0
  265. package/src/migrations/v1.0.0-rc.50/0002-restrict-cors-to-custom-domains/migration.d.ts +6 -0
  266. package/src/migrations/v1.0.0-rc.50/0002-restrict-cors-to-custom-domains/migration.js +108 -0
  267. package/src/migrations/v1.0.0-rc.50/0002-restrict-cors-to-custom-domains/migration.js.map +1 -0
  268. package/src/migrations/v1.0.0-rc.50/0003-terraform-bootstrap-adopt-existing-bucket/metadata.json +3 -0
  269. package/src/migrations/v1.0.0-rc.50/0003-terraform-bootstrap-adopt-existing-bucket/migration.d.ts +6 -0
  270. package/src/migrations/v1.0.0-rc.50/0003-terraform-bootstrap-adopt-existing-bucket/migration.js +128 -0
  271. package/src/migrations/v1.0.0-rc.50/0003-terraform-bootstrap-adopt-existing-bucket/migration.js.map +1 -0
  272. package/src/migrations/v1.0.0-rc.52/0001-user-identity-waf-allow-localhost-callback/metadata.json +3 -0
  273. package/src/migrations/v1.0.0-rc.52/0001-user-identity-waf-allow-localhost-callback/migration.d.ts +6 -0
  274. package/src/migrations/v1.0.0-rc.52/0001-user-identity-waf-allow-localhost-callback/migration.js +215 -0
  275. package/src/migrations/v1.0.0-rc.52/0001-user-identity-waf-allow-localhost-callback/migration.js.map +1 -0
  276. package/src/migrations/v1.0.0-rc.54/0001-order-access-log-delivery-after-bucket-policy/metadata.json +3 -0
  277. package/src/migrations/v1.0.0-rc.54/0001-order-access-log-delivery-after-bucket-policy/migration.d.ts +6 -0
  278. package/src/migrations/v1.0.0-rc.54/0001-order-access-log-delivery-after-bucket-policy/migration.js +93 -0
  279. package/src/migrations/v1.0.0-rc.54/0001-order-access-log-delivery-after-bucket-policy/migration.js.map +1 -0
  280. package/src/migrations/v1.0.0-rc.55/0001-backfill-generator-metadata/metadata.json +3 -0
  281. package/src/migrations/v1.0.0-rc.55/0001-backfill-generator-metadata/migration.d.ts +96 -0
  282. package/src/migrations/v1.0.0-rc.55/0001-backfill-generator-metadata/migration.js +670 -0
  283. package/src/migrations/v1.0.0-rc.55/0001-backfill-generator-metadata/migration.js.map +1 -0
  284. package/src/migrations/v1.0.0-rc.56/0001-dynamodb-local-remove-optimize-flag/metadata.json +3 -0
  285. package/src/migrations/v1.0.0-rc.56/0001-dynamodb-local-remove-optimize-flag/migration.d.ts +6 -0
  286. package/src/migrations/v1.0.0-rc.56/0001-dynamodb-local-remove-optimize-flag/migration.js +89 -0
  287. package/src/migrations/v1.0.0-rc.56/0001-dynamodb-local-remove-optimize-flag/migration.js.map +1 -0
  288. package/src/migrations/v1.0.0-rc.57/0001-ts-agent-session-management-support/metadata.json +3 -0
  289. package/src/migrations/v1.0.0-rc.57/0001-ts-agent-session-management-support/migration.d.ts +6 -0
  290. package/src/migrations/v1.0.0-rc.57/0001-ts-agent-session-management-support/migration.js +449 -0
  291. package/src/migrations/v1.0.0-rc.57/0001-ts-agent-session-management-support/migration.js.map +1 -0
  292. package/src/migrations/v1.0.0-rc.58/0001-smithy-ssdk-bundle-pins/metadata.json +3 -0
  293. package/src/migrations/v1.0.0-rc.58/0001-smithy-ssdk-bundle-pins/migration.d.ts +6 -0
  294. package/src/migrations/v1.0.0-rc.58/0001-smithy-ssdk-bundle-pins/migration.js +78 -0
  295. package/src/migrations/v1.0.0-rc.58/0001-smithy-ssdk-bundle-pins/migration.js.map +1 -0
  296. package/src/{smithy/project/files/build.Dockerfile.template → migrations/v1.0.0-rc.58/0001-smithy-ssdk-bundle-pins/rc57-service.Dockerfile.fixture} +1 -4
  297. package/src/migrations/v1.0.0-rc.59/0001-gateway-agent-targets/__snapshots__/migration.spec.ts.snap +223 -0
  298. package/src/migrations/v1.0.0-rc.59/0001-gateway-agent-targets/metadata.json +3 -0
  299. package/src/migrations/v1.0.0-rc.59/0001-gateway-agent-targets/migration.d.ts +6 -0
  300. package/src/migrations/v1.0.0-rc.59/0001-gateway-agent-targets/migration.js +217 -0
  301. package/src/migrations/v1.0.0-rc.59/0001-gateway-agent-targets/migration.js.map +1 -0
  302. package/src/migrations/v1.0.0-rc.60/0001-gateway-cognito-passthrough/__snapshots__/migration.spec.ts.snap +149 -0
  303. package/src/migrations/v1.0.0-rc.60/0001-gateway-cognito-passthrough/metadata.json +3 -0
  304. package/src/migrations/v1.0.0-rc.60/0001-gateway-cognito-passthrough/migration.d.ts +6 -0
  305. package/src/migrations/v1.0.0-rc.60/0001-gateway-cognito-passthrough/migration.js +195 -0
  306. package/src/migrations/v1.0.0-rc.60/0001-gateway-cognito-passthrough/migration.js.map +1 -0
  307. package/src/migrations/v1.0.0-rc.60/0002-waf-log-group-removal/__snapshots__/migration.spec.ts.snap +57 -0
  308. package/src/migrations/v1.0.0-rc.60/0002-waf-log-group-removal/metadata.json +3 -0
  309. package/src/migrations/v1.0.0-rc.60/0002-waf-log-group-removal/migration.d.ts +6 -0
  310. package/src/migrations/v1.0.0-rc.60/0002-waf-log-group-removal/migration.js +77 -0
  311. package/src/migrations/v1.0.0-rc.60/0002-waf-log-group-removal/migration.js.map +1 -0
  312. package/src/migrations/v1.0.0-rc.60/0003-strip-pip-from-python-images/__snapshots__/migration.spec.ts.snap +48 -0
  313. package/src/migrations/v1.0.0-rc.60/0003-strip-pip-from-python-images/metadata.json +3 -0
  314. package/src/migrations/v1.0.0-rc.60/0003-strip-pip-from-python-images/migration.d.ts +6 -0
  315. package/src/migrations/v1.0.0-rc.60/0003-strip-pip-from-python-images/migration.js +91 -0
  316. package/src/migrations/v1.0.0-rc.60/0003-strip-pip-from-python-images/migration.js.map +1 -0
  317. package/src/migrations/v1.0.0-rc.61/0001-py-agent-lifespan-construction/metadata.json +3 -0
  318. package/src/migrations/v1.0.0-rc.61/0001-py-agent-lifespan-construction/migration.d.ts +6 -0
  319. package/src/migrations/v1.0.0-rc.61/0001-py-agent-lifespan-construction/migration.js +412 -0
  320. package/src/migrations/v1.0.0-rc.61/0001-py-agent-lifespan-construction/migration.js.map +1 -0
  321. package/src/migrations/v1.0.0-rc.62/0001-rename-load-runtime-config-target/metadata.json +3 -0
  322. package/src/migrations/v1.0.0-rc.62/0001-rename-load-runtime-config-target/migration.d.ts +6 -0
  323. package/src/migrations/v1.0.0-rc.62/0001-rename-load-runtime-config-target/migration.js +33 -0
  324. package/src/migrations/v1.0.0-rc.62/0001-rename-load-runtime-config-target/migration.js.map +1 -0
  325. package/src/migrations/v1.0.0-rc.63/0001-add-deploy-sandbox-target/metadata.json +3 -0
  326. package/src/migrations/v1.0.0-rc.63/0001-add-deploy-sandbox-target/migration.d.ts +6 -0
  327. package/src/migrations/v1.0.0-rc.63/0001-add-deploy-sandbox-target/migration.js +83 -0
  328. package/src/migrations/v1.0.0-rc.63/0001-add-deploy-sandbox-target/migration.js.map +1 -0
  329. package/src/migrations/v1.0.0-rc.64/0001-smithy-build-without-docker/legacy-service.Dockerfile.fixture +95 -0
  330. package/src/migrations/v1.0.0-rc.64/0001-smithy-build-without-docker/metadata.json +3 -0
  331. package/src/migrations/v1.0.0-rc.64/0001-smithy-build-without-docker/migration.d.ts +6 -0
  332. package/src/migrations/v1.0.0-rc.64/0001-smithy-build-without-docker/migration.js +154 -0
  333. package/src/migrations/v1.0.0-rc.64/0001-smithy-build-without-docker/migration.js.map +1 -0
  334. package/src/migrations/v1.0.0-rc.65/0001-vite-config-import-meta-dirname/metadata.json +3 -0
  335. package/src/migrations/v1.0.0-rc.65/0001-vite-config-import-meta-dirname/migration.d.ts +6 -0
  336. package/src/migrations/v1.0.0-rc.65/0001-vite-config-import-meta-dirname/migration.js +71 -0
  337. package/src/migrations/v1.0.0-rc.65/0001-vite-config-import-meta-dirname/migration.js.map +1 -0
  338. package/src/migrations/v1.0.0-rc.65/0002-rolldown-code-splitting/metadata.json +3 -0
  339. package/src/migrations/v1.0.0-rc.65/0002-rolldown-code-splitting/migration.d.ts +6 -0
  340. package/src/migrations/v1.0.0-rc.65/0002-rolldown-code-splitting/migration.js +57 -0
  341. package/src/migrations/v1.0.0-rc.65/0002-rolldown-code-splitting/migration.js.map +1 -0
  342. package/src/migrations/v1.0.0-rc.67/0001-smithy-server-package-rename/metadata.json +3 -0
  343. package/src/migrations/v1.0.0-rc.67/0001-smithy-server-package-rename/migration.d.ts +6 -0
  344. package/src/migrations/v1.0.0-rc.67/0001-smithy-server-package-rename/migration.js +123 -0
  345. package/src/migrations/v1.0.0-rc.67/0001-smithy-server-package-rename/migration.js.map +1 -0
  346. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/files/build-system-prompt.ts.fixture +23 -0
  347. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/files/build-user-prompt.ts.fixture +96 -0
  348. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/files/config-path.ts.fixture +1 -0
  349. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/files/file-to-translate.ts.fixture +10 -0
  350. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/files/get-files-to-translate.ts.fixture +96 -0
  351. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/files/header.ts.fixture +15 -0
  352. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/files/log.ts.fixture +18 -0
  353. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/files/main-call.ts.fixture +6 -0
  354. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/files/main.ts.fixture +79 -0
  355. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/files/project-root.ts.fixture +1 -0
  356. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/files/run-with-concurrency.ts.fixture +57 -0
  357. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/files/scripts-dir.ts.fixture +1 -0
  358. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/files/translate-file-for-language.ts.fixture +61 -0
  359. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/grit/already-migrated.grit +1 -0
  360. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/grit/build-system-prompt.grit +6 -0
  361. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/grit/build-user-prompt.grit +6 -0
  362. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/grit/config-path.grit +1 -0
  363. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/grit/file-to-translate.grit +11 -0
  364. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/grit/get-files-to-translate.grit +7 -0
  365. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/grit/log.grit +3 -0
  366. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/grit/main-call.grit +1 -0
  367. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/grit/main.grit +8 -0
  368. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/grit/project-root.grit +1 -0
  369. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/grit/run-with-concurrency.grit +6 -0
  370. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/grit/translate-file-for-language.grit +6 -0
  371. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/metadata.json +3 -0
  372. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/migration.d.ts +2 -0
  373. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/migration.js +103 -0
  374. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/migration.js.map +1 -0
  375. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/test-fixtures/released-script-first.ts.fixture +430 -0
  376. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/test-fixtures/released-script.ts.fixture +431 -0
  377. package/src/migrations/v1.0.0-rc.70/0001-py-agent-a2a-lifespan-construction/metadata.json +3 -0
  378. package/src/migrations/v1.0.0-rc.70/0001-py-agent-a2a-lifespan-construction/migration.d.ts +6 -0
  379. package/src/migrations/v1.0.0-rc.70/0001-py-agent-a2a-lifespan-construction/migration.js +246 -0
  380. package/src/migrations/v1.0.0-rc.70/0001-py-agent-a2a-lifespan-construction/migration.js.map +1 -0
  381. package/src/migrations/v1.0.0-rc.71/0001-py-agent-a2a-httpx-client-per-call/metadata.json +3 -0
  382. package/src/migrations/v1.0.0-rc.71/0001-py-agent-a2a-httpx-client-per-call/migration.d.ts +6 -0
  383. package/src/migrations/v1.0.0-rc.71/0001-py-agent-a2a-httpx-client-per-call/migration.js +329 -0
  384. package/src/migrations/v1.0.0-rc.71/0001-py-agent-a2a-httpx-client-per-call/migration.js.map +1 -0
  385. package/src/migrations/v1.0.0-rc.72/0001-py-agent-session-management-support/metadata.json +3 -0
  386. package/src/migrations/v1.0.0-rc.72/0001-py-agent-session-management-support/migration.d.ts +2 -0
  387. package/src/migrations/v1.0.0-rc.72/0001-py-agent-session-management-support/migration.js +308 -0
  388. package/src/migrations/v1.0.0-rc.72/0001-py-agent-session-management-support/migration.js.map +1 -0
  389. package/src/migrations/v1.0.0-rc.73/0001-remove-mcp-inspector-license-exceptions/metadata.json +3 -0
  390. package/{sdk/utils/ast.d.ts → src/migrations/v1.0.0-rc.73/0001-remove-mcp-inspector-license-exceptions/migration.d.ts} +2 -2
  391. package/src/migrations/v1.0.0-rc.73/0001-remove-mcp-inspector-license-exceptions/migration.js +38 -0
  392. package/src/migrations/v1.0.0-rc.73/0001-remove-mcp-inspector-license-exceptions/migration.js.map +1 -0
  393. package/src/migrations/v1.0.0-rc.73/0002-apt-security-updates-in-container-images/__snapshots__/migration.spec.ts.snap +90 -0
  394. package/src/migrations/v1.0.0-rc.73/0002-apt-security-updates-in-container-images/metadata.json +3 -0
  395. package/src/migrations/v1.0.0-rc.73/0002-apt-security-updates-in-container-images/migration.d.ts +6 -0
  396. package/src/migrations/v1.0.0-rc.73/0002-apt-security-updates-in-container-images/migration.js +90 -0
  397. package/src/migrations/v1.0.0-rc.73/0002-apt-security-updates-in-container-images/migration.js.map +1 -0
  398. package/src/migrations/v1.0.0-rc.74/0001-py-agent-session-id-middleware-extraction/metadata.json +3 -0
  399. package/src/migrations/v1.0.0-rc.74/0001-py-agent-session-id-middleware-extraction/migration.d.ts +2 -0
  400. package/src/migrations/v1.0.0-rc.74/0001-py-agent-session-id-middleware-extraction/migration.js +118 -0
  401. package/src/migrations/v1.0.0-rc.74/0001-py-agent-session-id-middleware-extraction/migration.js.map +1 -0
  402. package/src/migrations/v1.0.0-rc.75/0001-ts-agent-session-id-middleware-extraction/metadata.json +3 -0
  403. package/src/migrations/v1.0.0-rc.75/0001-ts-agent-session-id-middleware-extraction/migration.d.ts +2 -0
  404. package/src/migrations/v1.0.0-rc.75/0001-ts-agent-session-id-middleware-extraction/migration.js +136 -0
  405. package/src/migrations/v1.0.0-rc.75/0001-ts-agent-session-id-middleware-extraction/migration.js.map +1 -0
  406. package/src/migrations/v1.0.0-rc.77/0001-dynamodb-configurable-encryption/__snapshots__/migration.spec.ts.snap +429 -0
  407. package/src/migrations/v1.0.0-rc.77/0001-dynamodb-configurable-encryption/metadata.json +3 -0
  408. package/src/migrations/v1.0.0-rc.77/0001-dynamodb-configurable-encryption/migration.d.ts +6 -0
  409. package/src/migrations/v1.0.0-rc.77/0001-dynamodb-configurable-encryption/migration.js +325 -0
  410. package/src/migrations/v1.0.0-rc.77/0001-dynamodb-configurable-encryption/migration.js.map +1 -0
  411. package/src/migrations/v1.0.0-rc.77/0002-user-identity-configurable-mfa/metadata.json +3 -0
  412. package/src/migrations/v1.0.0-rc.77/0002-user-identity-configurable-mfa/migration.d.ts +6 -0
  413. package/src/migrations/v1.0.0-rc.77/0002-user-identity-configurable-mfa/migration.js +362 -0
  414. package/src/migrations/v1.0.0-rc.77/0002-user-identity-configurable-mfa/migration.js.map +1 -0
  415. package/src/migrations/v1.0.0-rc.78/0001-static-website-configurable-waf-encryption/metadata.json +3 -0
  416. package/src/migrations/v1.0.0-rc.78/0001-static-website-configurable-waf-encryption/migration.d.ts +6 -0
  417. package/src/migrations/v1.0.0-rc.78/0001-static-website-configurable-waf-encryption/migration.js +418 -0
  418. package/src/migrations/v1.0.0-rc.78/0001-static-website-configurable-waf-encryption/migration.js.map +1 -0
  419. package/src/migrations/v1.0.0-rc.79/0001-py-agent-ag-ui-hooks/metadata.json +3 -0
  420. package/src/migrations/v1.0.0-rc.79/0001-py-agent-ag-ui-hooks/migration.d.ts +6 -0
  421. package/src/migrations/v1.0.0-rc.79/0001-py-agent-ag-ui-hooks/migration.js +113 -0
  422. package/src/migrations/v1.0.0-rc.79/0001-py-agent-ag-ui-hooks/migration.js.map +1 -0
  423. package/src/migrations/v1.0.0-rc.82/0001-terraform-load-runtime-config-src-path/metadata.json +3 -0
  424. package/src/migrations/v1.0.0-rc.82/0001-terraform-load-runtime-config-src-path/migration.d.ts +6 -0
  425. package/src/migrations/v1.0.0-rc.82/0001-terraform-load-runtime-config-src-path/migration.js +48 -0
  426. package/src/migrations/v1.0.0-rc.82/0001-terraform-load-runtime-config-src-path/migration.js.map +1 -0
  427. package/src/migrations/v1.0.0-rc.82/0002-runtime-config-namespace-defaults/metadata.json +3 -0
  428. package/src/migrations/v1.0.0-rc.82/0002-runtime-config-namespace-defaults/migration.d.ts +6 -0
  429. package/src/migrations/v1.0.0-rc.82/0002-runtime-config-namespace-defaults/migration.js +131 -0
  430. package/src/migrations/v1.0.0-rc.82/0002-runtime-config-namespace-defaults/migration.js.map +1 -0
  431. package/src/migrations/v1.0.0-rc.83/0001-dynamodb-terraform-app-module-outputs/metadata.json +3 -0
  432. package/src/migrations/v1.0.0-rc.83/0001-dynamodb-terraform-app-module-outputs/migration.d.ts +6 -0
  433. package/src/migrations/v1.0.0-rc.83/0001-dynamodb-terraform-app-module-outputs/migration.js +69 -0
  434. package/src/migrations/v1.0.0-rc.83/0001-dynamodb-terraform-app-module-outputs/migration.js.map +1 -0
  435. package/src/migrations/v1.0.0-rc.83/0002-terraform-data-resource-destroy-protection/__snapshots__/migration.spec.ts.snap +75 -0
  436. package/src/migrations/v1.0.0-rc.83/0002-terraform-data-resource-destroy-protection/metadata.json +3 -0
  437. package/src/migrations/v1.0.0-rc.83/0002-terraform-data-resource-destroy-protection/migration.d.ts +6 -0
  438. package/src/migrations/v1.0.0-rc.83/0002-terraform-data-resource-destroy-protection/migration.js +78 -0
  439. package/src/migrations/v1.0.0-rc.83/0002-terraform-data-resource-destroy-protection/migration.js.map +1 -0
  440. package/src/migrations/v1.0.0-rc.83/0003-terraform-gateway-cedar-render-root-ejs/metadata.json +3 -0
  441. package/src/migrations/v1.0.0-rc.83/0003-terraform-gateway-cedar-render-root-ejs/migration.d.ts +6 -0
  442. package/src/migrations/v1.0.0-rc.83/0003-terraform-gateway-cedar-render-root-ejs/migration.js +38 -0
  443. package/src/migrations/v1.0.0-rc.83/0003-terraform-gateway-cedar-render-root-ejs/migration.js.map +1 -0
  444. package/src/migrations/v1.0.0-rc.84/0001-rdb-terraform-aurora-app-module-defaults/__snapshots__/migration.spec.ts.snap +54 -0
  445. package/src/migrations/v1.0.0-rc.84/0001-rdb-terraform-aurora-app-module-defaults/metadata.json +3 -0
  446. package/src/migrations/v1.0.0-rc.84/0001-rdb-terraform-aurora-app-module-defaults/migration.d.ts +6 -0
  447. package/src/migrations/v1.0.0-rc.84/0001-rdb-terraform-aurora-app-module-defaults/migration.js +86 -0
  448. package/src/migrations/v1.0.0-rc.84/0001-rdb-terraform-aurora-app-module-defaults/migration.js.map +1 -0
  449. package/src/migrations/v1.0.0-rc.84/0002-agentcore-harness-no-tools-by-default/__snapshots__/migration.spec.ts.snap +79 -0
  450. package/src/migrations/v1.0.0-rc.84/0002-agentcore-harness-no-tools-by-default/metadata.json +3 -0
  451. package/src/migrations/v1.0.0-rc.84/0002-agentcore-harness-no-tools-by-default/migration.d.ts +6 -0
  452. package/src/migrations/v1.0.0-rc.84/0002-agentcore-harness-no-tools-by-default/migration.js +87 -0
  453. package/src/migrations/v1.0.0-rc.84/0002-agentcore-harness-no-tools-by-default/migration.js.map +1 -0
  454. package/src/migrations/v1.0.0-rc.84/0003-terraform-declare-used-providers/metadata.json +3 -0
  455. package/src/migrations/v1.0.0-rc.84/0003-terraform-declare-used-providers/migration.d.ts +6 -0
  456. package/src/migrations/v1.0.0-rc.84/0003-terraform-declare-used-providers/migration.js +147 -0
  457. package/src/migrations/v1.0.0-rc.84/0003-terraform-declare-used-providers/migration.js.map +1 -0
  458. package/src/migrations/v1.0.0-rc.84/0004-waf-log-group-cmk-encryption/__snapshots__/migration.spec.ts.snap +286 -0
  459. package/src/migrations/v1.0.0-rc.84/0004-waf-log-group-cmk-encryption/metadata.json +3 -0
  460. package/src/migrations/v1.0.0-rc.84/0004-waf-log-group-cmk-encryption/migration.d.ts +6 -0
  461. package/src/migrations/v1.0.0-rc.84/0004-waf-log-group-cmk-encryption/migration.js +238 -0
  462. package/src/migrations/v1.0.0-rc.84/0004-waf-log-group-cmk-encryption/migration.js.map +1 -0
  463. package/src/migrations/v1.0.0-rc.84/0005-terraform-harness-vpc-role-and-memory-grant/__snapshots__/migration.spec.ts.snap +521 -0
  464. package/src/migrations/v1.0.0-rc.84/0005-terraform-harness-vpc-role-and-memory-grant/metadata.json +3 -0
  465. package/src/migrations/v1.0.0-rc.84/0005-terraform-harness-vpc-role-and-memory-grant/migration.d.ts +6 -0
  466. package/src/migrations/v1.0.0-rc.84/0005-terraform-harness-vpc-role-and-memory-grant/migration.js +504 -0
  467. package/src/migrations/v1.0.0-rc.84/0005-terraform-harness-vpc-role-and-memory-grant/migration.js.map +1 -0
  468. package/src/migrations/v1.0.0-rc.85/0001-terraform-project-checkov-target-and-bootstrap-destroy/metadata.json +3 -0
  469. package/src/migrations/v1.0.0-rc.85/0001-terraform-project-checkov-target-and-bootstrap-destroy/migration.d.ts +6 -0
  470. package/src/migrations/v1.0.0-rc.85/0001-terraform-project-checkov-target-and-bootstrap-destroy/migration.js +146 -0
  471. package/src/migrations/v1.0.0-rc.85/0001-terraform-project-checkov-target-and-bootstrap-destroy/migration.js.map +1 -0
  472. package/src/open-api/codegen/executor-schema.d.js +6 -0
  473. package/src/open-api/codegen/executor-schema.d.js.map +1 -0
  474. package/src/open-api/codegen/executor-schema.d.ts +18 -0
  475. package/src/open-api/codegen/executor-schema.json +33 -0
  476. package/src/open-api/codegen/executor.d.ts +18 -0
  477. package/src/open-api/codegen/executor.js +56 -0
  478. package/src/open-api/codegen/executor.js.map +1 -0
  479. package/src/open-api/json-metadata/generator.d.ts +24 -0
  480. package/src/open-api/json-metadata/generator.js +37 -0
  481. package/src/open-api/json-metadata/generator.js.map +1 -0
  482. package/src/open-api/json-metadata/schema.d.js +6 -0
  483. package/src/open-api/json-metadata/schema.d.js.map +1 -0
  484. package/src/open-api/json-metadata/schema.d.ts +9 -0
  485. package/src/open-api/json-metadata/schema.json +20 -0
  486. package/src/open-api/ts-client/__snapshots__/generator.additional-properties.spec.ts.snap +147 -33
  487. package/src/open-api/ts-client/__snapshots__/generator.arrays.spec.ts.snap +308 -75
  488. package/src/open-api/ts-client/__snapshots__/generator.complex-types.spec.ts.snap +298 -74
  489. package/src/open-api/ts-client/__snapshots__/generator.composite-types.spec.ts.snap +748 -103
  490. package/src/open-api/ts-client/__snapshots__/generator.content-type.spec.ts.snap +230 -37
  491. package/src/open-api/ts-client/__snapshots__/generator.duplicate-types.spec.ts.snap +249 -52
  492. package/src/open-api/ts-client/__snapshots__/generator.edge-cases.spec.ts.snap +5535 -0
  493. package/src/open-api/ts-client/__snapshots__/generator.fast-api-gaps.spec.ts.snap +736 -0
  494. package/src/open-api/ts-client/__snapshots__/generator.fast-api.spec.ts.snap +305 -71
  495. package/src/open-api/ts-client/__snapshots__/generator.multipart.spec.ts.snap +298 -0
  496. package/src/open-api/ts-client/__snapshots__/generator.primitive-types.spec.ts.snap +353 -82
  497. package/src/open-api/ts-client/__snapshots__/generator.request.spec.ts.snap +176 -55
  498. package/src/open-api/ts-client/__snapshots__/generator.reserved-keywords.spec.ts.snap +100 -24
  499. package/src/open-api/ts-client/__snapshots__/generator.response.spec.ts.snap +147 -33
  500. package/src/open-api/ts-client/__snapshots__/generator.spec-compliance.spec.ts.snap +1883 -0
  501. package/src/open-api/ts-client/__snapshots__/generator.streaming.spec.ts.snap +392 -88
  502. package/src/open-api/ts-client/__snapshots__/generator.tags.spec.ts.snap +200 -48
  503. package/src/open-api/ts-client/files/client.gen.ts.template +358 -24
  504. package/src/open-api/ts-client/files/types.gen.ts.template +6 -4
  505. package/src/open-api/ts-client/generator.d.ts +1 -1
  506. package/src/open-api/ts-client/generator.js +41 -26
  507. package/src/open-api/ts-client/generator.js.map +1 -1
  508. package/src/open-api/ts-client/schema.d.js +6 -0
  509. package/src/open-api/ts-client/schema.d.js.map +1 -0
  510. package/src/open-api/ts-hooks/files/options-proxy.gen.ts.template +7 -2
  511. package/src/open-api/ts-hooks/generator.d.ts +1 -1
  512. package/src/open-api/ts-hooks/generator.js +17 -21
  513. package/src/open-api/ts-hooks/generator.js.map +1 -1
  514. package/src/open-api/ts-hooks/generator.spec.tsx +76 -6
  515. package/src/open-api/ts-hooks/schema.d.js +6 -0
  516. package/src/open-api/ts-hooks/schema.d.js.map +1 -0
  517. package/src/open-api/ts-metadata/generator.d.ts +1 -1
  518. package/src/open-api/ts-metadata/generator.js +12 -20
  519. package/src/open-api/ts-metadata/generator.js.map +1 -1
  520. package/src/open-api/ts-metadata/schema.d.js +6 -0
  521. package/src/open-api/ts-metadata/schema.d.js.map +1 -0
  522. package/src/open-api/utils/codegen-data/languages.d.ts +1 -7
  523. package/src/open-api/utils/codegen-data/languages.js +59 -66
  524. package/src/open-api/utils/codegen-data/languages.js.map +1 -1
  525. package/src/open-api/utils/codegen-data/types.d.ts +255 -10
  526. package/src/open-api/utils/codegen-data/types.js +54 -30
  527. package/src/open-api/utils/codegen-data/types.js.map +1 -1
  528. package/src/open-api/utils/codegen-data.d.ts +4 -4
  529. package/src/open-api/utils/codegen-data.js +494 -674
  530. package/src/open-api/utils/codegen-data.js.map +1 -1
  531. package/src/open-api/utils/normalise.d.ts +1 -1
  532. package/src/open-api/utils/normalise.js +437 -246
  533. package/src/open-api/utils/normalise.js.map +1 -1
  534. package/src/open-api/utils/parse.d.ts +1 -1
  535. package/src/open-api/utils/parse.js +8 -14
  536. package/src/open-api/utils/parse.js.map +1 -1
  537. package/src/open-api/utils/parser.d.ts +55 -0
  538. package/src/open-api/utils/parser.js +787 -0
  539. package/src/open-api/utils/parser.js.map +1 -0
  540. package/src/open-api/utils/refs.d.ts +1 -1
  541. package/src/open-api/utils/refs.js +12 -22
  542. package/src/open-api/utils/refs.js.map +1 -1
  543. package/src/open-api/utils/types.d.ts +18 -0
  544. package/src/open-api/utils/types.js +5 -2
  545. package/src/open-api/utils/types.js.map +1 -1
  546. package/src/preset/__snapshots__/generator.spec.ts.snap +104 -21
  547. package/src/preset/generator.d.ts +15 -2
  548. package/src/preset/generator.js +76 -151
  549. package/src/preset/generator.js.map +1 -1
  550. package/src/preset/schema.d.js +6 -0
  551. package/src/preset/schema.d.js.map +1 -0
  552. package/src/preset/schema.d.ts +6 -3
  553. package/src/preset/schema.json +16 -5
  554. package/src/py/agent/__snapshots__/generator.constructs.spec.ts.snap +931 -0
  555. package/src/py/agent/__snapshots__/generator.core.spec.ts.snap +186 -0
  556. package/src/py/agent/__snapshots__/generator.frameworks.spec.ts.snap +771 -0
  557. package/src/py/agent/__snapshots__/generator.protocols.spec.ts.snap +1322 -0
  558. package/src/py/agent/a2a-connection/__snapshots__/generator.spec.ts.snap +231 -0
  559. package/src/py/agent/a2a-connection/files/agent-connection/app/__targetAgentSnakeCase___client_strands.py.template +29 -0
  560. package/src/py/agent/a2a-connection/files/langchain/agent-connection/app/__targetAgentSnakeCase___client_langchain.py.template +29 -0
  561. package/src/py/agent/a2a-connection/generator.d.ts +25 -1
  562. package/src/py/agent/a2a-connection/generator.js +146 -95
  563. package/src/py/agent/a2a-connection/generator.js.map +1 -1
  564. package/src/py/agent/a2a-connection/schema.d.js +8 -0
  565. package/src/py/agent/a2a-connection/schema.d.js.map +1 -0
  566. package/src/py/agent/a2a-connection/schema.d.ts +2 -1
  567. package/src/py/agent/a2a-connection/schema.json +5 -0
  568. package/src/py/agent/files/deploy/Dockerfile.template +15 -1
  569. package/src/py/agent/files/langchain/a2a/main.py.template +83 -0
  570. package/src/py/agent/files/langchain/ag-ui/main.py.template +76 -0
  571. package/src/py/agent/files/langchain/common/agent.py.template +30 -0
  572. package/src/py/agent/files/langchain/common/middleware/__init__.py.template +0 -0
  573. package/src/py/agent/files/langchain/common/middleware/session_id_middleware.py.template +17 -0
  574. package/src/py/agent/files/langchain/common/session.py.template +61 -0
  575. package/src/py/agent/files/{http → langchain/http}/init.py.template +17 -25
  576. package/src/py/agent/files/langchain/http/main.py.template +69 -0
  577. package/src/py/agent/files/strands/a2a/main.py.template +61 -0
  578. package/src/py/agent/files/strands/ag-ui/main.py.template +86 -0
  579. package/src/py/agent/files/strands/common/__init__.py.template +0 -0
  580. package/src/py/agent/files/{common → strands/common}/agent.py.template +13 -2
  581. package/src/py/agent/files/strands/common/middleware/__init__.py.template +0 -0
  582. package/src/py/agent/files/strands/common/middleware/session_id_middleware.py.template +17 -0
  583. package/src/py/agent/files/strands/common/session.py.template +36 -0
  584. package/src/py/agent/files/strands/http/init.py.template +97 -0
  585. package/src/py/agent/files/{http → strands/http}/main.py.template +5 -29
  586. package/src/py/agent/gateway-connection/__snapshots__/generator.spec.ts.snap +184 -0
  587. package/src/py/agent/gateway-connection/files/agent-connection/app/__gatewaySnakeCase___client_strands.py.template +35 -0
  588. package/src/py/agent/gateway-connection/files/langchain/agent-connection/app/__gatewaySnakeCase___client_langchain.py.template +34 -0
  589. package/src/py/agent/gateway-connection/generator.d.ts +33 -0
  590. package/src/py/agent/gateway-connection/generator.js +153 -0
  591. package/src/py/agent/gateway-connection/generator.js.map +1 -0
  592. package/src/py/agent/gateway-connection/schema.d.js +6 -0
  593. package/src/py/agent/gateway-connection/schema.d.js.map +1 -0
  594. package/src/py/agent/gateway-connection/schema.d.ts +13 -0
  595. package/src/py/agent/gateway-connection/schema.json +31 -0
  596. package/src/py/agent/generator.d.ts +165 -2
  597. package/src/py/agent/generator.js +445 -199
  598. package/src/py/agent/generator.js.map +1 -1
  599. package/src/py/agent/mcp-connection/__snapshots__/generator.spec.ts.snap +155 -92
  600. package/src/py/agent/mcp-connection/files/agent-connection/app/__mcpServerSnakeCase___client_strands.py.template +31 -0
  601. package/src/py/agent/mcp-connection/files/langchain/agent-connection/app/__mcpServerSnakeCase___client_langchain.py.template +31 -0
  602. package/src/py/agent/mcp-connection/generator.d.ts +24 -1
  603. package/src/py/agent/mcp-connection/generator.js +103 -130
  604. package/src/py/agent/mcp-connection/generator.js.map +1 -1
  605. package/src/py/agent/mcp-connection/schema.d.js +8 -0
  606. package/src/py/agent/mcp-connection/schema.d.js.map +1 -0
  607. package/src/py/agent/mcp-connection/schema.d.ts +2 -1
  608. package/src/py/agent/mcp-connection/schema.json +5 -0
  609. package/src/py/agent/react-connection/__snapshots__/generator.spec.ts.snap +17 -19
  610. package/src/py/agent/react-connection/generator.d.ts +152 -3
  611. package/src/py/agent/react-connection/generator.js +114 -65
  612. package/src/py/agent/react-connection/generator.js.map +1 -1
  613. package/src/py/agent/react-connection/{serve-local.d.ts → local-dev.d.ts} +5 -5
  614. package/src/py/agent/react-connection/local-dev.js +36 -0
  615. package/src/py/agent/react-connection/local-dev.js.map +1 -0
  616. package/src/py/agent/react-connection/schema.d.js +8 -0
  617. package/src/py/agent/react-connection/schema.d.js.map +1 -0
  618. package/src/py/agent/react-connection/schema.d.ts +2 -1
  619. package/src/py/agent/react-connection/schema.json +5 -0
  620. package/src/py/agent/schema.d.js +6 -0
  621. package/src/py/agent/schema.d.js.map +1 -0
  622. package/src/py/agent/schema.d.ts +6 -2
  623. package/src/py/agent/schema.json +15 -1
  624. package/src/py/api/generator.d.ts +1 -1
  625. package/src/py/api/generator.js +12 -11
  626. package/src/py/api/generator.js.map +1 -1
  627. package/src/py/api/schema.d.js +6 -0
  628. package/src/py/api/schema.d.js.map +1 -0
  629. package/src/py/api/schema.d.ts +2 -1
  630. package/src/py/api/schema.json +5 -0
  631. package/src/py/dynamodb/__snapshots__/generator.spec.ts.snap +1592 -0
  632. package/src/py/dynamodb/agent-connection/__snapshots__/generator.spec.ts.snap +63 -0
  633. package/src/py/dynamodb/agent-connection/generator.d.ts +10 -0
  634. package/src/py/dynamodb/agent-connection/generator.js +45 -0
  635. package/src/py/dynamodb/agent-connection/generator.js.map +1 -0
  636. package/src/py/dynamodb/agent-connection/schema.d.js +8 -0
  637. package/src/py/dynamodb/agent-connection/schema.d.js.map +1 -0
  638. package/src/py/dynamodb/agent-connection/schema.d.ts +15 -0
  639. package/src/py/dynamodb/agent-connection/schema.json +27 -0
  640. package/src/py/dynamodb/fast-api-connection/__snapshots__/generator.spec.ts.snap +32 -0
  641. package/src/py/dynamodb/fast-api-connection/generator.d.ts +10 -0
  642. package/src/py/dynamodb/fast-api-connection/generator.js +39 -0
  643. package/src/py/dynamodb/fast-api-connection/generator.js.map +1 -0
  644. package/src/py/dynamodb/fast-api-connection/schema.d.js +8 -0
  645. package/src/py/dynamodb/fast-api-connection/schema.d.js.map +1 -0
  646. package/src/py/dynamodb/fast-api-connection/schema.d.ts +13 -0
  647. package/src/py/dynamodb/fast-api-connection/schema.json +23 -0
  648. package/src/py/dynamodb/files/__name__/__init__.py.template +6 -0
  649. package/src/py/dynamodb/files/__name__/client.py.template +39 -0
  650. package/src/py/dynamodb/files/__name__/entities/__init__.py.template +4 -0
  651. package/src/py/dynamodb/files/__name__/entities/base.py.template +63 -0
  652. package/src/py/dynamodb/files/__name__/entities/example.py.template +64 -0
  653. package/src/py/dynamodb/files/config.json.template +24 -0
  654. package/src/py/dynamodb/generator.d.ts +39 -0
  655. package/src/py/dynamodb/generator.js +130 -0
  656. package/src/py/dynamodb/generator.js.map +1 -0
  657. package/src/py/dynamodb/mcp-server-connection/__snapshots__/generator.spec.ts.snap +63 -0
  658. package/src/py/dynamodb/mcp-server-connection/generator.d.ts +10 -0
  659. package/src/py/dynamodb/mcp-server-connection/generator.js +45 -0
  660. package/src/py/dynamodb/mcp-server-connection/generator.js.map +1 -0
  661. package/src/py/dynamodb/mcp-server-connection/schema.d.js +8 -0
  662. package/src/py/dynamodb/mcp-server-connection/schema.d.js.map +1 -0
  663. package/src/py/dynamodb/mcp-server-connection/schema.d.ts +15 -0
  664. package/src/py/dynamodb/mcp-server-connection/schema.json +27 -0
  665. package/src/py/dynamodb/schema.d.js +6 -0
  666. package/src/py/dynamodb/schema.d.js.map +1 -0
  667. package/src/py/dynamodb/schema.d.ts +16 -0
  668. package/src/py/dynamodb/schema.json +76 -0
  669. package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +240 -4693
  670. package/src/py/fast-api/__snapshots__/generator.terraform.spec.ts.snap +6661 -0
  671. package/src/py/fast-api/files/app/__name__/init.py.template +5 -22
  672. package/src/py/fast-api/files/app/__name__/main.py.template +4 -1
  673. package/src/py/fast-api/generator.d.ts +73 -1
  674. package/src/py/fast-api/generator.js +126 -92
  675. package/src/py/fast-api/generator.js.map +1 -1
  676. package/src/py/fast-api/react/__snapshots__/generator.spec.ts.snap +4 -3
  677. package/src/py/fast-api/react/generator.d.ts +17 -2
  678. package/src/py/fast-api/react/generator.js +38 -26
  679. package/src/py/fast-api/react/generator.js.map +1 -1
  680. package/src/py/fast-api/react/open-api.js +44 -33
  681. package/src/py/fast-api/react/open-api.js.map +1 -1
  682. package/src/py/fast-api/react/schema.d.js +6 -0
  683. package/src/py/fast-api/react/schema.d.js.map +1 -0
  684. package/src/py/fast-api/react/schema.d.ts +1 -0
  685. package/src/py/fast-api/react/schema.json +5 -0
  686. package/src/py/fast-api/schema.d.js +6 -0
  687. package/src/py/fast-api/schema.d.js.map +1 -0
  688. package/src/py/fast-api/schema.d.ts +2 -1
  689. package/src/py/fast-api/schema.json +6 -0
  690. package/src/py/lambda-function/__snapshots__/generator.spec.ts.snap +12 -4
  691. package/src/py/lambda-function/files/handler/__lambdaFunctionSnakeCase__.py.template +1 -1
  692. package/src/py/lambda-function/generator.d.ts +28 -1
  693. package/src/py/lambda-function/generator.js +88 -61
  694. package/src/py/lambda-function/generator.js.map +1 -1
  695. package/src/py/lambda-function/schema.d.js +6 -0
  696. package/src/py/lambda-function/schema.d.js.map +1 -0
  697. package/src/py/lambda-function/schema.d.ts +3 -2
  698. package/src/py/lambda-function/schema.json +6 -1
  699. package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +225 -21
  700. package/src/py/mcp-server/files/deploy/Dockerfile.template +15 -1
  701. package/src/py/mcp-server/generator.d.ts +69 -1
  702. package/src/py/mcp-server/generator.js +211 -137
  703. package/src/py/mcp-server/generator.js.map +1 -1
  704. package/src/py/mcp-server/schema.d.js +6 -0
  705. package/src/py/mcp-server/schema.d.js.map +1 -0
  706. package/src/py/mcp-server/schema.d.ts +2 -1
  707. package/src/py/mcp-server/schema.json +6 -0
  708. package/src/py/project/generator.d.ts +37 -2
  709. package/src/py/project/generator.js +246 -90
  710. package/src/py/project/generator.js.map +1 -1
  711. package/src/py/project/schema.d.js +6 -0
  712. package/src/py/project/schema.d.js.map +1 -0
  713. package/src/py/project/schema.d.ts +1 -0
  714. package/src/py/project/schema.json +6 -0
  715. package/src/py/rdb/__snapshots__/generator.spec.ts.snap +2113 -0
  716. package/src/py/rdb/agent-connection/__snapshots__/generator.spec.ts.snap +123 -0
  717. package/src/py/rdb/agent-connection/generator.d.ts +10 -0
  718. package/src/py/rdb/agent-connection/generator.js +52 -0
  719. package/src/py/rdb/agent-connection/generator.js.map +1 -0
  720. package/src/py/rdb/agent-connection/schema.d.js +8 -0
  721. package/src/py/rdb/agent-connection/schema.d.js.map +1 -0
  722. package/src/py/rdb/agent-connection/schema.d.ts +15 -0
  723. package/src/py/rdb/agent-connection/schema.json +27 -0
  724. package/src/py/rdb/fast-api-connection/__snapshots__/generator.spec.ts.snap +51 -0
  725. package/src/py/rdb/fast-api-connection/files/__apiModuleName__/dependencies/__rdbNameSnake__.py.template +14 -0
  726. package/src/py/rdb/fast-api-connection/generator.d.ts +10 -0
  727. package/src/py/rdb/fast-api-connection/generator.js +56 -0
  728. package/src/py/rdb/fast-api-connection/generator.js.map +1 -0
  729. package/src/py/rdb/fast-api-connection/schema.d.js +8 -0
  730. package/src/py/rdb/fast-api-connection/schema.d.js.map +1 -0
  731. package/src/py/rdb/fast-api-connection/schema.d.ts +13 -0
  732. package/src/py/rdb/fast-api-connection/schema.json +23 -0
  733. package/src/py/rdb/files/Dockerfile.create-db-user.template +14 -0
  734. package/src/py/rdb/files/Dockerfile.migration.template +14 -0
  735. package/src/py/rdb/files/__name__/connection.py.template +55 -0
  736. package/src/py/rdb/files/__name__/create_db_user_handler.py.template +97 -0
  737. package/src/py/rdb/files/__name__/migration_handler.py.template +36 -0
  738. package/src/py/rdb/files/__name__/models/__init__.py.template +3 -0
  739. package/src/py/rdb/files/__name__/models/example.py.template +8 -0
  740. package/src/py/rdb/files/__name__/utils.py.template +120 -0
  741. package/src/py/rdb/files/alembic.ini.template +38 -0
  742. package/src/py/rdb/files/migrations/env.py.template +80 -0
  743. package/src/py/rdb/files/migrations/script.py.mako.template +28 -0
  744. package/src/py/rdb/generator.d.ts +103 -0
  745. package/src/py/rdb/generator.js +368 -0
  746. package/src/py/rdb/generator.js.map +1 -0
  747. package/src/py/rdb/mcp-server-connection/__snapshots__/generator.spec.ts.snap +119 -0
  748. package/src/py/rdb/mcp-server-connection/generator.d.ts +10 -0
  749. package/src/py/rdb/mcp-server-connection/generator.js +52 -0
  750. package/src/py/rdb/mcp-server-connection/generator.js.map +1 -0
  751. package/src/py/rdb/mcp-server-connection/schema.d.js +8 -0
  752. package/src/py/rdb/mcp-server-connection/schema.d.js.map +1 -0
  753. package/src/py/rdb/mcp-server-connection/schema.d.ts +15 -0
  754. package/src/py/rdb/mcp-server-connection/schema.json +27 -0
  755. package/src/py/rdb/schema.d.js +6 -0
  756. package/src/py/rdb/schema.d.js.map +1 -0
  757. package/src/py/rdb/schema.d.ts +16 -0
  758. package/src/py/rdb/schema.json +78 -0
  759. package/src/py/rdb/utils.d.ts +6 -0
  760. package/src/py/rdb/utils.js +16 -0
  761. package/src/py/rdb/utils.js.map +1 -0
  762. package/src/sdk/agentcore-gateway.d.ts +6 -0
  763. package/src/sdk/agentcore-gateway.js +7 -0
  764. package/src/sdk/agentcore-gateway.js.map +1 -0
  765. package/src/sdk/agentcore-harness.d.ts +6 -0
  766. package/src/sdk/agentcore-harness.js +7 -0
  767. package/src/sdk/agentcore-harness.js.map +1 -0
  768. package/src/sdk/connection.d.ts +6 -0
  769. package/src/sdk/connection.js +6 -0
  770. package/src/sdk/connection.js.map +1 -0
  771. package/src/sdk/license.d.ts +8 -0
  772. package/src/sdk/license.js +7 -0
  773. package/src/sdk/license.js.map +1 -0
  774. package/src/sdk/open-api.d.ts +12 -0
  775. package/src/sdk/open-api.js +9 -0
  776. package/src/sdk/open-api.js.map +1 -0
  777. package/src/sdk/py.d.ts +18 -0
  778. package/src/sdk/py.js +12 -0
  779. package/src/sdk/py.js.map +1 -0
  780. package/src/sdk/smithy.d.ts +6 -0
  781. package/src/sdk/smithy.js +7 -0
  782. package/src/sdk/smithy.js.map +1 -0
  783. package/src/sdk/terraform.d.ts +6 -0
  784. package/src/sdk/terraform.js +7 -0
  785. package/src/sdk/terraform.js.map +1 -0
  786. package/src/sdk/ts.d.ts +39 -0
  787. package/src/sdk/ts.js +41 -0
  788. package/src/sdk/ts.js.map +1 -0
  789. package/{sdk/utils/format.d.ts → src/sdk/utils/ast.d.ts} +1 -1
  790. package/src/sdk/utils/ast.js +6 -0
  791. package/src/sdk/utils/ast.js.map +1 -0
  792. package/src/sdk/utils/format.d.ts +5 -0
  793. package/src/sdk/utils/format.js +6 -0
  794. package/src/sdk/utils/format.js.map +1 -0
  795. package/{sdk → src/sdk}/utils/test.d.ts +1 -1
  796. package/src/sdk/utils/test.js +6 -0
  797. package/src/sdk/utils/test.js.map +1 -0
  798. package/src/smithy/project/__snapshots__/generator.spec.ts.snap +244 -226
  799. package/src/smithy/project/files/{smithy-build.json.template → service/smithy-build.json.template} +5 -5
  800. package/src/smithy/project/files/{src → service/src}/operations/echo.smithy.template +1 -0
  801. package/src/smithy/project/files/shapes/smithy-build.json.template +7 -0
  802. package/src/smithy/project/files/shapes/src/main.smithy.template +11 -0
  803. package/src/smithy/project/files/ssdk-bundle/ssdk.rolldown.config.mjs.template +52 -0
  804. package/src/smithy/project/generator.d.ts +91 -1
  805. package/src/smithy/project/generator.js +220 -45
  806. package/src/smithy/project/generator.js.map +1 -1
  807. package/src/smithy/project/schema.d.js +6 -0
  808. package/src/smithy/project/schema.d.js.map +1 -0
  809. package/src/smithy/project/schema.d.ts +2 -0
  810. package/src/smithy/project/schema.json +28 -1
  811. package/src/smithy/react-connection/__snapshots__/generator.spec.ts.snap +1 -1
  812. package/src/smithy/react-connection/generator.d.ts +16 -1
  813. package/src/smithy/react-connection/generator.js +54 -48
  814. package/src/smithy/react-connection/generator.js.map +1 -1
  815. package/src/smithy/react-connection/schema.d.js +6 -0
  816. package/src/smithy/react-connection/schema.d.js.map +1 -0
  817. package/src/smithy/react-connection/schema.d.ts +1 -0
  818. package/src/smithy/react-connection/schema.json +5 -0
  819. package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +2618 -550
  820. package/src/smithy/ts/api/files/handler.ts.template +3 -3
  821. package/src/smithy/ts/api/files/local-server.ts.template +3 -3
  822. package/src/smithy/ts/api/files/operations/echo.ts.template +2 -2
  823. package/src/smithy/ts/api/files/service.ts.template +3 -3
  824. package/src/smithy/ts/api/generator.d.ts +89 -1
  825. package/src/smithy/ts/api/generator.js +185 -119
  826. package/src/smithy/ts/api/generator.js.map +1 -1
  827. package/src/smithy/ts/api/schema.d.js +6 -0
  828. package/src/smithy/ts/api/schema.d.js.map +1 -0
  829. package/src/smithy/ts/api/schema.d.ts +2 -1
  830. package/src/smithy/ts/api/schema.json +6 -0
  831. package/src/terraform/project/files/application/bootstrap/providers.tf.template +11 -0
  832. package/src/terraform/project/files/application/scripts/bootstrap-destroy.ts.template +111 -0
  833. package/src/terraform/project/files/application/scripts/bootstrap.ts.template +40 -1
  834. package/src/terraform/project/files/application/scripts/env.ts.template +11 -0
  835. package/src/terraform/project/files/application/scripts/init.ts.template +2 -1
  836. package/src/terraform/project/files/application/src/main.tf.template +6 -1
  837. package/src/terraform/project/files/application/src/providers.tf.template +9 -3
  838. package/src/terraform/project/files/checkov/checkov.yml.template +8 -0
  839. package/src/terraform/project/files/library/src/main.tf.template +9 -0
  840. package/src/terraform/project/generator.d.ts +62 -2
  841. package/src/terraform/project/generator.js +340 -117
  842. package/src/terraform/project/generator.js.map +1 -1
  843. package/src/terraform/project/schema.d.js +9 -0
  844. package/src/terraform/project/schema.d.js.map +1 -0
  845. package/src/terraform/project/schema.d.ts +1 -0
  846. package/src/terraform/project/schema.json +5 -0
  847. package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +2957 -725
  848. package/src/trpc/backend/files/src/client/index.ts.template +1 -1
  849. package/src/trpc/backend/files/src/handler.ts.template +1 -1
  850. package/src/trpc/backend/files/src/index.ts.template +5 -5
  851. package/src/trpc/backend/files/src/init.ts.template +1 -1
  852. package/src/trpc/backend/files/src/local-server.ts.template +1 -1
  853. package/src/trpc/backend/files/src/middleware/index.ts.template +7 -7
  854. package/src/trpc/backend/files/src/procedures/echo.ts.template +3 -3
  855. package/src/trpc/backend/files/src/router.ts.template +2 -2
  856. package/src/trpc/backend/files/src/schema/echo.ts.template +2 -2
  857. package/src/trpc/backend/files/src/schema/index.ts.template +1 -1
  858. package/src/trpc/backend/files-operations/scripts/generate-operations.ts.template +40 -0
  859. package/src/trpc/backend/generator.d.ts +91 -2
  860. package/src/trpc/backend/generator.js +186 -100
  861. package/src/trpc/backend/generator.js.map +1 -1
  862. package/src/trpc/backend/schema.d.js +6 -0
  863. package/src/trpc/backend/schema.d.js.map +1 -0
  864. package/src/trpc/backend/schema.d.ts +2 -1
  865. package/src/trpc/backend/schema.json +6 -0
  866. package/src/trpc/react/__snapshots__/generator.spec.ts.snap +4 -4
  867. package/src/trpc/react/files/src/components/__apiNameClassName__ClientProvider.tsx.template +3 -3
  868. package/src/trpc/react/generator.d.ts +39 -2
  869. package/src/trpc/react/generator.js +105 -63
  870. package/src/trpc/react/generator.js.map +1 -1
  871. package/src/trpc/react/schema.d.js +6 -0
  872. package/src/trpc/react/schema.d.js.map +1 -0
  873. package/src/trpc/react/schema.d.ts +1 -0
  874. package/src/trpc/react/schema.json +5 -0
  875. package/src/ts/agent/__snapshots__/generator.spec.ts.snap +1490 -31
  876. package/src/ts/agent/a2a-connection/__snapshots__/generator.spec.ts.snap +258 -0
  877. package/src/ts/agent/a2a-connection/files/agent-connection/app/__targetAgentKebabCase__-client-strands.ts.template +23 -0
  878. package/src/ts/agent/a2a-connection/generator.d.ts +47 -1
  879. package/src/ts/agent/a2a-connection/generator.js +112 -63
  880. package/src/ts/agent/a2a-connection/generator.js.map +1 -1
  881. package/src/ts/agent/a2a-connection/schema.d.js +8 -0
  882. package/src/ts/agent/a2a-connection/schema.d.js.map +1 -0
  883. package/src/ts/agent/a2a-connection/schema.d.ts +2 -1
  884. package/src/ts/agent/a2a-connection/schema.json +5 -0
  885. package/src/ts/agent/files/a2a/index.ts.template +5 -12
  886. package/src/ts/agent/files/ag-ui/index.ts.template +29 -14
  887. package/src/ts/agent/files/common/agent.ts.template +11 -2
  888. package/src/ts/agent/files/common/session.ts.template +43 -0
  889. package/src/ts/agent/files/common-express/middleware/session-id-middleware.ts.template +16 -0
  890. package/src/ts/agent/files/deploy/Dockerfile.template +20 -2
  891. package/src/ts/agent/files/http/client.ts.template +2 -2
  892. package/src/ts/agent/files/http/index.ts.template +2 -2
  893. package/src/ts/agent/files/http/router.ts.template +5 -5
  894. package/src/ts/agent/gateway-connection/__snapshots__/generator.spec.ts.snap +222 -0
  895. package/src/ts/agent/gateway-connection/files/agent-connection/app/__gatewayKebabCase__-client-strands.ts.template +30 -0
  896. package/src/ts/agent/gateway-connection/generator.d.ts +47 -0
  897. package/src/ts/agent/gateway-connection/generator.js +144 -0
  898. package/src/ts/agent/gateway-connection/generator.js.map +1 -0
  899. package/src/ts/agent/gateway-connection/schema.d.js +6 -0
  900. package/src/ts/agent/gateway-connection/schema.d.js.map +1 -0
  901. package/src/ts/agent/gateway-connection/schema.d.ts +13 -0
  902. package/src/ts/agent/gateway-connection/schema.json +31 -0
  903. package/src/ts/agent/generator.d.ts +206 -1
  904. package/src/ts/agent/generator.js +342 -159
  905. package/src/ts/agent/generator.js.map +1 -1
  906. package/src/ts/agent/mcp-connection/__snapshots__/generator.spec.ts.snap +194 -114
  907. package/src/ts/agent/mcp-connection/files/agent-connection/app/__mcpServerKebabCase__-client-strands.ts.template +23 -0
  908. package/src/ts/agent/mcp-connection/generator.d.ts +38 -1
  909. package/src/ts/agent/mcp-connection/generator.js +88 -65
  910. package/src/ts/agent/mcp-connection/generator.js.map +1 -1
  911. package/src/ts/agent/mcp-connection/schema.d.js +8 -0
  912. package/src/ts/agent/mcp-connection/schema.d.js.map +1 -0
  913. package/src/ts/agent/mcp-connection/schema.d.ts +2 -1
  914. package/src/ts/agent/mcp-connection/schema.json +5 -0
  915. package/src/ts/agent/react-connection/__snapshots__/generator.spec.ts.snap +8 -4
  916. package/src/ts/agent/react-connection/files/src/components/__agentNameClassName__AgentClientProvider.tsx.template +2 -1
  917. package/src/ts/agent/react-connection/generator.d.ts +204 -3
  918. package/src/ts/agent/react-connection/generator.js +158 -99
  919. package/src/ts/agent/react-connection/generator.js.map +1 -1
  920. package/src/ts/agent/react-connection/local-dev.d.ts +13 -0
  921. package/src/ts/agent/react-connection/local-dev.js +17 -0
  922. package/src/ts/agent/react-connection/local-dev.js.map +1 -0
  923. package/src/ts/agent/react-connection/schema.d.js +8 -0
  924. package/src/ts/agent/react-connection/schema.d.js.map +1 -0
  925. package/src/ts/agent/react-connection/schema.d.ts +2 -1
  926. package/src/ts/agent/react-connection/schema.json +5 -0
  927. package/src/ts/agent/schema.d.js +6 -0
  928. package/src/ts/agent/schema.d.js.map +1 -0
  929. package/src/ts/agent/schema.d.ts +5 -1
  930. package/src/ts/agent/schema.json +14 -0
  931. package/src/ts/api/generator.d.ts +1 -1
  932. package/src/ts/api/generator.js +16 -16
  933. package/src/ts/api/generator.js.map +1 -1
  934. package/src/ts/api/schema.d.js +6 -0
  935. package/src/ts/api/schema.d.js.map +1 -0
  936. package/src/ts/api/schema.d.ts +2 -1
  937. package/src/ts/api/schema.json +5 -0
  938. package/src/ts/astro-docs/__snapshots__/generator.spec.ts.snap +22 -8
  939. package/src/ts/astro-docs/files/base/src/content/docs/en/index.mdx.template +2 -2
  940. package/src/ts/astro-docs/files/base/src/content.config.ts.template +4 -4
  941. package/src/ts/astro-docs/files/translation/scripts/translate.ts.template +272 -150
  942. package/src/ts/astro-docs/generator.d.ts +41 -1
  943. package/src/ts/astro-docs/generator.js +133 -59
  944. package/src/ts/astro-docs/generator.js.map +1 -1
  945. package/src/ts/astro-docs/schema.d.js +6 -0
  946. package/src/ts/astro-docs/schema.d.js.map +1 -0
  947. package/src/ts/astro-docs/schema.d.ts +2 -2
  948. package/src/ts/astro-docs/schema.json +3 -3
  949. package/src/ts/dcr-proxy/__snapshots__/generator.spec.ts.snap +986 -0
  950. package/src/ts/dcr-proxy/generator.d.ts +34 -0
  951. package/src/ts/dcr-proxy/generator.js +115 -0
  952. package/src/ts/dcr-proxy/generator.js.map +1 -0
  953. package/src/ts/dcr-proxy/schema.d.js +6 -0
  954. package/src/ts/dcr-proxy/schema.d.js.map +1 -0
  955. package/src/ts/dcr-proxy/schema.d.ts +13 -0
  956. package/src/ts/dcr-proxy/schema.json +44 -0
  957. package/src/ts/docs/generator.d.ts +1 -1
  958. package/src/ts/docs/generator.js +10 -10
  959. package/src/ts/docs/generator.js.map +1 -1
  960. package/src/ts/docs/schema.d.js +6 -0
  961. package/src/ts/docs/schema.d.js.map +1 -0
  962. package/src/ts/docs/schema.d.ts +1 -4
  963. package/src/ts/docs/schema.json +3 -3
  964. package/src/ts/dynamodb/__snapshots__/generator.spec.ts.snap +481 -527
  965. package/src/ts/dynamodb/agent-connection/__snapshots__/generator.spec.ts.snap +23 -5
  966. package/src/ts/dynamodb/agent-connection/generator.d.ts +1 -1
  967. package/src/ts/dynamodb/agent-connection/generator.js +27 -23
  968. package/src/ts/dynamodb/agent-connection/generator.js.map +1 -1
  969. package/src/ts/dynamodb/agent-connection/schema.d.js +8 -0
  970. package/src/ts/dynamodb/agent-connection/schema.d.js.map +1 -0
  971. package/src/ts/dynamodb/agent-connection/schema.d.ts +2 -1
  972. package/src/ts/dynamodb/agent-connection/schema.json +5 -0
  973. package/src/ts/dynamodb/files/config.json.template +24 -0
  974. package/src/ts/dynamodb/files/src/client.ts.template +30 -11
  975. package/src/ts/dynamodb/files/src/entities/example.ts.template +1 -1
  976. package/src/ts/dynamodb/files/src/entities/index.ts.template +1 -1
  977. package/src/ts/dynamodb/files/src/index.ts.template +2 -11
  978. package/src/ts/dynamodb/generator.d.ts +42 -1
  979. package/src/ts/dynamodb/generator.js +110 -64
  980. package/src/ts/dynamodb/generator.js.map +1 -1
  981. package/src/ts/dynamodb/mcp-server-connection/__snapshots__/generator.spec.ts.snap +23 -5
  982. package/src/ts/dynamodb/mcp-server-connection/generator.d.ts +1 -1
  983. package/src/ts/dynamodb/mcp-server-connection/generator.js +27 -23
  984. package/src/ts/dynamodb/mcp-server-connection/generator.js.map +1 -1
  985. package/src/ts/dynamodb/mcp-server-connection/schema.d.js +8 -0
  986. package/src/ts/dynamodb/mcp-server-connection/schema.d.js.map +1 -0
  987. package/src/ts/dynamodb/mcp-server-connection/schema.d.ts +2 -1
  988. package/src/ts/dynamodb/mcp-server-connection/schema.json +5 -0
  989. package/src/ts/dynamodb/schema.d.js +6 -0
  990. package/src/ts/dynamodb/schema.d.js.map +1 -0
  991. package/src/ts/dynamodb/schema.d.ts +3 -1
  992. package/src/ts/dynamodb/schema.json +26 -2
  993. package/src/ts/dynamodb/smithy-connection/__snapshots__/generator.spec.ts.snap +12 -3
  994. package/src/ts/dynamodb/smithy-connection/generator.d.ts +1 -1
  995. package/src/ts/dynamodb/smithy-connection/generator.js +22 -22
  996. package/src/ts/dynamodb/smithy-connection/generator.js.map +1 -1
  997. package/src/ts/dynamodb/smithy-connection/schema.d.js +8 -0
  998. package/src/ts/dynamodb/smithy-connection/schema.d.js.map +1 -0
  999. package/src/ts/dynamodb/smithy-connection/schema.d.ts +1 -0
  1000. package/src/ts/dynamodb/smithy-connection/schema.json +5 -0
  1001. package/src/ts/dynamodb/trpc-connection/__snapshots__/generator.spec.ts.snap +12 -3
  1002. package/src/ts/dynamodb/trpc-connection/generator.d.ts +1 -1
  1003. package/src/ts/dynamodb/trpc-connection/generator.js +22 -22
  1004. package/src/ts/dynamodb/trpc-connection/generator.js.map +1 -1
  1005. package/src/ts/dynamodb/trpc-connection/schema.d.js +8 -0
  1006. package/src/ts/dynamodb/trpc-connection/schema.d.js.map +1 -0
  1007. package/src/ts/dynamodb/trpc-connection/schema.d.ts +1 -0
  1008. package/src/ts/dynamodb/trpc-connection/schema.json +5 -0
  1009. package/src/ts/lambda-function/__snapshots__/generator.spec.ts.snap +19 -9
  1010. package/src/ts/lambda-function/files/handler/__lambdaFunctionKebabCase__.ts.template +2 -1
  1011. package/src/ts/lambda-function/generator.d.ts +39 -1
  1012. package/src/ts/lambda-function/generator.js +98 -65
  1013. package/src/ts/lambda-function/generator.js.map +1 -1
  1014. package/src/ts/lambda-function/io.js +83 -52
  1015. package/src/ts/lambda-function/io.js.map +1 -1
  1016. package/src/ts/lambda-function/schema.d.js +6 -0
  1017. package/src/ts/lambda-function/schema.d.js.map +1 -0
  1018. package/src/ts/lambda-function/schema.d.ts +2 -1
  1019. package/src/ts/lambda-function/schema.json +5 -0
  1020. package/src/ts/lib/__snapshots__/generator.spec.ts.snap +83 -36
  1021. package/src/ts/lib/__snapshots__/vitest.spec.ts.snap +2 -2
  1022. package/src/ts/lib/biome.d.ts +1 -1
  1023. package/src/ts/lib/biome.js +71 -38
  1024. package/src/ts/lib/biome.js.map +1 -1
  1025. package/src/ts/lib/generator.d.ts +24 -1
  1026. package/src/ts/lib/generator.js +128 -94
  1027. package/src/ts/lib/generator.js.map +1 -1
  1028. package/src/ts/lib/grit/vitest-pass-with-no-tests.grit +1 -4
  1029. package/src/ts/lib/schema.d.js +6 -0
  1030. package/src/ts/lib/schema.d.js.map +1 -0
  1031. package/src/ts/lib/schema.d.ts +1 -1
  1032. package/src/ts/lib/schema.json +5 -0
  1033. package/src/ts/lib/ts-project-utils.d.ts +4 -2
  1034. package/src/ts/lib/ts-project-utils.js +85 -74
  1035. package/src/ts/lib/ts-project-utils.js.map +1 -1
  1036. package/src/ts/lib/types.d.ts +6 -0
  1037. package/src/ts/lib/types.js +5 -2
  1038. package/src/ts/lib/types.js.map +1 -1
  1039. package/src/ts/lib/vitest.d.ts +22 -2
  1040. package/src/ts/lib/vitest.js +83 -26
  1041. package/src/ts/lib/vitest.js.map +1 -1
  1042. package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +225 -44
  1043. package/src/ts/mcp-server/files/Dockerfile.template +20 -2
  1044. package/src/ts/mcp-server/generator.d.ts +111 -1
  1045. package/src/ts/mcp-server/generator.js +234 -168
  1046. package/src/ts/mcp-server/generator.js.map +1 -1
  1047. package/src/ts/mcp-server/schema.d.js +6 -0
  1048. package/src/ts/mcp-server/schema.d.js.map +1 -0
  1049. package/src/ts/mcp-server/schema.d.ts +2 -1
  1050. package/src/ts/mcp-server/schema.json +6 -0
  1051. package/src/ts/nx-generator/__snapshots__/generator.spec.ts.snap +28 -13
  1052. package/src/ts/nx-generator/files/common/schema.d.ts.template +1 -0
  1053. package/src/ts/nx-generator/files/common/schema.json.template +5 -0
  1054. package/src/ts/nx-generator/files/local/generator.spec.ts.template +1 -1
  1055. package/src/ts/nx-generator/files/local/generator.ts.template +3 -3
  1056. package/src/ts/nx-generator/files/nx-plugin-for-aws/generator/generator.ts.template +6 -7
  1057. package/src/ts/nx-generator/generator.d.ts +3 -0
  1058. package/src/ts/nx-generator/generator.js +122 -75
  1059. package/src/ts/nx-generator/generator.js.map +1 -1
  1060. package/src/ts/nx-generator/schema.d.js +6 -0
  1061. package/src/ts/nx-generator/schema.d.js.map +1 -0
  1062. package/src/ts/nx-generator/schema.d.ts +1 -0
  1063. package/src/ts/nx-generator/schema.json +5 -0
  1064. package/src/ts/nx-migration/files/migration.spec.ts.template +26 -0
  1065. package/src/ts/nx-migration/files/migration.ts.template +65 -0
  1066. package/src/ts/nx-migration/files/prompt.md.template +46 -0
  1067. package/src/ts/nx-migration/generator.d.ts +27 -0
  1068. package/src/ts/nx-migration/generator.js +147 -0
  1069. package/src/ts/nx-migration/generator.js.map +1 -0
  1070. package/src/ts/nx-migration/schema.d.js +6 -0
  1071. package/src/ts/nx-migration/schema.d.js.map +1 -0
  1072. package/src/ts/nx-migration/schema.d.ts +13 -0
  1073. package/src/ts/nx-migration/schema.json +63 -0
  1074. package/src/ts/nx-plugin/__snapshots__/generator.spec.ts.snap +36 -25
  1075. package/src/ts/nx-plugin/files/mcp-server/resources/GENERAL_GUIDANCE.md.template +5 -6
  1076. package/src/ts/nx-plugin/files/mcp-server/server.ts.template +5 -5
  1077. package/src/ts/nx-plugin/files/mcp-server/tools/create-workspace-command.ts.template +2 -2
  1078. package/src/ts/nx-plugin/files/mcp-server/tools/general-guidance.ts.template +1 -1
  1079. package/src/ts/nx-plugin/files/mcp-server/tools/generator-guide.ts.template +2 -2
  1080. package/src/ts/nx-plugin/files/mcp-server/tools/list-generators.ts.template +2 -2
  1081. package/src/ts/nx-plugin/files/mcp-server/utils.ts.template +4 -4
  1082. package/src/ts/nx-plugin/generator.d.ts +4 -1
  1083. package/src/ts/nx-plugin/generator.js +64 -48
  1084. package/src/ts/nx-plugin/generator.js.map +1 -1
  1085. package/src/ts/nx-plugin/schema.d.js +9 -0
  1086. package/src/ts/nx-plugin/schema.d.js.map +1 -0
  1087. package/src/ts/nx-plugin/schema.json +5 -0
  1088. package/src/ts/nx-plugin/utils.d.ts +27 -2
  1089. package/src/ts/nx-plugin/utils.js +66 -57
  1090. package/src/ts/nx-plugin/utils.js.map +1 -1
  1091. package/src/ts/rdb/__snapshots__/generator.spec.ts.snap +711 -877
  1092. package/src/ts/rdb/agent-connection/__snapshots__/generator.spec.ts.snap +16 -6
  1093. package/src/ts/rdb/agent-connection/generator.d.ts +1 -1
  1094. package/src/ts/rdb/agent-connection/generator.js +44 -39
  1095. package/src/ts/rdb/agent-connection/generator.js.map +1 -1
  1096. package/src/ts/rdb/agent-connection/schema.d.js +8 -0
  1097. package/src/ts/rdb/agent-connection/schema.d.js.map +1 -0
  1098. package/src/ts/rdb/agent-connection/schema.d.ts +2 -1
  1099. package/src/ts/rdb/agent-connection/schema.json +5 -0
  1100. package/src/ts/rdb/files/Dockerfile.template +19 -1
  1101. package/src/ts/rdb/files/prisma.config.ts.template +6 -5
  1102. package/src/ts/rdb/files/src/create-db-user-handler.ts.template +9 -11
  1103. package/src/ts/rdb/files/src/migration-handler.ts.template +5 -30
  1104. package/src/ts/rdb/files/src/prisma.ts.template +33 -25
  1105. package/src/ts/rdb/files/src/utils.ts.template +31 -1
  1106. package/src/ts/rdb/generator.d.ts +118 -5
  1107. package/src/ts/rdb/generator.js +272 -127
  1108. package/src/ts/rdb/generator.js.map +1 -1
  1109. package/src/ts/rdb/mcp-server-connection/__snapshots__/generator.spec.ts.snap +16 -6
  1110. package/src/ts/rdb/mcp-server-connection/generator.d.ts +1 -1
  1111. package/src/ts/rdb/mcp-server-connection/generator.js +42 -37
  1112. package/src/ts/rdb/mcp-server-connection/generator.js.map +1 -1
  1113. package/src/ts/rdb/mcp-server-connection/schema.d.js +8 -0
  1114. package/src/ts/rdb/mcp-server-connection/schema.d.js.map +1 -0
  1115. package/src/ts/rdb/mcp-server-connection/schema.d.ts +2 -1
  1116. package/src/ts/rdb/mcp-server-connection/schema.json +5 -0
  1117. package/src/ts/rdb/schema.d.js +6 -0
  1118. package/src/ts/rdb/schema.d.js.map +1 -0
  1119. package/src/ts/rdb/schema.d.ts +2 -1
  1120. package/src/ts/rdb/schema.json +7 -1
  1121. package/src/ts/rdb/smithy-connection/__snapshots__/generator.spec.ts.snap +18 -9
  1122. package/src/ts/rdb/smithy-connection/generator.d.ts +1 -1
  1123. package/src/ts/rdb/smithy-connection/generator.js +48 -43
  1124. package/src/ts/rdb/smithy-connection/generator.js.map +1 -1
  1125. package/src/ts/rdb/smithy-connection/schema.d.js +8 -0
  1126. package/src/ts/rdb/smithy-connection/schema.d.js.map +1 -0
  1127. package/src/ts/rdb/smithy-connection/schema.d.ts +1 -0
  1128. package/src/ts/rdb/smithy-connection/schema.json +5 -0
  1129. package/src/ts/rdb/trpc-connection/__snapshots__/generator.spec.ts.snap +12 -3
  1130. package/src/ts/rdb/trpc-connection/generator.d.ts +1 -1
  1131. package/src/ts/rdb/trpc-connection/generator.js +37 -31
  1132. package/src/ts/rdb/trpc-connection/generator.js.map +1 -1
  1133. package/src/ts/rdb/trpc-connection/schema.d.js +8 -0
  1134. package/src/ts/rdb/trpc-connection/schema.d.js.map +1 -0
  1135. package/src/ts/rdb/trpc-connection/schema.d.ts +1 -0
  1136. package/src/ts/rdb/trpc-connection/schema.json +5 -0
  1137. package/src/ts/rdb/utils.js +9 -11
  1138. package/src/ts/rdb/utils.js.map +1 -1
  1139. package/src/ts/react-website/agui/files/common/src/hooks/useAgui__agentNameClassName__.tsx.template +17 -16
  1140. package/src/ts/react-website/agui/generator.d.ts +97 -0
  1141. package/src/ts/react-website/agui/generator.js +114 -67
  1142. package/src/ts/react-website/agui/generator.js.map +1 -1
  1143. package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +1705 -717
  1144. package/src/ts/react-website/app/files/app/common/README.md.template +1 -1
  1145. package/src/ts/react-website/app/generator.d.ts +146 -3
  1146. package/src/ts/react-website/app/generator.js +363 -209
  1147. package/src/ts/react-website/app/generator.js.map +1 -1
  1148. package/src/ts/react-website/app/schema.d.js +6 -0
  1149. package/src/ts/react-website/app/schema.d.js.map +1 -0
  1150. package/src/ts/react-website/app/schema.d.ts +3 -3
  1151. package/src/ts/react-website/app/schema.json +12 -6
  1152. package/src/ts/react-website/cognito-auth/__snapshots__/generator.spec.ts.snap +225 -31
  1153. package/src/ts/react-website/cognito-auth/__snapshots__/generator.terraform.spec.ts.snap +78 -12
  1154. package/src/ts/react-website/cognito-auth/files/app/components/CognitoAuth/index.tsx.template +2 -2
  1155. package/src/ts/react-website/cognito-auth/generator.d.ts +26 -2
  1156. package/src/ts/react-website/cognito-auth/generator.js +79 -61
  1157. package/src/ts/react-website/cognito-auth/generator.js.map +1 -1
  1158. package/src/ts/react-website/cognito-auth/schema.d.js +6 -0
  1159. package/src/ts/react-website/cognito-auth/schema.d.js.map +1 -0
  1160. package/src/ts/react-website/cognito-auth/schema.d.ts +2 -1
  1161. package/src/ts/react-website/cognito-auth/schema.json +5 -0
  1162. package/src/ts/react-website/cognito-auth/utils.js +34 -32
  1163. package/src/ts/react-website/cognito-auth/utils.js.map +1 -1
  1164. package/src/ts/react-website/runtime-config/__snapshots__/generator.spec.ts.snap +2 -2
  1165. package/src/ts/react-website/runtime-config/files/app/components/RuntimeConfig/index.tsx.template +2 -2
  1166. package/src/ts/react-website/runtime-config/generator.d.ts +1 -1
  1167. package/src/ts/react-website/runtime-config/generator.js +29 -31
  1168. package/src/ts/react-website/runtime-config/generator.js.map +1 -1
  1169. package/src/ts/react-website/runtime-config/schema.d.js +6 -0
  1170. package/src/ts/react-website/runtime-config/schema.d.js.map +1 -0
  1171. package/src/ts/react-website/runtime-config/schema.d.ts +1 -0
  1172. package/src/ts/react-website/runtime-config/schema.json +5 -0
  1173. package/src/ts/sync/generator.d.ts +1 -1
  1174. package/src/ts/sync/generator.js +154 -60
  1175. package/src/ts/sync/generator.js.map +1 -1
  1176. package/src/ts/website/app/generator.d.ts +1 -1
  1177. package/src/ts/website/app/generator.js +10 -10
  1178. package/src/ts/website/app/generator.js.map +1 -1
  1179. package/src/ts/website/app/schema.d.js +6 -0
  1180. package/src/ts/website/app/schema.d.js.map +1 -0
  1181. package/src/ts/website/app/schema.d.ts +3 -3
  1182. package/src/ts/website/app/schema.json +11 -6
  1183. package/src/ts/website/auth/generator.d.ts +2 -2
  1184. package/src/ts/website/auth/generator.js +11 -10
  1185. package/src/ts/website/auth/generator.js.map +1 -1
  1186. package/src/ts/website/auth/schema.d.js +6 -0
  1187. package/src/ts/website/auth/schema.d.js.map +1 -0
  1188. package/src/ts/website/auth/schema.d.ts +2 -1
  1189. package/src/ts/website/auth/schema.json +5 -0
  1190. package/src/utils/__snapshots__/shared-constructs.spec.ts.snap +21 -0
  1191. package/src/utils/add-dependencies.d.ts +38 -0
  1192. package/src/utils/add-dependencies.js +83 -0
  1193. package/src/utils/add-dependencies.js.map +1 -0
  1194. package/src/utils/agent-chat/agent-chat.d.ts +31 -0
  1195. package/src/utils/agent-chat/agent-chat.js +31 -0
  1196. package/src/utils/agent-chat/agent-chat.js.map +1 -0
  1197. package/src/utils/agent-chat/files/a2a/chat.ts.template +33 -0
  1198. package/src/utils/agent-chat/files/ag-ui/chat.ts.template +17 -0
  1199. package/src/utils/agent-chat/files/common/agentcore.ts.template +81 -0
  1200. package/src/utils/agent-chat/files/http-py/chat.ts.template +43 -0
  1201. package/src/utils/agent-chat/files/http-ts/chat.ts.template +54 -0
  1202. package/src/utils/agent-connection/agent-connection.d.ts +149 -26
  1203. package/src/utils/agent-connection/agent-connection.js +510 -108
  1204. package/src/utils/agent-connection/agent-connection.js.map +1 -1
  1205. package/src/utils/agent-connection/files/core-a2a/agentcore-a2a-client-config.ts.template +93 -0
  1206. package/src/utils/agent-connection/files/core-auth/agentcore-endpoints.ts.template +37 -0
  1207. package/src/utils/agent-connection/files/core-auth/agentcore-fetch.ts.template +54 -0
  1208. package/src/utils/agent-connection/files/core-gateway/agentcore-gateway-mcp-transport.ts.template +41 -0
  1209. package/src/utils/agent-connection/files/core-mcp/agentcore-mcp-transport.ts.template +70 -0
  1210. package/src/utils/agent-connection/files/core-runtime-config/runtime-config.ts.template +35 -33
  1211. package/src/utils/agent-connection/files/core-shared/agentcore-transport.ts.template +39 -0
  1212. package/src/utils/agent-connection/files/core-strands/a2a/agentcore-a2a-client-strands.ts.template +61 -0
  1213. package/src/utils/agent-connection/files/{core-runtime-config/model-errors.ts.template → core-strands/base/model-errors-strands.ts.template} +26 -15
  1214. package/src/utils/agent-connection/files/core-strands/base/tool-errors-strands.ts.template +29 -0
  1215. package/src/utils/agent-connection/files/{core-runtime-config/with-session-id.ts.template → core-strands/base/with-session-id-strands.ts.template} +1 -1
  1216. package/src/utils/agent-connection/files/core-strands/gateway/agentcore-gateway-mcp-client-strands.ts.template +26 -0
  1217. package/src/utils/agent-connection/files/core-strands/mcp/agentcore-mcp-client-strands.ts.template +27 -0
  1218. package/src/utils/agent-connection/files/py-core-a2a/agentcore_a2a_client_config.py.template +43 -0
  1219. package/src/utils/agent-connection/files/py-core-auth/agentcore_endpoints.py.template +47 -0
  1220. package/src/utils/agent-connection/files/py-core-auth/auth/session.py.template +51 -0
  1221. package/src/utils/agent-connection/files/py-core-gateway/agentcore_gateway_mcp_transport.py.template +28 -0
  1222. package/src/utils/agent-connection/files/py-core-langchain/a2a/agentcore_a2a_client_langchain.py.template +105 -0
  1223. package/src/utils/agent-connection/files/py-core-langchain/gateway/agentcore_gateway_mcp_client_langchain.py.template +57 -0
  1224. package/src/utils/agent-connection/files/py-core-langchain/mcp/agentcore_mcp_client_langchain.py.template +64 -0
  1225. package/src/utils/agent-connection/files/py-core-langchain/s3/s3_checkpoint_saver_langchain.py.template +360 -0
  1226. package/src/utils/agent-connection/files/py-core-mcp/agentcore_mcp_transport.py.template +34 -0
  1227. package/src/utils/agent-connection/files/py-core-runtime-config/runtime_config.py.template +4 -18
  1228. package/src/utils/agent-connection/files/py-core-runtime-config/session_context.py.template +2 -2
  1229. package/src/utils/agent-connection/files/py-core-shared/agentcore_transport.py.template +40 -0
  1230. package/src/utils/agent-connection/files/py-core-strands/a2a/agentcore_a2a_client_strands.py.template +111 -0
  1231. package/src/utils/agent-connection/files/py-core-strands/base/tool_errors_strands.py.template +24 -0
  1232. package/src/utils/agent-connection/files/{py-core-runtime-config/with_session_id.py.template → py-core-strands/base/with_session_id_strands.py.template} +2 -2
  1233. package/src/utils/agent-connection/files/py-core-strands/gateway/agentcore_gateway_mcp_client_strands.py.template +22 -0
  1234. package/src/utils/agent-connection/files/py-core-strands/mcp/agentcore_mcp_client_strands.py.template +30 -0
  1235. package/src/utils/agent-core-constructs/agent-core-constructs.d.ts +88 -2
  1236. package/src/utils/agent-core-constructs/agent-core-constructs.js +227 -30
  1237. package/src/utils/agent-core-constructs/agent-core-constructs.js.map +1 -1
  1238. package/src/utils/agent-core-constructs/files/cdk/app/agent-core/__nameKebabCase__/__nameKebabCase__.ts.template +273 -9
  1239. package/src/utils/agent-core-constructs/files/cdk/app/agentcore-gateway/__nameKebabCase__/__nameKebabCase__.ts.template +87 -0
  1240. package/src/utils/agent-core-constructs/files/cdk/app/agentcore-harness/__nameKebabCase__/__nameKebabCase__.ts.template +404 -0
  1241. package/src/utils/agent-core-constructs/files/cdk/core/agentcore-gateway/agentcore-gateway.ts.template +624 -0
  1242. package/src/utils/agent-core-constructs/files/cdk/core/agentcore-gateway/readiness-probe/index.js.template +55 -0
  1243. package/src/utils/agent-core-constructs/files/terraform/app/agent-core/__nameKebabCase__/__nameKebabCase__.tf.template +288 -1
  1244. package/src/utils/agent-core-constructs/files/terraform/app/agentcore-gateway/__nameKebabCase__/__nameKebabCase__.tf.template +576 -0
  1245. package/src/utils/agent-core-constructs/files/terraform/app/agentcore-gateway/__nameKebabCase__/render-cedar.cjs.template +13 -0
  1246. package/src/utils/agent-core-constructs/files/terraform/app/agentcore-harness/__nameKebabCase__/__nameKebabCase__.tf.template +516 -0
  1247. package/src/utils/agent-core-constructs/files/terraform/core/agent-core/runtime.tf.template +163 -13
  1248. package/src/utils/agent-core-constructs/files/terraform/core/agentcore-gateway/gateway.tf.template +14 -0
  1249. package/src/utils/api-constructs/api-constructs.d.ts +31 -3
  1250. package/src/utils/api-constructs/api-constructs.js +96 -32
  1251. package/src/utils/api-constructs/api-constructs.js.map +1 -1
  1252. package/src/utils/api-constructs/files/cdk/app/apis/http/__apiNameKebabCase__.ts.template +24 -19
  1253. package/src/utils/api-constructs/files/cdk/app/apis/rest/__apiNameKebabCase__.ts.template +28 -20
  1254. package/src/utils/api-constructs/files/cdk/core/api/http/http-api.ts.template +26 -9
  1255. package/src/utils/api-constructs/files/cdk/core/api/rest/api-gateway-account/index.js.template +70 -0
  1256. package/src/utils/api-constructs/files/cdk/core/api/rest/api-gateway-account.ts.template +82 -0
  1257. package/src/utils/api-constructs/files/cdk/core/api/rest/rest-api.ts.template +56 -17
  1258. package/src/utils/api-constructs/files/cdk/core/api/trpc/trpc-utils.ts.template +1 -1
  1259. package/src/utils/api-constructs/files/cdk/core/api/utils/utils.ts.template +61 -7
  1260. package/src/utils/api-constructs/files/terraform/app/apis/http/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +356 -19
  1261. package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +635 -14
  1262. package/src/utils/api-constructs/files/terraform/core/api/http/http-api/http-api.tf.template +1 -1
  1263. package/src/utils/api-constructs/files/terraform/core/api/rest/api-gateway-account/api-gateway-account.tf.template +83 -0
  1264. package/src/utils/api-constructs/files/terraform/core/api/rest/rest-api/rest-api.tf.template +65 -18
  1265. package/src/utils/api-constructs/open-api-metadata.d.ts +21 -4
  1266. package/src/utils/api-constructs/open-api-metadata.js +74 -39
  1267. package/src/utils/api-constructs/open-api-metadata.js.map +1 -1
  1268. package/src/utils/api-constructs/trpc-operations-metadata.d.ts +21 -0
  1269. package/src/utils/api-constructs/trpc-operations-metadata.js +61 -0
  1270. package/src/utils/api-constructs/trpc-operations-metadata.js.map +1 -0
  1271. package/src/utils/ast/website.js +17 -19
  1272. package/src/utils/ast/website.js.map +1 -1
  1273. package/src/utils/ast.d.ts +47 -0
  1274. package/src/utils/ast.js +185 -96
  1275. package/src/utils/ast.js.map +1 -1
  1276. package/src/utils/base-tsconfig.d.ts +30 -0
  1277. package/src/utils/base-tsconfig.js +32 -0
  1278. package/src/utils/base-tsconfig.js.map +1 -0
  1279. package/src/utils/bundle/bundle.d.ts +6 -1
  1280. package/src/utils/bundle/bundle.js +70 -48
  1281. package/src/utils/bundle/bundle.js.map +1 -1
  1282. package/src/utils/commands.d.ts +14 -3
  1283. package/src/utils/commands.js +83 -28
  1284. package/src/utils/commands.js.map +1 -1
  1285. package/src/utils/config/index.d.ts +22 -6
  1286. package/src/utils/config/index.js +5 -4
  1287. package/src/utils/config/index.js.map +1 -1
  1288. package/src/utils/config/utils.d.ts +9 -3
  1289. package/src/utils/config/utils.js +56 -59
  1290. package/src/utils/config/utils.js.map +1 -1
  1291. package/src/utils/connection/open-api/files/components/__apiNameClassName__Provider.tsx.template +16 -3
  1292. package/src/utils/connection/open-api/react.d.ts +31 -5
  1293. package/src/utils/connection/open-api/react.js +106 -86
  1294. package/src/utils/connection/open-api/react.js.map +1 -1
  1295. package/src/utils/containers.js +22 -23
  1296. package/src/utils/containers.js.map +1 -1
  1297. package/src/utils/dcr-proxy-constructs/dcr-proxy-constructs.d.ts +38 -0
  1298. package/src/utils/dcr-proxy-constructs/dcr-proxy-constructs.js +95 -0
  1299. package/src/utils/dcr-proxy-constructs/dcr-proxy-constructs.js.map +1 -0
  1300. package/src/utils/dcr-proxy-constructs/files/cdk/app/dcr-proxies/__nameKebabCase__/__nameKebabCase__.ts.template +227 -0
  1301. package/src/utils/dcr-proxy-constructs/files/project/src/handlers/authorization-server-metadata.ts.template +39 -0
  1302. package/src/utils/dcr-proxy-constructs/files/project/src/handlers/authorize.ts.template +26 -0
  1303. package/src/utils/dcr-proxy-constructs/files/project/src/handlers/mcp-proxy.ts.template +89 -0
  1304. package/src/utils/dcr-proxy-constructs/files/project/src/handlers/protected-resource-metadata.ts.template +13 -0
  1305. package/src/utils/dcr-proxy-constructs/files/project/src/handlers/register.ts.template +70 -0
  1306. package/src/utils/dcr-proxy-constructs/files/project/src/handlers/token.ts.template +75 -0
  1307. package/src/utils/dcr-proxy-constructs/files/terraform/app/dcr-proxies/__nameKebabCase__/__nameKebabCase__.tf.template +414 -0
  1308. package/src/utils/declared-dependencies.d.ts +140 -0
  1309. package/src/utils/declared-dependencies.js +64 -0
  1310. package/src/utils/declared-dependencies.js.map +1 -0
  1311. package/src/utils/dependencies.d.ts +18 -0
  1312. package/src/utils/dependencies.js +206 -0
  1313. package/src/utils/dependencies.js.map +1 -0
  1314. package/src/utils/docker.d.ts +101 -0
  1315. package/src/utils/docker.js +135 -0
  1316. package/src/utils/docker.js.map +1 -0
  1317. package/src/utils/dynamodb-constructs/dynamodb-constructs.d.ts +2 -2
  1318. package/src/utils/dynamodb-constructs/dynamodb-constructs.js +39 -33
  1319. package/src/utils/dynamodb-constructs/dynamodb-constructs.js.map +1 -1
  1320. package/src/utils/dynamodb-constructs/files/cdk/app/dynamodb/__nameKebabCase__.ts.template +18 -5
  1321. package/src/utils/dynamodb-constructs/files/cdk/core/dynamodb.ts.template +24 -4
  1322. package/src/utils/dynamodb-constructs/files/terraform/app/dynamodb/__nameKebabCase__/__nameKebabCase__.tf.template +66 -18
  1323. package/src/utils/dynamodb-constructs/files/terraform/core/dynamodb/dynamodb.tf.template +46 -6
  1324. package/src/utils/files/common/constructs/src/core/cloudfront.ts.template +14 -0
  1325. package/src/utils/files/common/constructs/src/core/index.ts.template +5 -4
  1326. package/src/utils/files/common/constructs/src/index.ts.template +2 -2
  1327. package/src/utils/files/common/infra-config/src/index.ts.template +3 -3
  1328. package/src/utils/files/common/infra-config/src/resolve-stage.ts.template +2 -2
  1329. package/src/utils/files/common/infra-config/src/stages.config.ts.template +1 -1
  1330. package/src/{ts/dynamodb/files/scripts → utils/files/common/scripts/src/dynamodb}/create-local-table.ts.template +65 -31
  1331. package/src/utils/files/common/scripts/src/dynamodb/pull-image.ts.template +13 -0
  1332. package/src/{ts/dynamodb/files/scripts → utils/files/common/scripts/src/dynamodb}/start-container.ts.template +29 -22
  1333. package/src/utils/files/common/scripts/src/infra/infra-deploy.ts.template +2 -0
  1334. package/src/utils/files/common/scripts/src/infra/infra-destroy.ts.template +2 -0
  1335. package/src/utils/files/common/scripts/src/infra/stage-credentials/cdk-command.ts.template +25 -0
  1336. package/src/utils/files/common/scripts/src/{stage-credentials → infra/stage-credentials}/run.ts.template +3 -3
  1337. package/src/utils/files/common/scripts/src/rdb/mysql/wait-for-mysql-db.ts.template +32 -0
  1338. package/src/utils/files/common/scripts/src/rdb/postgres/wait-for-postgres-db.ts.template +32 -0
  1339. package/src/utils/files/common/scripts/src/rdb/shared/pull-image.ts.template +16 -0
  1340. package/src/utils/files/common/scripts/src/rdb/shared/start-container.ts.template +100 -0
  1341. package/src/utils/files/common/shadcn/readme/README.md.template +3 -3
  1342. package/src/utils/files/common/shadcn/src/index.ts.template +2 -2
  1343. package/src/utils/files/shadcn/components.json.template +1 -1
  1344. package/src/utils/files/terraform/src/core/asset-bucket/asset-bucket.tf.template +63 -72
  1345. package/src/utils/files/terraform/src/core/runtime-config/appconfig/appconfig.tf.template +3 -3
  1346. package/src/utils/files/terraform/src/core/runtime-config/appconfig-deployment/appconfig-deployment.tf.template +13 -7
  1347. package/src/utils/files/terraform/src/core/runtime-config/entry/entry.tf.template +1 -1
  1348. package/src/utils/files/terraform/src/core/runtime-config/read/read.tf.template +9 -4
  1349. package/src/utils/files/terraform/src/metrics/providers.tf.template +12 -0
  1350. package/src/utils/files/website/hooks/sigv4/useSigV4.tsx.template +1 -1
  1351. package/src/utils/format.d.ts +44 -4
  1352. package/src/utils/format.js +323 -198
  1353. package/src/utils/format.js.map +1 -1
  1354. package/src/utils/fs.d.ts +24 -5
  1355. package/src/utils/fs.js +40 -15
  1356. package/src/utils/fs.js.map +1 -1
  1357. package/src/utils/function-constructs/files/cdk/app/lambda-functions/__nameKebabCase__.ts.template +1 -1
  1358. package/src/utils/function-constructs/files/terraform/app/lambda-functions/__nameKebabCase__/__nameKebabCase__.tf.template +6 -2
  1359. package/src/utils/function-constructs/function-constructs.d.ts +1 -1
  1360. package/src/utils/function-constructs/function-constructs.js +24 -27
  1361. package/src/utils/function-constructs/function-constructs.js.map +1 -1
  1362. package/src/utils/generators.d.ts +33 -0
  1363. package/src/utils/generators.js +33 -20
  1364. package/src/utils/generators.js.map +1 -1
  1365. package/src/utils/git.js +19 -34
  1366. package/src/utils/git.js.map +1 -1
  1367. package/src/utils/iac-providers.js +5 -5
  1368. package/src/utils/iac-providers.js.map +1 -1
  1369. package/src/utils/iac.d.ts +2 -2
  1370. package/src/utils/iac.js +12 -14
  1371. package/src/utils/iac.js.map +1 -1
  1372. package/src/utils/identity-constructs/files/cdk/core/user-identity.ts.template +218 -30
  1373. package/src/utils/identity-constructs/files/terraform/core/user-identity/add-callback-url/add-callback-url.tf.template +32 -9
  1374. package/src/utils/identity-constructs/files/terraform/core/user-identity/identity/identity.tf.template +226 -25
  1375. package/src/utils/identity-constructs/files/terraform/core/user-identity/main.tf.template +33 -0
  1376. package/src/utils/identity-constructs/identity-constructs.d.ts +11 -1
  1377. package/src/utils/identity-constructs/identity-constructs.js +52 -31
  1378. package/src/utils/identity-constructs/identity-constructs.js.map +1 -1
  1379. package/src/utils/init.d.ts +73 -0
  1380. package/src/utils/init.js +300 -0
  1381. package/src/utils/init.js.map +1 -0
  1382. package/src/utils/install.d.ts +32 -0
  1383. package/src/utils/install.js +58 -0
  1384. package/src/utils/install.js.map +1 -0
  1385. package/src/utils/js.js +27 -20
  1386. package/src/utils/js.js.map +1 -1
  1387. package/src/utils/lambda-runtime-resolution.d.ts +53 -0
  1388. package/src/utils/lambda-runtime-resolution.js +83 -0
  1389. package/src/utils/lambda-runtime-resolution.js.map +1 -0
  1390. package/src/utils/mcp.d.ts +9 -0
  1391. package/src/utils/mcp.js +42 -41
  1392. package/src/utils/mcp.js.map +1 -1
  1393. package/src/utils/metrics.d.ts +1 -1
  1394. package/src/utils/metrics.js +30 -45
  1395. package/src/utils/metrics.js.map +1 -1
  1396. package/src/utils/migration-manifest.d.ts +58 -0
  1397. package/src/utils/migration-manifest.js +76 -0
  1398. package/src/utils/migration-manifest.js.map +1 -0
  1399. package/src/utils/migration-versions.d.ts +145 -0
  1400. package/src/utils/migration-versions.js +250 -0
  1401. package/src/utils/migration-versions.js.map +1 -0
  1402. package/src/utils/mock-project-graph.js +12 -13
  1403. package/src/utils/mock-project-graph.js.map +1 -1
  1404. package/src/utils/module-format.d.ts +25 -0
  1405. package/src/utils/module-format.js +45 -0
  1406. package/src/utils/module-format.js.map +1 -0
  1407. package/src/utils/names.d.ts +22 -0
  1408. package/src/utils/names.js +44 -48
  1409. package/src/utils/names.js.map +1 -1
  1410. package/src/utils/npm-scope.d.ts +0 -4
  1411. package/src/utils/npm-scope.js +7 -20
  1412. package/src/utils/npm-scope.js.map +1 -1
  1413. package/src/utils/nx.d.ts +91 -6
  1414. package/src/utils/nx.js +186 -74
  1415. package/src/utils/nx.js.map +1 -1
  1416. package/src/utils/nxlv-python.js +27 -15
  1417. package/src/utils/nxlv-python.js.map +1 -1
  1418. package/src/utils/object.js +5 -11
  1419. package/src/utils/object.js.map +1 -1
  1420. package/src/utils/open-api-codegen-target.d.ts +28 -0
  1421. package/src/utils/open-api-codegen-target.js +31 -0
  1422. package/src/utils/open-api-codegen-target.js.map +1 -0
  1423. package/src/utils/paths.js +11 -15
  1424. package/src/utils/paths.js.map +1 -1
  1425. package/src/utils/pkg-manager.d.ts +1 -1
  1426. package/src/utils/pkg-manager.js +4 -9
  1427. package/src/utils/pkg-manager.js.map +1 -1
  1428. package/src/utils/pnpm-workspace.d.ts +1 -12
  1429. package/src/utils/pnpm-workspace.js +16 -43
  1430. package/src/utils/pnpm-workspace.js.map +1 -1
  1431. package/src/utils/port.d.ts +6 -5
  1432. package/src/utils/port.js +31 -39
  1433. package/src/utils/port.js.map +1 -1
  1434. package/src/utils/project-package-json.d.ts +39 -0
  1435. package/src/utils/project-package-json.js +119 -0
  1436. package/src/utils/project-package-json.js.map +1 -0
  1437. package/src/utils/py.d.ts +18 -4
  1438. package/src/utils/py.js +64 -46
  1439. package/src/utils/py.js.map +1 -1
  1440. package/src/utils/rdb-constructs/files/cdk/app/dbs/__nameKebabCase__.ts.template +72 -18
  1441. package/src/utils/rdb-constructs/files/cdk/core/rdb/aurora.ts.template +130 -88
  1442. package/src/utils/rdb-constructs/files/terraform/app/dbs/__nameKebabCase__/__nameKebabCase__.tf.template +132 -66
  1443. package/src/utils/rdb-constructs/files/terraform/core/rdb/aurora/aurora.tf.template +78 -162
  1444. package/src/utils/rdb-constructs/rdb-constructs.d.ts +26 -3
  1445. package/src/utils/rdb-constructs/rdb-constructs.js +61 -33
  1446. package/src/utils/rdb-constructs/rdb-constructs.js.map +1 -1
  1447. package/src/utils/ruff.d.ts +59 -0
  1448. package/src/utils/ruff.js +140 -0
  1449. package/src/utils/ruff.js.map +1 -0
  1450. package/src/utils/shared-constructs-constants.d.ts +68 -4
  1451. package/src/utils/shared-constructs-constants.js +68 -15
  1452. package/src/utils/shared-constructs-constants.js.map +1 -1
  1453. package/src/utils/shared-constructs.d.ts +5 -2
  1454. package/src/utils/shared-constructs.js +62 -67
  1455. package/src/utils/shared-constructs.js.map +1 -1
  1456. package/src/utils/shared-dynamodb-scripts.d.ts +18 -0
  1457. package/src/utils/shared-dynamodb-scripts.js +35 -0
  1458. package/src/utils/shared-dynamodb-scripts.js.map +1 -0
  1459. package/src/utils/shared-infra-config.js +31 -30
  1460. package/src/utils/shared-infra-config.js.map +1 -1
  1461. package/src/utils/shared-infra-scripts.d.ts +26 -0
  1462. package/src/utils/shared-infra-scripts.js +58 -0
  1463. package/src/utils/shared-infra-scripts.js.map +1 -0
  1464. package/src/utils/shared-rdb-scripts.d.ts +27 -0
  1465. package/src/utils/shared-rdb-scripts.js +59 -0
  1466. package/src/utils/shared-rdb-scripts.js.map +1 -0
  1467. package/src/utils/shared-scripts.d.ts +1 -4
  1468. package/src/utils/shared-scripts.js +12 -38
  1469. package/src/utils/shared-scripts.js.map +1 -1
  1470. package/src/utils/shared-shadcn.d.ts +26 -1
  1471. package/src/utils/shared-shadcn.js +93 -67
  1472. package/src/utils/shared-shadcn.js.map +1 -1
  1473. package/src/utils/smithy.d.ts +59 -0
  1474. package/src/utils/smithy.js +73 -0
  1475. package/src/utils/smithy.js.map +1 -0
  1476. package/src/utils/test-git.d.ts +39 -0
  1477. package/src/utils/test-git.js +117 -0
  1478. package/src/utils/test-git.js.map +1 -0
  1479. package/src/utils/test.d.ts +10 -0
  1480. package/src/utils/test.js +44 -25
  1481. package/src/utils/test.js.map +1 -1
  1482. package/src/utils/toml.d.ts +5 -0
  1483. package/src/utils/toml.js +17 -15
  1484. package/src/utils/toml.js.map +1 -1
  1485. package/src/utils/version-upgrade-migration/migration.d.ts +14 -0
  1486. package/src/utils/version-upgrade-migration/migration.js +16 -0
  1487. package/src/utils/version-upgrade-migration/migration.js.map +1 -0
  1488. package/src/utils/version-upgrade-migration/nx-package-updates.d.ts +45 -0
  1489. package/src/utils/version-upgrade-migration/nx-package-updates.js +50 -0
  1490. package/src/utils/version-upgrade-migration/nx-package-updates.js.map +1 -0
  1491. package/src/utils/version-upgrade-migration/owned-dependencies.d.ts +76 -0
  1492. package/src/utils/version-upgrade-migration/owned-dependencies.js +146 -0
  1493. package/src/utils/version-upgrade-migration/owned-dependencies.js.map +1 -0
  1494. package/src/utils/version-upgrade-migration/register.d.ts +29 -0
  1495. package/src/utils/version-upgrade-migration/register.js +42 -0
  1496. package/src/utils/version-upgrade-migration/register.js.map +1 -0
  1497. package/src/utils/version-upgrade-migration/sync-embedded-versions.d.ts +26 -0
  1498. package/src/utils/version-upgrade-migration/sync-embedded-versions.js +352 -0
  1499. package/src/utils/version-upgrade-migration/sync-embedded-versions.js.map +1 -0
  1500. package/src/utils/version-upgrade-migration/sync-lambda-runtimes.d.ts +28 -0
  1501. package/src/utils/version-upgrade-migration/sync-lambda-runtimes.js +148 -0
  1502. package/src/utils/version-upgrade-migration/sync-lambda-runtimes.js.map +1 -0
  1503. package/src/utils/version-upgrade-migration/sync-metrics-version.d.ts +13 -0
  1504. package/src/utils/version-upgrade-migration/sync-metrics-version.js +33 -0
  1505. package/src/utils/version-upgrade-migration/sync-metrics-version.js.map +1 -0
  1506. package/src/utils/version-upgrade-migration/sync-python-language-version.d.ts +31 -0
  1507. package/src/utils/version-upgrade-migration/sync-python-language-version.js +110 -0
  1508. package/src/utils/version-upgrade-migration/sync-python-language-version.js.map +1 -0
  1509. package/src/utils/version-upgrade-migration/sync-vended-versions.d.ts +10 -0
  1510. package/src/utils/version-upgrade-migration/sync-vended-versions.js +547 -0
  1511. package/src/utils/version-upgrade-migration/sync-vended-versions.js.map +1 -0
  1512. package/src/utils/version-upgrade-migration/vended-upgrade.d.ts +14 -0
  1513. package/src/utils/version-upgrade-migration/vended-upgrade.js +40 -0
  1514. package/src/utils/version-upgrade-migration/vended-upgrade.js.map +1 -0
  1515. package/src/utils/versions.d.ts +300 -108
  1516. package/src/utils/versions.js +317 -121
  1517. package/src/utils/versions.js.map +1 -1
  1518. package/src/utils/website-constructs/files/cdk/app/static-websites/__websiteNameKebabCase__.ts.template +12 -3
  1519. package/src/utils/website-constructs/files/cdk/core/static-website.ts.template +252 -49
  1520. package/src/utils/website-constructs/files/terraform/app/static-websites/__websiteNameKebabCase__/__websiteNameKebabCase__.tf.template +56 -1
  1521. package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template +205 -107
  1522. package/src/utils/website-constructs/website-constructs.d.ts +12 -1
  1523. package/src/utils/website-constructs/website-constructs.js +49 -30
  1524. package/src/utils/website-constructs/website-constructs.js.map +1 -1
  1525. package/bin/aws-nx-mcp.d.ts +0 -2
  1526. package/bin/aws-nx-mcp.js +0 -17
  1527. package/bin/aws-nx-mcp.js.map +0 -1
  1528. package/sdk/connection.d.ts +0 -6
  1529. package/sdk/connection.js +0 -10
  1530. package/sdk/connection.js.map +0 -1
  1531. package/sdk/license.d.ts +0 -8
  1532. package/sdk/license.js +0 -13
  1533. package/sdk/license.js.map +0 -1
  1534. package/sdk/open-api.d.ts +0 -10
  1535. package/sdk/open-api.js +0 -15
  1536. package/sdk/open-api.js.map +0 -1
  1537. package/sdk/py.d.ts +0 -14
  1538. package/sdk/py.js +0 -18
  1539. package/sdk/py.js.map +0 -1
  1540. package/sdk/terraform.d.ts +0 -6
  1541. package/sdk/terraform.js +0 -11
  1542. package/sdk/terraform.js.map +0 -1
  1543. package/sdk/ts.d.ts +0 -29
  1544. package/sdk/ts.js +0 -47
  1545. package/sdk/ts.js.map +0 -1
  1546. package/sdk/utils/ast.js +0 -12
  1547. package/sdk/utils/ast.js.map +0 -1
  1548. package/sdk/utils/format.js +0 -10
  1549. package/sdk/utils/format.js.map +0 -1
  1550. package/sdk/utils/test.js +0 -10
  1551. package/sdk/utils/test.js.map +0 -1
  1552. package/src/connection/agent-serve-local.d.ts +0 -33
  1553. package/src/connection/agent-serve-local.js +0 -48
  1554. package/src/connection/agent-serve-local.js.map +0 -1
  1555. package/src/connection/serve-local.js +0 -41
  1556. package/src/connection/serve-local.js.map +0 -1
  1557. package/src/py/agent/__snapshots__/generator.spec.ts.snap +0 -771
  1558. package/src/py/agent/a2a-connection/files/agent-connection/app/__targetAgentSnakeCase___client.py.template +0 -22
  1559. package/src/py/agent/files/a2a/main.py.template +0 -54
  1560. package/src/py/agent/files/ag-ui/main.py.template +0 -37
  1561. package/src/py/agent/mcp-connection/files/agent-connection/app/__mcpServerSnakeCase___client.py.template +0 -22
  1562. package/src/py/agent/react-connection/serve-local.js +0 -30
  1563. package/src/py/agent/react-connection/serve-local.js.map +0 -1
  1564. package/src/py/agent/scripts/http/chat.ts.template +0 -38
  1565. package/src/ts/agent/a2a-connection/files/agent-connection/app/__targetAgentKebabCase__-client.ts.template +0 -19
  1566. package/src/ts/agent/mcp-connection/files/agent-connection/app/__mcpServerKebabCase__-client.ts.template +0 -19
  1567. package/src/ts/agent/react-connection/serve-local.d.ts +0 -13
  1568. package/src/ts/agent/react-connection/serve-local.js +0 -19
  1569. package/src/ts/agent/react-connection/serve-local.js.map +0 -1
  1570. package/src/ts/agent/scripts/http/chat.ts.template +0 -35
  1571. package/src/ts/dynamodb/files/scripts/pull-image.ts.template +0 -15
  1572. package/src/ts/dynamodb/files/src/constants.ts.template +0 -9
  1573. package/src/ts/dynamodb/files/src/gsi.ts.template +0 -14
  1574. package/src/ts/rdb/files/scripts/pull-image.ts.template +0 -15
  1575. package/src/ts/rdb/files/scripts/start-container.ts.template +0 -84
  1576. package/src/ts/rdb/files/scripts/wait-for-db.ts.template +0 -55
  1577. package/src/ts/rdb/files/src/constants.ts.template +0 -8
  1578. package/src/ts/rdb/files/src/index.ts.template +0 -2
  1579. package/src/utils/agent-connection/files/core-a2a/agentcore-a2a-client.ts.template +0 -123
  1580. package/src/utils/agent-connection/files/core-mcp/agentcore-mcp-client.ts.template +0 -128
  1581. package/src/utils/agent-connection/files/py-core-a2a/agentcore_a2a_client.py.template +0 -120
  1582. package/src/utils/agent-connection/files/py-core-mcp/agentcore_mcp_client.py.template +0 -89
  1583. package/src/utils/files/common/scripts/src/infra-deploy.ts.template +0 -2
  1584. package/src/utils/files/common/scripts/src/infra-destroy.ts.template +0 -2
  1585. package/src/utils/files/common/scripts/src/stage-credentials/cdk-command.ts.template +0 -18
  1586. package/src/utils/files/terraform/scripts/reset-runtime-config.ts.template +0 -25
  1587. /package/src/py/agent/files/{common → langchain/common}/__init__.py.template +0 -0
  1588. /package/src/smithy/project/files/{src → service/src}/main.smithy.template +0 -0
  1589. /package/src/utils/agent-connection/files/{py-core-runtime-config/model_errors.py.template → py-core-strands/base/model_errors_strands.py.template} +0 -0
  1590. /package/src/utils/files/common/scripts/src/{index.ts.template → infra/index.ts.template} +0 -0
  1591. /package/src/utils/files/common/scripts/src/{stage-credentials → infra/stage-credentials}/credentials.ts.template +0 -0
  1592. /package/src/utils/files/common/scripts/src/{stage-credentials → infra/stage-credentials}/stage-parser.ts.template +0 -0
@@ -32,6 +32,7 @@ import {
32
32
  IntegrationBuilder,
33
33
  RestApiIntegration,
34
34
  } from '../../core/api/utils.js';
35
+ import { findCloudFrontDomainNames } from '../../core/cloudfront.js';
35
36
  import { AddCorsPreflightAspect, RestApi } from '../../core/api/rest-api.js';
36
37
  import {
37
38
  OPERATION_DETAILS,
@@ -86,8 +87,8 @@ export class TestApi<
86
87
  return IntegrationBuilder.rest({
87
88
  pattern: 'isolated',
88
89
  operations: OPERATION_DETAILS,
89
- defaultIntegrationOptions: <FunctionProps>{
90
- runtime: Runtime.NODEJS_LATEST,
90
+ defaultIntegrationOptions: {
91
+ runtime: Runtime.NODEJS_24_X,
91
92
  handler: 'index.handler',
92
93
  code: Code.fromAsset(
93
94
  url.fileURLToPath(
@@ -99,7 +100,7 @@ export class TestApi<
99
100
  ),
100
101
  timeout: Duration.seconds(30),
101
102
  tracing: Tracing.ACTIVE,
102
- },
103
+ } as FunctionProps,
103
104
  buildDefaultIntegration: (op, props: FunctionProps) => {
104
105
  const handler = new Function(scope, \`TestApi\${op}Handler\`, props);
105
106
  handler.addEnvironment(
@@ -127,6 +128,9 @@ export class TestApi<
127
128
  authorizer: new CognitoUserPoolsAuthorizer(scope, 'TestApiAuthorizer', {
128
129
  cognitoUserPools: [props.identity.userPool],
129
130
  }),
131
+ // Accept access tokens from both sign-in flows: 'openid' (Cognito hosted UI)
132
+ // and 'aws.cognito.signin.user.admin' (Cognito admin/SRP auth APIs).
133
+ authorizationScopes: ['openid', 'aws.cognito.signin.user.admin'],
130
134
  },
131
135
  deployOptions: {
132
136
  tracingEnabled: true,
@@ -153,7 +157,9 @@ export class TestApi<
153
157
  *
154
158
  * Configures the provided CloudFront distribution domains or origin strings
155
159
  * as the only permitted CORS origins in API Gateway preflight responses and the
156
- * AWS Lambda integrations.
160
+ * AWS Lambda integrations. Any custom domain names (aliases) configured on a
161
+ * CloudFront distribution are included automatically alongside its default
162
+ * \`*.cloudfront.net\` domain.
157
163
  *
158
164
  * @param origins - The origin strings, CloudFront distributions, or objects containing a CloudFront distribution to grant CORS from
159
165
  */
@@ -164,12 +170,14 @@ export class TestApi<
164
170
  | { cloudFrontDistribution: Distribution }
165
171
  )[]
166
172
  ) {
167
- const allowedOrigins = origins.map((origin) =>
173
+ const allowedOrigins = origins.flatMap((origin) =>
168
174
  typeof origin === 'string'
169
- ? origin
170
- : 'cloudFrontDistribution' in origin
171
- ? \`https://\${origin.cloudFrontDistribution.distributionDomainName}\`
172
- : \`https://\${origin.distributionDomainName}\`,
175
+ ? [origin]
176
+ : findCloudFrontDomainNames(
177
+ 'cloudFrontDistribution' in origin
178
+ ? origin.cloudFrontDistribution
179
+ : origin,
180
+ ).map((domain) => \`https://\${domain}\`),
173
181
  );
174
182
 
175
183
  this.allowedOrigins = allowedOrigins;
@@ -218,6 +226,7 @@ import {
218
226
  IntegrationBuilder,
219
227
  RestApiIntegration,
220
228
  } from '../../core/api/utils.js';
229
+ import { findCloudFrontDomainNames } from '../../core/cloudfront.js';
221
230
  import { AddCorsPreflightAspect, RestApi } from '../../core/api/rest-api.js';
222
231
  import {
223
232
  OPERATION_DETAILS,
@@ -266,8 +275,8 @@ export class TestApi<
266
275
  return IntegrationBuilder.rest({
267
276
  pattern: 'isolated',
268
277
  operations: OPERATION_DETAILS,
269
- defaultIntegrationOptions: <FunctionProps>{
270
- runtime: Runtime.NODEJS_LATEST,
278
+ defaultIntegrationOptions: {
279
+ runtime: Runtime.NODEJS_24_X,
271
280
  handler: 'index.handler',
272
281
  code: Code.fromAsset(
273
282
  url.fileURLToPath(
@@ -279,7 +288,7 @@ export class TestApi<
279
288
  ),
280
289
  timeout: Duration.seconds(30),
281
290
  tracing: Tracing.ACTIVE,
282
- },
291
+ } as FunctionProps,
283
292
  buildDefaultIntegration: (op, props: FunctionProps) => {
284
293
  const handler = new Function(scope, \`TestApi\${op}Handler\`, props);
285
294
  handler.addEnvironment(
@@ -330,7 +339,9 @@ export class TestApi<
330
339
  *
331
340
  * Configures the provided CloudFront distribution domains or origin strings
332
341
  * as the only permitted CORS origins in API Gateway preflight responses and the
333
- * AWS Lambda integrations.
342
+ * AWS Lambda integrations. Any custom domain names (aliases) configured on a
343
+ * CloudFront distribution are included automatically alongside its default
344
+ * \`*.cloudfront.net\` domain.
334
345
  *
335
346
  * @param origins - The origin strings, CloudFront distributions, or objects containing a CloudFront distribution to grant CORS from
336
347
  */
@@ -341,12 +352,14 @@ export class TestApi<
341
352
  | { cloudFrontDistribution: Distribution }
342
353
  )[]
343
354
  ) {
344
- const allowedOrigins = origins.map((origin) =>
355
+ const allowedOrigins = origins.flatMap((origin) =>
345
356
  typeof origin === 'string'
346
- ? origin
347
- : 'cloudFrontDistribution' in origin
348
- ? \`https://\${origin.cloudFrontDistribution.distributionDomainName}\`
349
- : \`https://\${origin.distributionDomainName}\`,
357
+ ? [origin]
358
+ : findCloudFrontDomainNames(
359
+ 'cloudFrontDistribution' in origin
360
+ ? origin.cloudFrontDistribution
361
+ : origin,
362
+ ).map((domain) => \`https://\${domain}\`),
350
363
  );
351
364
 
352
365
  this.allowedOrigins = allowedOrigins;
@@ -400,11 +413,11 @@ terraform {
400
413
  required_providers {
401
414
  aws = {
402
415
  source = "hashicorp/aws"
403
- version = "~> 6.33"
416
+ version = "6.62.0"
404
417
  }
405
418
  random = {
406
419
  source = "hashicorp/random"
407
- version = "~> 3.1"
420
+ version = "3.9.0"
408
421
  }
409
422
  }
410
423
  }
@@ -474,8 +487,6 @@ data "aws_caller_identity" "current" {}
474
487
 
475
488
  # Resources
476
489
 
477
- # Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
478
-
479
490
  # REST API Gateway
480
491
  resource "aws_api_gateway_rest_api" "rest_api" {
481
492
  name = var.api_name
@@ -492,10 +503,8 @@ resource "aws_api_gateway_rest_api" "rest_api" {
492
503
  tags = var.tags
493
504
  }
494
505
 
495
- # Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
496
- # after all methods and integrations are defined
497
-
498
- # Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
506
+ # Note: Deployment, stage and access logging are created in the consuming module
507
+ # (e.g., foo-api.tf) after all methods and integrations are defined
499
508
 
500
509
  # Gateway Response for CORS (4XX errors)
501
510
  resource "aws_api_gateway_gateway_response" "cors_4xx" {
@@ -597,22 +606,75 @@ resource "aws_wafv2_web_acl" "api_waf" {
597
606
  }
598
607
  }
599
608
 
609
+ resource "random_id" "waf_log_suffix" {
610
+ count = var.enable_waf ? 1 : 0
611
+ byte_length = 4
612
+ }
613
+
614
+ locals {
615
+ waf_log_group_name = var.enable_waf ? "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}" : null
616
+ }
617
+
618
+ # KMS key for encrypting WAF request logs at rest
619
+ resource "aws_kms_key" "waf_logs" {
620
+ count = var.enable_waf ? 1 : 0
621
+
622
+ description = "\${var.api_name} API WAF log encryption"
623
+ deletion_window_in_days = 7
624
+ enable_key_rotation = true
625
+
626
+ policy = jsonencode({
627
+ Version = "2012-10-17"
628
+ Statement = [
629
+ {
630
+ Sid = "Enable IAM User Permissions"
631
+ Effect = "Allow"
632
+ Principal = { AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root" }
633
+ Action = "kms:*"
634
+ Resource = "*"
635
+ },
636
+ {
637
+ Sid = "Allow CloudWatch Logs"
638
+ Effect = "Allow"
639
+ Principal = { Service = "logs.\${data.aws_region.current.region}.amazonaws.com" }
640
+ Action = [
641
+ "kms:Encrypt",
642
+ "kms:Decrypt",
643
+ "kms:ReEncrypt*",
644
+ "kms:GenerateDataKey*",
645
+ "kms:DescribeKey"
646
+ ]
647
+ Resource = "*"
648
+ Condition = {
649
+ ArnEquals = {
650
+ "kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:log-group:\${local.waf_log_group_name}"
651
+ }
652
+ }
653
+ }
654
+ ]
655
+ })
656
+
657
+ tags = var.tags
658
+ }
659
+
660
+ resource "aws_kms_alias" "waf_logs" {
661
+ count = var.enable_waf ? 1 : 0
662
+
663
+ name = "alias/\${var.api_name}-\${random_id.waf_log_suffix[0].hex}-waf-logs"
664
+ target_key_id = aws_kms_key.waf_logs[0].key_id
665
+ }
666
+
600
667
  # CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
601
668
  resource "aws_cloudwatch_log_group" "api_waf_logs" {
602
- #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
603
669
  #checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
604
670
  count = var.enable_waf ? 1 : 0
605
671
 
606
- name = "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}"
672
+ name = local.waf_log_group_name
607
673
  retention_in_days = 30
674
+ kms_key_id = aws_kms_key.waf_logs[0].arn
608
675
  tags = var.tags
609
676
  }
610
677
 
611
- resource "random_id" "waf_log_suffix" {
612
- count = var.enable_waf ? 1 : 0
613
- byte_length = 4
614
- }
615
-
616
678
  resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
617
679
  count = var.enable_waf ? 1 : 0
618
680
 
@@ -653,9 +715,7 @@ output "waf_web_acl_arn" {
653
715
  value = var.enable_waf ? aws_wafv2_web_acl.api_waf[0].arn : null
654
716
  }
655
717
 
656
- # Note: Stage and deployment outputs are provided by the consuming module
657
-
658
- # Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement"
718
+ # Note: Stage, deployment and access log outputs are provided by the consuming module"
659
719
  `;
660
720
 
661
721
  exports[`tsSmithyApiGenerator > should generate smithy ts api with Terraform provider and None auth > none-auth-terraform.tf 1`] = `
@@ -663,9 +723,17 @@ exports[`tsSmithyApiGenerator > should generate smithy ts api with Terraform pro
663
723
  required_version = ">= 1.0"
664
724
 
665
725
  required_providers {
726
+ archive = {
727
+ source = "hashicorp/archive"
728
+ version = "2.8.0"
729
+ }
666
730
  aws = {
667
731
  source = "hashicorp/aws"
668
- version = "~> 6.33"
732
+ version = "6.62.0"
733
+ }
734
+ random = {
735
+ source = "hashicorp/random"
736
+ version = "3.9.0"
669
737
  }
670
738
  }
671
739
  }
@@ -687,11 +755,47 @@ variable "additional_iam_policy_statements" {
687
755
  default = []
688
756
  }
689
757
 
758
+ variable "appconfig_application_id" {
759
+ description = "ID of the shared runtime-config AppConfig application (from \`core/runtime-config/appconfig.application_id\`). When set, the Lambda functions receive \`RUNTIME_CONFIG_APP_ID\` and read access to the application."
760
+ type = string
761
+ default = null
762
+ }
763
+
764
+ variable "appconfig_application_arn" {
765
+ description = "ARN of the shared runtime-config AppConfig application (from \`core/runtime-config/appconfig.application_arn\`). Used to scope the IAM read permission granted to the Lambda functions."
766
+ type = string
767
+ default = null
768
+ }
769
+
690
770
  variable "asset_bucket_name" {
691
771
  description = "Name of the shared asset S3 bucket used to stage the Lambda deployment zip. Instantiate the \`core/asset-bucket\` module once per deployment and pass its \`bucket_name\` output here."
692
772
  type = string
693
773
  }
694
774
 
775
+ # VPC Configuration (optional)
776
+ variable "enable_vpc" {
777
+ description = "Deploy the Lambda function into a VPC. Set alongside vpc_id/subnet_ids."
778
+ type = bool
779
+ default = false
780
+ }
781
+
782
+ variable "vpc_id" {
783
+ description = "VPC ID to deploy the Lambda function into. Required when enable_vpc is true."
784
+ type = string
785
+ default = null
786
+
787
+ validation {
788
+ condition = !var.enable_vpc || (var.vpc_id != null && var.subnet_ids != null)
789
+ error_message = "vpc_id and subnet_ids must be set when enable_vpc is true."
790
+ }
791
+ }
792
+
793
+ variable "subnet_ids" {
794
+ description = "Subnet IDs to deploy the Lambda function into. Required when enable_vpc is true."
795
+ type = list(string)
796
+ default = null
797
+ }
798
+
695
799
  # CORS Configuration (passed to core module)
696
800
 
697
801
  variable "cors_allow_headers" {
@@ -736,6 +840,235 @@ resource "random_string" "suffix" {
736
840
  upper = false
737
841
  }
738
842
 
843
+ locals {
844
+ # Generated at build time from the API definition
845
+ operations_file = "\${path.module}/../../../generated/test-api/operations.json"
846
+ operations = fileexists(local.operations_file) ? jsondecode(file(local.operations_file)) : {}
847
+
848
+ operation_slug = { for op in keys(local.operations) : op => replace(op, "/[^a-zA-Z0-9-_]/", "-") }
849
+ operation_hash = { for op in keys(local.operations) : op => substr(sha256(op), 0, 8) }
850
+
851
+ function_name = { for op in keys(local.operations) : op =>
852
+ "\${substr("TestApi-\${local.operation_slug[op]}", 0, 64 - length(local.operation_hash[op]) - length(random_string.suffix.result) - 2)}-\${local.operation_hash[op]}-\${random_string.suffix.result}"
853
+ }
854
+ role_name = { for op in keys(local.operations) : op =>
855
+ "\${substr("TestApi-\${local.operation_slug[op]}-role", 0, 64 - length(local.operation_hash[op]) - length(random_string.suffix.result) - 2)}-\${local.operation_hash[op]}-\${random_string.suffix.result}"
856
+ }
857
+
858
+ # A REST API needs a resource per path segment, so each operation's path is
859
+ # split into the distinct path prefixes the resource tree is built from below
860
+ operation_segments = { for op, details in local.operations : op => compact(split("/", details.path)) }
861
+
862
+ path_nodes = merge([
863
+ for op, segments in local.operation_segments : {
864
+ for i, segment in segments : join("/", slice(segments, 0, i + 1)) => {
865
+ depth = i
866
+ parent = i == 0 ? "" : join("/", slice(segments, 0, i))
867
+ part = segment
868
+ }
869
+ }
870
+ ]...)
871
+
872
+ # Instances of a resource cannot reference one another, so each depth is a
873
+ # separate resource. Deeper paths are reported by the precondition below.
874
+ max_path_depth = 16
875
+ path_nodes_by_depth = { for depth in range(local.max_path_depth) : depth => {
876
+ for path, node in local.path_nodes : path => node if node.depth == depth
877
+ } }
878
+ paths_too_deep = [for path, node in local.path_nodes : path if node.depth >= local.max_path_depth]
879
+
880
+ resource_ids = merge(
881
+ { for path, resource in aws_api_gateway_resource.path_depth_0 : path => resource.id },
882
+ { for path, resource in aws_api_gateway_resource.path_depth_1 : path => resource.id },
883
+ { for path, resource in aws_api_gateway_resource.path_depth_2 : path => resource.id },
884
+ { for path, resource in aws_api_gateway_resource.path_depth_3 : path => resource.id },
885
+ { for path, resource in aws_api_gateway_resource.path_depth_4 : path => resource.id },
886
+ { for path, resource in aws_api_gateway_resource.path_depth_5 : path => resource.id },
887
+ { for path, resource in aws_api_gateway_resource.path_depth_6 : path => resource.id },
888
+ { for path, resource in aws_api_gateway_resource.path_depth_7 : path => resource.id },
889
+ { for path, resource in aws_api_gateway_resource.path_depth_8 : path => resource.id },
890
+ { for path, resource in aws_api_gateway_resource.path_depth_9 : path => resource.id },
891
+ { for path, resource in aws_api_gateway_resource.path_depth_10 : path => resource.id },
892
+ { for path, resource in aws_api_gateway_resource.path_depth_11 : path => resource.id },
893
+ { for path, resource in aws_api_gateway_resource.path_depth_12 : path => resource.id },
894
+ { for path, resource in aws_api_gateway_resource.path_depth_13 : path => resource.id },
895
+ { for path, resource in aws_api_gateway_resource.path_depth_14 : path => resource.id },
896
+ { for path, resource in aws_api_gateway_resource.path_depth_15 : path => resource.id },
897
+ )
898
+
899
+ operation_resource_id = { for op, segments in local.operation_segments : op =>
900
+ local.resource_ids[join("/", segments)]
901
+ }
902
+
903
+ permission_source_suffix = { for op, details in local.operations : op =>
904
+ "\${upper(details.method)}/\${replace(join("/", local.operation_segments[op]), "/{[^}]*}/", "*")}"
905
+ }
906
+ }
907
+
908
+ resource "terraform_data" "operations_metadata" {
909
+ # Fails the plan when the operations metadata has not been generated
910
+ input = local.operations_file
911
+
912
+ lifecycle {
913
+ precondition {
914
+ condition = fileexists(local.operations_file)
915
+ error_message = "Operations metadata not found at \${local.operations_file}. Build the TestApi project to generate it."
916
+ }
917
+ precondition {
918
+ condition = length(local.operations) > 0
919
+ error_message = "No operations found in \${local.operations_file}. The TestApi API must define at least one operation."
920
+ }
921
+ precondition {
922
+ condition = length(local.paths_too_deep) == 0
923
+ error_message = "Operation paths exceed the supported depth of \${local.max_path_depth} segments: \${join(", ", local.paths_too_deep)}. Add further aws_api_gateway_resource levels to this module to support them."
924
+ }
925
+ }
926
+ }
927
+
928
+ # API Gateway resources at path depth 0
929
+ resource "aws_api_gateway_resource" "path_depth_0" {
930
+ for_each = local.path_nodes_by_depth[0]
931
+
932
+ rest_api_id = module.rest_api.api_id
933
+ parent_id = module.rest_api.api_root_resource_id
934
+ path_part = each.value.part
935
+ }
936
+
937
+ # API Gateway resources at path depth 1
938
+ resource "aws_api_gateway_resource" "path_depth_1" {
939
+ for_each = local.path_nodes_by_depth[1]
940
+
941
+ rest_api_id = module.rest_api.api_id
942
+ parent_id = aws_api_gateway_resource.path_depth_0[each.value.parent].id
943
+ path_part = each.value.part
944
+ }
945
+
946
+ # API Gateway resources at path depth 2
947
+ resource "aws_api_gateway_resource" "path_depth_2" {
948
+ for_each = local.path_nodes_by_depth[2]
949
+
950
+ rest_api_id = module.rest_api.api_id
951
+ parent_id = aws_api_gateway_resource.path_depth_1[each.value.parent].id
952
+ path_part = each.value.part
953
+ }
954
+
955
+ # API Gateway resources at path depth 3
956
+ resource "aws_api_gateway_resource" "path_depth_3" {
957
+ for_each = local.path_nodes_by_depth[3]
958
+
959
+ rest_api_id = module.rest_api.api_id
960
+ parent_id = aws_api_gateway_resource.path_depth_2[each.value.parent].id
961
+ path_part = each.value.part
962
+ }
963
+
964
+ # API Gateway resources at path depth 4
965
+ resource "aws_api_gateway_resource" "path_depth_4" {
966
+ for_each = local.path_nodes_by_depth[4]
967
+
968
+ rest_api_id = module.rest_api.api_id
969
+ parent_id = aws_api_gateway_resource.path_depth_3[each.value.parent].id
970
+ path_part = each.value.part
971
+ }
972
+
973
+ # API Gateway resources at path depth 5
974
+ resource "aws_api_gateway_resource" "path_depth_5" {
975
+ for_each = local.path_nodes_by_depth[5]
976
+
977
+ rest_api_id = module.rest_api.api_id
978
+ parent_id = aws_api_gateway_resource.path_depth_4[each.value.parent].id
979
+ path_part = each.value.part
980
+ }
981
+
982
+ # API Gateway resources at path depth 6
983
+ resource "aws_api_gateway_resource" "path_depth_6" {
984
+ for_each = local.path_nodes_by_depth[6]
985
+
986
+ rest_api_id = module.rest_api.api_id
987
+ parent_id = aws_api_gateway_resource.path_depth_5[each.value.parent].id
988
+ path_part = each.value.part
989
+ }
990
+
991
+ # API Gateway resources at path depth 7
992
+ resource "aws_api_gateway_resource" "path_depth_7" {
993
+ for_each = local.path_nodes_by_depth[7]
994
+
995
+ rest_api_id = module.rest_api.api_id
996
+ parent_id = aws_api_gateway_resource.path_depth_6[each.value.parent].id
997
+ path_part = each.value.part
998
+ }
999
+
1000
+ # API Gateway resources at path depth 8
1001
+ resource "aws_api_gateway_resource" "path_depth_8" {
1002
+ for_each = local.path_nodes_by_depth[8]
1003
+
1004
+ rest_api_id = module.rest_api.api_id
1005
+ parent_id = aws_api_gateway_resource.path_depth_7[each.value.parent].id
1006
+ path_part = each.value.part
1007
+ }
1008
+
1009
+ # API Gateway resources at path depth 9
1010
+ resource "aws_api_gateway_resource" "path_depth_9" {
1011
+ for_each = local.path_nodes_by_depth[9]
1012
+
1013
+ rest_api_id = module.rest_api.api_id
1014
+ parent_id = aws_api_gateway_resource.path_depth_8[each.value.parent].id
1015
+ path_part = each.value.part
1016
+ }
1017
+
1018
+ # API Gateway resources at path depth 10
1019
+ resource "aws_api_gateway_resource" "path_depth_10" {
1020
+ for_each = local.path_nodes_by_depth[10]
1021
+
1022
+ rest_api_id = module.rest_api.api_id
1023
+ parent_id = aws_api_gateway_resource.path_depth_9[each.value.parent].id
1024
+ path_part = each.value.part
1025
+ }
1026
+
1027
+ # API Gateway resources at path depth 11
1028
+ resource "aws_api_gateway_resource" "path_depth_11" {
1029
+ for_each = local.path_nodes_by_depth[11]
1030
+
1031
+ rest_api_id = module.rest_api.api_id
1032
+ parent_id = aws_api_gateway_resource.path_depth_10[each.value.parent].id
1033
+ path_part = each.value.part
1034
+ }
1035
+
1036
+ # API Gateway resources at path depth 12
1037
+ resource "aws_api_gateway_resource" "path_depth_12" {
1038
+ for_each = local.path_nodes_by_depth[12]
1039
+
1040
+ rest_api_id = module.rest_api.api_id
1041
+ parent_id = aws_api_gateway_resource.path_depth_11[each.value.parent].id
1042
+ path_part = each.value.part
1043
+ }
1044
+
1045
+ # API Gateway resources at path depth 13
1046
+ resource "aws_api_gateway_resource" "path_depth_13" {
1047
+ for_each = local.path_nodes_by_depth[13]
1048
+
1049
+ rest_api_id = module.rest_api.api_id
1050
+ parent_id = aws_api_gateway_resource.path_depth_12[each.value.parent].id
1051
+ path_part = each.value.part
1052
+ }
1053
+
1054
+ # API Gateway resources at path depth 14
1055
+ resource "aws_api_gateway_resource" "path_depth_14" {
1056
+ for_each = local.path_nodes_by_depth[14]
1057
+
1058
+ rest_api_id = module.rest_api.api_id
1059
+ parent_id = aws_api_gateway_resource.path_depth_13[each.value.parent].id
1060
+ path_part = each.value.part
1061
+ }
1062
+
1063
+ # API Gateway resources at path depth 15
1064
+ resource "aws_api_gateway_resource" "path_depth_15" {
1065
+ for_each = local.path_nodes_by_depth[15]
1066
+
1067
+ rest_api_id = module.rest_api.api_id
1068
+ parent_id = aws_api_gateway_resource.path_depth_14[each.value.parent].id
1069
+ path_part = each.value.part
1070
+ }
1071
+
739
1072
  # Resources
740
1073
 
741
1074
  # Create Lambda ZIP file from the bundle directory
@@ -754,6 +1087,11 @@ resource "aws_s3_object" "lambda_zip" {
754
1087
  }
755
1088
 
756
1089
  # Use the core REST API module
1090
+ # Account-level CloudWatch role required for REST API access logging
1091
+ module "account" {
1092
+ source = "../../../core/api/api-gateway-account"
1093
+ }
1094
+
757
1095
  module "rest_api" {
758
1096
  source = "../../../core/api/rest-api"
759
1097
 
@@ -783,9 +1121,33 @@ resource "aws_wafv2_web_acl_association" "api_waf_association" {
783
1121
  depends_on = [aws_api_gateway_stage.api_stage]
784
1122
  }
785
1123
 
786
- # Lambda function
1124
+ # Security group for the API Lambda function, used when deployed into a VPC
1125
+ resource "aws_security_group" "api_lambda" {
1126
+ count = var.enable_vpc ? 1 : 0
1127
+
1128
+ #checkov:skip=CKV2_AWS_5:Attached to api_lambda via vpc_config block; Checkov cannot resolve this reference
1129
+ name_prefix = "test-api-lambda-"
1130
+ description = "Security group for the TestApi API Lambda function"
1131
+ vpc_id = var.vpc_id
1132
+ tags = var.tags
1133
+ }
1134
+
1135
+ resource "aws_vpc_security_group_egress_rule" "api_lambda_https" {
1136
+ count = var.enable_vpc ? 1 : 0
1137
+
1138
+ security_group_id = aws_security_group.api_lambda[0].id
1139
+ cidr_ipv4 = "0.0.0.0/0"
1140
+ from_port = 443
1141
+ to_port = 443
1142
+ ip_protocol = "tcp"
1143
+ description = "Allow outbound HTTPS to AWS service endpoints"
1144
+ }
1145
+
787
1146
  resource "aws_lambda_function" "api_lambda" {
788
- #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
1147
+ # One lambda function per operation, each serving just that operation
1148
+ for_each = local.operations
1149
+
1150
+ #checkov:skip=CKV_AWS_117:Lambda function is optionally deployed into a VPC via vpc_id/subnet_ids; not required for this use case
789
1151
  #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
790
1152
  #checkov:skip=CKV_AWS_272:Code signing not required for this use case
791
1153
  #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
@@ -793,10 +1155,10 @@ resource "aws_lambda_function" "api_lambda" {
793
1155
  s3_bucket = aws_s3_object.lambda_zip.bucket
794
1156
  s3_key = aws_s3_object.lambda_zip.key
795
1157
  s3_object_version = aws_s3_object.lambda_zip.version_id
796
- function_name = "TestApiHandler-\${random_string.suffix.result}"
797
- role = aws_iam_role.lambda_execution_role.arn
1158
+ function_name = local.function_name[each.key]
1159
+ role = aws_iam_role.lambda_execution_role[each.key].arn
798
1160
  handler = "index.handler"
799
- runtime = "nodejs22.x"
1161
+ runtime = "nodejs24.x"
800
1162
  timeout = 30
801
1163
  memory_size = 128
802
1164
 
@@ -807,18 +1169,32 @@ resource "aws_lambda_function" "api_lambda" {
807
1169
  mode = "Active"
808
1170
  }
809
1171
 
1172
+ dynamic "vpc_config" {
1173
+ for_each = var.enable_vpc ? [1] : []
1174
+ content {
1175
+ subnet_ids = var.subnet_ids
1176
+ security_group_ids = [aws_security_group.api_lambda[0].id]
1177
+ }
1178
+ }
1179
+
810
1180
 
811
1181
  environment {
812
1182
  variables = merge({
813
- }, var.env)
1183
+ }, var.appconfig_application_id != null ? {
1184
+ RUNTIME_CONFIG_APP_ID = var.appconfig_application_id
1185
+ } : {}, var.env)
814
1186
  }
815
1187
 
816
1188
  tags = var.tags
1189
+
1190
+ depends_on = [aws_iam_role_policy_attachment.lambda_vpc_access]
817
1191
  }
818
1192
 
819
1193
  # IAM role for Lambda execution
820
1194
  resource "aws_iam_role" "lambda_execution_role" {
821
- name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
1195
+ for_each = local.operations
1196
+
1197
+ name = local.role_name[each.key]
822
1198
 
823
1199
  assume_role_policy = jsonencode({
824
1200
  Version = "2012-10-17"
@@ -838,35 +1214,73 @@ resource "aws_iam_role" "lambda_execution_role" {
838
1214
 
839
1215
  # Attach basic execution policy to Lambda role
840
1216
  resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
1217
+ for_each = local.operations
1218
+
841
1219
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
842
- role = aws_iam_role.lambda_execution_role.name
1220
+ role = aws_iam_role.lambda_execution_role[each.key].name
843
1221
  }
844
1222
 
845
1223
  # Attach X-Ray tracing policy to Lambda role
846
1224
  resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
1225
+ for_each = local.operations
1226
+
847
1227
  policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
848
- role = aws_iam_role.lambda_execution_role.name
1228
+ role = aws_iam_role.lambda_execution_role[each.key].name
1229
+ }
1230
+
1231
+ # Attach VPC access policy to Lambda role when deployed into a VPC
1232
+ resource "aws_iam_role_policy_attachment" "lambda_vpc_access" {
1233
+ for_each = var.enable_vpc ? local.operations : {}
1234
+
1235
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
1236
+ role = aws_iam_role.lambda_execution_role[each.key].name
849
1237
  }
850
1238
 
851
1239
  # Additional IAM policies for Lambda (if provided)
852
1240
  resource "aws_iam_role_policy" "lambda_additional_policies" {
853
- count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
854
- name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
855
- role = aws_iam_role.lambda_execution_role.id
1241
+ for_each = local.operations
1242
+
1243
+ name = "\${substr(local.function_name[each.key], 0, 55)}-policies"
1244
+ role = aws_iam_role.lambda_execution_role[each.key].id
856
1245
 
857
1246
  policy = jsonencode({
858
1247
  Version = "2012-10-17"
859
- Statement = var.additional_iam_policy_statements
1248
+ Statement = local.lambda_policy_document_statements
860
1249
  })
861
1250
  }
862
1251
 
1252
+ locals {
1253
+ # Grant read access to the runtime-config AppConfig application when wired
1254
+ lambda_policy_statements = concat(var.appconfig_application_arn != null ? [
1255
+ {
1256
+ Effect = "Allow"
1257
+ Action = [
1258
+ "appconfig:StartConfigurationSession",
1259
+ "appconfig:GetLatestConfiguration"
1260
+ ]
1261
+ Resource = ["\${var.appconfig_application_arn}/*"]
1262
+ }
1263
+ ] : [], var.additional_iam_policy_statements)
1264
+
1265
+ # IAM rejects an empty statement list, so fall back to a no-op deny
1266
+ lambda_policy_document_statements = length(local.lambda_policy_statements) > 0 ? local.lambda_policy_statements : [
1267
+ {
1268
+ Effect = "Deny"
1269
+ Action = ["appconfig:GetLatestConfiguration"]
1270
+ Resource = ["arn:aws:appconfig:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:application/none"]
1271
+ }
1272
+ ]
1273
+ }
1274
+
863
1275
  # CloudWatch Log Group for Lambda
864
1276
  resource "aws_cloudwatch_log_group" "lambda_logs" {
865
1277
  #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
866
1278
  #checkov:skip=CKV_AWS_338:Log retention set to forever
867
1279
  #checkov:skip=CKV_AWS_66:Log retention set to forever
868
- name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
869
- tags = var.tags
1280
+ for_each = local.operations
1281
+
1282
+ name = "/aws/lambda/\${local.function_name[each.key]}"
1283
+ tags = var.tags
870
1284
  }
871
1285
 
872
1286
 
@@ -883,7 +1297,7 @@ resource "aws_lambda_function" "authorizer_lambda" {
883
1297
  function_name = "TestApiAuthorizer-\${random_string.suffix.result}"
884
1298
  role = aws_iam_role.authorizer_execution_role.arn
885
1299
  handler = "index.handler"
886
- runtime = "nodejs22.x"
1300
+ runtime = "nodejs24.x"
887
1301
  timeout = 10
888
1302
  memory_size = 128
889
1303
 
@@ -963,58 +1377,62 @@ resource "aws_lambda_permission" "authorizer_invoke" {
963
1377
  source_arn = "\${module.rest_api.api_execution_arn}/authorizers/\${aws_api_gateway_authorizer.custom_authorizer.id}"
964
1378
  }
965
1379
 
966
- # Create proxy resource (captures all paths)
967
- resource "aws_api_gateway_resource" "proxy_resource" {
968
- rest_api_id = module.rest_api.api_id
969
- parent_id = module.rest_api.api_root_resource_id
970
- path_part = "{proxy+}"
971
- }
972
-
973
- # Lambda integration for REST API
974
- resource "aws_api_gateway_integration" "lambda_integration" {
975
- rest_api_id = module.rest_api.api_id
976
- resource_id = aws_api_gateway_resource.proxy_resource.id
977
- http_method = aws_api_gateway_method.proxy_method.http_method
978
-
979
- integration_http_method = "POST"
980
- type = "AWS_PROXY"
981
- uri = aws_lambda_function.api_lambda.invoke_arn
982
-
983
- depends_on = [aws_lambda_function.api_lambda]
984
- }
985
-
986
- # Method for proxy integration
987
- resource "aws_api_gateway_method" "proxy_method" {
1380
+ # Method per operation
1381
+ resource "aws_api_gateway_method" "operation_methods" {
988
1382
  #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
1383
+ for_each = local.operations
1384
+
989
1385
  rest_api_id = module.rest_api.api_id
990
- resource_id = aws_api_gateway_resource.proxy_resource.id
991
- http_method = "ANY"
1386
+ resource_id = local.operation_resource_id[each.key]
1387
+ http_method = upper(each.value.method)
992
1388
 
993
1389
  authorization = "CUSTOM"
994
1390
  authorizer_id = aws_api_gateway_authorizer.custom_authorizer.id
995
1391
 
1392
+ # Path parameters must be declared on the method
996
1393
  request_parameters = {
997
- "method.request.path.proxy" = true
1394
+ for segment in local.operation_segments[each.key] :
1395
+ "method.request.path.\${trimsuffix(trimprefix(segment, "{"), "}")}" => true
1396
+ if startswith(segment, "{")
998
1397
  }
999
1398
 
1000
1399
  depends_on = [aws_api_gateway_authorizer.custom_authorizer]
1001
1400
  }
1002
1401
 
1003
- # OPTIONS method for CORS preflight
1402
+ # Lambda integration per operation
1403
+ resource "aws_api_gateway_integration" "lambda_integration" {
1404
+ for_each = local.operations
1405
+
1406
+ rest_api_id = module.rest_api.api_id
1407
+ resource_id = local.operation_resource_id[each.key]
1408
+ http_method = aws_api_gateway_method.operation_methods[each.key].http_method
1409
+
1410
+ integration_http_method = "POST"
1411
+ type = "AWS_PROXY"
1412
+ uri = aws_lambda_function.api_lambda[each.key].invoke_arn
1413
+
1414
+ depends_on = [aws_lambda_function.api_lambda]
1415
+ }
1416
+
1417
+ # OPTIONS method for CORS preflight, on every resource an operation is served from
1004
1418
  resource "aws_api_gateway_method" "options_method" {
1005
1419
  #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
1006
1420
  #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
1421
+ for_each = local.resource_ids
1422
+
1007
1423
  rest_api_id = module.rest_api.api_id
1008
- resource_id = aws_api_gateway_resource.proxy_resource.id
1424
+ resource_id = each.value
1009
1425
  http_method = "OPTIONS"
1010
1426
  authorization = "NONE"
1011
1427
  }
1012
1428
 
1013
- # CORS integration for OPTIONS method
1429
+ # CORS integration for OPTIONS methods
1014
1430
  resource "aws_api_gateway_integration" "options_integration" {
1431
+ for_each = local.resource_ids
1432
+
1015
1433
  rest_api_id = module.rest_api.api_id
1016
- resource_id = aws_api_gateway_resource.proxy_resource.id
1017
- http_method = aws_api_gateway_method.options_method.http_method
1434
+ resource_id = each.value
1435
+ http_method = aws_api_gateway_method.options_method[each.key].http_method
1018
1436
 
1019
1437
  type = "MOCK"
1020
1438
  request_templates = {
@@ -1022,11 +1440,13 @@ resource "aws_api_gateway_integration" "options_integration" {
1022
1440
  }
1023
1441
  }
1024
1442
 
1025
- # OPTIONS method response
1443
+ # OPTIONS method responses
1026
1444
  resource "aws_api_gateway_method_response" "options_response" {
1445
+ for_each = local.resource_ids
1446
+
1027
1447
  rest_api_id = module.rest_api.api_id
1028
- resource_id = aws_api_gateway_resource.proxy_resource.id
1029
- http_method = aws_api_gateway_method.options_method.http_method
1448
+ resource_id = each.value
1449
+ http_method = aws_api_gateway_method.options_method[each.key].http_method
1030
1450
  status_code = "204"
1031
1451
 
1032
1452
  response_parameters = {
@@ -1036,18 +1456,22 @@ resource "aws_api_gateway_method_response" "options_response" {
1036
1456
  }
1037
1457
  }
1038
1458
 
1039
- # OPTIONS integration response
1459
+ # OPTIONS integration responses
1040
1460
  resource "aws_api_gateway_integration_response" "options_integration_response" {
1461
+ for_each = local.resource_ids
1462
+
1041
1463
  rest_api_id = module.rest_api.api_id
1042
- resource_id = aws_api_gateway_resource.proxy_resource.id
1043
- http_method = aws_api_gateway_method.options_method.http_method
1044
- status_code = aws_api_gateway_method_response.options_response.status_code
1464
+ resource_id = each.value
1465
+ http_method = aws_api_gateway_method.options_method[each.key].http_method
1466
+ status_code = aws_api_gateway_method_response.options_response[each.key].status_code
1045
1467
 
1046
1468
  response_parameters = {
1047
1469
  "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
1048
1470
  "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
1049
1471
  "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
1050
1472
  }
1473
+
1474
+ depends_on = [aws_api_gateway_integration.options_integration]
1051
1475
  }
1052
1476
 
1053
1477
  # API Gateway deployment
@@ -1055,13 +1479,28 @@ resource "aws_api_gateway_deployment" "api_deployment" {
1055
1479
  rest_api_id = module.rest_api.api_id
1056
1480
 
1057
1481
  triggers = {
1058
- redeployment = sha1(jsonencode([
1059
- aws_api_gateway_resource.proxy_resource.id,
1060
- aws_api_gateway_method.proxy_method.id,
1061
- aws_api_gateway_integration.lambda_integration.id,
1062
- aws_api_gateway_method.options_method.id,
1063
- aws_api_gateway_integration.options_integration.id,
1064
- ]))
1482
+ redeployment = sha1(jsonencode(concat(
1483
+ [for r in aws_api_gateway_resource.path_depth_0 : r.id],
1484
+ [for r in aws_api_gateway_resource.path_depth_1 : r.id],
1485
+ [for r in aws_api_gateway_resource.path_depth_2 : r.id],
1486
+ [for r in aws_api_gateway_resource.path_depth_3 : r.id],
1487
+ [for r in aws_api_gateway_resource.path_depth_4 : r.id],
1488
+ [for r in aws_api_gateway_resource.path_depth_5 : r.id],
1489
+ [for r in aws_api_gateway_resource.path_depth_6 : r.id],
1490
+ [for r in aws_api_gateway_resource.path_depth_7 : r.id],
1491
+ [for r in aws_api_gateway_resource.path_depth_8 : r.id],
1492
+ [for r in aws_api_gateway_resource.path_depth_9 : r.id],
1493
+ [for r in aws_api_gateway_resource.path_depth_10 : r.id],
1494
+ [for r in aws_api_gateway_resource.path_depth_11 : r.id],
1495
+ [for r in aws_api_gateway_resource.path_depth_12 : r.id],
1496
+ [for r in aws_api_gateway_resource.path_depth_13 : r.id],
1497
+ [for r in aws_api_gateway_resource.path_depth_14 : r.id],
1498
+ [for r in aws_api_gateway_resource.path_depth_15 : r.id],
1499
+ [for m in aws_api_gateway_method.operation_methods : m.id],
1500
+ [for i in aws_api_gateway_integration.lambda_integration : i.id],
1501
+ [for m in aws_api_gateway_method.options_method : m.id],
1502
+ [for i in aws_api_gateway_integration.options_integration : i.id],
1503
+ )))
1065
1504
  }
1066
1505
 
1067
1506
  lifecycle {
@@ -1069,7 +1508,7 @@ resource "aws_api_gateway_deployment" "api_deployment" {
1069
1508
  }
1070
1509
 
1071
1510
  depends_on = [
1072
- aws_api_gateway_method.proxy_method,
1511
+ aws_api_gateway_method.operation_methods,
1073
1512
  aws_api_gateway_integration.lambda_integration,
1074
1513
  aws_api_gateway_method.options_method,
1075
1514
  aws_api_gateway_integration.options_integration,
@@ -1079,11 +1518,63 @@ resource "aws_api_gateway_deployment" "api_deployment" {
1079
1518
  ]
1080
1519
  }
1081
1520
 
1521
+ # KMS key for encrypting access logs at rest
1522
+ resource "aws_kms_key" "access_logs" {
1523
+ description = "TestApi API access log encryption"
1524
+ deletion_window_in_days = 7
1525
+ enable_key_rotation = true
1526
+
1527
+ policy = jsonencode({
1528
+ Version = "2012-10-17"
1529
+ Statement = [
1530
+ {
1531
+ Sid = "Enable IAM User Permissions"
1532
+ Effect = "Allow"
1533
+ Principal = { AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root" }
1534
+ Action = "kms:*"
1535
+ Resource = "*"
1536
+ },
1537
+ {
1538
+ Sid = "Allow CloudWatch Logs"
1539
+ Effect = "Allow"
1540
+ Principal = { Service = "logs.\${data.aws_region.current.region}.amazonaws.com" }
1541
+ Action = [
1542
+ "kms:Encrypt",
1543
+ "kms:Decrypt",
1544
+ "kms:ReEncrypt*",
1545
+ "kms:GenerateDataKey*",
1546
+ "kms:DescribeKey"
1547
+ ]
1548
+ Resource = "*"
1549
+ Condition = {
1550
+ ArnEquals = {
1551
+ "kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:log-group:/aws/apigateway/test-api-\${random_string.suffix.result}/access"
1552
+ }
1553
+ }
1554
+ }
1555
+ ]
1556
+ })
1557
+
1558
+ tags = var.tags
1559
+ }
1560
+
1561
+ resource "aws_kms_alias" "access_logs" {
1562
+ name = "alias/test-api-\${random_string.suffix.result}-access-logs"
1563
+ target_key_id = aws_kms_key.access_logs.key_id
1564
+ }
1565
+
1566
+ # Access logs for the API Gateway stage
1567
+ resource "aws_cloudwatch_log_group" "access_logs" {
1568
+ name = "/aws/apigateway/test-api-\${random_string.suffix.result}/access"
1569
+ retention_in_days = 365
1570
+ kms_key_id = aws_kms_key.access_logs.arn
1571
+ tags = var.tags
1572
+ }
1573
+
1082
1574
  # API Gateway stage
1083
1575
  resource "aws_api_gateway_stage" "api_stage" {
1084
1576
  #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
1085
- #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
1086
- #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
1577
+ #checkov:skip=CKV2_AWS_4:Access logging is enabled below; verbose execution logging is intentionally not enabled
1087
1578
  #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
1088
1579
  #checkov:skip=CKV2_AWS_77:WAFv2 Web ACL is attached via aws_wafv2_web_acl_association when var.enable_waf is true (default) and includes AWSManagedRulesKnownBadInputsRuleSet for Log4j protection; Checkov does not track the association across modules
1089
1580
  deployment_id = aws_api_gateway_deployment.api_deployment.id
@@ -1091,9 +1582,39 @@ resource "aws_api_gateway_stage" "api_stage" {
1091
1582
  stage_name = "prod"
1092
1583
  xray_tracing_enabled = true
1093
1584
 
1585
+ access_log_settings {
1586
+ destination_arn = aws_cloudwatch_log_group.access_logs.arn
1587
+ format = jsonencode({
1588
+ requestId = "$context.requestId"
1589
+ ip = "$context.identity.sourceIp"
1590
+ caller = "$context.identity.caller"
1591
+ user = "$context.identity.user"
1592
+ requestTime = "$context.requestTime"
1593
+ httpMethod = "$context.httpMethod"
1594
+ resourcePath = "$context.resourcePath"
1595
+ status = "$context.status"
1596
+ protocol = "$context.protocol"
1597
+ responseLength = "$context.responseLength"
1598
+ })
1599
+ }
1600
+
1094
1601
  tags = var.tags
1095
1602
 
1096
- depends_on = [aws_api_gateway_deployment.api_deployment]
1603
+ # Ensure the account-level role is configured before the stage is created
1604
+ depends_on = [aws_api_gateway_deployment.api_deployment, module.account]
1605
+ }
1606
+
1607
+ # Default throttling applied to all resource paths and methods
1608
+ resource "aws_api_gateway_method_settings" "throttling" {
1609
+ #checkov:skip=CKV_AWS_225:API Gateway caching not required for this use case
1610
+ rest_api_id = module.rest_api.api_id
1611
+ stage_name = aws_api_gateway_stage.api_stage.stage_name
1612
+ method_path = "*/*"
1613
+
1614
+ settings {
1615
+ throttling_rate_limit = 10000
1616
+ throttling_burst_limit = 5000
1617
+ }
1097
1618
  }
1098
1619
 
1099
1620
  # API Gateway Resource Policy
@@ -1116,11 +1637,13 @@ resource "aws_api_gateway_rest_api_policy" "api_policy" {
1116
1637
 
1117
1638
  # Lambda permission for API Gateway to invoke the function
1118
1639
  resource "aws_lambda_permission" "api_gateway_invoke" {
1640
+ for_each = local.operations
1641
+
1119
1642
  statement_id = "AllowExecutionFromAPIGateway"
1120
1643
  action = "lambda:InvokeFunction"
1121
- function_name = aws_lambda_function.api_lambda.function_name
1644
+ function_name = aws_lambda_function.api_lambda[each.key].function_name
1122
1645
  principal = "apigateway.amazonaws.com"
1123
- source_arn = "\${module.rest_api.api_execution_arn}/*/*"
1646
+ source_arn = "\${module.rest_api.api_execution_arn}/*/\${local.permission_source_suffix[each.key]}"
1124
1647
 
1125
1648
  depends_on = [module.rest_api, aws_lambda_function.api_lambda]
1126
1649
  }
@@ -1132,7 +1655,7 @@ module "add_url_to_runtime_config" {
1132
1655
 
1133
1656
  namespace = "connection"
1134
1657
  key = "apis"
1135
- value = { "TestApi" = aws_api_gateway_stage.api_stage.invoke_url }
1658
+ value = { "TestApi" = "\${aws_api_gateway_stage.api_stage.invoke_url}/" }
1136
1659
 
1137
1660
  depends_on = [aws_api_gateway_stage.api_stage]
1138
1661
  }
@@ -1185,78 +1708,83 @@ output "stage_id" {
1185
1708
  value = aws_api_gateway_stage.api_stage.id
1186
1709
  }
1187
1710
 
1188
- # Lambda Function Outputs
1189
- output "lambda_function_name" {
1190
- description = "Name of the Lambda function"
1191
- value = aws_lambda_function.api_lambda.function_name
1711
+ # Lambda Function Outputs, keyed by operation name
1712
+ output "operations" {
1713
+ description = "Names of the API operations, each with its own Lambda function"
1714
+ value = keys(local.operations)
1715
+ }
1716
+
1717
+ output "lambda_function_names" {
1718
+ description = "Name of each operation's Lambda function, keyed by operation name"
1719
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.function_name }
1192
1720
  }
1193
1721
 
1194
- output "lambda_function_arn" {
1195
- description = "ARN of the Lambda function"
1196
- value = aws_lambda_function.api_lambda.arn
1722
+ output "lambda_function_arns" {
1723
+ description = "ARN of each operation's Lambda function, keyed by operation name"
1724
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.arn }
1197
1725
  }
1198
1726
 
1199
- output "lambda_invoke_arn" {
1200
- description = "Invoke ARN of the Lambda function"
1201
- value = aws_lambda_function.api_lambda.invoke_arn
1727
+ output "lambda_invoke_arns" {
1728
+ description = "Invoke ARN of each operation's Lambda function, keyed by operation name"
1729
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.invoke_arn }
1202
1730
  }
1203
1731
 
1204
- output "lambda_qualified_arn" {
1205
- description = "Qualified ARN of the Lambda function"
1206
- value = aws_lambda_function.api_lambda.qualified_arn
1732
+ output "lambda_qualified_arns" {
1733
+ description = "Qualified ARN of each operation's Lambda function, keyed by operation name"
1734
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.qualified_arn }
1207
1735
  }
1208
1736
 
1209
- output "lambda_version" {
1210
- description = "Version of the Lambda function"
1211
- value = aws_lambda_function.api_lambda.version
1737
+ output "lambda_versions" {
1738
+ description = "Version of each operation's Lambda function, keyed by operation name"
1739
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.version }
1212
1740
  }
1213
1741
 
1214
1742
  output "lambda_source_code_hash" {
1215
- description = "Base64-encoded SHA256 hash of the Lambda deployment package"
1216
- value = aws_lambda_function.api_lambda.source_code_hash
1743
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package, shared by every operation"
1744
+ value = data.archive_file.lambda_zip.output_base64sha256
1217
1745
  }
1218
1746
 
1219
- output "lambda_source_code_size" {
1220
- description = "Size of the Lambda deployment package in bytes"
1221
- value = aws_lambda_function.api_lambda.source_code_size
1747
+ # IAM Role Outputs, keyed by operation name
1748
+ output "lambda_execution_role_arns" {
1749
+ description = "ARN of each operation's Lambda execution role, keyed by operation name"
1750
+ value = { for op, role in aws_iam_role.lambda_execution_role : op => role.arn }
1222
1751
  }
1223
1752
 
1224
- # IAM Role Outputs
1225
- output "lambda_execution_role_arn" {
1226
- description = "ARN of the Lambda execution role"
1227
- value = aws_iam_role.lambda_execution_role.arn
1753
+ output "lambda_execution_role_names" {
1754
+ description = "Name of each operation's Lambda execution role, keyed by operation name. Attach additional policies to a specific operation's role by name."
1755
+ value = { for op, role in aws_iam_role.lambda_execution_role : op => role.name }
1228
1756
  }
1229
1757
 
1230
- output "lambda_execution_role_name" {
1231
- description = "Name of the Lambda execution role"
1232
- value = aws_iam_role.lambda_execution_role.name
1758
+ output "security_group_id" {
1759
+ description = "Security group ID shared by the API Lambda functions, for use in ingress rules on resources they must reach (e.g. a database). Null unless enable_vpc is true."
1760
+ value = var.enable_vpc ? aws_security_group.api_lambda[0].id : null
1233
1761
  }
1234
1762
 
1235
- # Integration Outputs
1236
- output "integration_id" {
1237
- description = "ID of the Lambda integration"
1238
- value = aws_api_gateway_integration.lambda_integration.id
1763
+ # Integration Outputs, keyed by operation name
1764
+ output "integration_ids" {
1765
+ description = "ID of each operation's Lambda integration, keyed by operation name"
1766
+ value = { for op, i in aws_api_gateway_integration.lambda_integration : op => i.id }
1239
1767
  }
1240
1768
 
1241
- output "proxy_resource_id" {
1242
- description = "ID of the proxy resource"
1243
- value = aws_api_gateway_resource.proxy_resource.id
1769
+ output "method_ids" {
1770
+ description = "ID of each operation's API Gateway method, keyed by operation name"
1771
+ value = { for op, m in aws_api_gateway_method.operation_methods : op => m.id }
1244
1772
  }
1245
1773
 
1246
- output "proxy_method_id" {
1247
- description = "ID of the proxy method"
1248
- value = aws_api_gateway_method.proxy_method.id
1774
+ output "resource_ids" {
1775
+ description = "ID of each API Gateway resource, keyed by its path"
1776
+ value = local.resource_ids
1249
1777
  }
1250
1778
 
1251
- # CloudWatch Log Groups
1252
- output "lambda_log_group_name" {
1253
- description = "Name of the Lambda CloudWatch log group"
1254
- value = aws_cloudwatch_log_group.lambda_logs.name
1779
+ # CloudWatch Log Groups, keyed by operation name
1780
+ output "lambda_log_group_names" {
1781
+ description = "Name of each operation's Lambda CloudWatch log group, keyed by operation name"
1782
+ value = { for op, lg in aws_cloudwatch_log_group.lambda_logs : op => lg.name }
1255
1783
  }
1256
1784
 
1257
- output "lambda_log_group_arn" {
1258
- description = "ARN of the Lambda CloudWatch log group"
1259
- value = aws_cloudwatch_log_group.lambda_logs.arn
1785
+ output "lambda_log_group_arns" {
1786
+ description = "ARN of each operation's Lambda CloudWatch log group, keyed by operation name"
1787
+ value = { for op, lg in aws_cloudwatch_log_group.lambda_logs : op => lg.arn }
1260
1788
  }
1261
1789
  "
1262
1790
  `;
@@ -1296,25 +1824,46 @@ exports[`tsSmithyApiGenerator > should generate smithy ts api with default optio
1296
1824
  "apiName": "test-api",
1297
1825
  "auth": "custom",
1298
1826
  "modelProject": "@proj/test-api-model",
1827
+ "iac": "cdk",
1299
1828
  "ports": [3001]
1300
1829
  },
1301
1830
  "targets": {
1831
+ "assemble": {
1832
+ "dependsOn": ["compile", "bundle"]
1833
+ },
1302
1834
  "build": {
1303
1835
  "dependsOn": ["lint", "compile", "test", "bundle"]
1304
1836
  },
1305
1837
  "compile": {
1306
1838
  "executor": "nx:run-commands",
1839
+ "dependsOn": ["copy-ssdk"],
1307
1840
  "outputs": ["{workspaceRoot}/dist/{projectRoot}/tsc"],
1308
1841
  "options": {
1309
1842
  "command": "tsc --build tsconfig.lib.json",
1310
1843
  "cwd": "{projectRoot}"
1844
+ }
1845
+ },
1846
+ "format": {
1847
+ "executor": "nx:run-commands",
1848
+ "cache": true,
1849
+ "inputs": ["biome"],
1850
+ "options": {
1851
+ "command": "biome format {projectRoot}"
1311
1852
  },
1312
- "dependsOn": ["copy-ssdk"]
1853
+ "configurations": {
1854
+ "fix": {
1855
+ "command": "biome format --write {projectRoot}"
1856
+ },
1857
+ "skip-lint": {
1858
+ "command": "node -e \\"\\""
1859
+ }
1860
+ }
1313
1861
  },
1314
1862
  "lint": {
1315
1863
  "executor": "nx:run-commands",
1316
1864
  "cache": true,
1317
1865
  "inputs": ["biome"],
1866
+ "dependsOn": ["format"],
1318
1867
  "options": {
1319
1868
  "command": "biome lint {projectRoot}"
1320
1869
  },
@@ -1327,22 +1876,16 @@ exports[`tsSmithyApiGenerator > should generate smithy ts api with default optio
1327
1876
  }
1328
1877
  }
1329
1878
  },
1330
- "test": {
1331
- "executor": "@nx/vitest:test",
1332
- "outputs": ["{options.reportsDirectory}"],
1333
- "options": {
1334
- "reportsDirectory": "{workspaceRoot}/coverage/{projectRoot}"
1335
- }
1336
- },
1337
1879
  "bundle": {
1880
+ "executor": "nx:run-commands",
1881
+ "dependsOn": ["compile"],
1338
1882
  "cache": true,
1883
+ "inputs": ["default"],
1339
1884
  "outputs": ["{workspaceRoot}/dist/{projectRoot}/bundle"],
1340
- "executor": "nx:run-commands",
1341
1885
  "options": {
1342
1886
  "command": "rolldown -c rolldown.config.ts",
1343
1887
  "cwd": "{projectRoot}"
1344
- },
1345
- "dependsOn": ["compile"]
1888
+ }
1346
1889
  },
1347
1890
  "copy-ssdk": {
1348
1891
  "cache": true,
@@ -1365,31 +1908,31 @@ exports[`tsSmithyApiGenerator > should generate smithy ts api with default optio
1365
1908
  },
1366
1909
  "watch-copy-ssdk": {
1367
1910
  "executor": "nx:run-commands",
1911
+ "continuous": true,
1368
1912
  "options": {
1369
- "command": "nx watch --projects=@proj/test-api-model --includeDependentProjects -- nx run @proj/test-api:copy-ssdk"
1370
- },
1371
- "continuous": true
1913
+ "command": "nx watch --projects=@proj/test-api-model --includeDependencies -- nx run @proj/test-api:copy-ssdk"
1914
+ }
1372
1915
  },
1373
1916
  "serve": {
1374
1917
  "executor": "nx:run-commands",
1918
+ "dependsOn": ["copy-ssdk", "watch-copy-ssdk"],
1919
+ "continuous": true,
1375
1920
  "options": {
1376
1921
  "command": "tsx --watch src/local-server.ts",
1377
1922
  "cwd": "{projectRoot}"
1378
- },
1379
- "continuous": true,
1380
- "dependsOn": ["copy-ssdk", "watch-copy-ssdk"]
1923
+ }
1381
1924
  },
1382
- "serve-local": {
1925
+ "dev": {
1383
1926
  "executor": "nx:run-commands",
1927
+ "dependsOn": ["copy-ssdk", "watch-copy-ssdk"],
1928
+ "continuous": true,
1384
1929
  "options": {
1385
1930
  "command": "tsx --watch src/local-server.ts",
1386
1931
  "cwd": "{projectRoot}",
1387
1932
  "env": {
1388
- "SERVE_LOCAL": "true"
1933
+ "LOCAL_DEV": "true"
1389
1934
  }
1390
- },
1391
- "continuous": true,
1392
- "dependsOn": ["copy-ssdk", "watch-copy-ssdk"]
1935
+ }
1393
1936
  }
1394
1937
  }
1395
1938
  }
@@ -1427,7 +1970,7 @@ exports[`tsSmithyApiGenerator > should generate smithy ts api with default optio
1427
1970
  "import {
1428
1971
  convertEvent,
1429
1972
  convertVersion1Response,
1430
- } from '@aws-smithy/server-apigateway';
1973
+ } from '@smithy/server-apigateway';
1431
1974
  import type { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
1432
1975
  import middy from '@middy/core';
1433
1976
  import { Tracer } from '@aws-lambda-powertools/tracer';
@@ -1492,7 +2035,7 @@ export const handler = middy<APIGatewayProxyEvent, APIGatewayProxyResult>()
1492
2035
 
1493
2036
  exports[`tsSmithyApiGenerator > should generate smithy ts api with default options > local-server.ts 1`] = `
1494
2037
  "import { IncomingMessage, ServerResponse, createServer } from 'http';
1495
- import { convertRequest, writeResponse } from '@aws-smithy/server-node';
2038
+ import { convertRequest, writeResponse } from '@smithy/server-node';
1496
2039
  import { Logger } from '@aws-lambda-powertools/logger';
1497
2040
  import { Metrics } from '@aws-lambda-powertools/metrics';
1498
2041
  import { Tracer } from '@aws-lambda-powertools/tracer';
@@ -1543,22 +2086,46 @@ exports[`tsSmithyApiGenerator > should generate smithy ts api with default optio
1543
2086
  "projectType": "library",
1544
2087
  "metadata": {
1545
2088
  "generator": "smithy#project",
2089
+ "smithyType": "service",
2090
+ "namespace": "proj",
1546
2091
  "apiName": "test-api-model",
1547
2092
  "backendProject": "@proj/test-api"
1548
2093
  },
1549
2094
  "targets": {
1550
2095
  "build": {
1551
- "dependsOn": ["compile"]
2096
+ "dependsOn": ["compile", "generate-ssdk"]
2097
+ },
2098
+ "assemble": {
2099
+ "dependsOn": ["compile", "generate-ssdk"]
1552
2100
  },
1553
2101
  "compile": {
1554
2102
  "cache": true,
1555
- "outputs": ["{workspaceRoot}/dist/{projectRoot}/build"],
2103
+ "outputs": [
2104
+ "{workspaceRoot}/dist/{projectRoot}/smithy",
2105
+ "{workspaceRoot}/dist/{projectRoot}/build/openapi"
2106
+ ],
1556
2107
  "executor": "nx:run-commands",
1557
2108
  "options": {
1558
2109
  "commands": [
1559
2110
  "rimraf dist/{projectRoot}/build",
2111
+ "rimraf dist/{projectRoot}/smithy",
1560
2112
  "make-dir dist/{projectRoot}/build",
1561
- "docker build -f {projectRoot}/build.Dockerfile --target export --output type=local,dest=dist/{projectRoot}/build {projectRoot}"
2113
+ "npx -y mise@2026.8.14 exec smithy@1.73.0 -- smithy build -c {projectRoot}/smithy-build.json --output dist/{projectRoot}/smithy",
2114
+ "cpy \\"dist/{projectRoot}/smithy/source/openapi/*.openapi.json\\" dist/{projectRoot}/build/openapi --flat --rename=openapi.json"
2115
+ ],
2116
+ "parallel": false,
2117
+ "cwd": "{workspaceRoot}"
2118
+ }
2119
+ },
2120
+ "generate-ssdk": {
2121
+ "cache": true,
2122
+ "outputs": ["{workspaceRoot}/dist/{projectRoot}/build/ssdk"],
2123
+ "executor": "nx:run-commands",
2124
+ "dependsOn": ["compile"],
2125
+ "options": {
2126
+ "commands": [
2127
+ "npm install --prefix dist/{projectRoot}/smithy/source/typescript-ssdk-codegen --include=dev --ignore-scripts --no-audit --no-fund",
2128
+ "rolldown -c {projectRoot}/ssdk.rolldown.config.mjs"
1562
2129
  ],
1563
2130
  "parallel": false,
1564
2131
  "cwd": "{workspaceRoot}"
@@ -1585,7 +2152,7 @@ exports[`tsSmithyApiGenerator > should handle kebab-case API names correctly > k
1585
2152
  "import {
1586
2153
  convertEvent,
1587
2154
  convertVersion1Response,
1588
- } from '@aws-smithy/server-apigateway';
2155
+ } from '@smithy/server-apigateway';
1589
2156
  import type { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
1590
2157
  import middy from '@middy/core';
1591
2158
  import { Tracer } from '@aws-lambda-powertools/tracer';
@@ -1682,11 +2249,11 @@ terraform {
1682
2249
  required_providers {
1683
2250
  aws = {
1684
2251
  source = "hashicorp/aws"
1685
- version = "~> 6.33"
2252
+ version = "6.62.0"
1686
2253
  }
1687
2254
  random = {
1688
2255
  source = "hashicorp/random"
1689
- version = "~> 3.1"
2256
+ version = "3.9.0"
1690
2257
  }
1691
2258
  }
1692
2259
  }
@@ -1756,8 +2323,6 @@ data "aws_caller_identity" "current" {}
1756
2323
 
1757
2324
  # Resources
1758
2325
 
1759
- # Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
1760
-
1761
2326
  # REST API Gateway
1762
2327
  resource "aws_api_gateway_rest_api" "rest_api" {
1763
2328
  name = var.api_name
@@ -1774,10 +2339,8 @@ resource "aws_api_gateway_rest_api" "rest_api" {
1774
2339
  tags = var.tags
1775
2340
  }
1776
2341
 
1777
- # Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
1778
- # after all methods and integrations are defined
1779
-
1780
- # Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
2342
+ # Note: Deployment, stage and access logging are created in the consuming module
2343
+ # (e.g., foo-api.tf) after all methods and integrations are defined
1781
2344
 
1782
2345
  # Gateway Response for CORS (4XX errors)
1783
2346
  resource "aws_api_gateway_gateway_response" "cors_4xx" {
@@ -1879,22 +2442,75 @@ resource "aws_wafv2_web_acl" "api_waf" {
1879
2442
  }
1880
2443
  }
1881
2444
 
2445
+ resource "random_id" "waf_log_suffix" {
2446
+ count = var.enable_waf ? 1 : 0
2447
+ byte_length = 4
2448
+ }
2449
+
2450
+ locals {
2451
+ waf_log_group_name = var.enable_waf ? "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}" : null
2452
+ }
2453
+
2454
+ # KMS key for encrypting WAF request logs at rest
2455
+ resource "aws_kms_key" "waf_logs" {
2456
+ count = var.enable_waf ? 1 : 0
2457
+
2458
+ description = "\${var.api_name} API WAF log encryption"
2459
+ deletion_window_in_days = 7
2460
+ enable_key_rotation = true
2461
+
2462
+ policy = jsonencode({
2463
+ Version = "2012-10-17"
2464
+ Statement = [
2465
+ {
2466
+ Sid = "Enable IAM User Permissions"
2467
+ Effect = "Allow"
2468
+ Principal = { AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root" }
2469
+ Action = "kms:*"
2470
+ Resource = "*"
2471
+ },
2472
+ {
2473
+ Sid = "Allow CloudWatch Logs"
2474
+ Effect = "Allow"
2475
+ Principal = { Service = "logs.\${data.aws_region.current.region}.amazonaws.com" }
2476
+ Action = [
2477
+ "kms:Encrypt",
2478
+ "kms:Decrypt",
2479
+ "kms:ReEncrypt*",
2480
+ "kms:GenerateDataKey*",
2481
+ "kms:DescribeKey"
2482
+ ]
2483
+ Resource = "*"
2484
+ Condition = {
2485
+ ArnEquals = {
2486
+ "kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:log-group:\${local.waf_log_group_name}"
2487
+ }
2488
+ }
2489
+ }
2490
+ ]
2491
+ })
2492
+
2493
+ tags = var.tags
2494
+ }
2495
+
2496
+ resource "aws_kms_alias" "waf_logs" {
2497
+ count = var.enable_waf ? 1 : 0
2498
+
2499
+ name = "alias/\${var.api_name}-\${random_id.waf_log_suffix[0].hex}-waf-logs"
2500
+ target_key_id = aws_kms_key.waf_logs[0].key_id
2501
+ }
2502
+
1882
2503
  # CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
1883
2504
  resource "aws_cloudwatch_log_group" "api_waf_logs" {
1884
- #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
1885
2505
  #checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
1886
2506
  count = var.enable_waf ? 1 : 0
1887
2507
 
1888
- name = "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}"
2508
+ name = local.waf_log_group_name
1889
2509
  retention_in_days = 30
2510
+ kms_key_id = aws_kms_key.waf_logs[0].arn
1890
2511
  tags = var.tags
1891
2512
  }
1892
2513
 
1893
- resource "random_id" "waf_log_suffix" {
1894
- count = var.enable_waf ? 1 : 0
1895
- byte_length = 4
1896
- }
1897
-
1898
2514
  resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
1899
2515
  count = var.enable_waf ? 1 : 0
1900
2516
 
@@ -1935,16 +2551,22 @@ output "waf_web_acl_arn" {
1935
2551
  value = var.enable_waf ? aws_wafv2_web_acl.api_waf[0].arn : null
1936
2552
  }
1937
2553
 
1938
- # Note: Stage and deployment outputs are provided by the consuming module
1939
-
1940
- # Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
2554
+ # Note: Stage, deployment and access log outputs are provided by the consuming module",
1941
2555
  "test-api.tf": "terraform {
1942
2556
  required_version = ">= 1.0"
1943
2557
 
1944
2558
  required_providers {
2559
+ archive = {
2560
+ source = "hashicorp/archive"
2561
+ version = "2.8.0"
2562
+ }
1945
2563
  aws = {
1946
2564
  source = "hashicorp/aws"
1947
- version = "~> 6.33"
2565
+ version = "6.62.0"
2566
+ }
2567
+ random = {
2568
+ source = "hashicorp/random"
2569
+ version = "3.9.0"
1948
2570
  }
1949
2571
  }
1950
2572
  }
@@ -1976,11 +2598,47 @@ variable "additional_iam_policy_statements" {
1976
2598
  default = []
1977
2599
  }
1978
2600
 
2601
+ variable "appconfig_application_id" {
2602
+ description = "ID of the shared runtime-config AppConfig application (from \`core/runtime-config/appconfig.application_id\`). When set, the Lambda functions receive \`RUNTIME_CONFIG_APP_ID\` and read access to the application."
2603
+ type = string
2604
+ default = null
2605
+ }
2606
+
2607
+ variable "appconfig_application_arn" {
2608
+ description = "ARN of the shared runtime-config AppConfig application (from \`core/runtime-config/appconfig.application_arn\`). Used to scope the IAM read permission granted to the Lambda functions."
2609
+ type = string
2610
+ default = null
2611
+ }
2612
+
1979
2613
  variable "asset_bucket_name" {
1980
2614
  description = "Name of the shared asset S3 bucket used to stage the Lambda deployment zip. Instantiate the \`core/asset-bucket\` module once per deployment and pass its \`bucket_name\` output here."
1981
2615
  type = string
1982
2616
  }
1983
2617
 
2618
+ # VPC Configuration (optional)
2619
+ variable "enable_vpc" {
2620
+ description = "Deploy the Lambda function into a VPC. Set alongside vpc_id/subnet_ids."
2621
+ type = bool
2622
+ default = false
2623
+ }
2624
+
2625
+ variable "vpc_id" {
2626
+ description = "VPC ID to deploy the Lambda function into. Required when enable_vpc is true."
2627
+ type = string
2628
+ default = null
2629
+
2630
+ validation {
2631
+ condition = !var.enable_vpc || (var.vpc_id != null && var.subnet_ids != null)
2632
+ error_message = "vpc_id and subnet_ids must be set when enable_vpc is true."
2633
+ }
2634
+ }
2635
+
2636
+ variable "subnet_ids" {
2637
+ description = "Subnet IDs to deploy the Lambda function into. Required when enable_vpc is true."
2638
+ type = list(string)
2639
+ default = null
2640
+ }
2641
+
1984
2642
  # CORS Configuration (passed to core module)
1985
2643
 
1986
2644
  variable "cors_allow_headers" {
@@ -2025,6 +2683,235 @@ resource "random_string" "suffix" {
2025
2683
  upper = false
2026
2684
  }
2027
2685
 
2686
+ locals {
2687
+ # Generated at build time from the API definition
2688
+ operations_file = "\${path.module}/../../../generated/test-api/operations.json"
2689
+ operations = fileexists(local.operations_file) ? jsondecode(file(local.operations_file)) : {}
2690
+
2691
+ operation_slug = { for op in keys(local.operations) : op => replace(op, "/[^a-zA-Z0-9-_]/", "-") }
2692
+ operation_hash = { for op in keys(local.operations) : op => substr(sha256(op), 0, 8) }
2693
+
2694
+ function_name = { for op in keys(local.operations) : op =>
2695
+ "\${substr("TestApi-\${local.operation_slug[op]}", 0, 64 - length(local.operation_hash[op]) - length(random_string.suffix.result) - 2)}-\${local.operation_hash[op]}-\${random_string.suffix.result}"
2696
+ }
2697
+ role_name = { for op in keys(local.operations) : op =>
2698
+ "\${substr("TestApi-\${local.operation_slug[op]}-role", 0, 64 - length(local.operation_hash[op]) - length(random_string.suffix.result) - 2)}-\${local.operation_hash[op]}-\${random_string.suffix.result}"
2699
+ }
2700
+
2701
+ # A REST API needs a resource per path segment, so each operation's path is
2702
+ # split into the distinct path prefixes the resource tree is built from below
2703
+ operation_segments = { for op, details in local.operations : op => compact(split("/", details.path)) }
2704
+
2705
+ path_nodes = merge([
2706
+ for op, segments in local.operation_segments : {
2707
+ for i, segment in segments : join("/", slice(segments, 0, i + 1)) => {
2708
+ depth = i
2709
+ parent = i == 0 ? "" : join("/", slice(segments, 0, i))
2710
+ part = segment
2711
+ }
2712
+ }
2713
+ ]...)
2714
+
2715
+ # Instances of a resource cannot reference one another, so each depth is a
2716
+ # separate resource. Deeper paths are reported by the precondition below.
2717
+ max_path_depth = 16
2718
+ path_nodes_by_depth = { for depth in range(local.max_path_depth) : depth => {
2719
+ for path, node in local.path_nodes : path => node if node.depth == depth
2720
+ } }
2721
+ paths_too_deep = [for path, node in local.path_nodes : path if node.depth >= local.max_path_depth]
2722
+
2723
+ resource_ids = merge(
2724
+ { for path, resource in aws_api_gateway_resource.path_depth_0 : path => resource.id },
2725
+ { for path, resource in aws_api_gateway_resource.path_depth_1 : path => resource.id },
2726
+ { for path, resource in aws_api_gateway_resource.path_depth_2 : path => resource.id },
2727
+ { for path, resource in aws_api_gateway_resource.path_depth_3 : path => resource.id },
2728
+ { for path, resource in aws_api_gateway_resource.path_depth_4 : path => resource.id },
2729
+ { for path, resource in aws_api_gateway_resource.path_depth_5 : path => resource.id },
2730
+ { for path, resource in aws_api_gateway_resource.path_depth_6 : path => resource.id },
2731
+ { for path, resource in aws_api_gateway_resource.path_depth_7 : path => resource.id },
2732
+ { for path, resource in aws_api_gateway_resource.path_depth_8 : path => resource.id },
2733
+ { for path, resource in aws_api_gateway_resource.path_depth_9 : path => resource.id },
2734
+ { for path, resource in aws_api_gateway_resource.path_depth_10 : path => resource.id },
2735
+ { for path, resource in aws_api_gateway_resource.path_depth_11 : path => resource.id },
2736
+ { for path, resource in aws_api_gateway_resource.path_depth_12 : path => resource.id },
2737
+ { for path, resource in aws_api_gateway_resource.path_depth_13 : path => resource.id },
2738
+ { for path, resource in aws_api_gateway_resource.path_depth_14 : path => resource.id },
2739
+ { for path, resource in aws_api_gateway_resource.path_depth_15 : path => resource.id },
2740
+ )
2741
+
2742
+ operation_resource_id = { for op, segments in local.operation_segments : op =>
2743
+ local.resource_ids[join("/", segments)]
2744
+ }
2745
+
2746
+ permission_source_suffix = { for op, details in local.operations : op =>
2747
+ "\${upper(details.method)}/\${replace(join("/", local.operation_segments[op]), "/{[^}]*}/", "*")}"
2748
+ }
2749
+ }
2750
+
2751
+ resource "terraform_data" "operations_metadata" {
2752
+ # Fails the plan when the operations metadata has not been generated
2753
+ input = local.operations_file
2754
+
2755
+ lifecycle {
2756
+ precondition {
2757
+ condition = fileexists(local.operations_file)
2758
+ error_message = "Operations metadata not found at \${local.operations_file}. Build the TestApi project to generate it."
2759
+ }
2760
+ precondition {
2761
+ condition = length(local.operations) > 0
2762
+ error_message = "No operations found in \${local.operations_file}. The TestApi API must define at least one operation."
2763
+ }
2764
+ precondition {
2765
+ condition = length(local.paths_too_deep) == 0
2766
+ error_message = "Operation paths exceed the supported depth of \${local.max_path_depth} segments: \${join(", ", local.paths_too_deep)}. Add further aws_api_gateway_resource levels to this module to support them."
2767
+ }
2768
+ }
2769
+ }
2770
+
2771
+ # API Gateway resources at path depth 0
2772
+ resource "aws_api_gateway_resource" "path_depth_0" {
2773
+ for_each = local.path_nodes_by_depth[0]
2774
+
2775
+ rest_api_id = module.rest_api.api_id
2776
+ parent_id = module.rest_api.api_root_resource_id
2777
+ path_part = each.value.part
2778
+ }
2779
+
2780
+ # API Gateway resources at path depth 1
2781
+ resource "aws_api_gateway_resource" "path_depth_1" {
2782
+ for_each = local.path_nodes_by_depth[1]
2783
+
2784
+ rest_api_id = module.rest_api.api_id
2785
+ parent_id = aws_api_gateway_resource.path_depth_0[each.value.parent].id
2786
+ path_part = each.value.part
2787
+ }
2788
+
2789
+ # API Gateway resources at path depth 2
2790
+ resource "aws_api_gateway_resource" "path_depth_2" {
2791
+ for_each = local.path_nodes_by_depth[2]
2792
+
2793
+ rest_api_id = module.rest_api.api_id
2794
+ parent_id = aws_api_gateway_resource.path_depth_1[each.value.parent].id
2795
+ path_part = each.value.part
2796
+ }
2797
+
2798
+ # API Gateway resources at path depth 3
2799
+ resource "aws_api_gateway_resource" "path_depth_3" {
2800
+ for_each = local.path_nodes_by_depth[3]
2801
+
2802
+ rest_api_id = module.rest_api.api_id
2803
+ parent_id = aws_api_gateway_resource.path_depth_2[each.value.parent].id
2804
+ path_part = each.value.part
2805
+ }
2806
+
2807
+ # API Gateway resources at path depth 4
2808
+ resource "aws_api_gateway_resource" "path_depth_4" {
2809
+ for_each = local.path_nodes_by_depth[4]
2810
+
2811
+ rest_api_id = module.rest_api.api_id
2812
+ parent_id = aws_api_gateway_resource.path_depth_3[each.value.parent].id
2813
+ path_part = each.value.part
2814
+ }
2815
+
2816
+ # API Gateway resources at path depth 5
2817
+ resource "aws_api_gateway_resource" "path_depth_5" {
2818
+ for_each = local.path_nodes_by_depth[5]
2819
+
2820
+ rest_api_id = module.rest_api.api_id
2821
+ parent_id = aws_api_gateway_resource.path_depth_4[each.value.parent].id
2822
+ path_part = each.value.part
2823
+ }
2824
+
2825
+ # API Gateway resources at path depth 6
2826
+ resource "aws_api_gateway_resource" "path_depth_6" {
2827
+ for_each = local.path_nodes_by_depth[6]
2828
+
2829
+ rest_api_id = module.rest_api.api_id
2830
+ parent_id = aws_api_gateway_resource.path_depth_5[each.value.parent].id
2831
+ path_part = each.value.part
2832
+ }
2833
+
2834
+ # API Gateway resources at path depth 7
2835
+ resource "aws_api_gateway_resource" "path_depth_7" {
2836
+ for_each = local.path_nodes_by_depth[7]
2837
+
2838
+ rest_api_id = module.rest_api.api_id
2839
+ parent_id = aws_api_gateway_resource.path_depth_6[each.value.parent].id
2840
+ path_part = each.value.part
2841
+ }
2842
+
2843
+ # API Gateway resources at path depth 8
2844
+ resource "aws_api_gateway_resource" "path_depth_8" {
2845
+ for_each = local.path_nodes_by_depth[8]
2846
+
2847
+ rest_api_id = module.rest_api.api_id
2848
+ parent_id = aws_api_gateway_resource.path_depth_7[each.value.parent].id
2849
+ path_part = each.value.part
2850
+ }
2851
+
2852
+ # API Gateway resources at path depth 9
2853
+ resource "aws_api_gateway_resource" "path_depth_9" {
2854
+ for_each = local.path_nodes_by_depth[9]
2855
+
2856
+ rest_api_id = module.rest_api.api_id
2857
+ parent_id = aws_api_gateway_resource.path_depth_8[each.value.parent].id
2858
+ path_part = each.value.part
2859
+ }
2860
+
2861
+ # API Gateway resources at path depth 10
2862
+ resource "aws_api_gateway_resource" "path_depth_10" {
2863
+ for_each = local.path_nodes_by_depth[10]
2864
+
2865
+ rest_api_id = module.rest_api.api_id
2866
+ parent_id = aws_api_gateway_resource.path_depth_9[each.value.parent].id
2867
+ path_part = each.value.part
2868
+ }
2869
+
2870
+ # API Gateway resources at path depth 11
2871
+ resource "aws_api_gateway_resource" "path_depth_11" {
2872
+ for_each = local.path_nodes_by_depth[11]
2873
+
2874
+ rest_api_id = module.rest_api.api_id
2875
+ parent_id = aws_api_gateway_resource.path_depth_10[each.value.parent].id
2876
+ path_part = each.value.part
2877
+ }
2878
+
2879
+ # API Gateway resources at path depth 12
2880
+ resource "aws_api_gateway_resource" "path_depth_12" {
2881
+ for_each = local.path_nodes_by_depth[12]
2882
+
2883
+ rest_api_id = module.rest_api.api_id
2884
+ parent_id = aws_api_gateway_resource.path_depth_11[each.value.parent].id
2885
+ path_part = each.value.part
2886
+ }
2887
+
2888
+ # API Gateway resources at path depth 13
2889
+ resource "aws_api_gateway_resource" "path_depth_13" {
2890
+ for_each = local.path_nodes_by_depth[13]
2891
+
2892
+ rest_api_id = module.rest_api.api_id
2893
+ parent_id = aws_api_gateway_resource.path_depth_12[each.value.parent].id
2894
+ path_part = each.value.part
2895
+ }
2896
+
2897
+ # API Gateway resources at path depth 14
2898
+ resource "aws_api_gateway_resource" "path_depth_14" {
2899
+ for_each = local.path_nodes_by_depth[14]
2900
+
2901
+ rest_api_id = module.rest_api.api_id
2902
+ parent_id = aws_api_gateway_resource.path_depth_13[each.value.parent].id
2903
+ path_part = each.value.part
2904
+ }
2905
+
2906
+ # API Gateway resources at path depth 15
2907
+ resource "aws_api_gateway_resource" "path_depth_15" {
2908
+ for_each = local.path_nodes_by_depth[15]
2909
+
2910
+ rest_api_id = module.rest_api.api_id
2911
+ parent_id = aws_api_gateway_resource.path_depth_14[each.value.parent].id
2912
+ path_part = each.value.part
2913
+ }
2914
+
2028
2915
  # Resources
2029
2916
 
2030
2917
  # Create Lambda ZIP file from the bundle directory
@@ -2043,6 +2930,11 @@ resource "aws_s3_object" "lambda_zip" {
2043
2930
  }
2044
2931
 
2045
2932
  # Use the core REST API module
2933
+ # Account-level CloudWatch role required for REST API access logging
2934
+ module "account" {
2935
+ source = "../../../core/api/api-gateway-account"
2936
+ }
2937
+
2046
2938
  module "rest_api" {
2047
2939
  source = "../../../core/api/rest-api"
2048
2940
 
@@ -2072,9 +2964,33 @@ resource "aws_wafv2_web_acl_association" "api_waf_association" {
2072
2964
  depends_on = [aws_api_gateway_stage.api_stage]
2073
2965
  }
2074
2966
 
2075
- # Lambda function
2967
+ # Security group for the API Lambda function, used when deployed into a VPC
2968
+ resource "aws_security_group" "api_lambda" {
2969
+ count = var.enable_vpc ? 1 : 0
2970
+
2971
+ #checkov:skip=CKV2_AWS_5:Attached to api_lambda via vpc_config block; Checkov cannot resolve this reference
2972
+ name_prefix = "test-api-lambda-"
2973
+ description = "Security group for the TestApi API Lambda function"
2974
+ vpc_id = var.vpc_id
2975
+ tags = var.tags
2976
+ }
2977
+
2978
+ resource "aws_vpc_security_group_egress_rule" "api_lambda_https" {
2979
+ count = var.enable_vpc ? 1 : 0
2980
+
2981
+ security_group_id = aws_security_group.api_lambda[0].id
2982
+ cidr_ipv4 = "0.0.0.0/0"
2983
+ from_port = 443
2984
+ to_port = 443
2985
+ ip_protocol = "tcp"
2986
+ description = "Allow outbound HTTPS to AWS service endpoints"
2987
+ }
2988
+
2076
2989
  resource "aws_lambda_function" "api_lambda" {
2077
- #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
2990
+ # One lambda function per operation, each serving just that operation
2991
+ for_each = local.operations
2992
+
2993
+ #checkov:skip=CKV_AWS_117:Lambda function is optionally deployed into a VPC via vpc_id/subnet_ids; not required for this use case
2078
2994
  #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
2079
2995
  #checkov:skip=CKV_AWS_272:Code signing not required for this use case
2080
2996
  #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
@@ -2082,10 +2998,10 @@ resource "aws_lambda_function" "api_lambda" {
2082
2998
  s3_bucket = aws_s3_object.lambda_zip.bucket
2083
2999
  s3_key = aws_s3_object.lambda_zip.key
2084
3000
  s3_object_version = aws_s3_object.lambda_zip.version_id
2085
- function_name = "TestApiHandler-\${random_string.suffix.result}"
2086
- role = aws_iam_role.lambda_execution_role.arn
3001
+ function_name = local.function_name[each.key]
3002
+ role = aws_iam_role.lambda_execution_role[each.key].arn
2087
3003
  handler = "index.handler"
2088
- runtime = "nodejs22.x"
3004
+ runtime = "nodejs24.x"
2089
3005
  timeout = 30
2090
3006
  memory_size = 128
2091
3007
 
@@ -2096,18 +3012,32 @@ resource "aws_lambda_function" "api_lambda" {
2096
3012
  mode = "Active"
2097
3013
  }
2098
3014
 
3015
+ dynamic "vpc_config" {
3016
+ for_each = var.enable_vpc ? [1] : []
3017
+ content {
3018
+ subnet_ids = var.subnet_ids
3019
+ security_group_ids = [aws_security_group.api_lambda[0].id]
3020
+ }
3021
+ }
3022
+
2099
3023
 
2100
3024
  environment {
2101
3025
  variables = merge({
2102
- }, var.env)
3026
+ }, var.appconfig_application_id != null ? {
3027
+ RUNTIME_CONFIG_APP_ID = var.appconfig_application_id
3028
+ } : {}, var.env)
2103
3029
  }
2104
3030
 
2105
3031
  tags = var.tags
3032
+
3033
+ depends_on = [aws_iam_role_policy_attachment.lambda_vpc_access]
2106
3034
  }
2107
3035
 
2108
3036
  # IAM role for Lambda execution
2109
3037
  resource "aws_iam_role" "lambda_execution_role" {
2110
- name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
3038
+ for_each = local.operations
3039
+
3040
+ name = local.role_name[each.key]
2111
3041
 
2112
3042
  assume_role_policy = jsonencode({
2113
3043
  Version = "2012-10-17"
@@ -2127,35 +3057,73 @@ resource "aws_iam_role" "lambda_execution_role" {
2127
3057
 
2128
3058
  # Attach basic execution policy to Lambda role
2129
3059
  resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
3060
+ for_each = local.operations
3061
+
2130
3062
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
2131
- role = aws_iam_role.lambda_execution_role.name
3063
+ role = aws_iam_role.lambda_execution_role[each.key].name
2132
3064
  }
2133
3065
 
2134
3066
  # Attach X-Ray tracing policy to Lambda role
2135
3067
  resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
3068
+ for_each = local.operations
3069
+
2136
3070
  policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
2137
- role = aws_iam_role.lambda_execution_role.name
3071
+ role = aws_iam_role.lambda_execution_role[each.key].name
3072
+ }
3073
+
3074
+ # Attach VPC access policy to Lambda role when deployed into a VPC
3075
+ resource "aws_iam_role_policy_attachment" "lambda_vpc_access" {
3076
+ for_each = var.enable_vpc ? local.operations : {}
3077
+
3078
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
3079
+ role = aws_iam_role.lambda_execution_role[each.key].name
2138
3080
  }
2139
3081
 
2140
3082
  # Additional IAM policies for Lambda (if provided)
2141
3083
  resource "aws_iam_role_policy" "lambda_additional_policies" {
2142
- count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
2143
- name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
2144
- role = aws_iam_role.lambda_execution_role.id
3084
+ for_each = local.operations
3085
+
3086
+ name = "\${substr(local.function_name[each.key], 0, 55)}-policies"
3087
+ role = aws_iam_role.lambda_execution_role[each.key].id
2145
3088
 
2146
3089
  policy = jsonencode({
2147
3090
  Version = "2012-10-17"
2148
- Statement = var.additional_iam_policy_statements
3091
+ Statement = local.lambda_policy_document_statements
2149
3092
  })
2150
3093
  }
2151
3094
 
3095
+ locals {
3096
+ # Grant read access to the runtime-config AppConfig application when wired
3097
+ lambda_policy_statements = concat(var.appconfig_application_arn != null ? [
3098
+ {
3099
+ Effect = "Allow"
3100
+ Action = [
3101
+ "appconfig:StartConfigurationSession",
3102
+ "appconfig:GetLatestConfiguration"
3103
+ ]
3104
+ Resource = ["\${var.appconfig_application_arn}/*"]
3105
+ }
3106
+ ] : [], var.additional_iam_policy_statements)
3107
+
3108
+ # IAM rejects an empty statement list, so fall back to a no-op deny
3109
+ lambda_policy_document_statements = length(local.lambda_policy_statements) > 0 ? local.lambda_policy_statements : [
3110
+ {
3111
+ Effect = "Deny"
3112
+ Action = ["appconfig:GetLatestConfiguration"]
3113
+ Resource = ["arn:aws:appconfig:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:application/none"]
3114
+ }
3115
+ ]
3116
+ }
3117
+
2152
3118
  # CloudWatch Log Group for Lambda
2153
3119
  resource "aws_cloudwatch_log_group" "lambda_logs" {
2154
3120
  #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
2155
3121
  #checkov:skip=CKV_AWS_338:Log retention set to forever
2156
3122
  #checkov:skip=CKV_AWS_66:Log retention set to forever
2157
- name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
2158
- tags = var.tags
3123
+ for_each = local.operations
3124
+
3125
+ name = "/aws/lambda/\${local.function_name[each.key]}"
3126
+ tags = var.tags
2159
3127
  }
2160
3128
 
2161
3129
 
@@ -2168,58 +3136,65 @@ resource "aws_api_gateway_authorizer" "cognito_authorizer" {
2168
3136
  identity_source = "method.request.header.Authorization"
2169
3137
  }
2170
3138
 
2171
- # Create proxy resource (captures all paths)
2172
- resource "aws_api_gateway_resource" "proxy_resource" {
2173
- rest_api_id = module.rest_api.api_id
2174
- parent_id = module.rest_api.api_root_resource_id
2175
- path_part = "{proxy+}"
2176
- }
2177
-
2178
- # Lambda integration for REST API
2179
- resource "aws_api_gateway_integration" "lambda_integration" {
2180
- rest_api_id = module.rest_api.api_id
2181
- resource_id = aws_api_gateway_resource.proxy_resource.id
2182
- http_method = aws_api_gateway_method.proxy_method.http_method
2183
-
2184
- integration_http_method = "POST"
2185
- type = "AWS_PROXY"
2186
- uri = aws_lambda_function.api_lambda.invoke_arn
2187
-
2188
- depends_on = [aws_lambda_function.api_lambda]
2189
- }
2190
-
2191
- # Method for proxy integration
2192
- resource "aws_api_gateway_method" "proxy_method" {
3139
+ # Method per operation
3140
+ resource "aws_api_gateway_method" "operation_methods" {
2193
3141
  #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
3142
+ for_each = local.operations
3143
+
2194
3144
  rest_api_id = module.rest_api.api_id
2195
- resource_id = aws_api_gateway_resource.proxy_resource.id
2196
- http_method = "ANY"
3145
+ resource_id = local.operation_resource_id[each.key]
3146
+ http_method = upper(each.value.method)
2197
3147
 
2198
3148
  authorization = "COGNITO_USER_POOLS"
2199
3149
  authorizer_id = aws_api_gateway_authorizer.cognito_authorizer.id
3150
+ # Accept access tokens from both sign-in flows: 'openid' (Cognito hosted UI)
3151
+ # and 'aws.cognito.signin.user.admin' (Cognito admin/SRP auth APIs).
3152
+ authorization_scopes = ["openid", "aws.cognito.signin.user.admin"]
2200
3153
 
3154
+ # Path parameters must be declared on the method
2201
3155
  request_parameters = {
2202
- "method.request.path.proxy" = true
3156
+ for segment in local.operation_segments[each.key] :
3157
+ "method.request.path.\${trimsuffix(trimprefix(segment, "{"), "}")}" => true
3158
+ if startswith(segment, "{")
2203
3159
  }
2204
3160
 
2205
3161
  depends_on = [aws_api_gateway_authorizer.cognito_authorizer]
2206
3162
  }
2207
3163
 
2208
- # OPTIONS method for CORS preflight
3164
+ # Lambda integration per operation
3165
+ resource "aws_api_gateway_integration" "lambda_integration" {
3166
+ for_each = local.operations
3167
+
3168
+ rest_api_id = module.rest_api.api_id
3169
+ resource_id = local.operation_resource_id[each.key]
3170
+ http_method = aws_api_gateway_method.operation_methods[each.key].http_method
3171
+
3172
+ integration_http_method = "POST"
3173
+ type = "AWS_PROXY"
3174
+ uri = aws_lambda_function.api_lambda[each.key].invoke_arn
3175
+
3176
+ depends_on = [aws_lambda_function.api_lambda]
3177
+ }
3178
+
3179
+ # OPTIONS method for CORS preflight, on every resource an operation is served from
2209
3180
  resource "aws_api_gateway_method" "options_method" {
2210
3181
  #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
2211
3182
  #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
3183
+ for_each = local.resource_ids
3184
+
2212
3185
  rest_api_id = module.rest_api.api_id
2213
- resource_id = aws_api_gateway_resource.proxy_resource.id
3186
+ resource_id = each.value
2214
3187
  http_method = "OPTIONS"
2215
3188
  authorization = "NONE"
2216
3189
  }
2217
3190
 
2218
- # CORS integration for OPTIONS method
3191
+ # CORS integration for OPTIONS methods
2219
3192
  resource "aws_api_gateway_integration" "options_integration" {
3193
+ for_each = local.resource_ids
3194
+
2220
3195
  rest_api_id = module.rest_api.api_id
2221
- resource_id = aws_api_gateway_resource.proxy_resource.id
2222
- http_method = aws_api_gateway_method.options_method.http_method
3196
+ resource_id = each.value
3197
+ http_method = aws_api_gateway_method.options_method[each.key].http_method
2223
3198
 
2224
3199
  type = "MOCK"
2225
3200
  request_templates = {
@@ -2227,11 +3202,13 @@ resource "aws_api_gateway_integration" "options_integration" {
2227
3202
  }
2228
3203
  }
2229
3204
 
2230
- # OPTIONS method response
3205
+ # OPTIONS method responses
2231
3206
  resource "aws_api_gateway_method_response" "options_response" {
3207
+ for_each = local.resource_ids
3208
+
2232
3209
  rest_api_id = module.rest_api.api_id
2233
- resource_id = aws_api_gateway_resource.proxy_resource.id
2234
- http_method = aws_api_gateway_method.options_method.http_method
3210
+ resource_id = each.value
3211
+ http_method = aws_api_gateway_method.options_method[each.key].http_method
2235
3212
  status_code = "204"
2236
3213
 
2237
3214
  response_parameters = {
@@ -2241,18 +3218,22 @@ resource "aws_api_gateway_method_response" "options_response" {
2241
3218
  }
2242
3219
  }
2243
3220
 
2244
- # OPTIONS integration response
3221
+ # OPTIONS integration responses
2245
3222
  resource "aws_api_gateway_integration_response" "options_integration_response" {
3223
+ for_each = local.resource_ids
3224
+
2246
3225
  rest_api_id = module.rest_api.api_id
2247
- resource_id = aws_api_gateway_resource.proxy_resource.id
2248
- http_method = aws_api_gateway_method.options_method.http_method
2249
- status_code = aws_api_gateway_method_response.options_response.status_code
3226
+ resource_id = each.value
3227
+ http_method = aws_api_gateway_method.options_method[each.key].http_method
3228
+ status_code = aws_api_gateway_method_response.options_response[each.key].status_code
2250
3229
 
2251
3230
  response_parameters = {
2252
3231
  "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
2253
3232
  "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
2254
3233
  "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
2255
3234
  }
3235
+
3236
+ depends_on = [aws_api_gateway_integration.options_integration]
2256
3237
  }
2257
3238
 
2258
3239
  # API Gateway deployment
@@ -2260,13 +3241,28 @@ resource "aws_api_gateway_deployment" "api_deployment" {
2260
3241
  rest_api_id = module.rest_api.api_id
2261
3242
 
2262
3243
  triggers = {
2263
- redeployment = sha1(jsonencode([
2264
- aws_api_gateway_resource.proxy_resource.id,
2265
- aws_api_gateway_method.proxy_method.id,
2266
- aws_api_gateway_integration.lambda_integration.id,
2267
- aws_api_gateway_method.options_method.id,
2268
- aws_api_gateway_integration.options_integration.id,
2269
- ]))
3244
+ redeployment = sha1(jsonencode(concat(
3245
+ [for r in aws_api_gateway_resource.path_depth_0 : r.id],
3246
+ [for r in aws_api_gateway_resource.path_depth_1 : r.id],
3247
+ [for r in aws_api_gateway_resource.path_depth_2 : r.id],
3248
+ [for r in aws_api_gateway_resource.path_depth_3 : r.id],
3249
+ [for r in aws_api_gateway_resource.path_depth_4 : r.id],
3250
+ [for r in aws_api_gateway_resource.path_depth_5 : r.id],
3251
+ [for r in aws_api_gateway_resource.path_depth_6 : r.id],
3252
+ [for r in aws_api_gateway_resource.path_depth_7 : r.id],
3253
+ [for r in aws_api_gateway_resource.path_depth_8 : r.id],
3254
+ [for r in aws_api_gateway_resource.path_depth_9 : r.id],
3255
+ [for r in aws_api_gateway_resource.path_depth_10 : r.id],
3256
+ [for r in aws_api_gateway_resource.path_depth_11 : r.id],
3257
+ [for r in aws_api_gateway_resource.path_depth_12 : r.id],
3258
+ [for r in aws_api_gateway_resource.path_depth_13 : r.id],
3259
+ [for r in aws_api_gateway_resource.path_depth_14 : r.id],
3260
+ [for r in aws_api_gateway_resource.path_depth_15 : r.id],
3261
+ [for m in aws_api_gateway_method.operation_methods : m.id],
3262
+ [for i in aws_api_gateway_integration.lambda_integration : i.id],
3263
+ [for m in aws_api_gateway_method.options_method : m.id],
3264
+ [for i in aws_api_gateway_integration.options_integration : i.id],
3265
+ )))
2270
3266
  }
2271
3267
 
2272
3268
  lifecycle {
@@ -2274,7 +3270,7 @@ resource "aws_api_gateway_deployment" "api_deployment" {
2274
3270
  }
2275
3271
 
2276
3272
  depends_on = [
2277
- aws_api_gateway_method.proxy_method,
3273
+ aws_api_gateway_method.operation_methods,
2278
3274
  aws_api_gateway_integration.lambda_integration,
2279
3275
  aws_api_gateway_method.options_method,
2280
3276
  aws_api_gateway_integration.options_integration,
@@ -2283,11 +3279,63 @@ resource "aws_api_gateway_deployment" "api_deployment" {
2283
3279
  ]
2284
3280
  }
2285
3281
 
3282
+ # KMS key for encrypting access logs at rest
3283
+ resource "aws_kms_key" "access_logs" {
3284
+ description = "TestApi API access log encryption"
3285
+ deletion_window_in_days = 7
3286
+ enable_key_rotation = true
3287
+
3288
+ policy = jsonencode({
3289
+ Version = "2012-10-17"
3290
+ Statement = [
3291
+ {
3292
+ Sid = "Enable IAM User Permissions"
3293
+ Effect = "Allow"
3294
+ Principal = { AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root" }
3295
+ Action = "kms:*"
3296
+ Resource = "*"
3297
+ },
3298
+ {
3299
+ Sid = "Allow CloudWatch Logs"
3300
+ Effect = "Allow"
3301
+ Principal = { Service = "logs.\${data.aws_region.current.region}.amazonaws.com" }
3302
+ Action = [
3303
+ "kms:Encrypt",
3304
+ "kms:Decrypt",
3305
+ "kms:ReEncrypt*",
3306
+ "kms:GenerateDataKey*",
3307
+ "kms:DescribeKey"
3308
+ ]
3309
+ Resource = "*"
3310
+ Condition = {
3311
+ ArnEquals = {
3312
+ "kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:log-group:/aws/apigateway/test-api-\${random_string.suffix.result}/access"
3313
+ }
3314
+ }
3315
+ }
3316
+ ]
3317
+ })
3318
+
3319
+ tags = var.tags
3320
+ }
3321
+
3322
+ resource "aws_kms_alias" "access_logs" {
3323
+ name = "alias/test-api-\${random_string.suffix.result}-access-logs"
3324
+ target_key_id = aws_kms_key.access_logs.key_id
3325
+ }
3326
+
3327
+ # Access logs for the API Gateway stage
3328
+ resource "aws_cloudwatch_log_group" "access_logs" {
3329
+ name = "/aws/apigateway/test-api-\${random_string.suffix.result}/access"
3330
+ retention_in_days = 365
3331
+ kms_key_id = aws_kms_key.access_logs.arn
3332
+ tags = var.tags
3333
+ }
3334
+
2286
3335
  # API Gateway stage
2287
3336
  resource "aws_api_gateway_stage" "api_stage" {
2288
3337
  #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
2289
- #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
2290
- #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
3338
+ #checkov:skip=CKV2_AWS_4:Access logging is enabled below; verbose execution logging is intentionally not enabled
2291
3339
  #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
2292
3340
  #checkov:skip=CKV2_AWS_77:WAFv2 Web ACL is attached via aws_wafv2_web_acl_association when var.enable_waf is true (default) and includes AWSManagedRulesKnownBadInputsRuleSet for Log4j protection; Checkov does not track the association across modules
2293
3341
  deployment_id = aws_api_gateway_deployment.api_deployment.id
@@ -2295,9 +3343,39 @@ resource "aws_api_gateway_stage" "api_stage" {
2295
3343
  stage_name = "prod"
2296
3344
  xray_tracing_enabled = true
2297
3345
 
3346
+ access_log_settings {
3347
+ destination_arn = aws_cloudwatch_log_group.access_logs.arn
3348
+ format = jsonencode({
3349
+ requestId = "$context.requestId"
3350
+ ip = "$context.identity.sourceIp"
3351
+ caller = "$context.identity.caller"
3352
+ user = "$context.identity.user"
3353
+ requestTime = "$context.requestTime"
3354
+ httpMethod = "$context.httpMethod"
3355
+ resourcePath = "$context.resourcePath"
3356
+ status = "$context.status"
3357
+ protocol = "$context.protocol"
3358
+ responseLength = "$context.responseLength"
3359
+ })
3360
+ }
3361
+
2298
3362
  tags = var.tags
2299
3363
 
2300
- depends_on = [aws_api_gateway_deployment.api_deployment]
3364
+ # Ensure the account-level role is configured before the stage is created
3365
+ depends_on = [aws_api_gateway_deployment.api_deployment, module.account]
3366
+ }
3367
+
3368
+ # Default throttling applied to all resource paths and methods
3369
+ resource "aws_api_gateway_method_settings" "throttling" {
3370
+ #checkov:skip=CKV_AWS_225:API Gateway caching not required for this use case
3371
+ rest_api_id = module.rest_api.api_id
3372
+ stage_name = aws_api_gateway_stage.api_stage.stage_name
3373
+ method_path = "*/*"
3374
+
3375
+ settings {
3376
+ throttling_rate_limit = 10000
3377
+ throttling_burst_limit = 5000
3378
+ }
2301
3379
  }
2302
3380
 
2303
3381
  # API Gateway Resource Policy
@@ -2320,11 +3398,13 @@ resource "aws_api_gateway_rest_api_policy" "api_policy" {
2320
3398
 
2321
3399
  # Lambda permission for API Gateway to invoke the function
2322
3400
  resource "aws_lambda_permission" "api_gateway_invoke" {
3401
+ for_each = local.operations
3402
+
2323
3403
  statement_id = "AllowExecutionFromAPIGateway"
2324
3404
  action = "lambda:InvokeFunction"
2325
- function_name = aws_lambda_function.api_lambda.function_name
3405
+ function_name = aws_lambda_function.api_lambda[each.key].function_name
2326
3406
  principal = "apigateway.amazonaws.com"
2327
- source_arn = "\${module.rest_api.api_execution_arn}/*/*"
3407
+ source_arn = "\${module.rest_api.api_execution_arn}/*/\${local.permission_source_suffix[each.key]}"
2328
3408
 
2329
3409
  depends_on = [module.rest_api, aws_lambda_function.api_lambda]
2330
3410
  }
@@ -2336,7 +3416,7 @@ module "add_url_to_runtime_config" {
2336
3416
 
2337
3417
  namespace = "connection"
2338
3418
  key = "apis"
2339
- value = { "TestApi" = aws_api_gateway_stage.api_stage.invoke_url }
3419
+ value = { "TestApi" = "\${aws_api_gateway_stage.api_stage.invoke_url}/" }
2340
3420
 
2341
3421
  depends_on = [aws_api_gateway_stage.api_stage]
2342
3422
  }
@@ -2389,78 +3469,83 @@ output "stage_id" {
2389
3469
  value = aws_api_gateway_stage.api_stage.id
2390
3470
  }
2391
3471
 
2392
- # Lambda Function Outputs
2393
- output "lambda_function_name" {
2394
- description = "Name of the Lambda function"
2395
- value = aws_lambda_function.api_lambda.function_name
3472
+ # Lambda Function Outputs, keyed by operation name
3473
+ output "operations" {
3474
+ description = "Names of the API operations, each with its own Lambda function"
3475
+ value = keys(local.operations)
2396
3476
  }
2397
3477
 
2398
- output "lambda_function_arn" {
2399
- description = "ARN of the Lambda function"
2400
- value = aws_lambda_function.api_lambda.arn
3478
+ output "lambda_function_names" {
3479
+ description = "Name of each operation's Lambda function, keyed by operation name"
3480
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.function_name }
2401
3481
  }
2402
3482
 
2403
- output "lambda_invoke_arn" {
2404
- description = "Invoke ARN of the Lambda function"
2405
- value = aws_lambda_function.api_lambda.invoke_arn
3483
+ output "lambda_function_arns" {
3484
+ description = "ARN of each operation's Lambda function, keyed by operation name"
3485
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.arn }
2406
3486
  }
2407
3487
 
2408
- output "lambda_qualified_arn" {
2409
- description = "Qualified ARN of the Lambda function"
2410
- value = aws_lambda_function.api_lambda.qualified_arn
3488
+ output "lambda_invoke_arns" {
3489
+ description = "Invoke ARN of each operation's Lambda function, keyed by operation name"
3490
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.invoke_arn }
2411
3491
  }
2412
3492
 
2413
- output "lambda_version" {
2414
- description = "Version of the Lambda function"
2415
- value = aws_lambda_function.api_lambda.version
3493
+ output "lambda_qualified_arns" {
3494
+ description = "Qualified ARN of each operation's Lambda function, keyed by operation name"
3495
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.qualified_arn }
2416
3496
  }
2417
3497
 
2418
- output "lambda_source_code_hash" {
2419
- description = "Base64-encoded SHA256 hash of the Lambda deployment package"
2420
- value = aws_lambda_function.api_lambda.source_code_hash
3498
+ output "lambda_versions" {
3499
+ description = "Version of each operation's Lambda function, keyed by operation name"
3500
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.version }
2421
3501
  }
2422
3502
 
2423
- output "lambda_source_code_size" {
2424
- description = "Size of the Lambda deployment package in bytes"
2425
- value = aws_lambda_function.api_lambda.source_code_size
3503
+ output "lambda_source_code_hash" {
3504
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package, shared by every operation"
3505
+ value = data.archive_file.lambda_zip.output_base64sha256
3506
+ }
3507
+
3508
+ # IAM Role Outputs, keyed by operation name
3509
+ output "lambda_execution_role_arns" {
3510
+ description = "ARN of each operation's Lambda execution role, keyed by operation name"
3511
+ value = { for op, role in aws_iam_role.lambda_execution_role : op => role.arn }
2426
3512
  }
2427
3513
 
2428
- # IAM Role Outputs
2429
- output "lambda_execution_role_arn" {
2430
- description = "ARN of the Lambda execution role"
2431
- value = aws_iam_role.lambda_execution_role.arn
3514
+ output "lambda_execution_role_names" {
3515
+ description = "Name of each operation's Lambda execution role, keyed by operation name. Attach additional policies to a specific operation's role by name."
3516
+ value = { for op, role in aws_iam_role.lambda_execution_role : op => role.name }
2432
3517
  }
2433
3518
 
2434
- output "lambda_execution_role_name" {
2435
- description = "Name of the Lambda execution role"
2436
- value = aws_iam_role.lambda_execution_role.name
3519
+ output "security_group_id" {
3520
+ description = "Security group ID shared by the API Lambda functions, for use in ingress rules on resources they must reach (e.g. a database). Null unless enable_vpc is true."
3521
+ value = var.enable_vpc ? aws_security_group.api_lambda[0].id : null
2437
3522
  }
2438
3523
 
2439
- # Integration Outputs
2440
- output "integration_id" {
2441
- description = "ID of the Lambda integration"
2442
- value = aws_api_gateway_integration.lambda_integration.id
3524
+ # Integration Outputs, keyed by operation name
3525
+ output "integration_ids" {
3526
+ description = "ID of each operation's Lambda integration, keyed by operation name"
3527
+ value = { for op, i in aws_api_gateway_integration.lambda_integration : op => i.id }
2443
3528
  }
2444
3529
 
2445
- output "proxy_resource_id" {
2446
- description = "ID of the proxy resource"
2447
- value = aws_api_gateway_resource.proxy_resource.id
3530
+ output "method_ids" {
3531
+ description = "ID of each operation's API Gateway method, keyed by operation name"
3532
+ value = { for op, m in aws_api_gateway_method.operation_methods : op => m.id }
2448
3533
  }
2449
3534
 
2450
- output "proxy_method_id" {
2451
- description = "ID of the proxy method"
2452
- value = aws_api_gateway_method.proxy_method.id
3535
+ output "resource_ids" {
3536
+ description = "ID of each API Gateway resource, keyed by its path"
3537
+ value = local.resource_ids
2453
3538
  }
2454
3539
 
2455
- # CloudWatch Log Groups
2456
- output "lambda_log_group_name" {
2457
- description = "Name of the Lambda CloudWatch log group"
2458
- value = aws_cloudwatch_log_group.lambda_logs.name
3540
+ # CloudWatch Log Groups, keyed by operation name
3541
+ output "lambda_log_group_names" {
3542
+ description = "Name of each operation's Lambda CloudWatch log group, keyed by operation name"
3543
+ value = { for op, lg in aws_cloudwatch_log_group.lambda_logs : op => lg.name }
2459
3544
  }
2460
3545
 
2461
- output "lambda_log_group_arn" {
2462
- description = "ARN of the Lambda CloudWatch log group"
2463
- value = aws_cloudwatch_log_group.lambda_logs.arn
3546
+ output "lambda_log_group_arns" {
3547
+ description = "ARN of each operation's Lambda CloudWatch log group, keyed by operation name"
3548
+ value = { for op, lg in aws_cloudwatch_log_group.lambda_logs : op => lg.arn }
2464
3549
  }
2465
3550
  ",
2466
3551
  }
@@ -2477,11 +3562,11 @@ terraform {
2477
3562
  required_providers {
2478
3563
  aws = {
2479
3564
  source = "hashicorp/aws"
2480
- version = "~> 6.33"
3565
+ version = "6.62.0"
2481
3566
  }
2482
3567
  random = {
2483
3568
  source = "hashicorp/random"
2484
- version = "~> 3.1"
3569
+ version = "3.9.0"
2485
3570
  }
2486
3571
  }
2487
3572
  }
@@ -2551,8 +3636,6 @@ data "aws_caller_identity" "current" {}
2551
3636
 
2552
3637
  # Resources
2553
3638
 
2554
- # Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
2555
-
2556
3639
  # REST API Gateway
2557
3640
  resource "aws_api_gateway_rest_api" "rest_api" {
2558
3641
  name = var.api_name
@@ -2569,10 +3652,8 @@ resource "aws_api_gateway_rest_api" "rest_api" {
2569
3652
  tags = var.tags
2570
3653
  }
2571
3654
 
2572
- # Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
2573
- # after all methods and integrations are defined
2574
-
2575
- # Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
3655
+ # Note: Deployment, stage and access logging are created in the consuming module
3656
+ # (e.g., foo-api.tf) after all methods and integrations are defined
2576
3657
 
2577
3658
  # Gateway Response for CORS (4XX errors)
2578
3659
  resource "aws_api_gateway_gateway_response" "cors_4xx" {
@@ -2674,22 +3755,75 @@ resource "aws_wafv2_web_acl" "api_waf" {
2674
3755
  }
2675
3756
  }
2676
3757
 
3758
+ resource "random_id" "waf_log_suffix" {
3759
+ count = var.enable_waf ? 1 : 0
3760
+ byte_length = 4
3761
+ }
3762
+
3763
+ locals {
3764
+ waf_log_group_name = var.enable_waf ? "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}" : null
3765
+ }
3766
+
3767
+ # KMS key for encrypting WAF request logs at rest
3768
+ resource "aws_kms_key" "waf_logs" {
3769
+ count = var.enable_waf ? 1 : 0
3770
+
3771
+ description = "\${var.api_name} API WAF log encryption"
3772
+ deletion_window_in_days = 7
3773
+ enable_key_rotation = true
3774
+
3775
+ policy = jsonencode({
3776
+ Version = "2012-10-17"
3777
+ Statement = [
3778
+ {
3779
+ Sid = "Enable IAM User Permissions"
3780
+ Effect = "Allow"
3781
+ Principal = { AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root" }
3782
+ Action = "kms:*"
3783
+ Resource = "*"
3784
+ },
3785
+ {
3786
+ Sid = "Allow CloudWatch Logs"
3787
+ Effect = "Allow"
3788
+ Principal = { Service = "logs.\${data.aws_region.current.region}.amazonaws.com" }
3789
+ Action = [
3790
+ "kms:Encrypt",
3791
+ "kms:Decrypt",
3792
+ "kms:ReEncrypt*",
3793
+ "kms:GenerateDataKey*",
3794
+ "kms:DescribeKey"
3795
+ ]
3796
+ Resource = "*"
3797
+ Condition = {
3798
+ ArnEquals = {
3799
+ "kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:log-group:\${local.waf_log_group_name}"
3800
+ }
3801
+ }
3802
+ }
3803
+ ]
3804
+ })
3805
+
3806
+ tags = var.tags
3807
+ }
3808
+
3809
+ resource "aws_kms_alias" "waf_logs" {
3810
+ count = var.enable_waf ? 1 : 0
3811
+
3812
+ name = "alias/\${var.api_name}-\${random_id.waf_log_suffix[0].hex}-waf-logs"
3813
+ target_key_id = aws_kms_key.waf_logs[0].key_id
3814
+ }
3815
+
2677
3816
  # CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
2678
3817
  resource "aws_cloudwatch_log_group" "api_waf_logs" {
2679
- #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
2680
3818
  #checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
2681
3819
  count = var.enable_waf ? 1 : 0
2682
3820
 
2683
- name = "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}"
3821
+ name = local.waf_log_group_name
2684
3822
  retention_in_days = 30
3823
+ kms_key_id = aws_kms_key.waf_logs[0].arn
2685
3824
  tags = var.tags
2686
3825
  }
2687
3826
 
2688
- resource "random_id" "waf_log_suffix" {
2689
- count = var.enable_waf ? 1 : 0
2690
- byte_length = 4
2691
- }
2692
-
2693
3827
  resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
2694
3828
  count = var.enable_waf ? 1 : 0
2695
3829
 
@@ -2730,16 +3864,22 @@ output "waf_web_acl_arn" {
2730
3864
  value = var.enable_waf ? aws_wafv2_web_acl.api_waf[0].arn : null
2731
3865
  }
2732
3866
 
2733
- # Note: Stage and deployment outputs are provided by the consuming module
2734
-
2735
- # Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
3867
+ # Note: Stage, deployment and access log outputs are provided by the consuming module",
2736
3868
  "test-api.tf": "terraform {
2737
3869
  required_version = ">= 1.0"
2738
3870
 
2739
3871
  required_providers {
3872
+ archive = {
3873
+ source = "hashicorp/archive"
3874
+ version = "2.8.0"
3875
+ }
2740
3876
  aws = {
2741
3877
  source = "hashicorp/aws"
2742
- version = "~> 6.33"
3878
+ version = "6.62.0"
3879
+ }
3880
+ random = {
3881
+ source = "hashicorp/random"
3882
+ version = "3.9.0"
2743
3883
  }
2744
3884
  }
2745
3885
  }
@@ -2761,11 +3901,47 @@ variable "additional_iam_policy_statements" {
2761
3901
  default = []
2762
3902
  }
2763
3903
 
3904
+ variable "appconfig_application_id" {
3905
+ description = "ID of the shared runtime-config AppConfig application (from \`core/runtime-config/appconfig.application_id\`). When set, the Lambda functions receive \`RUNTIME_CONFIG_APP_ID\` and read access to the application."
3906
+ type = string
3907
+ default = null
3908
+ }
3909
+
3910
+ variable "appconfig_application_arn" {
3911
+ description = "ARN of the shared runtime-config AppConfig application (from \`core/runtime-config/appconfig.application_arn\`). Used to scope the IAM read permission granted to the Lambda functions."
3912
+ type = string
3913
+ default = null
3914
+ }
3915
+
2764
3916
  variable "asset_bucket_name" {
2765
3917
  description = "Name of the shared asset S3 bucket used to stage the Lambda deployment zip. Instantiate the \`core/asset-bucket\` module once per deployment and pass its \`bucket_name\` output here."
2766
3918
  type = string
2767
3919
  }
2768
3920
 
3921
+ # VPC Configuration (optional)
3922
+ variable "enable_vpc" {
3923
+ description = "Deploy the Lambda function into a VPC. Set alongside vpc_id/subnet_ids."
3924
+ type = bool
3925
+ default = false
3926
+ }
3927
+
3928
+ variable "vpc_id" {
3929
+ description = "VPC ID to deploy the Lambda function into. Required when enable_vpc is true."
3930
+ type = string
3931
+ default = null
3932
+
3933
+ validation {
3934
+ condition = !var.enable_vpc || (var.vpc_id != null && var.subnet_ids != null)
3935
+ error_message = "vpc_id and subnet_ids must be set when enable_vpc is true."
3936
+ }
3937
+ }
3938
+
3939
+ variable "subnet_ids" {
3940
+ description = "Subnet IDs to deploy the Lambda function into. Required when enable_vpc is true."
3941
+ type = list(string)
3942
+ default = null
3943
+ }
3944
+
2769
3945
  # CORS Configuration (passed to core module)
2770
3946
 
2771
3947
  variable "cors_allow_headers" {
@@ -2810,6 +3986,235 @@ resource "random_string" "suffix" {
2810
3986
  upper = false
2811
3987
  }
2812
3988
 
3989
+ locals {
3990
+ # Generated at build time from the API definition
3991
+ operations_file = "\${path.module}/../../../generated/test-api/operations.json"
3992
+ operations = fileexists(local.operations_file) ? jsondecode(file(local.operations_file)) : {}
3993
+
3994
+ operation_slug = { for op in keys(local.operations) : op => replace(op, "/[^a-zA-Z0-9-_]/", "-") }
3995
+ operation_hash = { for op in keys(local.operations) : op => substr(sha256(op), 0, 8) }
3996
+
3997
+ function_name = { for op in keys(local.operations) : op =>
3998
+ "\${substr("TestApi-\${local.operation_slug[op]}", 0, 64 - length(local.operation_hash[op]) - length(random_string.suffix.result) - 2)}-\${local.operation_hash[op]}-\${random_string.suffix.result}"
3999
+ }
4000
+ role_name = { for op in keys(local.operations) : op =>
4001
+ "\${substr("TestApi-\${local.operation_slug[op]}-role", 0, 64 - length(local.operation_hash[op]) - length(random_string.suffix.result) - 2)}-\${local.operation_hash[op]}-\${random_string.suffix.result}"
4002
+ }
4003
+
4004
+ # A REST API needs a resource per path segment, so each operation's path is
4005
+ # split into the distinct path prefixes the resource tree is built from below
4006
+ operation_segments = { for op, details in local.operations : op => compact(split("/", details.path)) }
4007
+
4008
+ path_nodes = merge([
4009
+ for op, segments in local.operation_segments : {
4010
+ for i, segment in segments : join("/", slice(segments, 0, i + 1)) => {
4011
+ depth = i
4012
+ parent = i == 0 ? "" : join("/", slice(segments, 0, i))
4013
+ part = segment
4014
+ }
4015
+ }
4016
+ ]...)
4017
+
4018
+ # Instances of a resource cannot reference one another, so each depth is a
4019
+ # separate resource. Deeper paths are reported by the precondition below.
4020
+ max_path_depth = 16
4021
+ path_nodes_by_depth = { for depth in range(local.max_path_depth) : depth => {
4022
+ for path, node in local.path_nodes : path => node if node.depth == depth
4023
+ } }
4024
+ paths_too_deep = [for path, node in local.path_nodes : path if node.depth >= local.max_path_depth]
4025
+
4026
+ resource_ids = merge(
4027
+ { for path, resource in aws_api_gateway_resource.path_depth_0 : path => resource.id },
4028
+ { for path, resource in aws_api_gateway_resource.path_depth_1 : path => resource.id },
4029
+ { for path, resource in aws_api_gateway_resource.path_depth_2 : path => resource.id },
4030
+ { for path, resource in aws_api_gateway_resource.path_depth_3 : path => resource.id },
4031
+ { for path, resource in aws_api_gateway_resource.path_depth_4 : path => resource.id },
4032
+ { for path, resource in aws_api_gateway_resource.path_depth_5 : path => resource.id },
4033
+ { for path, resource in aws_api_gateway_resource.path_depth_6 : path => resource.id },
4034
+ { for path, resource in aws_api_gateway_resource.path_depth_7 : path => resource.id },
4035
+ { for path, resource in aws_api_gateway_resource.path_depth_8 : path => resource.id },
4036
+ { for path, resource in aws_api_gateway_resource.path_depth_9 : path => resource.id },
4037
+ { for path, resource in aws_api_gateway_resource.path_depth_10 : path => resource.id },
4038
+ { for path, resource in aws_api_gateway_resource.path_depth_11 : path => resource.id },
4039
+ { for path, resource in aws_api_gateway_resource.path_depth_12 : path => resource.id },
4040
+ { for path, resource in aws_api_gateway_resource.path_depth_13 : path => resource.id },
4041
+ { for path, resource in aws_api_gateway_resource.path_depth_14 : path => resource.id },
4042
+ { for path, resource in aws_api_gateway_resource.path_depth_15 : path => resource.id },
4043
+ )
4044
+
4045
+ operation_resource_id = { for op, segments in local.operation_segments : op =>
4046
+ local.resource_ids[join("/", segments)]
4047
+ }
4048
+
4049
+ permission_source_suffix = { for op, details in local.operations : op =>
4050
+ "\${upper(details.method)}/\${replace(join("/", local.operation_segments[op]), "/{[^}]*}/", "*")}"
4051
+ }
4052
+ }
4053
+
4054
+ resource "terraform_data" "operations_metadata" {
4055
+ # Fails the plan when the operations metadata has not been generated
4056
+ input = local.operations_file
4057
+
4058
+ lifecycle {
4059
+ precondition {
4060
+ condition = fileexists(local.operations_file)
4061
+ error_message = "Operations metadata not found at \${local.operations_file}. Build the TestApi project to generate it."
4062
+ }
4063
+ precondition {
4064
+ condition = length(local.operations) > 0
4065
+ error_message = "No operations found in \${local.operations_file}. The TestApi API must define at least one operation."
4066
+ }
4067
+ precondition {
4068
+ condition = length(local.paths_too_deep) == 0
4069
+ error_message = "Operation paths exceed the supported depth of \${local.max_path_depth} segments: \${join(", ", local.paths_too_deep)}. Add further aws_api_gateway_resource levels to this module to support them."
4070
+ }
4071
+ }
4072
+ }
4073
+
4074
+ # API Gateway resources at path depth 0
4075
+ resource "aws_api_gateway_resource" "path_depth_0" {
4076
+ for_each = local.path_nodes_by_depth[0]
4077
+
4078
+ rest_api_id = module.rest_api.api_id
4079
+ parent_id = module.rest_api.api_root_resource_id
4080
+ path_part = each.value.part
4081
+ }
4082
+
4083
+ # API Gateway resources at path depth 1
4084
+ resource "aws_api_gateway_resource" "path_depth_1" {
4085
+ for_each = local.path_nodes_by_depth[1]
4086
+
4087
+ rest_api_id = module.rest_api.api_id
4088
+ parent_id = aws_api_gateway_resource.path_depth_0[each.value.parent].id
4089
+ path_part = each.value.part
4090
+ }
4091
+
4092
+ # API Gateway resources at path depth 2
4093
+ resource "aws_api_gateway_resource" "path_depth_2" {
4094
+ for_each = local.path_nodes_by_depth[2]
4095
+
4096
+ rest_api_id = module.rest_api.api_id
4097
+ parent_id = aws_api_gateway_resource.path_depth_1[each.value.parent].id
4098
+ path_part = each.value.part
4099
+ }
4100
+
4101
+ # API Gateway resources at path depth 3
4102
+ resource "aws_api_gateway_resource" "path_depth_3" {
4103
+ for_each = local.path_nodes_by_depth[3]
4104
+
4105
+ rest_api_id = module.rest_api.api_id
4106
+ parent_id = aws_api_gateway_resource.path_depth_2[each.value.parent].id
4107
+ path_part = each.value.part
4108
+ }
4109
+
4110
+ # API Gateway resources at path depth 4
4111
+ resource "aws_api_gateway_resource" "path_depth_4" {
4112
+ for_each = local.path_nodes_by_depth[4]
4113
+
4114
+ rest_api_id = module.rest_api.api_id
4115
+ parent_id = aws_api_gateway_resource.path_depth_3[each.value.parent].id
4116
+ path_part = each.value.part
4117
+ }
4118
+
4119
+ # API Gateway resources at path depth 5
4120
+ resource "aws_api_gateway_resource" "path_depth_5" {
4121
+ for_each = local.path_nodes_by_depth[5]
4122
+
4123
+ rest_api_id = module.rest_api.api_id
4124
+ parent_id = aws_api_gateway_resource.path_depth_4[each.value.parent].id
4125
+ path_part = each.value.part
4126
+ }
4127
+
4128
+ # API Gateway resources at path depth 6
4129
+ resource "aws_api_gateway_resource" "path_depth_6" {
4130
+ for_each = local.path_nodes_by_depth[6]
4131
+
4132
+ rest_api_id = module.rest_api.api_id
4133
+ parent_id = aws_api_gateway_resource.path_depth_5[each.value.parent].id
4134
+ path_part = each.value.part
4135
+ }
4136
+
4137
+ # API Gateway resources at path depth 7
4138
+ resource "aws_api_gateway_resource" "path_depth_7" {
4139
+ for_each = local.path_nodes_by_depth[7]
4140
+
4141
+ rest_api_id = module.rest_api.api_id
4142
+ parent_id = aws_api_gateway_resource.path_depth_6[each.value.parent].id
4143
+ path_part = each.value.part
4144
+ }
4145
+
4146
+ # API Gateway resources at path depth 8
4147
+ resource "aws_api_gateway_resource" "path_depth_8" {
4148
+ for_each = local.path_nodes_by_depth[8]
4149
+
4150
+ rest_api_id = module.rest_api.api_id
4151
+ parent_id = aws_api_gateway_resource.path_depth_7[each.value.parent].id
4152
+ path_part = each.value.part
4153
+ }
4154
+
4155
+ # API Gateway resources at path depth 9
4156
+ resource "aws_api_gateway_resource" "path_depth_9" {
4157
+ for_each = local.path_nodes_by_depth[9]
4158
+
4159
+ rest_api_id = module.rest_api.api_id
4160
+ parent_id = aws_api_gateway_resource.path_depth_8[each.value.parent].id
4161
+ path_part = each.value.part
4162
+ }
4163
+
4164
+ # API Gateway resources at path depth 10
4165
+ resource "aws_api_gateway_resource" "path_depth_10" {
4166
+ for_each = local.path_nodes_by_depth[10]
4167
+
4168
+ rest_api_id = module.rest_api.api_id
4169
+ parent_id = aws_api_gateway_resource.path_depth_9[each.value.parent].id
4170
+ path_part = each.value.part
4171
+ }
4172
+
4173
+ # API Gateway resources at path depth 11
4174
+ resource "aws_api_gateway_resource" "path_depth_11" {
4175
+ for_each = local.path_nodes_by_depth[11]
4176
+
4177
+ rest_api_id = module.rest_api.api_id
4178
+ parent_id = aws_api_gateway_resource.path_depth_10[each.value.parent].id
4179
+ path_part = each.value.part
4180
+ }
4181
+
4182
+ # API Gateway resources at path depth 12
4183
+ resource "aws_api_gateway_resource" "path_depth_12" {
4184
+ for_each = local.path_nodes_by_depth[12]
4185
+
4186
+ rest_api_id = module.rest_api.api_id
4187
+ parent_id = aws_api_gateway_resource.path_depth_11[each.value.parent].id
4188
+ path_part = each.value.part
4189
+ }
4190
+
4191
+ # API Gateway resources at path depth 13
4192
+ resource "aws_api_gateway_resource" "path_depth_13" {
4193
+ for_each = local.path_nodes_by_depth[13]
4194
+
4195
+ rest_api_id = module.rest_api.api_id
4196
+ parent_id = aws_api_gateway_resource.path_depth_12[each.value.parent].id
4197
+ path_part = each.value.part
4198
+ }
4199
+
4200
+ # API Gateway resources at path depth 14
4201
+ resource "aws_api_gateway_resource" "path_depth_14" {
4202
+ for_each = local.path_nodes_by_depth[14]
4203
+
4204
+ rest_api_id = module.rest_api.api_id
4205
+ parent_id = aws_api_gateway_resource.path_depth_13[each.value.parent].id
4206
+ path_part = each.value.part
4207
+ }
4208
+
4209
+ # API Gateway resources at path depth 15
4210
+ resource "aws_api_gateway_resource" "path_depth_15" {
4211
+ for_each = local.path_nodes_by_depth[15]
4212
+
4213
+ rest_api_id = module.rest_api.api_id
4214
+ parent_id = aws_api_gateway_resource.path_depth_14[each.value.parent].id
4215
+ path_part = each.value.part
4216
+ }
4217
+
2813
4218
  # Resources
2814
4219
 
2815
4220
  # Create Lambda ZIP file from the bundle directory
@@ -2828,6 +4233,11 @@ resource "aws_s3_object" "lambda_zip" {
2828
4233
  }
2829
4234
 
2830
4235
  # Use the core REST API module
4236
+ # Account-level CloudWatch role required for REST API access logging
4237
+ module "account" {
4238
+ source = "../../../core/api/api-gateway-account"
4239
+ }
4240
+
2831
4241
  module "rest_api" {
2832
4242
  source = "../../../core/api/rest-api"
2833
4243
 
@@ -2857,9 +4267,33 @@ resource "aws_wafv2_web_acl_association" "api_waf_association" {
2857
4267
  depends_on = [aws_api_gateway_stage.api_stage]
2858
4268
  }
2859
4269
 
2860
- # Lambda function
4270
+ # Security group for the API Lambda function, used when deployed into a VPC
4271
+ resource "aws_security_group" "api_lambda" {
4272
+ count = var.enable_vpc ? 1 : 0
4273
+
4274
+ #checkov:skip=CKV2_AWS_5:Attached to api_lambda via vpc_config block; Checkov cannot resolve this reference
4275
+ name_prefix = "test-api-lambda-"
4276
+ description = "Security group for the TestApi API Lambda function"
4277
+ vpc_id = var.vpc_id
4278
+ tags = var.tags
4279
+ }
4280
+
4281
+ resource "aws_vpc_security_group_egress_rule" "api_lambda_https" {
4282
+ count = var.enable_vpc ? 1 : 0
4283
+
4284
+ security_group_id = aws_security_group.api_lambda[0].id
4285
+ cidr_ipv4 = "0.0.0.0/0"
4286
+ from_port = 443
4287
+ to_port = 443
4288
+ ip_protocol = "tcp"
4289
+ description = "Allow outbound HTTPS to AWS service endpoints"
4290
+ }
4291
+
2861
4292
  resource "aws_lambda_function" "api_lambda" {
2862
- #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
4293
+ # One lambda function per operation, each serving just that operation
4294
+ for_each = local.operations
4295
+
4296
+ #checkov:skip=CKV_AWS_117:Lambda function is optionally deployed into a VPC via vpc_id/subnet_ids; not required for this use case
2863
4297
  #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
2864
4298
  #checkov:skip=CKV_AWS_272:Code signing not required for this use case
2865
4299
  #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
@@ -2867,10 +4301,10 @@ resource "aws_lambda_function" "api_lambda" {
2867
4301
  s3_bucket = aws_s3_object.lambda_zip.bucket
2868
4302
  s3_key = aws_s3_object.lambda_zip.key
2869
4303
  s3_object_version = aws_s3_object.lambda_zip.version_id
2870
- function_name = "TestApiHandler-\${random_string.suffix.result}"
2871
- role = aws_iam_role.lambda_execution_role.arn
4304
+ function_name = local.function_name[each.key]
4305
+ role = aws_iam_role.lambda_execution_role[each.key].arn
2872
4306
  handler = "index.handler"
2873
- runtime = "nodejs22.x"
4307
+ runtime = "nodejs24.x"
2874
4308
  timeout = 30
2875
4309
  memory_size = 128
2876
4310
 
@@ -2881,18 +4315,32 @@ resource "aws_lambda_function" "api_lambda" {
2881
4315
  mode = "Active"
2882
4316
  }
2883
4317
 
4318
+ dynamic "vpc_config" {
4319
+ for_each = var.enable_vpc ? [1] : []
4320
+ content {
4321
+ subnet_ids = var.subnet_ids
4322
+ security_group_ids = [aws_security_group.api_lambda[0].id]
4323
+ }
4324
+ }
4325
+
2884
4326
 
2885
4327
  environment {
2886
4328
  variables = merge({
2887
- }, var.env)
4329
+ }, var.appconfig_application_id != null ? {
4330
+ RUNTIME_CONFIG_APP_ID = var.appconfig_application_id
4331
+ } : {}, var.env)
2888
4332
  }
2889
4333
 
2890
4334
  tags = var.tags
4335
+
4336
+ depends_on = [aws_iam_role_policy_attachment.lambda_vpc_access]
2891
4337
  }
2892
4338
 
2893
4339
  # IAM role for Lambda execution
2894
4340
  resource "aws_iam_role" "lambda_execution_role" {
2895
- name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
4341
+ for_each = local.operations
4342
+
4343
+ name = local.role_name[each.key]
2896
4344
 
2897
4345
  assume_role_policy = jsonencode({
2898
4346
  Version = "2012-10-17"
@@ -2912,90 +4360,132 @@ resource "aws_iam_role" "lambda_execution_role" {
2912
4360
 
2913
4361
  # Attach basic execution policy to Lambda role
2914
4362
  resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
4363
+ for_each = local.operations
4364
+
2915
4365
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
2916
- role = aws_iam_role.lambda_execution_role.name
4366
+ role = aws_iam_role.lambda_execution_role[each.key].name
2917
4367
  }
2918
4368
 
2919
4369
  # Attach X-Ray tracing policy to Lambda role
2920
4370
  resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
4371
+ for_each = local.operations
4372
+
2921
4373
  policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
2922
- role = aws_iam_role.lambda_execution_role.name
4374
+ role = aws_iam_role.lambda_execution_role[each.key].name
4375
+ }
4376
+
4377
+ # Attach VPC access policy to Lambda role when deployed into a VPC
4378
+ resource "aws_iam_role_policy_attachment" "lambda_vpc_access" {
4379
+ for_each = var.enable_vpc ? local.operations : {}
4380
+
4381
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
4382
+ role = aws_iam_role.lambda_execution_role[each.key].name
2923
4383
  }
2924
4384
 
2925
4385
  # Additional IAM policies for Lambda (if provided)
2926
4386
  resource "aws_iam_role_policy" "lambda_additional_policies" {
2927
- count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
2928
- name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
2929
- role = aws_iam_role.lambda_execution_role.id
4387
+ for_each = local.operations
4388
+
4389
+ name = "\${substr(local.function_name[each.key], 0, 55)}-policies"
4390
+ role = aws_iam_role.lambda_execution_role[each.key].id
2930
4391
 
2931
4392
  policy = jsonencode({
2932
4393
  Version = "2012-10-17"
2933
- Statement = var.additional_iam_policy_statements
4394
+ Statement = local.lambda_policy_document_statements
2934
4395
  })
2935
4396
  }
2936
4397
 
4398
+ locals {
4399
+ # Grant read access to the runtime-config AppConfig application when wired
4400
+ lambda_policy_statements = concat(var.appconfig_application_arn != null ? [
4401
+ {
4402
+ Effect = "Allow"
4403
+ Action = [
4404
+ "appconfig:StartConfigurationSession",
4405
+ "appconfig:GetLatestConfiguration"
4406
+ ]
4407
+ Resource = ["\${var.appconfig_application_arn}/*"]
4408
+ }
4409
+ ] : [], var.additional_iam_policy_statements)
4410
+
4411
+ # IAM rejects an empty statement list, so fall back to a no-op deny
4412
+ lambda_policy_document_statements = length(local.lambda_policy_statements) > 0 ? local.lambda_policy_statements : [
4413
+ {
4414
+ Effect = "Deny"
4415
+ Action = ["appconfig:GetLatestConfiguration"]
4416
+ Resource = ["arn:aws:appconfig:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:application/none"]
4417
+ }
4418
+ ]
4419
+ }
4420
+
2937
4421
  # CloudWatch Log Group for Lambda
2938
4422
  resource "aws_cloudwatch_log_group" "lambda_logs" {
2939
4423
  #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
2940
4424
  #checkov:skip=CKV_AWS_338:Log retention set to forever
2941
4425
  #checkov:skip=CKV_AWS_66:Log retention set to forever
2942
- name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
2943
- tags = var.tags
2944
- }
4426
+ for_each = local.operations
2945
4427
 
2946
-
2947
-
2948
- # Create proxy resource (captures all paths)
2949
- resource "aws_api_gateway_resource" "proxy_resource" {
2950
- rest_api_id = module.rest_api.api_id
2951
- parent_id = module.rest_api.api_root_resource_id
2952
- path_part = "{proxy+}"
4428
+ name = "/aws/lambda/\${local.function_name[each.key]}"
4429
+ tags = var.tags
2953
4430
  }
2954
4431
 
2955
- # Lambda integration for REST API
2956
- resource "aws_api_gateway_integration" "lambda_integration" {
2957
- rest_api_id = module.rest_api.api_id
2958
- resource_id = aws_api_gateway_resource.proxy_resource.id
2959
- http_method = aws_api_gateway_method.proxy_method.http_method
2960
4432
 
2961
- integration_http_method = "POST"
2962
- type = "AWS_PROXY"
2963
- uri = aws_lambda_function.api_lambda.invoke_arn
2964
-
2965
- depends_on = [aws_lambda_function.api_lambda]
2966
- }
2967
4433
 
2968
- # Method for proxy integration
2969
- resource "aws_api_gateway_method" "proxy_method" {
4434
+ # Method per operation
4435
+ resource "aws_api_gateway_method" "operation_methods" {
2970
4436
  #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
4437
+ for_each = local.operations
4438
+
2971
4439
  rest_api_id = module.rest_api.api_id
2972
- resource_id = aws_api_gateway_resource.proxy_resource.id
2973
- http_method = "ANY"
4440
+ resource_id = local.operation_resource_id[each.key]
4441
+ http_method = upper(each.value.method)
2974
4442
 
2975
4443
  authorization = "AWS_IAM"
2976
4444
 
4445
+ # Path parameters must be declared on the method
2977
4446
  request_parameters = {
2978
- "method.request.path.proxy" = true
4447
+ for segment in local.operation_segments[each.key] :
4448
+ "method.request.path.\${trimsuffix(trimprefix(segment, "{"), "}")}" => true
4449
+ if startswith(segment, "{")
2979
4450
  }
2980
4451
 
2981
4452
  depends_on = []
2982
4453
  }
2983
4454
 
2984
- # OPTIONS method for CORS preflight
4455
+ # Lambda integration per operation
4456
+ resource "aws_api_gateway_integration" "lambda_integration" {
4457
+ for_each = local.operations
4458
+
4459
+ rest_api_id = module.rest_api.api_id
4460
+ resource_id = local.operation_resource_id[each.key]
4461
+ http_method = aws_api_gateway_method.operation_methods[each.key].http_method
4462
+
4463
+ integration_http_method = "POST"
4464
+ type = "AWS_PROXY"
4465
+ uri = aws_lambda_function.api_lambda[each.key].invoke_arn
4466
+
4467
+ depends_on = [aws_lambda_function.api_lambda]
4468
+ }
4469
+
4470
+ # OPTIONS method for CORS preflight, on every resource an operation is served from
2985
4471
  resource "aws_api_gateway_method" "options_method" {
2986
4472
  #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
2987
4473
  #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
4474
+ for_each = local.resource_ids
4475
+
2988
4476
  rest_api_id = module.rest_api.api_id
2989
- resource_id = aws_api_gateway_resource.proxy_resource.id
4477
+ resource_id = each.value
2990
4478
  http_method = "OPTIONS"
2991
4479
  authorization = "NONE"
2992
4480
  }
2993
4481
 
2994
- # CORS integration for OPTIONS method
4482
+ # CORS integration for OPTIONS methods
2995
4483
  resource "aws_api_gateway_integration" "options_integration" {
4484
+ for_each = local.resource_ids
4485
+
2996
4486
  rest_api_id = module.rest_api.api_id
2997
- resource_id = aws_api_gateway_resource.proxy_resource.id
2998
- http_method = aws_api_gateway_method.options_method.http_method
4487
+ resource_id = each.value
4488
+ http_method = aws_api_gateway_method.options_method[each.key].http_method
2999
4489
 
3000
4490
  type = "MOCK"
3001
4491
  request_templates = {
@@ -3003,11 +4493,13 @@ resource "aws_api_gateway_integration" "options_integration" {
3003
4493
  }
3004
4494
  }
3005
4495
 
3006
- # OPTIONS method response
4496
+ # OPTIONS method responses
3007
4497
  resource "aws_api_gateway_method_response" "options_response" {
4498
+ for_each = local.resource_ids
4499
+
3008
4500
  rest_api_id = module.rest_api.api_id
3009
- resource_id = aws_api_gateway_resource.proxy_resource.id
3010
- http_method = aws_api_gateway_method.options_method.http_method
4501
+ resource_id = each.value
4502
+ http_method = aws_api_gateway_method.options_method[each.key].http_method
3011
4503
  status_code = "204"
3012
4504
 
3013
4505
  response_parameters = {
@@ -3017,18 +4509,22 @@ resource "aws_api_gateway_method_response" "options_response" {
3017
4509
  }
3018
4510
  }
3019
4511
 
3020
- # OPTIONS integration response
4512
+ # OPTIONS integration responses
3021
4513
  resource "aws_api_gateway_integration_response" "options_integration_response" {
4514
+ for_each = local.resource_ids
4515
+
3022
4516
  rest_api_id = module.rest_api.api_id
3023
- resource_id = aws_api_gateway_resource.proxy_resource.id
3024
- http_method = aws_api_gateway_method.options_method.http_method
3025
- status_code = aws_api_gateway_method_response.options_response.status_code
4517
+ resource_id = each.value
4518
+ http_method = aws_api_gateway_method.options_method[each.key].http_method
4519
+ status_code = aws_api_gateway_method_response.options_response[each.key].status_code
3026
4520
 
3027
4521
  response_parameters = {
3028
4522
  "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
3029
4523
  "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
3030
4524
  "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
3031
4525
  }
4526
+
4527
+ depends_on = [aws_api_gateway_integration.options_integration]
3032
4528
  }
3033
4529
 
3034
4530
  # API Gateway deployment
@@ -3036,13 +4532,28 @@ resource "aws_api_gateway_deployment" "api_deployment" {
3036
4532
  rest_api_id = module.rest_api.api_id
3037
4533
 
3038
4534
  triggers = {
3039
- redeployment = sha1(jsonencode([
3040
- aws_api_gateway_resource.proxy_resource.id,
3041
- aws_api_gateway_method.proxy_method.id,
3042
- aws_api_gateway_integration.lambda_integration.id,
3043
- aws_api_gateway_method.options_method.id,
3044
- aws_api_gateway_integration.options_integration.id,
3045
- ]))
4535
+ redeployment = sha1(jsonencode(concat(
4536
+ [for r in aws_api_gateway_resource.path_depth_0 : r.id],
4537
+ [for r in aws_api_gateway_resource.path_depth_1 : r.id],
4538
+ [for r in aws_api_gateway_resource.path_depth_2 : r.id],
4539
+ [for r in aws_api_gateway_resource.path_depth_3 : r.id],
4540
+ [for r in aws_api_gateway_resource.path_depth_4 : r.id],
4541
+ [for r in aws_api_gateway_resource.path_depth_5 : r.id],
4542
+ [for r in aws_api_gateway_resource.path_depth_6 : r.id],
4543
+ [for r in aws_api_gateway_resource.path_depth_7 : r.id],
4544
+ [for r in aws_api_gateway_resource.path_depth_8 : r.id],
4545
+ [for r in aws_api_gateway_resource.path_depth_9 : r.id],
4546
+ [for r in aws_api_gateway_resource.path_depth_10 : r.id],
4547
+ [for r in aws_api_gateway_resource.path_depth_11 : r.id],
4548
+ [for r in aws_api_gateway_resource.path_depth_12 : r.id],
4549
+ [for r in aws_api_gateway_resource.path_depth_13 : r.id],
4550
+ [for r in aws_api_gateway_resource.path_depth_14 : r.id],
4551
+ [for r in aws_api_gateway_resource.path_depth_15 : r.id],
4552
+ [for m in aws_api_gateway_method.operation_methods : m.id],
4553
+ [for i in aws_api_gateway_integration.lambda_integration : i.id],
4554
+ [for m in aws_api_gateway_method.options_method : m.id],
4555
+ [for i in aws_api_gateway_integration.options_integration : i.id],
4556
+ )))
3046
4557
  }
3047
4558
 
3048
4559
  lifecycle {
@@ -3050,7 +4561,7 @@ resource "aws_api_gateway_deployment" "api_deployment" {
3050
4561
  }
3051
4562
 
3052
4563
  depends_on = [
3053
- aws_api_gateway_method.proxy_method,
4564
+ aws_api_gateway_method.operation_methods,
3054
4565
  aws_api_gateway_integration.lambda_integration,
3055
4566
  aws_api_gateway_method.options_method,
3056
4567
  aws_api_gateway_integration.options_integration,
@@ -3059,11 +4570,63 @@ resource "aws_api_gateway_deployment" "api_deployment" {
3059
4570
  ]
3060
4571
  }
3061
4572
 
4573
+ # KMS key for encrypting access logs at rest
4574
+ resource "aws_kms_key" "access_logs" {
4575
+ description = "TestApi API access log encryption"
4576
+ deletion_window_in_days = 7
4577
+ enable_key_rotation = true
4578
+
4579
+ policy = jsonencode({
4580
+ Version = "2012-10-17"
4581
+ Statement = [
4582
+ {
4583
+ Sid = "Enable IAM User Permissions"
4584
+ Effect = "Allow"
4585
+ Principal = { AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root" }
4586
+ Action = "kms:*"
4587
+ Resource = "*"
4588
+ },
4589
+ {
4590
+ Sid = "Allow CloudWatch Logs"
4591
+ Effect = "Allow"
4592
+ Principal = { Service = "logs.\${data.aws_region.current.region}.amazonaws.com" }
4593
+ Action = [
4594
+ "kms:Encrypt",
4595
+ "kms:Decrypt",
4596
+ "kms:ReEncrypt*",
4597
+ "kms:GenerateDataKey*",
4598
+ "kms:DescribeKey"
4599
+ ]
4600
+ Resource = "*"
4601
+ Condition = {
4602
+ ArnEquals = {
4603
+ "kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:log-group:/aws/apigateway/test-api-\${random_string.suffix.result}/access"
4604
+ }
4605
+ }
4606
+ }
4607
+ ]
4608
+ })
4609
+
4610
+ tags = var.tags
4611
+ }
4612
+
4613
+ resource "aws_kms_alias" "access_logs" {
4614
+ name = "alias/test-api-\${random_string.suffix.result}-access-logs"
4615
+ target_key_id = aws_kms_key.access_logs.key_id
4616
+ }
4617
+
4618
+ # Access logs for the API Gateway stage
4619
+ resource "aws_cloudwatch_log_group" "access_logs" {
4620
+ name = "/aws/apigateway/test-api-\${random_string.suffix.result}/access"
4621
+ retention_in_days = 365
4622
+ kms_key_id = aws_kms_key.access_logs.arn
4623
+ tags = var.tags
4624
+ }
4625
+
3062
4626
  # API Gateway stage
3063
4627
  resource "aws_api_gateway_stage" "api_stage" {
3064
4628
  #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
3065
- #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
3066
- #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
4629
+ #checkov:skip=CKV2_AWS_4:Access logging is enabled below; verbose execution logging is intentionally not enabled
3067
4630
  #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
3068
4631
  #checkov:skip=CKV2_AWS_77:WAFv2 Web ACL is attached via aws_wafv2_web_acl_association when var.enable_waf is true (default) and includes AWSManagedRulesKnownBadInputsRuleSet for Log4j protection; Checkov does not track the association across modules
3069
4632
  deployment_id = aws_api_gateway_deployment.api_deployment.id
@@ -3071,9 +4634,39 @@ resource "aws_api_gateway_stage" "api_stage" {
3071
4634
  stage_name = "prod"
3072
4635
  xray_tracing_enabled = true
3073
4636
 
4637
+ access_log_settings {
4638
+ destination_arn = aws_cloudwatch_log_group.access_logs.arn
4639
+ format = jsonencode({
4640
+ requestId = "$context.requestId"
4641
+ ip = "$context.identity.sourceIp"
4642
+ caller = "$context.identity.caller"
4643
+ user = "$context.identity.user"
4644
+ requestTime = "$context.requestTime"
4645
+ httpMethod = "$context.httpMethod"
4646
+ resourcePath = "$context.resourcePath"
4647
+ status = "$context.status"
4648
+ protocol = "$context.protocol"
4649
+ responseLength = "$context.responseLength"
4650
+ })
4651
+ }
4652
+
3074
4653
  tags = var.tags
3075
4654
 
3076
- depends_on = [aws_api_gateway_deployment.api_deployment]
4655
+ # Ensure the account-level role is configured before the stage is created
4656
+ depends_on = [aws_api_gateway_deployment.api_deployment, module.account]
4657
+ }
4658
+
4659
+ # Default throttling applied to all resource paths and methods
4660
+ resource "aws_api_gateway_method_settings" "throttling" {
4661
+ #checkov:skip=CKV_AWS_225:API Gateway caching not required for this use case
4662
+ rest_api_id = module.rest_api.api_id
4663
+ stage_name = aws_api_gateway_stage.api_stage.stage_name
4664
+ method_path = "*/*"
4665
+
4666
+ settings {
4667
+ throttling_rate_limit = 10000
4668
+ throttling_burst_limit = 5000
4669
+ }
3077
4670
  }
3078
4671
 
3079
4672
  # API Gateway Resource Policy
@@ -3107,11 +4700,13 @@ resource "aws_api_gateway_rest_api_policy" "api_policy" {
3107
4700
 
3108
4701
  # Lambda permission for API Gateway to invoke the function
3109
4702
  resource "aws_lambda_permission" "api_gateway_invoke" {
4703
+ for_each = local.operations
4704
+
3110
4705
  statement_id = "AllowExecutionFromAPIGateway"
3111
4706
  action = "lambda:InvokeFunction"
3112
- function_name = aws_lambda_function.api_lambda.function_name
4707
+ function_name = aws_lambda_function.api_lambda[each.key].function_name
3113
4708
  principal = "apigateway.amazonaws.com"
3114
- source_arn = "\${module.rest_api.api_execution_arn}/*/*"
4709
+ source_arn = "\${module.rest_api.api_execution_arn}/*/\${local.permission_source_suffix[each.key]}"
3115
4710
 
3116
4711
  depends_on = [module.rest_api, aws_lambda_function.api_lambda]
3117
4712
  }
@@ -3123,7 +4718,7 @@ module "add_url_to_runtime_config" {
3123
4718
 
3124
4719
  namespace = "connection"
3125
4720
  key = "apis"
3126
- value = { "TestApi" = aws_api_gateway_stage.api_stage.invoke_url }
4721
+ value = { "TestApi" = "\${aws_api_gateway_stage.api_stage.invoke_url}/" }
3127
4722
 
3128
4723
  depends_on = [aws_api_gateway_stage.api_stage]
3129
4724
  }
@@ -3176,78 +4771,83 @@ output "stage_id" {
3176
4771
  value = aws_api_gateway_stage.api_stage.id
3177
4772
  }
3178
4773
 
3179
- # Lambda Function Outputs
3180
- output "lambda_function_name" {
3181
- description = "Name of the Lambda function"
3182
- value = aws_lambda_function.api_lambda.function_name
4774
+ # Lambda Function Outputs, keyed by operation name
4775
+ output "operations" {
4776
+ description = "Names of the API operations, each with its own Lambda function"
4777
+ value = keys(local.operations)
3183
4778
  }
3184
4779
 
3185
- output "lambda_function_arn" {
3186
- description = "ARN of the Lambda function"
3187
- value = aws_lambda_function.api_lambda.arn
4780
+ output "lambda_function_names" {
4781
+ description = "Name of each operation's Lambda function, keyed by operation name"
4782
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.function_name }
3188
4783
  }
3189
4784
 
3190
- output "lambda_invoke_arn" {
3191
- description = "Invoke ARN of the Lambda function"
3192
- value = aws_lambda_function.api_lambda.invoke_arn
4785
+ output "lambda_function_arns" {
4786
+ description = "ARN of each operation's Lambda function, keyed by operation name"
4787
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.arn }
3193
4788
  }
3194
4789
 
3195
- output "lambda_qualified_arn" {
3196
- description = "Qualified ARN of the Lambda function"
3197
- value = aws_lambda_function.api_lambda.qualified_arn
4790
+ output "lambda_invoke_arns" {
4791
+ description = "Invoke ARN of each operation's Lambda function, keyed by operation name"
4792
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.invoke_arn }
3198
4793
  }
3199
4794
 
3200
- output "lambda_version" {
3201
- description = "Version of the Lambda function"
3202
- value = aws_lambda_function.api_lambda.version
4795
+ output "lambda_qualified_arns" {
4796
+ description = "Qualified ARN of each operation's Lambda function, keyed by operation name"
4797
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.qualified_arn }
4798
+ }
4799
+
4800
+ output "lambda_versions" {
4801
+ description = "Version of each operation's Lambda function, keyed by operation name"
4802
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.version }
3203
4803
  }
3204
4804
 
3205
4805
  output "lambda_source_code_hash" {
3206
- description = "Base64-encoded SHA256 hash of the Lambda deployment package"
3207
- value = aws_lambda_function.api_lambda.source_code_hash
4806
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package, shared by every operation"
4807
+ value = data.archive_file.lambda_zip.output_base64sha256
3208
4808
  }
3209
4809
 
3210
- output "lambda_source_code_size" {
3211
- description = "Size of the Lambda deployment package in bytes"
3212
- value = aws_lambda_function.api_lambda.source_code_size
4810
+ # IAM Role Outputs, keyed by operation name
4811
+ output "lambda_execution_role_arns" {
4812
+ description = "ARN of each operation's Lambda execution role, keyed by operation name"
4813
+ value = { for op, role in aws_iam_role.lambda_execution_role : op => role.arn }
3213
4814
  }
3214
4815
 
3215
- # IAM Role Outputs
3216
- output "lambda_execution_role_arn" {
3217
- description = "ARN of the Lambda execution role"
3218
- value = aws_iam_role.lambda_execution_role.arn
4816
+ output "lambda_execution_role_names" {
4817
+ description = "Name of each operation's Lambda execution role, keyed by operation name. Attach additional policies to a specific operation's role by name."
4818
+ value = { for op, role in aws_iam_role.lambda_execution_role : op => role.name }
3219
4819
  }
3220
4820
 
3221
- output "lambda_execution_role_name" {
3222
- description = "Name of the Lambda execution role"
3223
- value = aws_iam_role.lambda_execution_role.name
4821
+ output "security_group_id" {
4822
+ description = "Security group ID shared by the API Lambda functions, for use in ingress rules on resources they must reach (e.g. a database). Null unless enable_vpc is true."
4823
+ value = var.enable_vpc ? aws_security_group.api_lambda[0].id : null
3224
4824
  }
3225
4825
 
3226
- # Integration Outputs
3227
- output "integration_id" {
3228
- description = "ID of the Lambda integration"
3229
- value = aws_api_gateway_integration.lambda_integration.id
4826
+ # Integration Outputs, keyed by operation name
4827
+ output "integration_ids" {
4828
+ description = "ID of each operation's Lambda integration, keyed by operation name"
4829
+ value = { for op, i in aws_api_gateway_integration.lambda_integration : op => i.id }
3230
4830
  }
3231
4831
 
3232
- output "proxy_resource_id" {
3233
- description = "ID of the proxy resource"
3234
- value = aws_api_gateway_resource.proxy_resource.id
4832
+ output "method_ids" {
4833
+ description = "ID of each operation's API Gateway method, keyed by operation name"
4834
+ value = { for op, m in aws_api_gateway_method.operation_methods : op => m.id }
3235
4835
  }
3236
4836
 
3237
- output "proxy_method_id" {
3238
- description = "ID of the proxy method"
3239
- value = aws_api_gateway_method.proxy_method.id
4837
+ output "resource_ids" {
4838
+ description = "ID of each API Gateway resource, keyed by its path"
4839
+ value = local.resource_ids
3240
4840
  }
3241
4841
 
3242
- # CloudWatch Log Groups
3243
- output "lambda_log_group_name" {
3244
- description = "Name of the Lambda CloudWatch log group"
3245
- value = aws_cloudwatch_log_group.lambda_logs.name
4842
+ # CloudWatch Log Groups, keyed by operation name
4843
+ output "lambda_log_group_names" {
4844
+ description = "Name of each operation's Lambda CloudWatch log group, keyed by operation name"
4845
+ value = { for op, lg in aws_cloudwatch_log_group.lambda_logs : op => lg.name }
3246
4846
  }
3247
4847
 
3248
- output "lambda_log_group_arn" {
3249
- description = "ARN of the Lambda CloudWatch log group"
3250
- value = aws_cloudwatch_log_group.lambda_logs.arn
4848
+ output "lambda_log_group_arns" {
4849
+ description = "ARN of each operation's Lambda CloudWatch log group, keyed by operation name"
4850
+ value = { for op, lg in aws_cloudwatch_log_group.lambda_logs : op => lg.arn }
3251
4851
  }
3252
4852
  ",
3253
4853
  }
@@ -3258,9 +4858,17 @@ exports[`tsSmithyApiGenerator > terraform iac > should generate terraform with c
3258
4858
  required_version = ">= 1.0"
3259
4859
 
3260
4860
  required_providers {
4861
+ archive = {
4862
+ source = "hashicorp/archive"
4863
+ version = "2.8.0"
4864
+ }
3261
4865
  aws = {
3262
4866
  source = "hashicorp/aws"
3263
- version = "~> 6.33"
4867
+ version = "6.62.0"
4868
+ }
4869
+ random = {
4870
+ source = "hashicorp/random"
4871
+ version = "3.9.0"
3264
4872
  }
3265
4873
  }
3266
4874
  }
@@ -3282,11 +4890,47 @@ variable "additional_iam_policy_statements" {
3282
4890
  default = []
3283
4891
  }
3284
4892
 
4893
+ variable "appconfig_application_id" {
4894
+ description = "ID of the shared runtime-config AppConfig application (from \`core/runtime-config/appconfig.application_id\`). When set, the Lambda functions receive \`RUNTIME_CONFIG_APP_ID\` and read access to the application."
4895
+ type = string
4896
+ default = null
4897
+ }
4898
+
4899
+ variable "appconfig_application_arn" {
4900
+ description = "ARN of the shared runtime-config AppConfig application (from \`core/runtime-config/appconfig.application_arn\`). Used to scope the IAM read permission granted to the Lambda functions."
4901
+ type = string
4902
+ default = null
4903
+ }
4904
+
3285
4905
  variable "asset_bucket_name" {
3286
4906
  description = "Name of the shared asset S3 bucket used to stage the Lambda deployment zip. Instantiate the \`core/asset-bucket\` module once per deployment and pass its \`bucket_name\` output here."
3287
4907
  type = string
3288
4908
  }
3289
4909
 
4910
+ # VPC Configuration (optional)
4911
+ variable "enable_vpc" {
4912
+ description = "Deploy the Lambda function into a VPC. Set alongside vpc_id/subnet_ids."
4913
+ type = bool
4914
+ default = false
4915
+ }
4916
+
4917
+ variable "vpc_id" {
4918
+ description = "VPC ID to deploy the Lambda function into. Required when enable_vpc is true."
4919
+ type = string
4920
+ default = null
4921
+
4922
+ validation {
4923
+ condition = !var.enable_vpc || (var.vpc_id != null && var.subnet_ids != null)
4924
+ error_message = "vpc_id and subnet_ids must be set when enable_vpc is true."
4925
+ }
4926
+ }
4927
+
4928
+ variable "subnet_ids" {
4929
+ description = "Subnet IDs to deploy the Lambda function into. Required when enable_vpc is true."
4930
+ type = list(string)
4931
+ default = null
4932
+ }
4933
+
3290
4934
  # CORS Configuration (passed to core module)
3291
4935
 
3292
4936
  variable "cors_allow_headers" {
@@ -3331,6 +4975,235 @@ resource "random_string" "suffix" {
3331
4975
  upper = false
3332
4976
  }
3333
4977
 
4978
+ locals {
4979
+ # Generated at build time from the API definition
4980
+ operations_file = "\${path.module}/../../../generated/custom-api/operations.json"
4981
+ operations = fileexists(local.operations_file) ? jsondecode(file(local.operations_file)) : {}
4982
+
4983
+ operation_slug = { for op in keys(local.operations) : op => replace(op, "/[^a-zA-Z0-9-_]/", "-") }
4984
+ operation_hash = { for op in keys(local.operations) : op => substr(sha256(op), 0, 8) }
4985
+
4986
+ function_name = { for op in keys(local.operations) : op =>
4987
+ "\${substr("CustomApi-\${local.operation_slug[op]}", 0, 64 - length(local.operation_hash[op]) - length(random_string.suffix.result) - 2)}-\${local.operation_hash[op]}-\${random_string.suffix.result}"
4988
+ }
4989
+ role_name = { for op in keys(local.operations) : op =>
4990
+ "\${substr("CustomApi-\${local.operation_slug[op]}-role", 0, 64 - length(local.operation_hash[op]) - length(random_string.suffix.result) - 2)}-\${local.operation_hash[op]}-\${random_string.suffix.result}"
4991
+ }
4992
+
4993
+ # A REST API needs a resource per path segment, so each operation's path is
4994
+ # split into the distinct path prefixes the resource tree is built from below
4995
+ operation_segments = { for op, details in local.operations : op => compact(split("/", details.path)) }
4996
+
4997
+ path_nodes = merge([
4998
+ for op, segments in local.operation_segments : {
4999
+ for i, segment in segments : join("/", slice(segments, 0, i + 1)) => {
5000
+ depth = i
5001
+ parent = i == 0 ? "" : join("/", slice(segments, 0, i))
5002
+ part = segment
5003
+ }
5004
+ }
5005
+ ]...)
5006
+
5007
+ # Instances of a resource cannot reference one another, so each depth is a
5008
+ # separate resource. Deeper paths are reported by the precondition below.
5009
+ max_path_depth = 16
5010
+ path_nodes_by_depth = { for depth in range(local.max_path_depth) : depth => {
5011
+ for path, node in local.path_nodes : path => node if node.depth == depth
5012
+ } }
5013
+ paths_too_deep = [for path, node in local.path_nodes : path if node.depth >= local.max_path_depth]
5014
+
5015
+ resource_ids = merge(
5016
+ { for path, resource in aws_api_gateway_resource.path_depth_0 : path => resource.id },
5017
+ { for path, resource in aws_api_gateway_resource.path_depth_1 : path => resource.id },
5018
+ { for path, resource in aws_api_gateway_resource.path_depth_2 : path => resource.id },
5019
+ { for path, resource in aws_api_gateway_resource.path_depth_3 : path => resource.id },
5020
+ { for path, resource in aws_api_gateway_resource.path_depth_4 : path => resource.id },
5021
+ { for path, resource in aws_api_gateway_resource.path_depth_5 : path => resource.id },
5022
+ { for path, resource in aws_api_gateway_resource.path_depth_6 : path => resource.id },
5023
+ { for path, resource in aws_api_gateway_resource.path_depth_7 : path => resource.id },
5024
+ { for path, resource in aws_api_gateway_resource.path_depth_8 : path => resource.id },
5025
+ { for path, resource in aws_api_gateway_resource.path_depth_9 : path => resource.id },
5026
+ { for path, resource in aws_api_gateway_resource.path_depth_10 : path => resource.id },
5027
+ { for path, resource in aws_api_gateway_resource.path_depth_11 : path => resource.id },
5028
+ { for path, resource in aws_api_gateway_resource.path_depth_12 : path => resource.id },
5029
+ { for path, resource in aws_api_gateway_resource.path_depth_13 : path => resource.id },
5030
+ { for path, resource in aws_api_gateway_resource.path_depth_14 : path => resource.id },
5031
+ { for path, resource in aws_api_gateway_resource.path_depth_15 : path => resource.id },
5032
+ )
5033
+
5034
+ operation_resource_id = { for op, segments in local.operation_segments : op =>
5035
+ local.resource_ids[join("/", segments)]
5036
+ }
5037
+
5038
+ permission_source_suffix = { for op, details in local.operations : op =>
5039
+ "\${upper(details.method)}/\${replace(join("/", local.operation_segments[op]), "/{[^}]*}/", "*")}"
5040
+ }
5041
+ }
5042
+
5043
+ resource "terraform_data" "operations_metadata" {
5044
+ # Fails the plan when the operations metadata has not been generated
5045
+ input = local.operations_file
5046
+
5047
+ lifecycle {
5048
+ precondition {
5049
+ condition = fileexists(local.operations_file)
5050
+ error_message = "Operations metadata not found at \${local.operations_file}. Build the CustomApi project to generate it."
5051
+ }
5052
+ precondition {
5053
+ condition = length(local.operations) > 0
5054
+ error_message = "No operations found in \${local.operations_file}. The CustomApi API must define at least one operation."
5055
+ }
5056
+ precondition {
5057
+ condition = length(local.paths_too_deep) == 0
5058
+ error_message = "Operation paths exceed the supported depth of \${local.max_path_depth} segments: \${join(", ", local.paths_too_deep)}. Add further aws_api_gateway_resource levels to this module to support them."
5059
+ }
5060
+ }
5061
+ }
5062
+
5063
+ # API Gateway resources at path depth 0
5064
+ resource "aws_api_gateway_resource" "path_depth_0" {
5065
+ for_each = local.path_nodes_by_depth[0]
5066
+
5067
+ rest_api_id = module.rest_api.api_id
5068
+ parent_id = module.rest_api.api_root_resource_id
5069
+ path_part = each.value.part
5070
+ }
5071
+
5072
+ # API Gateway resources at path depth 1
5073
+ resource "aws_api_gateway_resource" "path_depth_1" {
5074
+ for_each = local.path_nodes_by_depth[1]
5075
+
5076
+ rest_api_id = module.rest_api.api_id
5077
+ parent_id = aws_api_gateway_resource.path_depth_0[each.value.parent].id
5078
+ path_part = each.value.part
5079
+ }
5080
+
5081
+ # API Gateway resources at path depth 2
5082
+ resource "aws_api_gateway_resource" "path_depth_2" {
5083
+ for_each = local.path_nodes_by_depth[2]
5084
+
5085
+ rest_api_id = module.rest_api.api_id
5086
+ parent_id = aws_api_gateway_resource.path_depth_1[each.value.parent].id
5087
+ path_part = each.value.part
5088
+ }
5089
+
5090
+ # API Gateway resources at path depth 3
5091
+ resource "aws_api_gateway_resource" "path_depth_3" {
5092
+ for_each = local.path_nodes_by_depth[3]
5093
+
5094
+ rest_api_id = module.rest_api.api_id
5095
+ parent_id = aws_api_gateway_resource.path_depth_2[each.value.parent].id
5096
+ path_part = each.value.part
5097
+ }
5098
+
5099
+ # API Gateway resources at path depth 4
5100
+ resource "aws_api_gateway_resource" "path_depth_4" {
5101
+ for_each = local.path_nodes_by_depth[4]
5102
+
5103
+ rest_api_id = module.rest_api.api_id
5104
+ parent_id = aws_api_gateway_resource.path_depth_3[each.value.parent].id
5105
+ path_part = each.value.part
5106
+ }
5107
+
5108
+ # API Gateway resources at path depth 5
5109
+ resource "aws_api_gateway_resource" "path_depth_5" {
5110
+ for_each = local.path_nodes_by_depth[5]
5111
+
5112
+ rest_api_id = module.rest_api.api_id
5113
+ parent_id = aws_api_gateway_resource.path_depth_4[each.value.parent].id
5114
+ path_part = each.value.part
5115
+ }
5116
+
5117
+ # API Gateway resources at path depth 6
5118
+ resource "aws_api_gateway_resource" "path_depth_6" {
5119
+ for_each = local.path_nodes_by_depth[6]
5120
+
5121
+ rest_api_id = module.rest_api.api_id
5122
+ parent_id = aws_api_gateway_resource.path_depth_5[each.value.parent].id
5123
+ path_part = each.value.part
5124
+ }
5125
+
5126
+ # API Gateway resources at path depth 7
5127
+ resource "aws_api_gateway_resource" "path_depth_7" {
5128
+ for_each = local.path_nodes_by_depth[7]
5129
+
5130
+ rest_api_id = module.rest_api.api_id
5131
+ parent_id = aws_api_gateway_resource.path_depth_6[each.value.parent].id
5132
+ path_part = each.value.part
5133
+ }
5134
+
5135
+ # API Gateway resources at path depth 8
5136
+ resource "aws_api_gateway_resource" "path_depth_8" {
5137
+ for_each = local.path_nodes_by_depth[8]
5138
+
5139
+ rest_api_id = module.rest_api.api_id
5140
+ parent_id = aws_api_gateway_resource.path_depth_7[each.value.parent].id
5141
+ path_part = each.value.part
5142
+ }
5143
+
5144
+ # API Gateway resources at path depth 9
5145
+ resource "aws_api_gateway_resource" "path_depth_9" {
5146
+ for_each = local.path_nodes_by_depth[9]
5147
+
5148
+ rest_api_id = module.rest_api.api_id
5149
+ parent_id = aws_api_gateway_resource.path_depth_8[each.value.parent].id
5150
+ path_part = each.value.part
5151
+ }
5152
+
5153
+ # API Gateway resources at path depth 10
5154
+ resource "aws_api_gateway_resource" "path_depth_10" {
5155
+ for_each = local.path_nodes_by_depth[10]
5156
+
5157
+ rest_api_id = module.rest_api.api_id
5158
+ parent_id = aws_api_gateway_resource.path_depth_9[each.value.parent].id
5159
+ path_part = each.value.part
5160
+ }
5161
+
5162
+ # API Gateway resources at path depth 11
5163
+ resource "aws_api_gateway_resource" "path_depth_11" {
5164
+ for_each = local.path_nodes_by_depth[11]
5165
+
5166
+ rest_api_id = module.rest_api.api_id
5167
+ parent_id = aws_api_gateway_resource.path_depth_10[each.value.parent].id
5168
+ path_part = each.value.part
5169
+ }
5170
+
5171
+ # API Gateway resources at path depth 12
5172
+ resource "aws_api_gateway_resource" "path_depth_12" {
5173
+ for_each = local.path_nodes_by_depth[12]
5174
+
5175
+ rest_api_id = module.rest_api.api_id
5176
+ parent_id = aws_api_gateway_resource.path_depth_11[each.value.parent].id
5177
+ path_part = each.value.part
5178
+ }
5179
+
5180
+ # API Gateway resources at path depth 13
5181
+ resource "aws_api_gateway_resource" "path_depth_13" {
5182
+ for_each = local.path_nodes_by_depth[13]
5183
+
5184
+ rest_api_id = module.rest_api.api_id
5185
+ parent_id = aws_api_gateway_resource.path_depth_12[each.value.parent].id
5186
+ path_part = each.value.part
5187
+ }
5188
+
5189
+ # API Gateway resources at path depth 14
5190
+ resource "aws_api_gateway_resource" "path_depth_14" {
5191
+ for_each = local.path_nodes_by_depth[14]
5192
+
5193
+ rest_api_id = module.rest_api.api_id
5194
+ parent_id = aws_api_gateway_resource.path_depth_13[each.value.parent].id
5195
+ path_part = each.value.part
5196
+ }
5197
+
5198
+ # API Gateway resources at path depth 15
5199
+ resource "aws_api_gateway_resource" "path_depth_15" {
5200
+ for_each = local.path_nodes_by_depth[15]
5201
+
5202
+ rest_api_id = module.rest_api.api_id
5203
+ parent_id = aws_api_gateway_resource.path_depth_14[each.value.parent].id
5204
+ path_part = each.value.part
5205
+ }
5206
+
3334
5207
  # Resources
3335
5208
 
3336
5209
  # Create Lambda ZIP file from the bundle directory
@@ -3349,6 +5222,11 @@ resource "aws_s3_object" "lambda_zip" {
3349
5222
  }
3350
5223
 
3351
5224
  # Use the core REST API module
5225
+ # Account-level CloudWatch role required for REST API access logging
5226
+ module "account" {
5227
+ source = "../../../core/api/api-gateway-account"
5228
+ }
5229
+
3352
5230
  module "rest_api" {
3353
5231
  source = "../../../core/api/rest-api"
3354
5232
 
@@ -3378,9 +5256,33 @@ resource "aws_wafv2_web_acl_association" "api_waf_association" {
3378
5256
  depends_on = [aws_api_gateway_stage.api_stage]
3379
5257
  }
3380
5258
 
3381
- # Lambda function
5259
+ # Security group for the API Lambda function, used when deployed into a VPC
5260
+ resource "aws_security_group" "api_lambda" {
5261
+ count = var.enable_vpc ? 1 : 0
5262
+
5263
+ #checkov:skip=CKV2_AWS_5:Attached to api_lambda via vpc_config block; Checkov cannot resolve this reference
5264
+ name_prefix = "custom-api-lambda-"
5265
+ description = "Security group for the CustomApi API Lambda function"
5266
+ vpc_id = var.vpc_id
5267
+ tags = var.tags
5268
+ }
5269
+
5270
+ resource "aws_vpc_security_group_egress_rule" "api_lambda_https" {
5271
+ count = var.enable_vpc ? 1 : 0
5272
+
5273
+ security_group_id = aws_security_group.api_lambda[0].id
5274
+ cidr_ipv4 = "0.0.0.0/0"
5275
+ from_port = 443
5276
+ to_port = 443
5277
+ ip_protocol = "tcp"
5278
+ description = "Allow outbound HTTPS to AWS service endpoints"
5279
+ }
5280
+
3382
5281
  resource "aws_lambda_function" "api_lambda" {
3383
- #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
5282
+ # One lambda function per operation, each serving just that operation
5283
+ for_each = local.operations
5284
+
5285
+ #checkov:skip=CKV_AWS_117:Lambda function is optionally deployed into a VPC via vpc_id/subnet_ids; not required for this use case
3384
5286
  #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
3385
5287
  #checkov:skip=CKV_AWS_272:Code signing not required for this use case
3386
5288
  #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
@@ -3388,10 +5290,10 @@ resource "aws_lambda_function" "api_lambda" {
3388
5290
  s3_bucket = aws_s3_object.lambda_zip.bucket
3389
5291
  s3_key = aws_s3_object.lambda_zip.key
3390
5292
  s3_object_version = aws_s3_object.lambda_zip.version_id
3391
- function_name = "CustomApiHandler-\${random_string.suffix.result}"
3392
- role = aws_iam_role.lambda_execution_role.arn
5293
+ function_name = local.function_name[each.key]
5294
+ role = aws_iam_role.lambda_execution_role[each.key].arn
3393
5295
  handler = "index.handler"
3394
- runtime = "nodejs22.x"
5296
+ runtime = "nodejs24.x"
3395
5297
  timeout = 30
3396
5298
  memory_size = 128
3397
5299
 
@@ -3402,18 +5304,32 @@ resource "aws_lambda_function" "api_lambda" {
3402
5304
  mode = "Active"
3403
5305
  }
3404
5306
 
5307
+ dynamic "vpc_config" {
5308
+ for_each = var.enable_vpc ? [1] : []
5309
+ content {
5310
+ subnet_ids = var.subnet_ids
5311
+ security_group_ids = [aws_security_group.api_lambda[0].id]
5312
+ }
5313
+ }
5314
+
3405
5315
 
3406
5316
  environment {
3407
5317
  variables = merge({
3408
- }, var.env)
5318
+ }, var.appconfig_application_id != null ? {
5319
+ RUNTIME_CONFIG_APP_ID = var.appconfig_application_id
5320
+ } : {}, var.env)
3409
5321
  }
3410
5322
 
3411
5323
  tags = var.tags
5324
+
5325
+ depends_on = [aws_iam_role_policy_attachment.lambda_vpc_access]
3412
5326
  }
3413
5327
 
3414
5328
  # IAM role for Lambda execution
3415
5329
  resource "aws_iam_role" "lambda_execution_role" {
3416
- name = "CustomApiHandler-execution-role-\${random_string.suffix.result}"
5330
+ for_each = local.operations
5331
+
5332
+ name = local.role_name[each.key]
3417
5333
 
3418
5334
  assume_role_policy = jsonencode({
3419
5335
  Version = "2012-10-17"
@@ -3433,35 +5349,73 @@ resource "aws_iam_role" "lambda_execution_role" {
3433
5349
 
3434
5350
  # Attach basic execution policy to Lambda role
3435
5351
  resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
5352
+ for_each = local.operations
5353
+
3436
5354
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
3437
- role = aws_iam_role.lambda_execution_role.name
5355
+ role = aws_iam_role.lambda_execution_role[each.key].name
3438
5356
  }
3439
5357
 
3440
5358
  # Attach X-Ray tracing policy to Lambda role
3441
5359
  resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
5360
+ for_each = local.operations
5361
+
3442
5362
  policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
3443
- role = aws_iam_role.lambda_execution_role.name
5363
+ role = aws_iam_role.lambda_execution_role[each.key].name
5364
+ }
5365
+
5366
+ # Attach VPC access policy to Lambda role when deployed into a VPC
5367
+ resource "aws_iam_role_policy_attachment" "lambda_vpc_access" {
5368
+ for_each = var.enable_vpc ? local.operations : {}
5369
+
5370
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
5371
+ role = aws_iam_role.lambda_execution_role[each.key].name
3444
5372
  }
3445
5373
 
3446
5374
  # Additional IAM policies for Lambda (if provided)
3447
5375
  resource "aws_iam_role_policy" "lambda_additional_policies" {
3448
- count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
3449
- name = "CustomApiHandler-additional-policies-\${random_string.suffix.result}"
3450
- role = aws_iam_role.lambda_execution_role.id
5376
+ for_each = local.operations
5377
+
5378
+ name = "\${substr(local.function_name[each.key], 0, 55)}-policies"
5379
+ role = aws_iam_role.lambda_execution_role[each.key].id
3451
5380
 
3452
5381
  policy = jsonencode({
3453
5382
  Version = "2012-10-17"
3454
- Statement = var.additional_iam_policy_statements
5383
+ Statement = local.lambda_policy_document_statements
3455
5384
  })
3456
5385
  }
3457
5386
 
5387
+ locals {
5388
+ # Grant read access to the runtime-config AppConfig application when wired
5389
+ lambda_policy_statements = concat(var.appconfig_application_arn != null ? [
5390
+ {
5391
+ Effect = "Allow"
5392
+ Action = [
5393
+ "appconfig:StartConfigurationSession",
5394
+ "appconfig:GetLatestConfiguration"
5395
+ ]
5396
+ Resource = ["\${var.appconfig_application_arn}/*"]
5397
+ }
5398
+ ] : [], var.additional_iam_policy_statements)
5399
+
5400
+ # IAM rejects an empty statement list, so fall back to a no-op deny
5401
+ lambda_policy_document_statements = length(local.lambda_policy_statements) > 0 ? local.lambda_policy_statements : [
5402
+ {
5403
+ Effect = "Deny"
5404
+ Action = ["appconfig:GetLatestConfiguration"]
5405
+ Resource = ["arn:aws:appconfig:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:application/none"]
5406
+ }
5407
+ ]
5408
+ }
5409
+
3458
5410
  # CloudWatch Log Group for Lambda
3459
5411
  resource "aws_cloudwatch_log_group" "lambda_logs" {
3460
5412
  #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
3461
5413
  #checkov:skip=CKV_AWS_338:Log retention set to forever
3462
5414
  #checkov:skip=CKV_AWS_66:Log retention set to forever
3463
- name = "/aws/lambda/CustomApiHandler-\${random_string.suffix.result}"
3464
- tags = var.tags
5415
+ for_each = local.operations
5416
+
5417
+ name = "/aws/lambda/\${local.function_name[each.key]}"
5418
+ tags = var.tags
3465
5419
  }
3466
5420
 
3467
5421
 
@@ -3478,7 +5432,7 @@ resource "aws_lambda_function" "authorizer_lambda" {
3478
5432
  function_name = "CustomApiAuthorizer-\${random_string.suffix.result}"
3479
5433
  role = aws_iam_role.authorizer_execution_role.arn
3480
5434
  handler = "index.handler"
3481
- runtime = "nodejs22.x"
5435
+ runtime = "nodejs24.x"
3482
5436
  timeout = 10
3483
5437
  memory_size = 128
3484
5438
 
@@ -3558,58 +5512,62 @@ resource "aws_lambda_permission" "authorizer_invoke" {
3558
5512
  source_arn = "\${module.rest_api.api_execution_arn}/authorizers/\${aws_api_gateway_authorizer.custom_authorizer.id}"
3559
5513
  }
3560
5514
 
3561
- # Create proxy resource (captures all paths)
3562
- resource "aws_api_gateway_resource" "proxy_resource" {
3563
- rest_api_id = module.rest_api.api_id
3564
- parent_id = module.rest_api.api_root_resource_id
3565
- path_part = "{proxy+}"
3566
- }
3567
-
3568
- # Lambda integration for REST API
3569
- resource "aws_api_gateway_integration" "lambda_integration" {
3570
- rest_api_id = module.rest_api.api_id
3571
- resource_id = aws_api_gateway_resource.proxy_resource.id
3572
- http_method = aws_api_gateway_method.proxy_method.http_method
3573
-
3574
- integration_http_method = "POST"
3575
- type = "AWS_PROXY"
3576
- uri = aws_lambda_function.api_lambda.invoke_arn
3577
-
3578
- depends_on = [aws_lambda_function.api_lambda]
3579
- }
3580
-
3581
- # Method for proxy integration
3582
- resource "aws_api_gateway_method" "proxy_method" {
5515
+ # Method per operation
5516
+ resource "aws_api_gateway_method" "operation_methods" {
3583
5517
  #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
5518
+ for_each = local.operations
5519
+
3584
5520
  rest_api_id = module.rest_api.api_id
3585
- resource_id = aws_api_gateway_resource.proxy_resource.id
3586
- http_method = "ANY"
5521
+ resource_id = local.operation_resource_id[each.key]
5522
+ http_method = upper(each.value.method)
3587
5523
 
3588
5524
  authorization = "CUSTOM"
3589
5525
  authorizer_id = aws_api_gateway_authorizer.custom_authorizer.id
3590
5526
 
5527
+ # Path parameters must be declared on the method
3591
5528
  request_parameters = {
3592
- "method.request.path.proxy" = true
5529
+ for segment in local.operation_segments[each.key] :
5530
+ "method.request.path.\${trimsuffix(trimprefix(segment, "{"), "}")}" => true
5531
+ if startswith(segment, "{")
3593
5532
  }
3594
5533
 
3595
5534
  depends_on = [aws_api_gateway_authorizer.custom_authorizer]
3596
5535
  }
3597
5536
 
3598
- # OPTIONS method for CORS preflight
5537
+ # Lambda integration per operation
5538
+ resource "aws_api_gateway_integration" "lambda_integration" {
5539
+ for_each = local.operations
5540
+
5541
+ rest_api_id = module.rest_api.api_id
5542
+ resource_id = local.operation_resource_id[each.key]
5543
+ http_method = aws_api_gateway_method.operation_methods[each.key].http_method
5544
+
5545
+ integration_http_method = "POST"
5546
+ type = "AWS_PROXY"
5547
+ uri = aws_lambda_function.api_lambda[each.key].invoke_arn
5548
+
5549
+ depends_on = [aws_lambda_function.api_lambda]
5550
+ }
5551
+
5552
+ # OPTIONS method for CORS preflight, on every resource an operation is served from
3599
5553
  resource "aws_api_gateway_method" "options_method" {
3600
5554
  #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
3601
5555
  #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
5556
+ for_each = local.resource_ids
5557
+
3602
5558
  rest_api_id = module.rest_api.api_id
3603
- resource_id = aws_api_gateway_resource.proxy_resource.id
5559
+ resource_id = each.value
3604
5560
  http_method = "OPTIONS"
3605
5561
  authorization = "NONE"
3606
5562
  }
3607
5563
 
3608
- # CORS integration for OPTIONS method
5564
+ # CORS integration for OPTIONS methods
3609
5565
  resource "aws_api_gateway_integration" "options_integration" {
5566
+ for_each = local.resource_ids
5567
+
3610
5568
  rest_api_id = module.rest_api.api_id
3611
- resource_id = aws_api_gateway_resource.proxy_resource.id
3612
- http_method = aws_api_gateway_method.options_method.http_method
5569
+ resource_id = each.value
5570
+ http_method = aws_api_gateway_method.options_method[each.key].http_method
3613
5571
 
3614
5572
  type = "MOCK"
3615
5573
  request_templates = {
@@ -3617,11 +5575,13 @@ resource "aws_api_gateway_integration" "options_integration" {
3617
5575
  }
3618
5576
  }
3619
5577
 
3620
- # OPTIONS method response
5578
+ # OPTIONS method responses
3621
5579
  resource "aws_api_gateway_method_response" "options_response" {
5580
+ for_each = local.resource_ids
5581
+
3622
5582
  rest_api_id = module.rest_api.api_id
3623
- resource_id = aws_api_gateway_resource.proxy_resource.id
3624
- http_method = aws_api_gateway_method.options_method.http_method
5583
+ resource_id = each.value
5584
+ http_method = aws_api_gateway_method.options_method[each.key].http_method
3625
5585
  status_code = "204"
3626
5586
 
3627
5587
  response_parameters = {
@@ -3631,18 +5591,22 @@ resource "aws_api_gateway_method_response" "options_response" {
3631
5591
  }
3632
5592
  }
3633
5593
 
3634
- # OPTIONS integration response
5594
+ # OPTIONS integration responses
3635
5595
  resource "aws_api_gateway_integration_response" "options_integration_response" {
5596
+ for_each = local.resource_ids
5597
+
3636
5598
  rest_api_id = module.rest_api.api_id
3637
- resource_id = aws_api_gateway_resource.proxy_resource.id
3638
- http_method = aws_api_gateway_method.options_method.http_method
3639
- status_code = aws_api_gateway_method_response.options_response.status_code
5599
+ resource_id = each.value
5600
+ http_method = aws_api_gateway_method.options_method[each.key].http_method
5601
+ status_code = aws_api_gateway_method_response.options_response[each.key].status_code
3640
5602
 
3641
5603
  response_parameters = {
3642
5604
  "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
3643
5605
  "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
3644
5606
  "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
3645
5607
  }
5608
+
5609
+ depends_on = [aws_api_gateway_integration.options_integration]
3646
5610
  }
3647
5611
 
3648
5612
  # API Gateway deployment
@@ -3650,13 +5614,28 @@ resource "aws_api_gateway_deployment" "api_deployment" {
3650
5614
  rest_api_id = module.rest_api.api_id
3651
5615
 
3652
5616
  triggers = {
3653
- redeployment = sha1(jsonencode([
3654
- aws_api_gateway_resource.proxy_resource.id,
3655
- aws_api_gateway_method.proxy_method.id,
3656
- aws_api_gateway_integration.lambda_integration.id,
3657
- aws_api_gateway_method.options_method.id,
3658
- aws_api_gateway_integration.options_integration.id,
3659
- ]))
5617
+ redeployment = sha1(jsonencode(concat(
5618
+ [for r in aws_api_gateway_resource.path_depth_0 : r.id],
5619
+ [for r in aws_api_gateway_resource.path_depth_1 : r.id],
5620
+ [for r in aws_api_gateway_resource.path_depth_2 : r.id],
5621
+ [for r in aws_api_gateway_resource.path_depth_3 : r.id],
5622
+ [for r in aws_api_gateway_resource.path_depth_4 : r.id],
5623
+ [for r in aws_api_gateway_resource.path_depth_5 : r.id],
5624
+ [for r in aws_api_gateway_resource.path_depth_6 : r.id],
5625
+ [for r in aws_api_gateway_resource.path_depth_7 : r.id],
5626
+ [for r in aws_api_gateway_resource.path_depth_8 : r.id],
5627
+ [for r in aws_api_gateway_resource.path_depth_9 : r.id],
5628
+ [for r in aws_api_gateway_resource.path_depth_10 : r.id],
5629
+ [for r in aws_api_gateway_resource.path_depth_11 : r.id],
5630
+ [for r in aws_api_gateway_resource.path_depth_12 : r.id],
5631
+ [for r in aws_api_gateway_resource.path_depth_13 : r.id],
5632
+ [for r in aws_api_gateway_resource.path_depth_14 : r.id],
5633
+ [for r in aws_api_gateway_resource.path_depth_15 : r.id],
5634
+ [for m in aws_api_gateway_method.operation_methods : m.id],
5635
+ [for i in aws_api_gateway_integration.lambda_integration : i.id],
5636
+ [for m in aws_api_gateway_method.options_method : m.id],
5637
+ [for i in aws_api_gateway_integration.options_integration : i.id],
5638
+ )))
3660
5639
  }
3661
5640
 
3662
5641
  lifecycle {
@@ -3664,7 +5643,7 @@ resource "aws_api_gateway_deployment" "api_deployment" {
3664
5643
  }
3665
5644
 
3666
5645
  depends_on = [
3667
- aws_api_gateway_method.proxy_method,
5646
+ aws_api_gateway_method.operation_methods,
3668
5647
  aws_api_gateway_integration.lambda_integration,
3669
5648
  aws_api_gateway_method.options_method,
3670
5649
  aws_api_gateway_integration.options_integration,
@@ -3674,11 +5653,63 @@ resource "aws_api_gateway_deployment" "api_deployment" {
3674
5653
  ]
3675
5654
  }
3676
5655
 
5656
+ # KMS key for encrypting access logs at rest
5657
+ resource "aws_kms_key" "access_logs" {
5658
+ description = "CustomApi API access log encryption"
5659
+ deletion_window_in_days = 7
5660
+ enable_key_rotation = true
5661
+
5662
+ policy = jsonencode({
5663
+ Version = "2012-10-17"
5664
+ Statement = [
5665
+ {
5666
+ Sid = "Enable IAM User Permissions"
5667
+ Effect = "Allow"
5668
+ Principal = { AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root" }
5669
+ Action = "kms:*"
5670
+ Resource = "*"
5671
+ },
5672
+ {
5673
+ Sid = "Allow CloudWatch Logs"
5674
+ Effect = "Allow"
5675
+ Principal = { Service = "logs.\${data.aws_region.current.region}.amazonaws.com" }
5676
+ Action = [
5677
+ "kms:Encrypt",
5678
+ "kms:Decrypt",
5679
+ "kms:ReEncrypt*",
5680
+ "kms:GenerateDataKey*",
5681
+ "kms:DescribeKey"
5682
+ ]
5683
+ Resource = "*"
5684
+ Condition = {
5685
+ ArnEquals = {
5686
+ "kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:log-group:/aws/apigateway/custom-api-\${random_string.suffix.result}/access"
5687
+ }
5688
+ }
5689
+ }
5690
+ ]
5691
+ })
5692
+
5693
+ tags = var.tags
5694
+ }
5695
+
5696
+ resource "aws_kms_alias" "access_logs" {
5697
+ name = "alias/custom-api-\${random_string.suffix.result}-access-logs"
5698
+ target_key_id = aws_kms_key.access_logs.key_id
5699
+ }
5700
+
5701
+ # Access logs for the API Gateway stage
5702
+ resource "aws_cloudwatch_log_group" "access_logs" {
5703
+ name = "/aws/apigateway/custom-api-\${random_string.suffix.result}/access"
5704
+ retention_in_days = 365
5705
+ kms_key_id = aws_kms_key.access_logs.arn
5706
+ tags = var.tags
5707
+ }
5708
+
3677
5709
  # API Gateway stage
3678
5710
  resource "aws_api_gateway_stage" "api_stage" {
3679
5711
  #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
3680
- #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
3681
- #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
5712
+ #checkov:skip=CKV2_AWS_4:Access logging is enabled below; verbose execution logging is intentionally not enabled
3682
5713
  #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
3683
5714
  #checkov:skip=CKV2_AWS_77:WAFv2 Web ACL is attached via aws_wafv2_web_acl_association when var.enable_waf is true (default) and includes AWSManagedRulesKnownBadInputsRuleSet for Log4j protection; Checkov does not track the association across modules
3684
5715
  deployment_id = aws_api_gateway_deployment.api_deployment.id
@@ -3686,9 +5717,39 @@ resource "aws_api_gateway_stage" "api_stage" {
3686
5717
  stage_name = "prod"
3687
5718
  xray_tracing_enabled = true
3688
5719
 
5720
+ access_log_settings {
5721
+ destination_arn = aws_cloudwatch_log_group.access_logs.arn
5722
+ format = jsonencode({
5723
+ requestId = "$context.requestId"
5724
+ ip = "$context.identity.sourceIp"
5725
+ caller = "$context.identity.caller"
5726
+ user = "$context.identity.user"
5727
+ requestTime = "$context.requestTime"
5728
+ httpMethod = "$context.httpMethod"
5729
+ resourcePath = "$context.resourcePath"
5730
+ status = "$context.status"
5731
+ protocol = "$context.protocol"
5732
+ responseLength = "$context.responseLength"
5733
+ })
5734
+ }
5735
+
3689
5736
  tags = var.tags
3690
5737
 
3691
- depends_on = [aws_api_gateway_deployment.api_deployment]
5738
+ # Ensure the account-level role is configured before the stage is created
5739
+ depends_on = [aws_api_gateway_deployment.api_deployment, module.account]
5740
+ }
5741
+
5742
+ # Default throttling applied to all resource paths and methods
5743
+ resource "aws_api_gateway_method_settings" "throttling" {
5744
+ #checkov:skip=CKV_AWS_225:API Gateway caching not required for this use case
5745
+ rest_api_id = module.rest_api.api_id
5746
+ stage_name = aws_api_gateway_stage.api_stage.stage_name
5747
+ method_path = "*/*"
5748
+
5749
+ settings {
5750
+ throttling_rate_limit = 10000
5751
+ throttling_burst_limit = 5000
5752
+ }
3692
5753
  }
3693
5754
 
3694
5755
  # API Gateway Resource Policy
@@ -3711,11 +5772,13 @@ resource "aws_api_gateway_rest_api_policy" "api_policy" {
3711
5772
 
3712
5773
  # Lambda permission for API Gateway to invoke the function
3713
5774
  resource "aws_lambda_permission" "api_gateway_invoke" {
5775
+ for_each = local.operations
5776
+
3714
5777
  statement_id = "AllowExecutionFromAPIGateway"
3715
5778
  action = "lambda:InvokeFunction"
3716
- function_name = aws_lambda_function.api_lambda.function_name
5779
+ function_name = aws_lambda_function.api_lambda[each.key].function_name
3717
5780
  principal = "apigateway.amazonaws.com"
3718
- source_arn = "\${module.rest_api.api_execution_arn}/*/*"
5781
+ source_arn = "\${module.rest_api.api_execution_arn}/*/\${local.permission_source_suffix[each.key]}"
3719
5782
 
3720
5783
  depends_on = [module.rest_api, aws_lambda_function.api_lambda]
3721
5784
  }
@@ -3727,7 +5790,7 @@ module "add_url_to_runtime_config" {
3727
5790
 
3728
5791
  namespace = "connection"
3729
5792
  key = "apis"
3730
- value = { "CustomApi" = aws_api_gateway_stage.api_stage.invoke_url }
5793
+ value = { "CustomApi" = "\${aws_api_gateway_stage.api_stage.invoke_url}/" }
3731
5794
 
3732
5795
  depends_on = [aws_api_gateway_stage.api_stage]
3733
5796
  }
@@ -3780,78 +5843,83 @@ output "stage_id" {
3780
5843
  value = aws_api_gateway_stage.api_stage.id
3781
5844
  }
3782
5845
 
3783
- # Lambda Function Outputs
3784
- output "lambda_function_name" {
3785
- description = "Name of the Lambda function"
3786
- value = aws_lambda_function.api_lambda.function_name
5846
+ # Lambda Function Outputs, keyed by operation name
5847
+ output "operations" {
5848
+ description = "Names of the API operations, each with its own Lambda function"
5849
+ value = keys(local.operations)
5850
+ }
5851
+
5852
+ output "lambda_function_names" {
5853
+ description = "Name of each operation's Lambda function, keyed by operation name"
5854
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.function_name }
3787
5855
  }
3788
5856
 
3789
- output "lambda_function_arn" {
3790
- description = "ARN of the Lambda function"
3791
- value = aws_lambda_function.api_lambda.arn
5857
+ output "lambda_function_arns" {
5858
+ description = "ARN of each operation's Lambda function, keyed by operation name"
5859
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.arn }
3792
5860
  }
3793
5861
 
3794
- output "lambda_invoke_arn" {
3795
- description = "Invoke ARN of the Lambda function"
3796
- value = aws_lambda_function.api_lambda.invoke_arn
5862
+ output "lambda_invoke_arns" {
5863
+ description = "Invoke ARN of each operation's Lambda function, keyed by operation name"
5864
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.invoke_arn }
3797
5865
  }
3798
5866
 
3799
- output "lambda_qualified_arn" {
3800
- description = "Qualified ARN of the Lambda function"
3801
- value = aws_lambda_function.api_lambda.qualified_arn
5867
+ output "lambda_qualified_arns" {
5868
+ description = "Qualified ARN of each operation's Lambda function, keyed by operation name"
5869
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.qualified_arn }
3802
5870
  }
3803
5871
 
3804
- output "lambda_version" {
3805
- description = "Version of the Lambda function"
3806
- value = aws_lambda_function.api_lambda.version
5872
+ output "lambda_versions" {
5873
+ description = "Version of each operation's Lambda function, keyed by operation name"
5874
+ value = { for op, fn in aws_lambda_function.api_lambda : op => fn.version }
3807
5875
  }
3808
5876
 
3809
5877
  output "lambda_source_code_hash" {
3810
- description = "Base64-encoded SHA256 hash of the Lambda deployment package"
3811
- value = aws_lambda_function.api_lambda.source_code_hash
5878
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package, shared by every operation"
5879
+ value = data.archive_file.lambda_zip.output_base64sha256
3812
5880
  }
3813
5881
 
3814
- output "lambda_source_code_size" {
3815
- description = "Size of the Lambda deployment package in bytes"
3816
- value = aws_lambda_function.api_lambda.source_code_size
5882
+ # IAM Role Outputs, keyed by operation name
5883
+ output "lambda_execution_role_arns" {
5884
+ description = "ARN of each operation's Lambda execution role, keyed by operation name"
5885
+ value = { for op, role in aws_iam_role.lambda_execution_role : op => role.arn }
3817
5886
  }
3818
5887
 
3819
- # IAM Role Outputs
3820
- output "lambda_execution_role_arn" {
3821
- description = "ARN of the Lambda execution role"
3822
- value = aws_iam_role.lambda_execution_role.arn
5888
+ output "lambda_execution_role_names" {
5889
+ description = "Name of each operation's Lambda execution role, keyed by operation name. Attach additional policies to a specific operation's role by name."
5890
+ value = { for op, role in aws_iam_role.lambda_execution_role : op => role.name }
3823
5891
  }
3824
5892
 
3825
- output "lambda_execution_role_name" {
3826
- description = "Name of the Lambda execution role"
3827
- value = aws_iam_role.lambda_execution_role.name
5893
+ output "security_group_id" {
5894
+ description = "Security group ID shared by the API Lambda functions, for use in ingress rules on resources they must reach (e.g. a database). Null unless enable_vpc is true."
5895
+ value = var.enable_vpc ? aws_security_group.api_lambda[0].id : null
3828
5896
  }
3829
5897
 
3830
- # Integration Outputs
3831
- output "integration_id" {
3832
- description = "ID of the Lambda integration"
3833
- value = aws_api_gateway_integration.lambda_integration.id
5898
+ # Integration Outputs, keyed by operation name
5899
+ output "integration_ids" {
5900
+ description = "ID of each operation's Lambda integration, keyed by operation name"
5901
+ value = { for op, i in aws_api_gateway_integration.lambda_integration : op => i.id }
3834
5902
  }
3835
5903
 
3836
- output "proxy_resource_id" {
3837
- description = "ID of the proxy resource"
3838
- value = aws_api_gateway_resource.proxy_resource.id
5904
+ output "method_ids" {
5905
+ description = "ID of each operation's API Gateway method, keyed by operation name"
5906
+ value = { for op, m in aws_api_gateway_method.operation_methods : op => m.id }
3839
5907
  }
3840
5908
 
3841
- output "proxy_method_id" {
3842
- description = "ID of the proxy method"
3843
- value = aws_api_gateway_method.proxy_method.id
5909
+ output "resource_ids" {
5910
+ description = "ID of each API Gateway resource, keyed by its path"
5911
+ value = local.resource_ids
3844
5912
  }
3845
5913
 
3846
- # CloudWatch Log Groups
3847
- output "lambda_log_group_name" {
3848
- description = "Name of the Lambda CloudWatch log group"
3849
- value = aws_cloudwatch_log_group.lambda_logs.name
5914
+ # CloudWatch Log Groups, keyed by operation name
5915
+ output "lambda_log_group_names" {
5916
+ description = "Name of each operation's Lambda CloudWatch log group, keyed by operation name"
5917
+ value = { for op, lg in aws_cloudwatch_log_group.lambda_logs : op => lg.name }
3850
5918
  }
3851
5919
 
3852
- output "lambda_log_group_arn" {
3853
- description = "ARN of the Lambda CloudWatch log group"
3854
- value = aws_cloudwatch_log_group.lambda_logs.arn
5920
+ output "lambda_log_group_arns" {
5921
+ description = "ARN of each operation's Lambda CloudWatch log group, keyed by operation name"
5922
+ value = { for op, lg in aws_cloudwatch_log_group.lambda_logs : op => lg.arn }
3855
5923
  }
3856
5924
  "
3857
5925
  `;