@aws/nx-plugin 1.0.0-rc.3 → 1.0.0-rc.30

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 (996) hide show
  1. package/LICENSE-THIRD-PARTY +7246 -5532
  2. package/README.md +8 -8
  3. package/executors.json +9 -0
  4. package/generators.json +100 -1
  5. package/package.json +35 -16
  6. package/src/agentcore-gateway/attach-upstream.d.ts +33 -0
  7. package/src/agentcore-gateway/attach-upstream.js +38 -0
  8. package/src/agentcore-gateway/attach-upstream.js.map +1 -0
  9. package/src/agentcore-gateway/files/cedar/policies/README.md +52 -0
  10. package/src/agentcore-gateway/files/cedar/policies/permit-all.cedar +11 -0
  11. package/src/agentcore-gateway/files/project/local-dev.ts.template +134 -0
  12. package/src/agentcore-gateway/gateway-connection/generator.d.ts +21 -0
  13. package/src/agentcore-gateway/gateway-connection/generator.js +101 -0
  14. package/src/agentcore-gateway/gateway-connection/generator.js.map +1 -0
  15. package/src/agentcore-gateway/gateway-connection/schema.d.js +6 -0
  16. package/src/agentcore-gateway/gateway-connection/schema.d.js.map +1 -0
  17. package/src/agentcore-gateway/gateway-connection/schema.d.ts +13 -0
  18. package/src/agentcore-gateway/gateway-connection/schema.json +31 -0
  19. package/src/agentcore-gateway/generator.d.ts +25 -0
  20. package/src/agentcore-gateway/generator.js +149 -0
  21. package/src/agentcore-gateway/generator.js.map +1 -0
  22. package/src/agentcore-gateway/mcp-connection/generator.d.ts +19 -0
  23. package/src/agentcore-gateway/mcp-connection/generator.js +59 -0
  24. package/src/agentcore-gateway/mcp-connection/generator.js.map +1 -0
  25. package/src/agentcore-gateway/mcp-connection/schema.d.js +6 -0
  26. package/src/agentcore-gateway/mcp-connection/schema.d.js.map +1 -0
  27. package/src/agentcore-gateway/mcp-connection/schema.d.ts +13 -0
  28. package/src/agentcore-gateway/mcp-connection/schema.json +31 -0
  29. package/src/agentcore-gateway/schema.d.js +6 -0
  30. package/src/agentcore-gateway/schema.d.js.map +1 -0
  31. package/src/agentcore-gateway/schema.d.ts +17 -0
  32. package/src/agentcore-gateway/schema.json +70 -0
  33. package/src/bin/aws-nx-mcp.js +14 -0
  34. package/src/bin/aws-nx-mcp.js.map +1 -0
  35. package/src/connection/{agent-serve-local.d.ts → agent-local-dev.d.ts} +7 -7
  36. package/src/connection/agent-local-dev.js +42 -0
  37. package/src/connection/agent-local-dev.js.map +1 -0
  38. package/src/connection/agent-runtime-config.d.ts +1 -1
  39. package/src/connection/agent-runtime-config.js +9 -14
  40. package/src/connection/agent-runtime-config.js.map +1 -1
  41. package/src/connection/generator.d.ts +7 -14
  42. package/src/connection/generator.js +158 -177
  43. package/src/connection/generator.js.map +1 -1
  44. package/src/connection/local-dev.d.ts +15 -0
  45. package/src/connection/local-dev.js +37 -0
  46. package/src/connection/local-dev.js.map +1 -0
  47. package/src/connection/schema.d.js +6 -0
  48. package/src/connection/schema.d.js.map +1 -0
  49. package/src/connection/schema.d.ts +1 -0
  50. package/src/connection/schema.json +5 -0
  51. package/src/connection/supported-connections.d.ts +98 -0
  52. package/src/connection/supported-connections.js +138 -0
  53. package/src/connection/supported-connections.js.map +1 -0
  54. package/src/index.js +2 -5
  55. package/src/index.js.map +1 -1
  56. package/src/infra/app/__snapshots__/generator.spec.ts.snap +51 -81
  57. package/src/infra/app/generator.d.ts +3 -3
  58. package/src/infra/app/generator.js +141 -123
  59. package/src/infra/app/generator.js.map +1 -1
  60. package/src/infra/app/schema.d.js +6 -0
  61. package/src/infra/app/schema.d.js.map +1 -0
  62. package/src/infra/app/schema.d.ts +1 -1
  63. package/src/infra/app/schema.json +5 -0
  64. package/src/license/__snapshots__/config.spec.ts.snap +23 -0
  65. package/src/license/config-types.d.ts +18 -1
  66. package/src/license/config-types.js +8 -2
  67. package/src/license/config-types.js.map +1 -1
  68. package/src/license/config.d.ts +65 -7
  69. package/src/license/config.js +442 -109
  70. package/src/license/config.js.map +1 -1
  71. package/src/license/dependency-check/check.d.ts +16 -0
  72. package/src/license/dependency-check/check.js +116 -0
  73. package/src/license/dependency-check/check.js.map +1 -0
  74. package/src/license/dependency-check/collectors/collector.d.ts +21 -0
  75. package/src/license/dependency-check/collectors/collector.js +46 -0
  76. package/src/license/dependency-check/collectors/collector.js.map +1 -0
  77. package/src/license/dependency-check/collectors/npm-collector.d.ts +10 -0
  78. package/src/license/dependency-check/collectors/npm-collector.js +68 -0
  79. package/src/license/dependency-check/collectors/npm-collector.js.map +1 -0
  80. package/src/license/dependency-check/collectors/python-collector.d.ts +23 -0
  81. package/src/license/dependency-check/collectors/python-collector.js +114 -0
  82. package/src/license/dependency-check/collectors/python-collector.js.map +1 -0
  83. package/src/license/dependency-check/evaluator.d.ts +9 -0
  84. package/src/license/dependency-check/evaluator.js +94 -0
  85. package/src/license/dependency-check/evaluator.js.map +1 -0
  86. package/src/license/dependency-check/executor-schema.json +6 -0
  87. package/src/license/dependency-check/executor.d.ts +9 -0
  88. package/src/license/dependency-check/executor.js +51 -0
  89. package/src/license/dependency-check/executor.js.map +1 -0
  90. package/src/license/dependency-check/pre-approved.d.ts +6 -0
  91. package/src/license/dependency-check/pre-approved.js +1893 -0
  92. package/src/license/dependency-check/pre-approved.js.map +1 -0
  93. package/src/license/dependency-check/types.d.ts +45 -0
  94. package/src/license/dependency-check/types.js +6 -0
  95. package/src/license/dependency-check/types.js.map +1 -0
  96. package/src/license/generator.d.ts +3 -3
  97. package/src/license/generator.js +127 -30
  98. package/src/license/generator.js.map +1 -1
  99. package/src/license/index.d.ts +8 -0
  100. package/src/license/index.js +7 -0
  101. package/src/license/index.js.map +1 -0
  102. package/src/license/known-exceptions.d.ts +7 -0
  103. package/src/license/known-exceptions.js +39 -0
  104. package/src/license/known-exceptions.js.map +1 -0
  105. package/src/license/schema.d.js +6 -0
  106. package/src/license/schema.d.js.map +1 -0
  107. package/src/license/schema.d.ts +7 -0
  108. package/src/license/schema.json +11 -0
  109. package/src/license/sync/generator.d.ts +2 -2
  110. package/src/license/sync/generator.js +100 -125
  111. package/src/license/sync/generator.js.map +1 -1
  112. package/src/license/sync/project-file-sync.d.ts +3 -3
  113. package/src/license/sync/project-file-sync.js +28 -35
  114. package/src/license/sync/project-file-sync.js.map +1 -1
  115. package/src/mcp-server/generator-info.d.ts +5 -1
  116. package/src/mcp-server/generator-info.js +130 -160
  117. package/src/mcp-server/generator-info.js.map +1 -1
  118. package/src/mcp-server/guide-pipeline.d.ts +1 -1
  119. package/src/mcp-server/guide-pipeline.js +184 -206
  120. package/src/mcp-server/guide-pipeline.js.map +1 -1
  121. package/src/mcp-server/guide-render.js +10 -25
  122. package/src/mcp-server/guide-render.js.map +1 -1
  123. package/src/mcp-server/index.js +6 -10
  124. package/src/mcp-server/index.js.map +1 -1
  125. package/src/mcp-server/mdx-ast.js +42 -56
  126. package/src/mcp-server/mdx-ast.js.map +1 -1
  127. package/src/mcp-server/option-filter.js +33 -47
  128. package/src/mcp-server/option-filter.js.map +1 -1
  129. package/src/mcp-server/schema-registry.d.ts +9 -3
  130. package/src/mcp-server/schema-registry.js +41 -38
  131. package/src/mcp-server/schema-registry.js.map +1 -1
  132. package/src/mcp-server/schema.js +9 -7
  133. package/src/mcp-server/schema.js.map +1 -1
  134. package/src/mcp-server/server.js +20 -24
  135. package/src/mcp-server/server.js.map +1 -1
  136. package/src/mcp-server/tools/create-workspace-command.d.ts +1 -1
  137. package/src/mcp-server/tools/create-workspace-command.js +23 -24
  138. package/src/mcp-server/tools/create-workspace-command.js.map +1 -1
  139. package/src/mcp-server/tools/general-guidance.d.ts +2 -2
  140. package/src/mcp-server/tools/general-guidance.js +46 -25
  141. package/src/mcp-server/tools/general-guidance.js.map +1 -1
  142. package/src/mcp-server/tools/generator-guide.d.ts +2 -2
  143. package/src/mcp-server/tools/generator-guide.js +29 -37
  144. package/src/mcp-server/tools/generator-guide.js.map +1 -1
  145. package/src/mcp-server/tools/list-generators.d.ts +2 -2
  146. package/src/mcp-server/tools/list-generators.js +19 -25
  147. package/src/mcp-server/tools/list-generators.js.map +1 -1
  148. package/src/open-api/ts-client/__snapshots__/generator.reserved-keywords.spec.ts.snap +1 -1
  149. package/src/open-api/ts-client/generator.d.ts +3 -3
  150. package/src/open-api/ts-client/generator.js +16 -26
  151. package/src/open-api/ts-client/generator.js.map +1 -1
  152. package/src/open-api/ts-client/schema.d.js +6 -0
  153. package/src/open-api/ts-client/schema.d.js.map +1 -0
  154. package/src/open-api/ts-hooks/generator.d.ts +3 -3
  155. package/src/open-api/ts-hooks/generator.js +13 -21
  156. package/src/open-api/ts-hooks/generator.js.map +1 -1
  157. package/src/open-api/ts-hooks/generator.spec.tsx +14 -14
  158. package/src/open-api/ts-hooks/schema.d.js +6 -0
  159. package/src/open-api/ts-hooks/schema.d.js.map +1 -0
  160. package/src/open-api/ts-metadata/generator.d.ts +3 -3
  161. package/src/open-api/ts-metadata/generator.js +12 -20
  162. package/src/open-api/ts-metadata/generator.js.map +1 -1
  163. package/src/open-api/ts-metadata/schema.d.js +6 -0
  164. package/src/open-api/ts-metadata/schema.d.js.map +1 -0
  165. package/src/open-api/utils/codegen-data/languages.d.ts +5 -1
  166. package/src/open-api/utils/codegen-data/languages.js +42 -60
  167. package/src/open-api/utils/codegen-data/languages.js.map +1 -1
  168. package/src/open-api/utils/codegen-data/types.js +31 -30
  169. package/src/open-api/utils/codegen-data/types.js.map +1 -1
  170. package/src/open-api/utils/codegen-data.d.ts +6 -2
  171. package/src/open-api/utils/codegen-data.js +253 -269
  172. package/src/open-api/utils/codegen-data.js.map +1 -1
  173. package/src/open-api/utils/normalise.d.ts +5 -1
  174. package/src/open-api/utils/normalise.js +242 -241
  175. package/src/open-api/utils/normalise.js.map +1 -1
  176. package/src/open-api/utils/parse.d.ts +2 -2
  177. package/src/open-api/utils/parse.js +11 -13
  178. package/src/open-api/utils/parse.js.map +1 -1
  179. package/src/open-api/utils/refs.d.ts +1 -1
  180. package/src/open-api/utils/refs.js +12 -22
  181. package/src/open-api/utils/refs.js.map +1 -1
  182. package/src/open-api/utils/types.js +5 -2
  183. package/src/open-api/utils/types.js.map +1 -1
  184. package/src/preset/__snapshots__/generator.spec.ts.snap +160 -17
  185. package/src/preset/generator.d.ts +4 -4
  186. package/src/preset/generator.js +140 -112
  187. package/src/preset/generator.js.map +1 -1
  188. package/src/preset/schema.d.js +6 -0
  189. package/src/preset/schema.d.js.map +1 -0
  190. package/src/preset/schema.d.ts +4 -2
  191. package/src/preset/schema.json +10 -5
  192. package/src/py/agent/__snapshots__/generator.constructs.spec.ts.snap +304 -0
  193. package/src/py/agent/__snapshots__/generator.frameworks.spec.ts.snap +734 -0
  194. package/src/py/agent/__snapshots__/{generator.spec.ts.snap → generator.protocols.spec.ts.snap} +80 -314
  195. package/src/py/agent/a2a-connection/__snapshots__/generator.spec.ts.snap +189 -0
  196. package/src/py/agent/a2a-connection/files/agent-connection/app/__targetAgentSnakeCase___client_strands.py.template +30 -0
  197. package/src/py/agent/a2a-connection/files/langchain/agent-connection/app/__targetAgentSnakeCase___client_langchain.py.template +28 -0
  198. package/src/py/agent/a2a-connection/generator.d.ts +3 -3
  199. package/src/py/agent/a2a-connection/generator.js +113 -85
  200. package/src/py/agent/a2a-connection/generator.js.map +1 -1
  201. package/src/py/agent/a2a-connection/schema.d.js +8 -0
  202. package/src/py/agent/a2a-connection/schema.d.js.map +1 -0
  203. package/src/py/agent/a2a-connection/schema.d.ts +1 -0
  204. package/src/py/agent/a2a-connection/schema.json +5 -0
  205. package/src/py/agent/files/a2a-langchain/main.py.template +89 -0
  206. package/src/py/agent/files/ag-ui-langchain/main.py.template +83 -0
  207. package/src/py/agent/files/common/agent.py.template +2 -0
  208. package/src/py/agent/files/common-langchain/agent.py.template +30 -0
  209. package/src/py/agent/files/http/init.py.template +7 -25
  210. package/src/py/agent/files/http/main.py.template +2 -3
  211. package/src/py/agent/files/http-langchain/main.py.template +84 -0
  212. package/src/py/agent/gateway-connection/__snapshots__/generator.spec.ts.snap +184 -0
  213. package/src/py/agent/gateway-connection/files/agent-connection/app/__gatewaySnakeCase___client_strands.py.template +35 -0
  214. package/src/py/agent/gateway-connection/files/langchain/agent-connection/app/__gatewaySnakeCase___client_langchain.py.template +34 -0
  215. package/src/py/agent/gateway-connection/generator.d.ts +10 -0
  216. package/src/py/agent/gateway-connection/generator.js +119 -0
  217. package/src/py/agent/gateway-connection/generator.js.map +1 -0
  218. package/src/py/agent/gateway-connection/schema.d.js +6 -0
  219. package/src/py/agent/gateway-connection/schema.d.js.map +1 -0
  220. package/src/py/agent/gateway-connection/schema.d.ts +13 -0
  221. package/src/py/agent/gateway-connection/schema.json +31 -0
  222. package/src/py/agent/generator.d.ts +3 -3
  223. package/src/py/agent/generator.js +280 -180
  224. package/src/py/agent/generator.js.map +1 -1
  225. package/src/py/agent/mcp-connection/__snapshots__/generator.spec.ts.snap +153 -89
  226. package/src/py/agent/mcp-connection/files/agent-connection/app/__mcpServerSnakeCase___client_strands.py.template +30 -0
  227. package/src/py/agent/mcp-connection/files/langchain/agent-connection/app/__mcpServerSnakeCase___client_langchain.py.template +30 -0
  228. package/src/py/agent/mcp-connection/generator.d.ts +3 -3
  229. package/src/py/agent/mcp-connection/generator.js +65 -126
  230. package/src/py/agent/mcp-connection/generator.js.map +1 -1
  231. package/src/py/agent/mcp-connection/schema.d.js +8 -0
  232. package/src/py/agent/mcp-connection/schema.d.js.map +1 -0
  233. package/src/py/agent/mcp-connection/schema.d.ts +1 -0
  234. package/src/py/agent/mcp-connection/schema.json +5 -0
  235. package/src/py/agent/react-connection/__snapshots__/generator.spec.ts.snap +11 -19
  236. package/src/py/agent/react-connection/generator.d.ts +4 -4
  237. package/src/py/agent/react-connection/generator.js +55 -59
  238. package/src/py/agent/react-connection/generator.js.map +1 -1
  239. package/src/py/agent/react-connection/{serve-local.d.ts → local-dev.d.ts} +6 -6
  240. package/src/py/agent/react-connection/local-dev.js +36 -0
  241. package/src/py/agent/react-connection/local-dev.js.map +1 -0
  242. package/src/py/agent/react-connection/schema.d.js +8 -0
  243. package/src/py/agent/react-connection/schema.d.js.map +1 -0
  244. package/src/py/agent/react-connection/schema.d.ts +1 -0
  245. package/src/py/agent/react-connection/schema.json +5 -0
  246. package/src/py/agent/schema.d.js +6 -0
  247. package/src/py/agent/schema.d.js.map +1 -0
  248. package/src/py/agent/schema.d.ts +2 -1
  249. package/src/py/agent/schema.json +6 -1
  250. package/src/py/api/generator.d.ts +3 -3
  251. package/src/py/api/generator.js +12 -11
  252. package/src/py/api/generator.js.map +1 -1
  253. package/src/py/api/schema.d.js +6 -0
  254. package/src/py/api/schema.d.js.map +1 -0
  255. package/src/py/api/schema.d.ts +1 -0
  256. package/src/py/api/schema.json +5 -0
  257. package/src/py/dynamodb/__snapshots__/generator.spec.ts.snap +1526 -0
  258. package/src/py/dynamodb/agent-connection/__snapshots__/generator.spec.ts.snap +45 -0
  259. package/src/py/dynamodb/agent-connection/generator.d.ts +10 -0
  260. package/src/py/dynamodb/agent-connection/generator.js +39 -0
  261. package/src/py/dynamodb/agent-connection/generator.js.map +1 -0
  262. package/src/py/dynamodb/agent-connection/schema.d.js +8 -0
  263. package/src/py/dynamodb/agent-connection/schema.d.js.map +1 -0
  264. package/src/py/dynamodb/agent-connection/schema.d.ts +15 -0
  265. package/src/py/dynamodb/agent-connection/schema.json +27 -0
  266. package/src/py/dynamodb/fast-api-connection/__snapshots__/generator.spec.ts.snap +23 -0
  267. package/src/py/dynamodb/fast-api-connection/generator.d.ts +10 -0
  268. package/src/py/dynamodb/fast-api-connection/generator.js +37 -0
  269. package/src/py/dynamodb/fast-api-connection/generator.js.map +1 -0
  270. package/src/py/dynamodb/fast-api-connection/schema.d.js +8 -0
  271. package/src/py/dynamodb/fast-api-connection/schema.d.js.map +1 -0
  272. package/src/py/dynamodb/fast-api-connection/schema.d.ts +13 -0
  273. package/src/py/dynamodb/fast-api-connection/schema.json +23 -0
  274. package/src/py/dynamodb/files/__name__/__init__.py.template +6 -0
  275. package/src/py/dynamodb/files/__name__/client.py.template +39 -0
  276. package/src/py/dynamodb/files/__name__/entities/__init__.py.template +4 -0
  277. package/src/py/dynamodb/files/__name__/entities/base.py.template +63 -0
  278. package/src/py/dynamodb/files/__name__/entities/example.py.template +64 -0
  279. package/src/py/dynamodb/files/config.json.template +24 -0
  280. package/src/py/dynamodb/generator.d.ts +10 -0
  281. package/src/py/dynamodb/generator.js +110 -0
  282. package/src/py/dynamodb/generator.js.map +1 -0
  283. package/src/py/dynamodb/mcp-server-connection/__snapshots__/generator.spec.ts.snap +45 -0
  284. package/src/py/dynamodb/mcp-server-connection/generator.d.ts +10 -0
  285. package/src/py/dynamodb/mcp-server-connection/generator.js +39 -0
  286. package/src/py/dynamodb/mcp-server-connection/generator.js.map +1 -0
  287. package/src/py/dynamodb/mcp-server-connection/schema.d.js +8 -0
  288. package/src/py/dynamodb/mcp-server-connection/schema.d.js.map +1 -0
  289. package/src/py/dynamodb/mcp-server-connection/schema.d.ts +15 -0
  290. package/src/py/dynamodb/mcp-server-connection/schema.json +27 -0
  291. package/src/py/dynamodb/schema.d.js +6 -0
  292. package/src/py/dynamodb/schema.d.js.map +1 -0
  293. package/src/py/dynamodb/schema.d.ts +16 -0
  294. package/src/py/dynamodb/schema.json +75 -0
  295. package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +212 -4678
  296. package/src/py/fast-api/__snapshots__/generator.terraform.spec.ts.snap +4833 -0
  297. package/src/py/fast-api/files/app/__name__/init.py.template +5 -22
  298. package/src/py/fast-api/files/app/__name__/main.py.template +4 -1
  299. package/src/py/fast-api/generator.d.ts +3 -3
  300. package/src/py/fast-api/generator.js +90 -87
  301. package/src/py/fast-api/generator.js.map +1 -1
  302. package/src/py/fast-api/react/__snapshots__/generator.spec.ts.snap +4 -3
  303. package/src/py/fast-api/react/generator.d.ts +4 -4
  304. package/src/py/fast-api/react/generator.js +25 -25
  305. package/src/py/fast-api/react/generator.js.map +1 -1
  306. package/src/py/fast-api/react/open-api.d.ts +1 -1
  307. package/src/py/fast-api/react/open-api.js +31 -29
  308. package/src/py/fast-api/react/open-api.js.map +1 -1
  309. package/src/py/fast-api/react/schema.d.js +6 -0
  310. package/src/py/fast-api/react/schema.d.js.map +1 -0
  311. package/src/py/fast-api/react/schema.d.ts +1 -0
  312. package/src/py/fast-api/react/schema.json +5 -0
  313. package/src/py/fast-api/schema.d.js +6 -0
  314. package/src/py/fast-api/schema.d.js.map +1 -0
  315. package/src/py/fast-api/schema.d.ts +1 -0
  316. package/src/py/fast-api/schema.json +5 -0
  317. package/src/py/lambda-function/__snapshots__/generator.spec.ts.snap +4 -4
  318. package/src/py/lambda-function/generator.d.ts +3 -3
  319. package/src/py/lambda-function/generator.js +60 -59
  320. package/src/py/lambda-function/generator.js.map +1 -1
  321. package/src/py/lambda-function/schema.d.js +6 -0
  322. package/src/py/lambda-function/schema.d.js.map +1 -0
  323. package/src/py/lambda-function/schema.d.ts +1 -0
  324. package/src/py/lambda-function/schema.json +5 -0
  325. package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +87 -17
  326. package/src/py/mcp-server/generator.d.ts +3 -3
  327. package/src/py/mcp-server/generator.js +152 -125
  328. package/src/py/mcp-server/generator.js.map +1 -1
  329. package/src/py/mcp-server/schema.d.js +6 -0
  330. package/src/py/mcp-server/schema.d.js.map +1 -0
  331. package/src/py/mcp-server/schema.d.ts +1 -0
  332. package/src/py/mcp-server/schema.json +5 -0
  333. package/src/py/project/generator.d.ts +17 -4
  334. package/src/py/project/generator.js +205 -127
  335. package/src/py/project/generator.js.map +1 -1
  336. package/src/py/project/schema.d.js +6 -0
  337. package/src/py/project/schema.d.js.map +1 -0
  338. package/src/py/project/schema.d.ts +1 -0
  339. package/src/py/project/schema.json +5 -0
  340. package/src/sdk/agentcore-gateway.d.ts +6 -0
  341. package/src/sdk/agentcore-gateway.js +7 -0
  342. package/src/sdk/agentcore-gateway.js.map +1 -0
  343. package/src/sdk/connection.d.ts +6 -0
  344. package/src/sdk/connection.js +6 -0
  345. package/src/sdk/connection.js.map +1 -0
  346. package/src/sdk/license.d.ts +8 -0
  347. package/src/sdk/license.js +7 -0
  348. package/src/sdk/license.js.map +1 -0
  349. package/src/sdk/open-api.d.ts +10 -0
  350. package/src/sdk/open-api.js +8 -0
  351. package/src/sdk/open-api.js.map +1 -0
  352. package/src/sdk/py.d.ts +14 -0
  353. package/src/sdk/py.js +10 -0
  354. package/src/sdk/py.js.map +1 -0
  355. package/src/sdk/terraform.d.ts +6 -0
  356. package/src/sdk/terraform.js +7 -0
  357. package/src/sdk/terraform.js.map +1 -0
  358. package/src/sdk/ts.d.ts +33 -0
  359. package/src/sdk/ts.js +35 -0
  360. package/src/sdk/ts.js.map +1 -0
  361. package/{sdk → src/sdk}/utils/ast.d.ts +1 -2
  362. package/src/sdk/utils/ast.js +6 -0
  363. package/src/sdk/utils/ast.js.map +1 -0
  364. package/{sdk → src/sdk}/utils/format.d.ts +1 -1
  365. package/src/sdk/utils/format.js +6 -0
  366. package/src/sdk/utils/format.js.map +1 -0
  367. package/{sdk → src/sdk}/utils/test.d.ts +1 -1
  368. package/src/sdk/utils/test.js +6 -0
  369. package/src/sdk/utils/test.js.map +1 -0
  370. package/src/smithy/project/__snapshots__/generator.spec.ts.snap +19 -24
  371. package/src/smithy/project/files/build.Dockerfile.template +1 -4
  372. package/src/smithy/project/files/src/operations/echo.smithy.template +1 -0
  373. package/src/smithy/project/generator.d.ts +3 -3
  374. package/src/smithy/project/generator.js +64 -57
  375. package/src/smithy/project/generator.js.map +1 -1
  376. package/src/smithy/project/schema.d.js +6 -0
  377. package/src/smithy/project/schema.d.js.map +1 -0
  378. package/src/smithy/project/schema.d.ts +1 -0
  379. package/src/smithy/project/schema.json +5 -0
  380. package/src/smithy/react-connection/__snapshots__/generator.spec.ts.snap +1 -1
  381. package/src/smithy/react-connection/generator.d.ts +3 -3
  382. package/src/smithy/react-connection/generator.js +39 -47
  383. package/src/smithy/react-connection/generator.js.map +1 -1
  384. package/src/smithy/react-connection/schema.d.js +6 -0
  385. package/src/smithy/react-connection/schema.d.js.map +1 -0
  386. package/src/smithy/react-connection/schema.d.ts +1 -0
  387. package/src/smithy/react-connection/schema.json +5 -0
  388. package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +409 -106
  389. package/src/smithy/ts/api/generator.d.ts +3 -3
  390. package/src/smithy/ts/api/generator.js +127 -106
  391. package/src/smithy/ts/api/generator.js.map +1 -1
  392. package/src/smithy/ts/api/schema.d.js +6 -0
  393. package/src/smithy/ts/api/schema.d.js.map +1 -0
  394. package/src/smithy/ts/api/schema.d.ts +1 -0
  395. package/src/smithy/ts/api/schema.json +5 -0
  396. package/src/terraform/project/files/application/src/main.tf.template +6 -1
  397. package/src/terraform/project/files/application/src/providers.tf.template +1 -1
  398. package/src/terraform/project/generator.d.ts +3 -3
  399. package/src/terraform/project/generator.js +156 -104
  400. package/src/terraform/project/generator.js.map +1 -1
  401. package/src/terraform/project/schema.d.js +9 -0
  402. package/src/terraform/project/schema.d.js.map +1 -0
  403. package/src/terraform/project/schema.d.ts +1 -0
  404. package/src/terraform/project/schema.json +5 -0
  405. package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +499 -110
  406. package/src/trpc/backend/files/src/procedures/echo.ts.template +1 -1
  407. package/src/trpc/backend/files/src/schema/echo.ts.template +2 -2
  408. package/src/trpc/backend/generator.d.ts +4 -4
  409. package/src/trpc/backend/generator.js +97 -85
  410. package/src/trpc/backend/generator.js.map +1 -1
  411. package/src/trpc/backend/schema.d.js +6 -0
  412. package/src/trpc/backend/schema.d.js.map +1 -0
  413. package/src/trpc/backend/schema.d.ts +3 -2
  414. package/src/trpc/backend/schema.json +5 -0
  415. package/src/trpc/react/__snapshots__/generator.spec.ts.snap +4 -4
  416. package/src/trpc/react/files/src/components/__apiNameClassName__ClientProvider.tsx.template +3 -3
  417. package/src/trpc/react/generator.d.ts +4 -4
  418. package/src/trpc/react/generator.js +63 -58
  419. package/src/trpc/react/generator.js.map +1 -1
  420. package/src/trpc/react/schema.d.js +6 -0
  421. package/src/trpc/react/schema.d.js.map +1 -0
  422. package/src/trpc/react/schema.d.ts +1 -0
  423. package/src/trpc/react/schema.json +5 -0
  424. package/src/ts/agent/__snapshots__/generator.spec.ts.snap +656 -18
  425. package/src/ts/agent/a2a-connection/__snapshots__/generator.spec.ts.snap +256 -0
  426. package/src/ts/agent/a2a-connection/files/agent-connection/app/__targetAgentKebabCase__-client-strands.ts.template +23 -0
  427. package/src/ts/agent/a2a-connection/generator.d.ts +3 -3
  428. package/src/ts/agent/a2a-connection/generator.js +63 -56
  429. package/src/ts/agent/a2a-connection/generator.js.map +1 -1
  430. package/src/ts/agent/a2a-connection/schema.d.js +8 -0
  431. package/src/ts/agent/a2a-connection/schema.d.js.map +1 -0
  432. package/src/ts/agent/a2a-connection/schema.d.ts +1 -0
  433. package/src/ts/agent/a2a-connection/schema.json +5 -0
  434. package/src/ts/agent/files/common/agent.ts.template +6 -2
  435. package/src/ts/agent/files/http/router.ts.template +1 -1
  436. package/src/ts/agent/gateway-connection/__snapshots__/generator.spec.ts.snap +220 -0
  437. package/src/ts/agent/gateway-connection/files/agent-connection/app/__gatewayKebabCase__-client-strands.ts.template +30 -0
  438. package/src/ts/agent/gateway-connection/generator.d.ts +10 -0
  439. package/src/ts/agent/gateway-connection/generator.js +114 -0
  440. package/src/ts/agent/gateway-connection/generator.js.map +1 -0
  441. package/src/ts/agent/gateway-connection/schema.d.js +6 -0
  442. package/src/ts/agent/gateway-connection/schema.d.js.map +1 -0
  443. package/src/ts/agent/gateway-connection/schema.d.ts +13 -0
  444. package/src/ts/agent/gateway-connection/schema.json +31 -0
  445. package/src/ts/agent/generator.d.ts +3 -3
  446. package/src/ts/agent/generator.js +188 -144
  447. package/src/ts/agent/generator.js.map +1 -1
  448. package/src/ts/agent/mcp-connection/__snapshots__/generator.spec.ts.snap +192 -111
  449. package/src/ts/agent/mcp-connection/files/agent-connection/app/__mcpServerKebabCase__-client-strands.ts.template +22 -0
  450. package/src/ts/agent/mcp-connection/generator.d.ts +3 -3
  451. package/src/ts/agent/mcp-connection/generator.js +53 -60
  452. package/src/ts/agent/mcp-connection/generator.js.map +1 -1
  453. package/src/ts/agent/mcp-connection/schema.d.js +8 -0
  454. package/src/ts/agent/mcp-connection/schema.d.js.map +1 -0
  455. package/src/ts/agent/mcp-connection/schema.d.ts +1 -0
  456. package/src/ts/agent/mcp-connection/schema.json +5 -0
  457. package/src/ts/agent/react-connection/generator.d.ts +4 -4
  458. package/src/ts/agent/react-connection/generator.js +86 -80
  459. package/src/ts/agent/react-connection/generator.js.map +1 -1
  460. package/src/ts/agent/react-connection/local-dev.d.ts +13 -0
  461. package/src/ts/agent/react-connection/local-dev.js +17 -0
  462. package/src/ts/agent/react-connection/local-dev.js.map +1 -0
  463. package/src/ts/agent/react-connection/schema.d.js +8 -0
  464. package/src/ts/agent/react-connection/schema.d.js.map +1 -0
  465. package/src/ts/agent/react-connection/schema.d.ts +1 -0
  466. package/src/ts/agent/react-connection/schema.json +5 -0
  467. package/src/ts/agent/schema.d.js +6 -0
  468. package/src/ts/agent/schema.d.js.map +1 -0
  469. package/src/ts/agent/schema.d.ts +1 -0
  470. package/src/ts/agent/schema.json +5 -0
  471. package/src/ts/api/generator.d.ts +3 -3
  472. package/src/ts/api/generator.js +16 -16
  473. package/src/ts/api/generator.js.map +1 -1
  474. package/src/ts/api/schema.d.js +6 -0
  475. package/src/ts/api/schema.d.js.map +1 -0
  476. package/src/ts/api/schema.d.ts +3 -1
  477. package/src/ts/api/schema.json +5 -0
  478. package/src/ts/astro-docs/__snapshots__/generator.spec.ts.snap +15 -16
  479. package/src/ts/astro-docs/generator.d.ts +3 -3
  480. package/src/ts/astro-docs/generator.js +69 -54
  481. package/src/ts/astro-docs/generator.js.map +1 -1
  482. package/src/ts/astro-docs/schema.d.js +6 -0
  483. package/src/ts/astro-docs/schema.d.js.map +1 -0
  484. package/src/ts/astro-docs/schema.d.ts +2 -2
  485. package/src/ts/astro-docs/schema.json +3 -3
  486. package/src/ts/docs/generator.d.ts +3 -3
  487. package/src/ts/docs/generator.js +10 -10
  488. package/src/ts/docs/generator.js.map +1 -1
  489. package/src/ts/docs/schema.d.js +6 -0
  490. package/src/ts/docs/schema.d.js.map +1 -0
  491. package/src/ts/docs/schema.d.ts +1 -4
  492. package/src/ts/docs/schema.json +3 -3
  493. package/src/ts/dynamodb/__snapshots__/generator.spec.ts.snap +1390 -0
  494. package/src/ts/dynamodb/agent-connection/__snapshots__/generator.spec.ts.snap +45 -0
  495. package/src/ts/dynamodb/agent-connection/generator.d.ts +10 -0
  496. package/src/ts/dynamodb/agent-connection/generator.js +30 -0
  497. package/src/ts/dynamodb/agent-connection/generator.js.map +1 -0
  498. package/src/ts/dynamodb/agent-connection/schema.d.js +8 -0
  499. package/src/ts/dynamodb/agent-connection/schema.d.js.map +1 -0
  500. package/src/ts/dynamodb/agent-connection/schema.d.ts +15 -0
  501. package/src/ts/dynamodb/agent-connection/schema.json +27 -0
  502. package/src/ts/dynamodb/files/config.json.template +24 -0
  503. package/src/ts/dynamodb/files/src/client.ts.template +61 -0
  504. package/src/ts/dynamodb/files/src/entities/example.ts.template +47 -0
  505. package/src/ts/dynamodb/files/src/entities/index.ts.template +1 -0
  506. package/src/ts/dynamodb/files/src/index.ts.template +2 -0
  507. package/src/ts/dynamodb/generator.d.ts +10 -0
  508. package/src/ts/dynamodb/generator.js +116 -0
  509. package/src/ts/dynamodb/generator.js.map +1 -0
  510. package/src/ts/dynamodb/mcp-server-connection/__snapshots__/generator.spec.ts.snap +45 -0
  511. package/src/ts/dynamodb/mcp-server-connection/generator.d.ts +10 -0
  512. package/src/ts/dynamodb/mcp-server-connection/generator.js +30 -0
  513. package/src/ts/dynamodb/mcp-server-connection/generator.js.map +1 -0
  514. package/src/ts/dynamodb/mcp-server-connection/schema.d.js +8 -0
  515. package/src/ts/dynamodb/mcp-server-connection/schema.d.js.map +1 -0
  516. package/src/ts/dynamodb/mcp-server-connection/schema.d.ts +15 -0
  517. package/src/ts/dynamodb/mcp-server-connection/schema.json +27 -0
  518. package/src/ts/dynamodb/schema.d.js +6 -0
  519. package/src/ts/dynamodb/schema.d.js.map +1 -0
  520. package/src/ts/dynamodb/schema.d.ts +16 -0
  521. package/src/ts/dynamodb/schema.json +75 -0
  522. package/src/ts/dynamodb/smithy-connection/__snapshots__/generator.spec.ts.snap +23 -0
  523. package/src/ts/dynamodb/smithy-connection/generator.d.ts +10 -0
  524. package/src/ts/dynamodb/smithy-connection/generator.js +28 -0
  525. package/src/ts/dynamodb/smithy-connection/generator.js.map +1 -0
  526. package/src/ts/dynamodb/smithy-connection/schema.d.js +8 -0
  527. package/src/ts/dynamodb/smithy-connection/schema.d.js.map +1 -0
  528. package/src/ts/dynamodb/smithy-connection/schema.d.ts +13 -0
  529. package/src/ts/dynamodb/smithy-connection/schema.json +23 -0
  530. package/src/ts/dynamodb/trpc-connection/__snapshots__/generator.spec.ts.snap +23 -0
  531. package/src/ts/dynamodb/trpc-connection/generator.d.ts +10 -0
  532. package/src/ts/dynamodb/trpc-connection/generator.js +28 -0
  533. package/src/ts/dynamodb/trpc-connection/generator.js.map +1 -0
  534. package/src/ts/dynamodb/trpc-connection/schema.d.js +8 -0
  535. package/src/ts/dynamodb/trpc-connection/schema.d.js.map +1 -0
  536. package/src/ts/dynamodb/trpc-connection/schema.d.ts +13 -0
  537. package/src/ts/dynamodb/trpc-connection/schema.json +23 -0
  538. package/src/ts/lambda-function/__snapshots__/generator.spec.ts.snap +4 -4
  539. package/src/ts/lambda-function/generator.d.ts +3 -3
  540. package/src/ts/lambda-function/generator.js +59 -56
  541. package/src/ts/lambda-function/generator.js.map +1 -1
  542. package/src/ts/lambda-function/io.js +83 -52
  543. package/src/ts/lambda-function/io.js.map +1 -1
  544. package/src/ts/lambda-function/schema.d.js +6 -0
  545. package/src/ts/lambda-function/schema.d.js.map +1 -0
  546. package/src/ts/lambda-function/schema.d.ts +1 -0
  547. package/src/ts/lambda-function/schema.json +5 -0
  548. package/src/ts/lib/__snapshots__/generator.spec.ts.snap +53 -99
  549. package/src/ts/lib/__snapshots__/vitest.spec.ts.snap +24 -0
  550. package/src/ts/lib/biome.d.ts +7 -0
  551. package/src/ts/lib/biome.js +58 -0
  552. package/src/ts/lib/biome.js.map +1 -0
  553. package/src/ts/lib/generator.d.ts +3 -3
  554. package/src/ts/lib/generator.js +96 -96
  555. package/src/ts/lib/generator.js.map +1 -1
  556. package/src/ts/lib/grit/vitest-pass-with-no-tests.grit +1 -4
  557. package/src/ts/lib/schema.d.js +6 -0
  558. package/src/ts/lib/schema.d.js.map +1 -0
  559. package/src/ts/lib/schema.d.ts +1 -1
  560. package/src/ts/lib/schema.json +5 -0
  561. package/src/ts/lib/ts-project-utils.d.ts +12 -2
  562. package/src/ts/lib/ts-project-utils.js +80 -65
  563. package/src/ts/lib/ts-project-utils.js.map +1 -1
  564. package/src/ts/lib/types.js +5 -2
  565. package/src/ts/lib/types.js.map +1 -1
  566. package/src/ts/lib/vitest.d.ts +2 -2
  567. package/src/ts/lib/vitest.js +29 -26
  568. package/src/ts/lib/vitest.js.map +1 -1
  569. package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +83 -13
  570. package/src/ts/mcp-server/generator.d.ts +3 -3
  571. package/src/ts/mcp-server/generator.js +164 -120
  572. package/src/ts/mcp-server/generator.js.map +1 -1
  573. package/src/ts/mcp-server/schema.d.js +6 -0
  574. package/src/ts/mcp-server/schema.d.js.map +1 -0
  575. package/src/ts/mcp-server/schema.d.ts +1 -0
  576. package/src/ts/mcp-server/schema.json +5 -0
  577. package/src/ts/nx-generator/__snapshots__/generator.spec.ts.snap +46 -26
  578. package/src/ts/nx-generator/files/common/schema.d.ts.template +1 -0
  579. package/src/ts/nx-generator/files/common/schema.json.template +5 -0
  580. package/src/ts/nx-generator/files/local/generator.ts.template +3 -3
  581. package/src/ts/nx-generator/files/nx-plugin-for-aws/generator/generator.ts.template +6 -7
  582. package/src/ts/nx-generator/generator.d.ts +2 -2
  583. package/src/ts/nx-generator/generator.js +111 -74
  584. package/src/ts/nx-generator/generator.js.map +1 -1
  585. package/src/ts/nx-generator/schema.d.js +6 -0
  586. package/src/ts/nx-generator/schema.d.js.map +1 -0
  587. package/src/ts/nx-generator/schema.d.ts +1 -0
  588. package/src/ts/nx-generator/schema.json +5 -0
  589. package/src/ts/nx-plugin/__snapshots__/generator.spec.ts.snap +30 -18
  590. package/src/ts/nx-plugin/files/mcp-server/server.ts.template +5 -5
  591. package/src/ts/nx-plugin/files/mcp-server/tools/create-workspace-command.ts.template +2 -2
  592. package/src/ts/nx-plugin/files/mcp-server/tools/general-guidance.ts.template +1 -1
  593. package/src/ts/nx-plugin/files/mcp-server/tools/generator-guide.ts.template +2 -2
  594. package/src/ts/nx-plugin/files/mcp-server/tools/list-generators.ts.template +2 -2
  595. package/src/ts/nx-plugin/files/mcp-server/utils.ts.template +3 -3
  596. package/src/ts/nx-plugin/generator.d.ts +3 -3
  597. package/src/ts/nx-plugin/generator.js +60 -51
  598. package/src/ts/nx-plugin/generator.js.map +1 -1
  599. package/src/ts/nx-plugin/schema.d.js +9 -0
  600. package/src/ts/nx-plugin/schema.d.js.map +1 -0
  601. package/src/ts/nx-plugin/schema.json +5 -0
  602. package/src/ts/nx-plugin/utils.d.ts +1 -1
  603. package/src/ts/nx-plugin/utils.js +32 -38
  604. package/src/ts/nx-plugin/utils.js.map +1 -1
  605. package/src/ts/rdb/__snapshots__/generator.spec.ts.snap +312 -482
  606. package/src/ts/rdb/agent-connection/__snapshots__/generator.spec.ts.snap +6 -6
  607. package/src/ts/rdb/agent-connection/generator.d.ts +3 -3
  608. package/src/ts/rdb/agent-connection/generator.js +38 -39
  609. package/src/ts/rdb/agent-connection/generator.js.map +1 -1
  610. package/src/ts/rdb/agent-connection/schema.d.js +8 -0
  611. package/src/ts/rdb/agent-connection/schema.d.js.map +1 -0
  612. package/src/ts/rdb/agent-connection/schema.d.ts +1 -0
  613. package/src/ts/rdb/agent-connection/schema.json +5 -0
  614. package/src/ts/rdb/files/config.json.template +14 -0
  615. package/src/ts/rdb/files/prisma.config.ts.template +6 -5
  616. package/src/ts/rdb/files/src/create-db-user-handler.ts.template +7 -9
  617. package/src/ts/rdb/files/src/index.ts.template +0 -1
  618. package/src/ts/rdb/files/src/migration-handler.ts.template +1 -28
  619. package/src/ts/rdb/files/src/prisma.ts.template +28 -23
  620. package/src/ts/rdb/files/src/utils.ts.template +3 -0
  621. package/src/ts/rdb/generator.d.ts +3 -7
  622. package/src/ts/rdb/generator.js +158 -130
  623. package/src/ts/rdb/generator.js.map +1 -1
  624. package/src/ts/rdb/mcp-server-connection/__snapshots__/generator.spec.ts.snap +6 -6
  625. package/src/ts/rdb/mcp-server-connection/generator.d.ts +3 -3
  626. package/src/ts/rdb/mcp-server-connection/generator.js +36 -37
  627. package/src/ts/rdb/mcp-server-connection/generator.js.map +1 -1
  628. package/src/ts/rdb/mcp-server-connection/schema.d.js +8 -0
  629. package/src/ts/rdb/mcp-server-connection/schema.d.js.map +1 -0
  630. package/src/ts/rdb/mcp-server-connection/schema.d.ts +1 -0
  631. package/src/ts/rdb/mcp-server-connection/schema.json +5 -0
  632. package/src/ts/rdb/schema.d.js +6 -0
  633. package/src/ts/rdb/schema.d.js.map +1 -0
  634. package/src/ts/rdb/schema.d.ts +1 -0
  635. package/src/ts/rdb/schema.json +5 -0
  636. package/src/ts/rdb/smithy-connection/__snapshots__/generator.spec.ts.snap +3 -3
  637. package/src/ts/rdb/smithy-connection/generator.d.ts +3 -3
  638. package/src/ts/rdb/smithy-connection/generator.js +46 -43
  639. package/src/ts/rdb/smithy-connection/generator.js.map +1 -1
  640. package/src/ts/rdb/smithy-connection/schema.d.js +8 -0
  641. package/src/ts/rdb/smithy-connection/schema.d.js.map +1 -0
  642. package/src/ts/rdb/smithy-connection/schema.d.ts +1 -0
  643. package/src/ts/rdb/smithy-connection/schema.json +5 -0
  644. package/src/ts/rdb/trpc-connection/__snapshots__/generator.spec.ts.snap +3 -3
  645. package/src/ts/rdb/trpc-connection/generator.d.ts +3 -3
  646. package/src/ts/rdb/trpc-connection/generator.js +36 -31
  647. package/src/ts/rdb/trpc-connection/generator.js.map +1 -1
  648. package/src/ts/rdb/trpc-connection/schema.d.js +8 -0
  649. package/src/ts/rdb/trpc-connection/schema.d.js.map +1 -0
  650. package/src/ts/rdb/trpc-connection/schema.d.ts +1 -0
  651. package/src/ts/rdb/trpc-connection/schema.json +5 -0
  652. package/src/ts/rdb/utils.d.ts +1 -1
  653. package/src/ts/rdb/utils.js +9 -11
  654. package/src/ts/rdb/utils.js.map +1 -1
  655. package/src/ts/react-website/agui/files/common/src/hooks/useAgui__agentNameClassName__.tsx.template +1 -16
  656. package/src/ts/react-website/agui/generator.d.ts +1 -1
  657. package/src/ts/react-website/agui/generator.js +69 -62
  658. package/src/ts/react-website/agui/generator.js.map +1 -1
  659. package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +626 -612
  660. package/src/ts/react-website/app/generator.d.ts +4 -4
  661. package/src/ts/react-website/app/generator.js +217 -192
  662. package/src/ts/react-website/app/generator.js.map +1 -1
  663. package/src/ts/react-website/app/schema.d.js +6 -0
  664. package/src/ts/react-website/app/schema.d.js.map +1 -0
  665. package/src/ts/react-website/app/schema.d.ts +1 -1
  666. package/src/ts/react-website/app/schema.json +11 -6
  667. package/src/ts/react-website/cognito-auth/__snapshots__/generator.spec.ts.snap +129 -18
  668. package/src/ts/react-website/cognito-auth/__snapshots__/generator.terraform.spec.ts.snap +36 -10
  669. package/src/ts/react-website/cognito-auth/__snapshots__/generator.ux-provider.spec.ts.snap +0 -20
  670. package/src/ts/react-website/cognito-auth/files/app/components/CognitoAuth/index.tsx.template +2 -2
  671. package/src/ts/react-website/cognito-auth/generator.d.ts +4 -4
  672. package/src/ts/react-website/cognito-auth/generator.js +64 -63
  673. package/src/ts/react-website/cognito-auth/generator.js.map +1 -1
  674. package/src/ts/react-website/cognito-auth/grit/cloudscape-auth-menu.grit +8 -6
  675. package/src/ts/react-website/cognito-auth/grit/cognito-auth-wrapper.grit +1 -1
  676. package/src/ts/react-website/cognito-auth/grit/none-auth-menu.grit +1 -0
  677. package/src/ts/react-website/cognito-auth/grit/shadcn-auth-menu.grit +3 -1
  678. package/src/ts/react-website/cognito-auth/grit/shadcn-state-declarations.grit +3 -1
  679. package/src/ts/react-website/cognito-auth/schema.d.js +6 -0
  680. package/src/ts/react-website/cognito-auth/schema.d.js.map +1 -0
  681. package/src/ts/react-website/cognito-auth/schema.d.ts +1 -0
  682. package/src/ts/react-website/cognito-auth/schema.json +5 -0
  683. package/src/ts/react-website/cognito-auth/utils.d.ts +1 -1
  684. package/src/ts/react-website/cognito-auth/utils.js +34 -32
  685. package/src/ts/react-website/cognito-auth/utils.js.map +1 -1
  686. package/src/ts/react-website/runtime-config/__snapshots__/generator.spec.ts.snap +2 -2
  687. package/src/ts/react-website/runtime-config/files/app/components/RuntimeConfig/index.tsx.template +2 -2
  688. package/src/ts/react-website/runtime-config/generator.d.ts +3 -3
  689. package/src/ts/react-website/runtime-config/generator.js +29 -31
  690. package/src/ts/react-website/runtime-config/generator.js.map +1 -1
  691. package/src/ts/react-website/runtime-config/schema.d.js +6 -0
  692. package/src/ts/react-website/runtime-config/schema.d.js.map +1 -0
  693. package/src/ts/react-website/runtime-config/schema.d.ts +1 -0
  694. package/src/ts/react-website/runtime-config/schema.json +5 -0
  695. package/src/ts/sync/generator.d.ts +3 -3
  696. package/src/ts/sync/generator.js +60 -59
  697. package/src/ts/sync/generator.js.map +1 -1
  698. package/src/ts/website/app/generator.d.ts +3 -3
  699. package/src/ts/website/app/generator.js +10 -10
  700. package/src/ts/website/app/generator.js.map +1 -1
  701. package/src/ts/website/app/schema.d.js +6 -0
  702. package/src/ts/website/app/schema.d.js.map +1 -0
  703. package/src/ts/website/app/schema.d.ts +1 -1
  704. package/src/ts/website/app/schema.json +11 -6
  705. package/src/ts/website/auth/generator.d.ts +4 -4
  706. package/src/ts/website/auth/generator.js +11 -10
  707. package/src/ts/website/auth/generator.js.map +1 -1
  708. package/src/ts/website/auth/schema.d.js +6 -0
  709. package/src/ts/website/auth/schema.d.js.map +1 -0
  710. package/src/ts/website/auth/schema.d.ts +1 -0
  711. package/src/ts/website/auth/schema.json +5 -0
  712. package/src/utils/agent-chat/agent-chat.d.ts +31 -0
  713. package/src/utils/agent-chat/agent-chat.js +29 -0
  714. package/src/utils/agent-chat/agent-chat.js.map +1 -0
  715. package/src/utils/agent-chat/files/a2a/chat.ts.template +33 -0
  716. package/src/utils/agent-chat/files/ag-ui/chat.ts.template +17 -0
  717. package/src/utils/agent-chat/files/common/agentcore.ts.template +81 -0
  718. package/src/utils/agent-chat/files/http-py/chat.ts.template +43 -0
  719. package/src/utils/agent-chat/files/http-ts/chat.ts.template +54 -0
  720. package/src/utils/agent-connection/agent-connection.d.ts +117 -25
  721. package/src/utils/agent-connection/agent-connection.js +411 -103
  722. package/src/utils/agent-connection/agent-connection.js.map +1 -1
  723. package/src/utils/agent-connection/files/core-a2a/agentcore-a2a-client-config.ts.template +93 -0
  724. package/src/utils/agent-connection/files/core-auth/agentcore-endpoints.ts.template +37 -0
  725. package/src/utils/agent-connection/files/core-auth/agentcore-fetch.ts.template +54 -0
  726. package/src/utils/agent-connection/files/core-gateway/agentcore-gateway-mcp-transport.ts.template +41 -0
  727. package/src/utils/agent-connection/files/core-mcp/agentcore-mcp-transport.ts.template +70 -0
  728. package/src/utils/agent-connection/files/core-runtime-config/runtime-config.ts.template +22 -32
  729. package/src/utils/agent-connection/files/core-shared/agentcore-transport.ts.template +39 -0
  730. package/src/utils/agent-connection/files/core-strands/a2a/agentcore-a2a-client-strands.ts.template +61 -0
  731. package/src/utils/agent-connection/files/core-strands/base/model-errors-strands.ts.template +36 -0
  732. package/src/utils/agent-connection/files/core-strands/gateway/agentcore-gateway-mcp-client-strands.ts.template +26 -0
  733. package/src/utils/agent-connection/files/core-strands/mcp/agentcore-mcp-client-strands.ts.template +27 -0
  734. package/src/utils/agent-connection/files/py-core-a2a/agentcore_a2a_client_config.py.template +43 -0
  735. package/src/utils/agent-connection/files/py-core-auth/agentcore_endpoints.py.template +47 -0
  736. package/src/utils/agent-connection/files/py-core-auth/auth/session.py.template +51 -0
  737. package/src/utils/agent-connection/files/py-core-gateway/agentcore_gateway_mcp_transport.py.template +28 -0
  738. package/src/utils/agent-connection/files/py-core-langchain/a2a/agentcore_a2a_client_langchain.py.template +99 -0
  739. package/src/utils/agent-connection/files/py-core-langchain/gateway/agentcore_gateway_mcp_client_langchain.py.template +57 -0
  740. package/src/utils/agent-connection/files/py-core-langchain/mcp/agentcore_mcp_client_langchain.py.template +64 -0
  741. package/src/utils/agent-connection/files/py-core-mcp/agentcore_mcp_transport.py.template +34 -0
  742. package/src/utils/agent-connection/files/py-core-runtime-config/runtime_config.py.template +5 -18
  743. package/src/utils/agent-connection/files/py-core-shared/agentcore_transport.py.template +40 -0
  744. package/src/utils/agent-connection/files/py-core-strands/a2a/agentcore_a2a_client_strands.py.template +66 -0
  745. package/src/utils/agent-connection/files/py-core-strands/base/model_errors_strands.py.template +40 -0
  746. package/src/utils/agent-connection/files/py-core-strands/gateway/agentcore_gateway_mcp_client_strands.py.template +22 -0
  747. package/src/utils/agent-connection/files/py-core-strands/mcp/agentcore_mcp_client_strands.py.template +30 -0
  748. package/src/utils/agent-core-constructs/agent-core-constructs.d.ts +11 -3
  749. package/src/utils/agent-core-constructs/agent-core-constructs.js +86 -38
  750. package/src/utils/agent-core-constructs/agent-core-constructs.js.map +1 -1
  751. package/src/utils/agent-core-constructs/files/cdk/app/agent-core/__nameKebabCase__/__nameKebabCase__.ts.template +4 -0
  752. package/src/utils/agent-core-constructs/files/cdk/app/agentcore-gateway/__nameKebabCase__/__nameKebabCase__.ts.template +50 -0
  753. package/src/utils/agent-core-constructs/files/cdk/core/agentcore-gateway/agentcore-gateway.ts.template +487 -0
  754. package/src/utils/agent-core-constructs/files/cdk/core/agentcore-gateway/readiness-probe/index.js.template +55 -0
  755. package/src/utils/agent-core-constructs/files/terraform/app/agentcore-gateway/__nameKebabCase__/__nameKebabCase__.tf.template +449 -0
  756. package/src/utils/agent-core-constructs/files/terraform/app/agentcore-gateway/__nameKebabCase__/render-cedar.cjs.template +13 -0
  757. package/src/utils/agent-core-constructs/files/terraform/core/agent-core/runtime.tf.template +80 -12
  758. package/src/utils/agent-core-constructs/files/terraform/core/agentcore-gateway/gateway.tf.template +14 -0
  759. package/src/utils/api-constructs/api-constructs.d.ts +2 -2
  760. package/src/utils/api-constructs/api-constructs.js +30 -31
  761. package/src/utils/api-constructs/api-constructs.js.map +1 -1
  762. package/src/utils/api-constructs/files/cdk/app/apis/rest/__apiNameKebabCase__.ts.template +3 -0
  763. package/src/utils/api-constructs/files/cdk/core/api/http/http-api.ts.template +23 -6
  764. package/src/utils/api-constructs/files/cdk/core/api/rest/api-gateway-account/index.js.template +58 -0
  765. package/src/utils/api-constructs/files/cdk/core/api/rest/api-gateway-account.ts.template +82 -0
  766. package/src/utils/api-constructs/files/cdk/core/api/rest/rest-api.ts.template +51 -15
  767. package/src/utils/api-constructs/files/cdk/core/api/utils/utils.ts.template +61 -7
  768. package/src/utils/api-constructs/files/terraform/app/apis/http/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +1 -8
  769. package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +94 -4
  770. package/src/utils/api-constructs/files/terraform/core/api/http/http-api/http-api.tf.template +1 -1
  771. package/src/utils/api-constructs/files/terraform/core/api/rest/api-gateway-account/api-gateway-account.tf.template +83 -0
  772. package/src/utils/api-constructs/files/terraform/core/api/rest/rest-api/rest-api.tf.template +5 -11
  773. package/src/utils/api-constructs/open-api-metadata.d.ts +2 -2
  774. package/src/utils/api-constructs/open-api-metadata.js +22 -22
  775. package/src/utils/api-constructs/open-api-metadata.js.map +1 -1
  776. package/src/utils/ast/website.d.ts +5 -1
  777. package/src/utils/ast/website.js +18 -20
  778. package/src/utils/ast/website.js.map +1 -1
  779. package/src/utils/ast.d.ts +33 -1
  780. package/src/utils/ast.js +130 -70
  781. package/src/utils/ast.js.map +1 -1
  782. package/src/utils/bundle/bundle.d.ts +1 -1
  783. package/src/utils/bundle/bundle.js +50 -47
  784. package/src/utils/bundle/bundle.js.map +1 -1
  785. package/src/utils/commands.js +47 -26
  786. package/src/utils/commands.js.map +1 -1
  787. package/src/utils/config/index.d.ts +5 -5
  788. package/src/utils/config/index.js +5 -4
  789. package/src/utils/config/index.js.map +1 -1
  790. package/src/utils/config/utils.d.ts +13 -3
  791. package/src/utils/config/utils.js +65 -43
  792. package/src/utils/config/utils.js.map +1 -1
  793. package/src/utils/connection/open-api/files/components/__apiNameClassName__Provider.tsx.template +1 -1
  794. package/src/utils/connection/open-api/react.d.ts +6 -6
  795. package/src/utils/connection/open-api/react.js +81 -77
  796. package/src/utils/connection/open-api/react.js.map +1 -1
  797. package/src/utils/containers.d.ts +1 -1
  798. package/src/utils/containers.js +22 -23
  799. package/src/utils/containers.js.map +1 -1
  800. package/src/utils/dynamodb-constructs/dynamodb-constructs.d.ts +18 -0
  801. package/src/utils/dynamodb-constructs/dynamodb-constructs.js +48 -0
  802. package/src/utils/dynamodb-constructs/dynamodb-constructs.js.map +1 -0
  803. package/src/utils/dynamodb-constructs/files/cdk/app/dynamodb/__nameKebabCase__.ts.template +43 -0
  804. package/src/utils/dynamodb-constructs/files/cdk/core/dynamodb.ts.template +105 -0
  805. package/src/utils/dynamodb-constructs/files/terraform/app/dynamodb/__nameKebabCase__/__nameKebabCase__.tf.template +72 -0
  806. package/src/utils/dynamodb-constructs/files/terraform/core/dynamodb/dynamodb.tf.template +155 -0
  807. package/src/utils/files/common/scripts/src/dynamodb/create-local-table.ts.template +195 -0
  808. package/src/utils/files/common/scripts/src/dynamodb/pull-image.ts.template +13 -0
  809. package/src/utils/files/common/scripts/src/dynamodb/start-container.ts.template +106 -0
  810. package/src/utils/files/common/scripts/src/{stage-credentials → infra/stage-credentials}/cdk-command.ts.template +6 -3
  811. package/src/utils/files/common/scripts/src/rdb/mysql/wait-for-mysql-db.ts.template +32 -0
  812. package/src/utils/files/common/scripts/src/rdb/postgres/wait-for-postgres-db.ts.template +32 -0
  813. package/src/utils/files/common/scripts/src/rdb/shared/pull-image.ts.template +16 -0
  814. package/src/utils/files/common/scripts/src/rdb/shared/start-container.ts.template +117 -0
  815. package/src/utils/files/terraform/src/core/asset-bucket/asset-bucket.tf.template +2 -2
  816. package/src/utils/files/terraform/src/core/runtime-config/appconfig/appconfig.tf.template +1 -1
  817. package/src/utils/files/terraform/src/core/runtime-config/appconfig-deployment/appconfig-deployment.tf.template +12 -6
  818. package/src/utils/files/terraform/src/core/runtime-config/entry/entry.tf.template +1 -1
  819. package/src/utils/files/terraform/src/core/runtime-config/read/read.tf.template +9 -4
  820. package/src/utils/files/website/hooks/sigv4/useSigV4.tsx.template +1 -1
  821. package/src/utils/format.d.ts +45 -2
  822. package/src/utils/format.js +323 -81
  823. package/src/utils/format.js.map +1 -1
  824. package/src/utils/fs.d.ts +1 -1
  825. package/src/utils/fs.js +14 -13
  826. package/src/utils/fs.js.map +1 -1
  827. package/src/utils/function-constructs/files/terraform/app/lambda-functions/__nameKebabCase__/__nameKebabCase__.tf.template +2 -2
  828. package/src/utils/function-constructs/function-constructs.d.ts +2 -2
  829. package/src/utils/function-constructs/function-constructs.js +21 -26
  830. package/src/utils/function-constructs/function-constructs.js.map +1 -1
  831. package/src/utils/generators.d.ts +4 -0
  832. package/src/utils/generators.js +18 -20
  833. package/src/utils/generators.js.map +1 -1
  834. package/src/utils/git.d.ts +1 -1
  835. package/src/utils/git.js +19 -34
  836. package/src/utils/git.js.map +1 -1
  837. package/src/utils/iac-providers.js +5 -5
  838. package/src/utils/iac-providers.js.map +1 -1
  839. package/src/utils/iac.d.ts +1 -1
  840. package/src/utils/iac.js +12 -14
  841. package/src/utils/iac.js.map +1 -1
  842. package/src/utils/identity-constructs/files/cdk/core/user-identity.ts.template +123 -14
  843. package/src/utils/identity-constructs/files/terraform/core/user-identity/add-callback-url/add-callback-url.tf.template +28 -9
  844. package/src/utils/identity-constructs/files/terraform/core/user-identity/identity/identity.tf.template +106 -6
  845. package/src/utils/identity-constructs/files/terraform/core/user-identity/main.tf.template +7 -0
  846. package/src/utils/identity-constructs/identity-constructs.d.ts +2 -2
  847. package/src/utils/identity-constructs/identity-constructs.js +24 -28
  848. package/src/utils/identity-constructs/identity-constructs.js.map +1 -1
  849. package/src/utils/install.d.ts +32 -0
  850. package/src/utils/install.js +56 -0
  851. package/src/utils/install.js.map +1 -0
  852. package/src/utils/js.d.ts +4 -0
  853. package/src/utils/js.js +27 -20
  854. package/src/utils/js.js.map +1 -1
  855. package/src/utils/mcp.d.ts +25 -0
  856. package/src/utils/mcp.js +74 -0
  857. package/src/utils/mcp.js.map +1 -0
  858. package/src/utils/metrics.d.ts +9 -3
  859. package/src/utils/metrics.js +61 -46
  860. package/src/utils/metrics.js.map +1 -1
  861. package/src/utils/mock-project-graph.js +8 -12
  862. package/src/utils/mock-project-graph.js.map +1 -1
  863. package/src/utils/names.d.ts +16 -0
  864. package/src/utils/names.js +39 -48
  865. package/src/utils/names.js.map +1 -1
  866. package/src/utils/npm-scope.d.ts +1 -1
  867. package/src/utils/npm-scope.js +9 -17
  868. package/src/utils/npm-scope.js.map +1 -1
  869. package/src/utils/nx.d.ts +25 -4
  870. package/src/utils/nx.js +144 -75
  871. package/src/utils/nx.js.map +1 -1
  872. package/src/utils/nxlv-python.d.ts +1 -1
  873. package/src/utils/nxlv-python.js +27 -15
  874. package/src/utils/nxlv-python.js.map +1 -1
  875. package/src/utils/object.js +5 -11
  876. package/src/utils/object.js.map +1 -1
  877. package/src/utils/paths.d.ts +1 -1
  878. package/src/utils/paths.js +11 -15
  879. package/src/utils/paths.js.map +1 -1
  880. package/src/utils/pkg-manager.js +4 -9
  881. package/src/utils/pkg-manager.js.map +1 -1
  882. package/src/utils/pnpm-workspace.d.ts +1 -1
  883. package/src/utils/pnpm-workspace.js +22 -22
  884. package/src/utils/pnpm-workspace.js.map +1 -1
  885. package/src/utils/port.d.ts +31 -3
  886. package/src/utils/port.js +72 -12
  887. package/src/utils/port.js.map +1 -1
  888. package/src/utils/py.d.ts +16 -3
  889. package/src/utils/py.js +72 -47
  890. package/src/utils/py.js.map +1 -1
  891. package/src/utils/rdb-constructs/files/cdk/app/dbs/__nameKebabCase__.ts.template +12 -2
  892. package/src/utils/rdb-constructs/files/cdk/core/rdb/aurora.ts.template +35 -16
  893. package/src/utils/rdb-constructs/files/terraform/app/dbs/__nameKebabCase__/__nameKebabCase__.tf.template +42 -48
  894. package/src/utils/rdb-constructs/files/terraform/core/rdb/aurora/aurora.tf.template +31 -16
  895. package/src/utils/rdb-constructs/rdb-constructs.d.ts +5 -4
  896. package/src/utils/rdb-constructs/rdb-constructs.js +31 -41
  897. package/src/utils/rdb-constructs/rdb-constructs.js.map +1 -1
  898. package/src/utils/shared-constructs-constants.d.ts +1 -0
  899. package/src/utils/shared-constructs-constants.js +16 -15
  900. package/src/utils/shared-constructs-constants.js.map +1 -1
  901. package/src/utils/shared-constructs.d.ts +2 -2
  902. package/src/utils/shared-constructs.js +57 -54
  903. package/src/utils/shared-constructs.js.map +1 -1
  904. package/src/utils/shared-dynamodb-scripts.d.ts +11 -0
  905. package/src/utils/shared-dynamodb-scripts.js +25 -0
  906. package/src/utils/shared-dynamodb-scripts.js.map +1 -0
  907. package/src/utils/shared-infra-config.d.ts +1 -1
  908. package/src/utils/shared-infra-config.js +30 -31
  909. package/src/utils/shared-infra-config.js.map +1 -1
  910. package/src/utils/shared-infra-scripts.d.ts +11 -0
  911. package/src/utils/shared-infra-scripts.js +40 -0
  912. package/src/utils/shared-infra-scripts.js.map +1 -0
  913. package/src/utils/shared-rdb-scripts.d.ts +16 -0
  914. package/src/utils/shared-rdb-scripts.js +28 -0
  915. package/src/utils/shared-rdb-scripts.js.map +1 -0
  916. package/src/utils/shared-scripts.d.ts +2 -5
  917. package/src/utils/shared-scripts.js +12 -38
  918. package/src/utils/shared-scripts.js.map +1 -1
  919. package/src/utils/shared-shadcn.d.ts +1 -1
  920. package/src/utils/shared-shadcn.js +64 -72
  921. package/src/utils/shared-shadcn.js.map +1 -1
  922. package/src/utils/test.d.ts +13 -1
  923. package/src/utils/test.js +35 -23
  924. package/src/utils/test.js.map +1 -1
  925. package/src/utils/toml.d.ts +1 -1
  926. package/src/utils/toml.js +10 -14
  927. package/src/utils/toml.js.map +1 -1
  928. package/src/utils/versions.d.ts +116 -84
  929. package/src/utils/versions.js +124 -102
  930. package/src/utils/versions.js.map +1 -1
  931. package/src/utils/website-constructs/files/cdk/core/static-website.ts.template +73 -5
  932. package/src/utils/website-constructs/files/terraform/app/static-websites/__websiteNameKebabCase__/__websiteNameKebabCase__.tf.template +1 -1
  933. package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template +68 -19
  934. package/src/utils/website-constructs/website-constructs.d.ts +2 -2
  935. package/src/utils/website-constructs/website-constructs.js +31 -30
  936. package/src/utils/website-constructs/website-constructs.js.map +1 -1
  937. package/bin/aws-nx-mcp.js +0 -17
  938. package/bin/aws-nx-mcp.js.map +0 -1
  939. package/sdk/connection.d.ts +0 -6
  940. package/sdk/connection.js +0 -10
  941. package/sdk/connection.js.map +0 -1
  942. package/sdk/open-api.d.ts +0 -10
  943. package/sdk/open-api.js +0 -15
  944. package/sdk/open-api.js.map +0 -1
  945. package/sdk/py.d.ts +0 -14
  946. package/sdk/py.js +0 -18
  947. package/sdk/py.js.map +0 -1
  948. package/sdk/terraform.d.ts +0 -6
  949. package/sdk/terraform.js +0 -11
  950. package/sdk/terraform.js.map +0 -1
  951. package/sdk/ts.d.ts +0 -29
  952. package/sdk/ts.js +0 -47
  953. package/sdk/ts.js.map +0 -1
  954. package/sdk/utils/ast.js +0 -12
  955. package/sdk/utils/ast.js.map +0 -1
  956. package/sdk/utils/format.js +0 -10
  957. package/sdk/utils/format.js.map +0 -1
  958. package/sdk/utils/test.js +0 -10
  959. package/sdk/utils/test.js.map +0 -1
  960. package/src/connection/agent-serve-local.js +0 -48
  961. package/src/connection/agent-serve-local.js.map +0 -1
  962. package/src/connection/serve-local.d.ts +0 -15
  963. package/src/connection/serve-local.js +0 -41
  964. package/src/connection/serve-local.js.map +0 -1
  965. package/src/py/agent/a2a-connection/files/agent-connection/app/__targetAgentSnakeCase___client.py.template +0 -22
  966. package/src/py/agent/mcp-connection/files/agent-connection/app/__mcpServerSnakeCase___client.py.template +0 -22
  967. package/src/py/agent/react-connection/serve-local.js +0 -30
  968. package/src/py/agent/react-connection/serve-local.js.map +0 -1
  969. package/src/py/agent/scripts/http/chat.ts.template +0 -38
  970. package/src/ts/agent/a2a-connection/files/agent-connection/app/__targetAgentKebabCase__-client.ts.template +0 -19
  971. package/src/ts/agent/mcp-connection/files/agent-connection/app/__mcpServerKebabCase__-client.ts.template +0 -19
  972. package/src/ts/agent/react-connection/serve-local.d.ts +0 -13
  973. package/src/ts/agent/react-connection/serve-local.js +0 -19
  974. package/src/ts/agent/react-connection/serve-local.js.map +0 -1
  975. package/src/ts/agent/scripts/http/chat.ts.template +0 -35
  976. package/src/ts/lib/eslint.d.ts +0 -14
  977. package/src/ts/lib/eslint.js +0 -90
  978. package/src/ts/lib/eslint.js.map +0 -1
  979. package/src/ts/lib/grit/eslint-dependency-checks-warn.grit +0 -1
  980. package/src/ts/rdb/files/scripts/docker-pull.ts.template +0 -15
  981. package/src/ts/rdb/files/scripts/docker-start.ts.template +0 -84
  982. package/src/ts/rdb/files/scripts/wait-for-db.ts.template +0 -55
  983. package/src/ts/rdb/files/src/constants.ts.template +0 -8
  984. package/src/utils/agent-connection/files/core-a2a/agentcore-a2a-client.ts.template +0 -123
  985. package/src/utils/agent-connection/files/core-mcp/agentcore-mcp-client.ts.template +0 -128
  986. package/src/utils/agent-connection/files/py-core-a2a/agentcore_a2a_client.py.template +0 -120
  987. package/src/utils/agent-connection/files/py-core-mcp/agentcore_mcp_client.py.template +0 -89
  988. /package/{bin → src/bin}/aws-nx-mcp.d.ts +0 -0
  989. /package/src/utils/agent-connection/files/{core-runtime-config/with-session-id.ts.template → core-strands/base/with-session-id-strands.ts.template} +0 -0
  990. /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} +0 -0
  991. /package/src/utils/files/common/scripts/src/{index.ts.template → infra/index.ts.template} +0 -0
  992. /package/src/utils/files/common/scripts/src/{infra-deploy.ts.template → infra/infra-deploy.ts.template} +0 -0
  993. /package/src/utils/files/common/scripts/src/{infra-destroy.ts.template → infra/infra-destroy.ts.template} +0 -0
  994. /package/src/utils/files/common/scripts/src/{stage-credentials → infra/stage-credentials}/credentials.ts.template +0 -0
  995. /package/src/utils/files/common/scripts/src/{stage-credentials → infra/stage-credentials}/run.ts.template +0 -0
  996. /package/src/utils/files/common/scripts/src/{stage-credentials → infra/stage-credentials}/stage-parser.ts.template +0 -0
@@ -0,0 +1,4833 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`fastapi project generator > terraform iac > should generate terraform files for HTTP API with Cognito auth and snapshot them > terraform-http-cognito-files 1`] = `
4
+ {
5
+ "http-api.tf": "# Core HTTP API Gateway module
6
+ # This module creates the API Gateway HTTP API, stage, and logging resources
7
+
8
+ terraform {
9
+ required_version = ">= 1.0"
10
+
11
+ required_providers {
12
+ aws = {
13
+ source = "hashicorp/aws"
14
+ version = "6.52.0"
15
+ }
16
+ }
17
+ }
18
+
19
+ # Core HTTP API Gateway Variables
20
+
21
+ variable "api_name" {
22
+ description = "Name of the HTTP API Gateway"
23
+ type = string
24
+ }
25
+
26
+ variable "api_description" {
27
+ description = "Description of the HTTP API Gateway"
28
+ type = string
29
+ default = "HTTP API Gateway"
30
+ }
31
+
32
+ variable "stage_name" {
33
+ description = "Name of the API Gateway stage"
34
+ type = string
35
+ default = "prod"
36
+ }
37
+
38
+ variable "stage_auto_deploy" {
39
+ description = "Whether to automatically deploy the API stage"
40
+ type = bool
41
+ default = true
42
+ }
43
+
44
+ # CORS Configuration
45
+
46
+ variable "cors_allow_credentials" {
47
+ description = "Whether to allow credentials in CORS requests"
48
+ type = bool
49
+ default = false
50
+ }
51
+
52
+ variable "cors_allow_headers" {
53
+ description = "List of allowed headers for CORS"
54
+ type = list(string)
55
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
56
+ }
57
+
58
+ variable "cors_allow_methods" {
59
+ description = "List of allowed HTTP methods for CORS"
60
+ type = list(string)
61
+ default = ["*"]
62
+ }
63
+
64
+ variable "cors_allow_origins" {
65
+ description = "List of allowed origins for CORS"
66
+ type = list(string)
67
+ default = ["*"]
68
+ }
69
+
70
+ variable "cors_expose_headers" {
71
+ description = "List of headers to expose in CORS responses"
72
+ type = list(string)
73
+ default = []
74
+ }
75
+
76
+ variable "cors_max_age" {
77
+ description = "Maximum age for CORS preflight requests in seconds"
78
+ type = number
79
+ default = 86400
80
+ }
81
+
82
+ # Tags
83
+
84
+ variable "tags" {
85
+ description = "Tags to apply to all resources"
86
+ type = map(string)
87
+ default = {}
88
+ }
89
+
90
+ # Data sources
91
+ data "aws_region" "current" {}
92
+ data "aws_caller_identity" "current" {}
93
+
94
+ # Resources
95
+
96
+ # KMS key for CloudWatch log group encryption
97
+ resource "aws_kms_key" "logs_key" {
98
+ description = "KMS key for CloudWatch log group encryption"
99
+ deletion_window_in_days = 7
100
+ enable_key_rotation = true
101
+
102
+ policy = jsonencode({
103
+ Version = "2012-10-17"
104
+ Statement = [
105
+ {
106
+ Sid = "Enable IAM User Permissions"
107
+ Effect = "Allow"
108
+ Principal = {
109
+ AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
110
+ }
111
+ Action = "kms:*"
112
+ Resource = "*"
113
+ },
114
+ {
115
+ Sid = "Allow CloudWatch Logs"
116
+ Effect = "Allow"
117
+ Principal = {
118
+ Service = "logs.\${data.aws_region.current.region}.amazonaws.com"
119
+ }
120
+ Action = [
121
+ "kms:Encrypt",
122
+ "kms:Decrypt",
123
+ "kms:ReEncrypt*",
124
+ "kms:GenerateDataKey*",
125
+ "kms:DescribeKey"
126
+ ]
127
+ Resource = "*"
128
+ Condition = {
129
+ ArnEquals = {
130
+ "kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:log-group:/aws/apigateway/\${var.api_name}"
131
+ }
132
+ }
133
+ }
134
+ ]
135
+ })
136
+
137
+ tags = var.tags
138
+ }
139
+
140
+ resource "aws_kms_alias" "logs_key_alias" {
141
+ name = "alias/\${var.api_name}-api-logs-encryption"
142
+ target_key_id = aws_kms_key.logs_key.key_id
143
+ }
144
+
145
+ # HTTP API Gateway
146
+ resource "aws_apigatewayv2_api" "http_api" {
147
+ name = var.api_name
148
+ protocol_type = "HTTP"
149
+ description = var.api_description
150
+
151
+ cors_configuration {
152
+ allow_credentials = var.cors_allow_credentials
153
+ allow_headers = var.cors_allow_headers
154
+ allow_methods = var.cors_allow_methods
155
+ allow_origins = var.cors_allow_origins
156
+ expose_headers = var.cors_expose_headers
157
+ max_age = var.cors_max_age
158
+ }
159
+
160
+ tags = var.tags
161
+ }
162
+
163
+ # API Gateway stage
164
+ resource "aws_apigatewayv2_stage" "api_stage" {
165
+ api_id = aws_apigatewayv2_api.http_api.id
166
+ name = var.stage_name
167
+ auto_deploy = var.stage_auto_deploy
168
+
169
+ access_log_settings {
170
+ destination_arn = aws_cloudwatch_log_group.api_logs.arn
171
+ format = jsonencode({
172
+ requestId = "$context.requestId"
173
+ ip = "$context.identity.sourceIp"
174
+ requestTime = "$context.requestTime"
175
+ httpMethod = "$context.httpMethod"
176
+ routeKey = "$context.routeKey"
177
+ status = "$context.status"
178
+ protocol = "$context.protocol"
179
+ responseLength = "$context.responseLength"
180
+ error = "$context.error.message"
181
+ integrationError = "$context.integrationErrorMessage"
182
+ })
183
+ }
184
+
185
+ default_route_settings {
186
+ throttling_burst_limit = 5000
187
+ throttling_rate_limit = 10000
188
+ }
189
+
190
+ tags = var.tags
191
+
192
+ depends_on = [aws_cloudwatch_log_group.api_logs]
193
+ }
194
+
195
+ # CloudWatch Log Group for API Gateway
196
+ resource "aws_cloudwatch_log_group" "api_logs" {
197
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
198
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
199
+ name = "/aws/apigateway/\${var.api_name}"
200
+ kms_key_id = aws_kms_key.logs_key.arn
201
+ tags = var.tags
202
+ }
203
+
204
+ # Outputs
205
+
206
+ output "api_id" {
207
+ description = "ID of the HTTP API Gateway"
208
+ value = aws_apigatewayv2_api.http_api.id
209
+ }
210
+
211
+ output "api_arn" {
212
+ description = "ARN of the HTTP API Gateway"
213
+ value = aws_apigatewayv2_api.http_api.arn
214
+ }
215
+
216
+ output "api_endpoint" {
217
+ description = "Base URL of the HTTP API Gateway"
218
+ value = aws_apigatewayv2_api.http_api.api_endpoint
219
+ }
220
+
221
+ output "api_execution_arn" {
222
+ description = "Execution ARN of the HTTP API Gateway"
223
+ value = aws_apigatewayv2_api.http_api.execution_arn
224
+ }
225
+
226
+ output "stage_id" {
227
+ description = "ID of the API Gateway stage"
228
+ value = aws_apigatewayv2_stage.api_stage.id
229
+ }
230
+
231
+ output "stage_arn" {
232
+ description = "ARN of the API Gateway stage"
233
+ value = aws_apigatewayv2_stage.api_stage.arn
234
+ }
235
+
236
+ output "stage_execution_arn" {
237
+ description = "Execution ARN of the API Gateway stage"
238
+ value = aws_apigatewayv2_stage.api_stage.execution_arn
239
+ }
240
+
241
+ output "stage_invoke_url" {
242
+ description = "Invoke URL of the API Gateway stage"
243
+ value = aws_apigatewayv2_stage.api_stage.invoke_url
244
+ }
245
+
246
+ output "api_log_group_name" {
247
+ description = "Name of the API Gateway CloudWatch log group"
248
+ value = aws_cloudwatch_log_group.api_logs.name
249
+ }
250
+
251
+ output "api_log_group_arn" {
252
+ description = "ARN of the API Gateway CloudWatch log group"
253
+ value = aws_cloudwatch_log_group.api_logs.arn
254
+ }",
255
+ "test-api.tf": "terraform {
256
+ required_version = ">= 1.0"
257
+
258
+ required_providers {
259
+ aws = {
260
+ source = "hashicorp/aws"
261
+ version = "6.52.0"
262
+ }
263
+ }
264
+ }
265
+
266
+ # Authentication Configuration
267
+ variable "user_pool_id" {
268
+ description = "Cognito User Pool ID for authentication"
269
+ type = string
270
+ }
271
+
272
+ variable "user_pool_client_ids" {
273
+ description = "List of Cognito User Pool Client IDs"
274
+ type = list(string)
275
+ }
276
+
277
+ variable "env" {
278
+ description = "Environment variables for the Lambda function"
279
+ type = map(string)
280
+ default = {}
281
+ }
282
+
283
+ variable "additional_iam_policy_statements" {
284
+ description = "Additional IAM policy statements for the Lambda function"
285
+ type = list(object({
286
+ Effect = string
287
+ Action = list(string)
288
+ Resource = list(string)
289
+ }))
290
+ default = []
291
+ }
292
+
293
+ variable "asset_bucket_name" {
294
+ 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."
295
+ type = string
296
+ }
297
+
298
+ # CORS Configuration (passed to core module)
299
+ variable "cors_allow_credentials" {
300
+ description = "Whether to allow credentials in CORS requests"
301
+ type = bool
302
+ default = false
303
+ }
304
+
305
+ variable "cors_allow_headers" {
306
+ description = "List of allowed headers for CORS"
307
+ type = list(string)
308
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
309
+ }
310
+
311
+ variable "cors_allow_methods" {
312
+ description = "List of allowed HTTP methods for CORS"
313
+ type = list(string)
314
+ default = ["*"]
315
+ }
316
+
317
+ variable "cors_allow_origins" {
318
+ description = "List of allowed origins for CORS"
319
+ type = list(string)
320
+ default = ["*"]
321
+ }
322
+
323
+ variable "cors_expose_headers" {
324
+ description = "List of headers to expose in CORS responses"
325
+ type = list(string)
326
+ default = []
327
+ }
328
+
329
+ # Tags
330
+ variable "tags" {
331
+ description = "Tags to apply to all resources"
332
+ type = map(string)
333
+ default = {}
334
+ }
335
+
336
+ # Get current AWS region and account ID
337
+ data "aws_region" "current" {}
338
+ data "aws_caller_identity" "current" {}
339
+
340
+ resource "random_string" "suffix" {
341
+ length = 8
342
+ special = false
343
+ upper = false
344
+ }
345
+
346
+ # Resources
347
+
348
+ # Create Lambda ZIP file from the bundle directory
349
+ data "archive_file" "lambda_zip" {
350
+ type = "zip"
351
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
352
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
353
+ }
354
+
355
+ resource "aws_s3_object" "lambda_zip" {
356
+ bucket = var.asset_bucket_name
357
+ key = "apis/test-api/\${data.archive_file.lambda_zip.output_sha256}.zip"
358
+ source = data.archive_file.lambda_zip.output_path
359
+ source_hash = data.archive_file.lambda_zip.output_base64sha256
360
+ etag = data.archive_file.lambda_zip.output_md5
361
+ }
362
+
363
+
364
+ # Use the core HTTP API module
365
+ module "http_api" {
366
+ source = "../../../core/api/http-api"
367
+
368
+ api_name = "TestApi-\${random_string.suffix.result}"
369
+ api_description = "TestApi HTTP API"
370
+ stage_name = "$default"
371
+ stage_auto_deploy = true
372
+
373
+ # CORS Configuration
374
+ cors_allow_credentials = var.cors_allow_credentials
375
+ cors_allow_headers = var.cors_allow_headers
376
+ cors_allow_methods = var.cors_allow_methods
377
+ cors_allow_origins = var.cors_allow_origins
378
+ cors_expose_headers = var.cors_expose_headers
379
+
380
+ # Tags
381
+ tags = var.tags
382
+ }
383
+
384
+ # Lambda function
385
+ # This configures a single "router" lambda to serve all requests
386
+ resource "aws_lambda_function" "api_lambda" {
387
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
388
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
389
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
390
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
391
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
392
+ s3_bucket = aws_s3_object.lambda_zip.bucket
393
+ s3_key = aws_s3_object.lambda_zip.key
394
+ s3_object_version = aws_s3_object.lambda_zip.version_id
395
+ function_name = "TestApiHandler-\${random_string.suffix.result}"
396
+ role = aws_iam_role.lambda_execution_role.arn
397
+ handler = "run.sh"
398
+ runtime = "python3.14"
399
+ layers = ["arn:aws:lambda:\${data.aws_region.current.region}:753240598075:layer:LambdaAdapterLayerX86:24"]
400
+ timeout = 30
401
+ memory_size = 128
402
+ publish = true
403
+
404
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
405
+
406
+ # Enable X-Ray tracing
407
+ tracing_config {
408
+ mode = "Active"
409
+ }
410
+
411
+ # Enable SnapStart for faster cold starts
412
+ snap_start {
413
+ apply_on = "PublishedVersions"
414
+ }
415
+
416
+ environment {
417
+ variables = merge({
418
+ PORT = "8000"
419
+ AWS_LWA_INVOKE_MODE = "buffered"
420
+ AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
421
+ }, var.env)
422
+ }
423
+
424
+ tags = var.tags
425
+ }
426
+
427
+ # IAM role for Lambda execution
428
+ resource "aws_iam_role" "lambda_execution_role" {
429
+ name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
430
+
431
+ assume_role_policy = jsonencode({
432
+ Version = "2012-10-17"
433
+ Statement = [
434
+ {
435
+ Action = "sts:AssumeRole"
436
+ Effect = "Allow"
437
+ Principal = {
438
+ Service = "lambda.amazonaws.com"
439
+ }
440
+ }
441
+ ]
442
+ })
443
+
444
+ tags = var.tags
445
+ }
446
+
447
+ # Attach basic execution policy to Lambda role
448
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
449
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
450
+ role = aws_iam_role.lambda_execution_role.name
451
+ }
452
+
453
+ # Attach X-Ray tracing policy to Lambda role
454
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
455
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
456
+ role = aws_iam_role.lambda_execution_role.name
457
+ }
458
+
459
+ # Additional IAM policies for Lambda (if provided)
460
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
461
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
462
+ name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
463
+ role = aws_iam_role.lambda_execution_role.id
464
+
465
+ policy = jsonencode({
466
+ Version = "2012-10-17"
467
+ Statement = var.additional_iam_policy_statements
468
+ })
469
+ }
470
+
471
+ # CloudWatch Log Group for Lambda
472
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
473
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
474
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
475
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
476
+ name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
477
+ tags = var.tags
478
+ }
479
+
480
+ # Lambda alias pointing to the latest published version for SnapStart
481
+ resource "aws_lambda_alias" "live" {
482
+ name = "live"
483
+ function_name = aws_lambda_function.api_lambda.function_name
484
+ function_version = aws_lambda_function.api_lambda.version
485
+
486
+ depends_on = [aws_lambda_function.api_lambda]
487
+ }
488
+
489
+ # Cognito User Pool Authorizer
490
+ resource "aws_apigatewayv2_authorizer" "cognito_authorizer" {
491
+ api_id = module.http_api.api_id
492
+ authorizer_type = "JWT"
493
+ identity_sources = ["$request.header.Authorization"]
494
+ name = "TestApiAuthorizer-\${random_string.suffix.result}"
495
+
496
+ jwt_configuration {
497
+ audience = var.user_pool_client_ids
498
+ issuer = "https://cognito-idp.\${data.aws_region.current.region}.amazonaws.com/\${var.user_pool_id}"
499
+ }
500
+ }
501
+
502
+ # Lambda integration for HTTP API
503
+ resource "aws_apigatewayv2_integration" "lambda_integration" {
504
+ api_id = module.http_api.api_id
505
+ integration_type = "AWS_PROXY"
506
+ integration_uri = aws_lambda_alias.live.invoke_arn
507
+
508
+ payload_format_version = "2.0"
509
+ timeout_milliseconds = 30000
510
+
511
+ depends_on = [aws_lambda_alias.live]
512
+ }
513
+
514
+ # Route for proxy integration (catches all requests)
515
+ resource "aws_apigatewayv2_route" "proxy_routes" {
516
+ # NB: OPTIONS is omitted here since API Gateway manages responding to preflight requests
517
+ # when cors settings are configured
518
+ for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"])
519
+
520
+ api_id = module.http_api.api_id
521
+ route_key = "\${each.key} /{proxy+}"
522
+ target = "integrations/\${aws_apigatewayv2_integration.lambda_integration.id}"
523
+
524
+ authorization_type = "JWT"
525
+ authorizer_id = aws_apigatewayv2_authorizer.cognito_authorizer.id
526
+
527
+ depends_on = [aws_apigatewayv2_integration.lambda_integration, aws_apigatewayv2_authorizer.cognito_authorizer]
528
+ }
529
+
530
+ # Add API url to runtime config
531
+ module "add_url_to_runtime_config" {
532
+ source = "../../../core/runtime-config/entry"
533
+
534
+ namespace = "connection"
535
+ key = "apis"
536
+ value = { "TestApi" = module.http_api.stage_invoke_url }
537
+
538
+ depends_on = [module.http_api]
539
+ }
540
+
541
+ # Lambda permission for API Gateway to invoke the function via alias
542
+ resource "aws_lambda_permission" "api_gateway_invoke" {
543
+ statement_id = "AllowExecutionFromAPIGateway"
544
+ action = "lambda:InvokeFunction"
545
+ function_name = aws_lambda_function.api_lambda.function_name
546
+ qualifier = aws_lambda_alias.live.name
547
+ principal = "apigateway.amazonaws.com"
548
+ source_arn = "\${module.http_api.api_execution_arn}/*/*"
549
+
550
+ depends_on = [module.http_api, aws_lambda_alias.live]
551
+ }
552
+
553
+ # Outputs
554
+
555
+ # API Gateway Outputs (from core module)
556
+ output "api_id" {
557
+ description = "ID of the HTTP API Gateway"
558
+ value = module.http_api.api_id
559
+ }
560
+
561
+ output "api_arn" {
562
+ description = "ARN of the HTTP API Gateway"
563
+ value = module.http_api.api_arn
564
+ }
565
+
566
+ output "api_endpoint" {
567
+ description = "Base URL of the HTTP API Gateway"
568
+ value = module.http_api.api_endpoint
569
+ }
570
+
571
+ output "api_execution_arn" {
572
+ description = "Execution ARN of the HTTP API Gateway"
573
+ value = module.http_api.api_execution_arn
574
+ }
575
+
576
+ output "stage_invoke_url" {
577
+ description = "Invoke URL of the API Gateway stage"
578
+ value = module.http_api.stage_invoke_url
579
+ }
580
+
581
+ output "stage_arn" {
582
+ description = "ARN of the API Gateway stage"
583
+ value = module.http_api.stage_arn
584
+ }
585
+
586
+ output "stage_execution_arn" {
587
+ description = "Execution ARN of the API Gateway stage"
588
+ value = module.http_api.stage_execution_arn
589
+ }
590
+
591
+ # Lambda Function Outputs
592
+ output "lambda_function_name" {
593
+ description = "Name of the Lambda function"
594
+ value = aws_lambda_function.api_lambda.function_name
595
+ }
596
+
597
+ output "lambda_function_arn" {
598
+ description = "ARN of the Lambda function"
599
+ value = aws_lambda_function.api_lambda.arn
600
+ }
601
+
602
+ output "lambda_invoke_arn" {
603
+ description = "Invoke ARN of the Lambda function"
604
+ value = aws_lambda_function.api_lambda.invoke_arn
605
+ }
606
+
607
+ output "lambda_qualified_arn" {
608
+ description = "Qualified ARN of the Lambda function"
609
+ value = aws_lambda_function.api_lambda.qualified_arn
610
+ }
611
+
612
+ output "lambda_version" {
613
+ description = "Version of the Lambda function"
614
+ value = aws_lambda_function.api_lambda.version
615
+ }
616
+
617
+ output "lambda_source_code_hash" {
618
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
619
+ value = aws_lambda_function.api_lambda.source_code_hash
620
+ }
621
+
622
+ output "lambda_source_code_size" {
623
+ description = "Size of the Lambda deployment package in bytes"
624
+ value = aws_lambda_function.api_lambda.source_code_size
625
+ }
626
+
627
+ # IAM Role Outputs
628
+ output "lambda_execution_role_arn" {
629
+ description = "ARN of the Lambda execution role"
630
+ value = aws_iam_role.lambda_execution_role.arn
631
+ }
632
+
633
+ output "lambda_execution_role_name" {
634
+ description = "Name of the Lambda execution role"
635
+ value = aws_iam_role.lambda_execution_role.name
636
+ }
637
+
638
+ # Integration Outputs
639
+ output "integration_id" {
640
+ description = "ID of the Lambda integration"
641
+ value = aws_apigatewayv2_integration.lambda_integration.id
642
+ }
643
+
644
+ # CloudWatch Log Groups
645
+ output "lambda_log_group_name" {
646
+ description = "Name of the Lambda CloudWatch log group"
647
+ value = aws_cloudwatch_log_group.lambda_logs.name
648
+ }
649
+
650
+ output "lambda_log_group_arn" {
651
+ description = "ARN of the Lambda CloudWatch log group"
652
+ value = aws_cloudwatch_log_group.lambda_logs.arn
653
+ }
654
+
655
+ output "api_log_group_name" {
656
+ description = "Name of the API Gateway CloudWatch log group"
657
+ value = module.http_api.api_log_group_name
658
+ }
659
+
660
+ output "api_log_group_arn" {
661
+ description = "ARN of the API Gateway CloudWatch log group"
662
+ value = module.http_api.api_log_group_arn
663
+ }",
664
+ }
665
+ `;
666
+
667
+ exports[`fastapi project generator > terraform iac > should generate terraform files for HTTP API with Custom auth and snapshot them > terraform-http-custom-files 1`] = `
668
+ {
669
+ "http-api.tf": "# Core HTTP API Gateway module
670
+ # This module creates the API Gateway HTTP API, stage, and logging resources
671
+
672
+ terraform {
673
+ required_version = ">= 1.0"
674
+
675
+ required_providers {
676
+ aws = {
677
+ source = "hashicorp/aws"
678
+ version = "6.52.0"
679
+ }
680
+ }
681
+ }
682
+
683
+ # Core HTTP API Gateway Variables
684
+
685
+ variable "api_name" {
686
+ description = "Name of the HTTP API Gateway"
687
+ type = string
688
+ }
689
+
690
+ variable "api_description" {
691
+ description = "Description of the HTTP API Gateway"
692
+ type = string
693
+ default = "HTTP API Gateway"
694
+ }
695
+
696
+ variable "stage_name" {
697
+ description = "Name of the API Gateway stage"
698
+ type = string
699
+ default = "prod"
700
+ }
701
+
702
+ variable "stage_auto_deploy" {
703
+ description = "Whether to automatically deploy the API stage"
704
+ type = bool
705
+ default = true
706
+ }
707
+
708
+ # CORS Configuration
709
+
710
+ variable "cors_allow_credentials" {
711
+ description = "Whether to allow credentials in CORS requests"
712
+ type = bool
713
+ default = false
714
+ }
715
+
716
+ variable "cors_allow_headers" {
717
+ description = "List of allowed headers for CORS"
718
+ type = list(string)
719
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
720
+ }
721
+
722
+ variable "cors_allow_methods" {
723
+ description = "List of allowed HTTP methods for CORS"
724
+ type = list(string)
725
+ default = ["*"]
726
+ }
727
+
728
+ variable "cors_allow_origins" {
729
+ description = "List of allowed origins for CORS"
730
+ type = list(string)
731
+ default = ["*"]
732
+ }
733
+
734
+ variable "cors_expose_headers" {
735
+ description = "List of headers to expose in CORS responses"
736
+ type = list(string)
737
+ default = []
738
+ }
739
+
740
+ variable "cors_max_age" {
741
+ description = "Maximum age for CORS preflight requests in seconds"
742
+ type = number
743
+ default = 86400
744
+ }
745
+
746
+ # Tags
747
+
748
+ variable "tags" {
749
+ description = "Tags to apply to all resources"
750
+ type = map(string)
751
+ default = {}
752
+ }
753
+
754
+ # Data sources
755
+ data "aws_region" "current" {}
756
+ data "aws_caller_identity" "current" {}
757
+
758
+ # Resources
759
+
760
+ # KMS key for CloudWatch log group encryption
761
+ resource "aws_kms_key" "logs_key" {
762
+ description = "KMS key for CloudWatch log group encryption"
763
+ deletion_window_in_days = 7
764
+ enable_key_rotation = true
765
+
766
+ policy = jsonencode({
767
+ Version = "2012-10-17"
768
+ Statement = [
769
+ {
770
+ Sid = "Enable IAM User Permissions"
771
+ Effect = "Allow"
772
+ Principal = {
773
+ AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
774
+ }
775
+ Action = "kms:*"
776
+ Resource = "*"
777
+ },
778
+ {
779
+ Sid = "Allow CloudWatch Logs"
780
+ Effect = "Allow"
781
+ Principal = {
782
+ Service = "logs.\${data.aws_region.current.region}.amazonaws.com"
783
+ }
784
+ Action = [
785
+ "kms:Encrypt",
786
+ "kms:Decrypt",
787
+ "kms:ReEncrypt*",
788
+ "kms:GenerateDataKey*",
789
+ "kms:DescribeKey"
790
+ ]
791
+ Resource = "*"
792
+ Condition = {
793
+ ArnEquals = {
794
+ "kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:log-group:/aws/apigateway/\${var.api_name}"
795
+ }
796
+ }
797
+ }
798
+ ]
799
+ })
800
+
801
+ tags = var.tags
802
+ }
803
+
804
+ resource "aws_kms_alias" "logs_key_alias" {
805
+ name = "alias/\${var.api_name}-api-logs-encryption"
806
+ target_key_id = aws_kms_key.logs_key.key_id
807
+ }
808
+
809
+ # HTTP API Gateway
810
+ resource "aws_apigatewayv2_api" "http_api" {
811
+ name = var.api_name
812
+ protocol_type = "HTTP"
813
+ description = var.api_description
814
+
815
+ cors_configuration {
816
+ allow_credentials = var.cors_allow_credentials
817
+ allow_headers = var.cors_allow_headers
818
+ allow_methods = var.cors_allow_methods
819
+ allow_origins = var.cors_allow_origins
820
+ expose_headers = var.cors_expose_headers
821
+ max_age = var.cors_max_age
822
+ }
823
+
824
+ tags = var.tags
825
+ }
826
+
827
+ # API Gateway stage
828
+ resource "aws_apigatewayv2_stage" "api_stage" {
829
+ api_id = aws_apigatewayv2_api.http_api.id
830
+ name = var.stage_name
831
+ auto_deploy = var.stage_auto_deploy
832
+
833
+ access_log_settings {
834
+ destination_arn = aws_cloudwatch_log_group.api_logs.arn
835
+ format = jsonencode({
836
+ requestId = "$context.requestId"
837
+ ip = "$context.identity.sourceIp"
838
+ requestTime = "$context.requestTime"
839
+ httpMethod = "$context.httpMethod"
840
+ routeKey = "$context.routeKey"
841
+ status = "$context.status"
842
+ protocol = "$context.protocol"
843
+ responseLength = "$context.responseLength"
844
+ error = "$context.error.message"
845
+ integrationError = "$context.integrationErrorMessage"
846
+ })
847
+ }
848
+
849
+ default_route_settings {
850
+ throttling_burst_limit = 5000
851
+ throttling_rate_limit = 10000
852
+ }
853
+
854
+ tags = var.tags
855
+
856
+ depends_on = [aws_cloudwatch_log_group.api_logs]
857
+ }
858
+
859
+ # CloudWatch Log Group for API Gateway
860
+ resource "aws_cloudwatch_log_group" "api_logs" {
861
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
862
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
863
+ name = "/aws/apigateway/\${var.api_name}"
864
+ kms_key_id = aws_kms_key.logs_key.arn
865
+ tags = var.tags
866
+ }
867
+
868
+ # Outputs
869
+
870
+ output "api_id" {
871
+ description = "ID of the HTTP API Gateway"
872
+ value = aws_apigatewayv2_api.http_api.id
873
+ }
874
+
875
+ output "api_arn" {
876
+ description = "ARN of the HTTP API Gateway"
877
+ value = aws_apigatewayv2_api.http_api.arn
878
+ }
879
+
880
+ output "api_endpoint" {
881
+ description = "Base URL of the HTTP API Gateway"
882
+ value = aws_apigatewayv2_api.http_api.api_endpoint
883
+ }
884
+
885
+ output "api_execution_arn" {
886
+ description = "Execution ARN of the HTTP API Gateway"
887
+ value = aws_apigatewayv2_api.http_api.execution_arn
888
+ }
889
+
890
+ output "stage_id" {
891
+ description = "ID of the API Gateway stage"
892
+ value = aws_apigatewayv2_stage.api_stage.id
893
+ }
894
+
895
+ output "stage_arn" {
896
+ description = "ARN of the API Gateway stage"
897
+ value = aws_apigatewayv2_stage.api_stage.arn
898
+ }
899
+
900
+ output "stage_execution_arn" {
901
+ description = "Execution ARN of the API Gateway stage"
902
+ value = aws_apigatewayv2_stage.api_stage.execution_arn
903
+ }
904
+
905
+ output "stage_invoke_url" {
906
+ description = "Invoke URL of the API Gateway stage"
907
+ value = aws_apigatewayv2_stage.api_stage.invoke_url
908
+ }
909
+
910
+ output "api_log_group_name" {
911
+ description = "Name of the API Gateway CloudWatch log group"
912
+ value = aws_cloudwatch_log_group.api_logs.name
913
+ }
914
+
915
+ output "api_log_group_arn" {
916
+ description = "ARN of the API Gateway CloudWatch log group"
917
+ value = aws_cloudwatch_log_group.api_logs.arn
918
+ }",
919
+ "test-api.tf": "terraform {
920
+ required_version = ">= 1.0"
921
+
922
+ required_providers {
923
+ aws = {
924
+ source = "hashicorp/aws"
925
+ version = "6.52.0"
926
+ }
927
+ }
928
+ }
929
+
930
+
931
+ variable "env" {
932
+ description = "Environment variables for the Lambda function"
933
+ type = map(string)
934
+ default = {}
935
+ }
936
+
937
+ variable "additional_iam_policy_statements" {
938
+ description = "Additional IAM policy statements for the Lambda function"
939
+ type = list(object({
940
+ Effect = string
941
+ Action = list(string)
942
+ Resource = list(string)
943
+ }))
944
+ default = []
945
+ }
946
+
947
+ variable "asset_bucket_name" {
948
+ 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."
949
+ type = string
950
+ }
951
+
952
+ # CORS Configuration (passed to core module)
953
+ variable "cors_allow_credentials" {
954
+ description = "Whether to allow credentials in CORS requests"
955
+ type = bool
956
+ default = false
957
+ }
958
+
959
+ variable "cors_allow_headers" {
960
+ description = "List of allowed headers for CORS"
961
+ type = list(string)
962
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
963
+ }
964
+
965
+ variable "cors_allow_methods" {
966
+ description = "List of allowed HTTP methods for CORS"
967
+ type = list(string)
968
+ default = ["*"]
969
+ }
970
+
971
+ variable "cors_allow_origins" {
972
+ description = "List of allowed origins for CORS"
973
+ type = list(string)
974
+ default = ["*"]
975
+ }
976
+
977
+ variable "cors_expose_headers" {
978
+ description = "List of headers to expose in CORS responses"
979
+ type = list(string)
980
+ default = []
981
+ }
982
+
983
+ # Tags
984
+ variable "tags" {
985
+ description = "Tags to apply to all resources"
986
+ type = map(string)
987
+ default = {}
988
+ }
989
+
990
+ # Get current AWS region and account ID
991
+ data "aws_region" "current" {}
992
+ data "aws_caller_identity" "current" {}
993
+
994
+ resource "random_string" "suffix" {
995
+ length = 8
996
+ special = false
997
+ upper = false
998
+ }
999
+
1000
+ # Resources
1001
+
1002
+ # Create Lambda ZIP file from the bundle directory
1003
+ data "archive_file" "lambda_zip" {
1004
+ type = "zip"
1005
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
1006
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
1007
+ }
1008
+
1009
+ resource "aws_s3_object" "lambda_zip" {
1010
+ bucket = var.asset_bucket_name
1011
+ key = "apis/test-api/\${data.archive_file.lambda_zip.output_sha256}.zip"
1012
+ source = data.archive_file.lambda_zip.output_path
1013
+ source_hash = data.archive_file.lambda_zip.output_base64sha256
1014
+ etag = data.archive_file.lambda_zip.output_md5
1015
+ }
1016
+
1017
+
1018
+ # Use the core HTTP API module
1019
+ module "http_api" {
1020
+ source = "../../../core/api/http-api"
1021
+
1022
+ api_name = "TestApi-\${random_string.suffix.result}"
1023
+ api_description = "TestApi HTTP API"
1024
+ stage_name = "$default"
1025
+ stage_auto_deploy = true
1026
+
1027
+ # CORS Configuration
1028
+ cors_allow_credentials = var.cors_allow_credentials
1029
+ cors_allow_headers = var.cors_allow_headers
1030
+ cors_allow_methods = var.cors_allow_methods
1031
+ cors_allow_origins = var.cors_allow_origins
1032
+ cors_expose_headers = var.cors_expose_headers
1033
+
1034
+ # Tags
1035
+ tags = var.tags
1036
+ }
1037
+
1038
+ # Lambda function
1039
+ # This configures a single "router" lambda to serve all requests
1040
+ resource "aws_lambda_function" "api_lambda" {
1041
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
1042
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
1043
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
1044
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
1045
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
1046
+ s3_bucket = aws_s3_object.lambda_zip.bucket
1047
+ s3_key = aws_s3_object.lambda_zip.key
1048
+ s3_object_version = aws_s3_object.lambda_zip.version_id
1049
+ function_name = "TestApiHandler-\${random_string.suffix.result}"
1050
+ role = aws_iam_role.lambda_execution_role.arn
1051
+ handler = "run.sh"
1052
+ runtime = "python3.14"
1053
+ layers = ["arn:aws:lambda:\${data.aws_region.current.region}:753240598075:layer:LambdaAdapterLayerX86:24"]
1054
+ timeout = 30
1055
+ memory_size = 128
1056
+ publish = true
1057
+
1058
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
1059
+
1060
+ # Enable X-Ray tracing
1061
+ tracing_config {
1062
+ mode = "Active"
1063
+ }
1064
+
1065
+ # Enable SnapStart for faster cold starts
1066
+ snap_start {
1067
+ apply_on = "PublishedVersions"
1068
+ }
1069
+
1070
+ environment {
1071
+ variables = merge({
1072
+ PORT = "8000"
1073
+ AWS_LWA_INVOKE_MODE = "buffered"
1074
+ AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
1075
+ }, var.env)
1076
+ }
1077
+
1078
+ tags = var.tags
1079
+ }
1080
+
1081
+ # IAM role for Lambda execution
1082
+ resource "aws_iam_role" "lambda_execution_role" {
1083
+ name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
1084
+
1085
+ assume_role_policy = jsonencode({
1086
+ Version = "2012-10-17"
1087
+ Statement = [
1088
+ {
1089
+ Action = "sts:AssumeRole"
1090
+ Effect = "Allow"
1091
+ Principal = {
1092
+ Service = "lambda.amazonaws.com"
1093
+ }
1094
+ }
1095
+ ]
1096
+ })
1097
+
1098
+ tags = var.tags
1099
+ }
1100
+
1101
+ # Attach basic execution policy to Lambda role
1102
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
1103
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
1104
+ role = aws_iam_role.lambda_execution_role.name
1105
+ }
1106
+
1107
+ # Attach X-Ray tracing policy to Lambda role
1108
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
1109
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
1110
+ role = aws_iam_role.lambda_execution_role.name
1111
+ }
1112
+
1113
+ # Additional IAM policies for Lambda (if provided)
1114
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
1115
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
1116
+ name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
1117
+ role = aws_iam_role.lambda_execution_role.id
1118
+
1119
+ policy = jsonencode({
1120
+ Version = "2012-10-17"
1121
+ Statement = var.additional_iam_policy_statements
1122
+ })
1123
+ }
1124
+
1125
+ # CloudWatch Log Group for Lambda
1126
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
1127
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
1128
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
1129
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
1130
+ name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
1131
+ tags = var.tags
1132
+ }
1133
+
1134
+ # Lambda alias pointing to the latest published version for SnapStart
1135
+ resource "aws_lambda_alias" "live" {
1136
+ name = "live"
1137
+ function_name = aws_lambda_function.api_lambda.function_name
1138
+ function_version = aws_lambda_function.api_lambda.version
1139
+
1140
+ depends_on = [aws_lambda_function.api_lambda]
1141
+ }
1142
+
1143
+ # Custom Lambda Authorizer
1144
+ resource "aws_lambda_function" "authorizer_lambda" {
1145
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
1146
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for authorizer
1147
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
1148
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
1149
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
1150
+ s3_bucket = aws_s3_object.authorizer_zip.bucket
1151
+ s3_key = aws_s3_object.authorizer_zip.key
1152
+ s3_object_version = aws_s3_object.authorizer_zip.version_id
1153
+ function_name = "TestApiAuthorizer-\${random_string.suffix.result}"
1154
+ role = aws_iam_role.authorizer_execution_role.arn
1155
+ handler = "proj_test_api.authorizer.handler"
1156
+ runtime = "python3.14"
1157
+ timeout = 10
1158
+ memory_size = 128
1159
+
1160
+ source_code_hash = data.archive_file.authorizer_zip.output_base64sha256
1161
+
1162
+ tracing_config {
1163
+ mode = "Active"
1164
+ }
1165
+
1166
+ tags = var.tags
1167
+ }
1168
+
1169
+ resource "aws_iam_role" "authorizer_execution_role" {
1170
+ name = "TestApiAuthorizer-role-\${random_string.suffix.result}"
1171
+
1172
+ assume_role_policy = jsonencode({
1173
+ Version = "2012-10-17"
1174
+ Statement = [
1175
+ {
1176
+ Action = "sts:AssumeRole"
1177
+ Effect = "Allow"
1178
+ Principal = {
1179
+ Service = "lambda.amazonaws.com"
1180
+ }
1181
+ }
1182
+ ]
1183
+ })
1184
+
1185
+ tags = var.tags
1186
+ }
1187
+
1188
+ resource "aws_iam_role_policy_attachment" "authorizer_basic_execution" {
1189
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
1190
+ role = aws_iam_role.authorizer_execution_role.name
1191
+ }
1192
+
1193
+ resource "aws_iam_role_policy_attachment" "authorizer_xray_execution" {
1194
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
1195
+ role = aws_iam_role.authorizer_execution_role.name
1196
+ }
1197
+
1198
+ resource "aws_cloudwatch_log_group" "authorizer_logs" {
1199
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
1200
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
1201
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
1202
+ name = "/aws/lambda/TestApiAuthorizer-\${random_string.suffix.result}"
1203
+ tags = var.tags
1204
+ }
1205
+
1206
+ data "archive_file" "authorizer_zip" {
1207
+ type = "zip"
1208
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
1209
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/authorizer.zip"
1210
+ }
1211
+
1212
+ resource "aws_s3_object" "authorizer_zip" {
1213
+ bucket = var.asset_bucket_name
1214
+ key = "apis/test-api/authorizer-\${data.archive_file.authorizer_zip.output_sha256}.zip"
1215
+ source = data.archive_file.authorizer_zip.output_path
1216
+ source_hash = data.archive_file.authorizer_zip.output_base64sha256
1217
+ etag = data.archive_file.authorizer_zip.output_md5
1218
+ }
1219
+
1220
+ resource "aws_apigatewayv2_authorizer" "custom_authorizer" {
1221
+ api_id = module.http_api.api_id
1222
+ authorizer_type = "REQUEST"
1223
+ authorizer_uri = aws_lambda_function.authorizer_lambda.invoke_arn
1224
+ authorizer_payload_format_version = "2.0"
1225
+ authorizer_result_ttl_in_seconds = 0
1226
+ enable_simple_responses = true
1227
+ name = "TestApiAuthorizer-\${random_string.suffix.result}"
1228
+ }
1229
+
1230
+ resource "aws_lambda_permission" "authorizer_invoke" {
1231
+ statement_id = "AllowAPIGatewayInvokeAuthorizer"
1232
+ action = "lambda:InvokeFunction"
1233
+ function_name = aws_lambda_function.authorizer_lambda.function_name
1234
+ principal = "apigateway.amazonaws.com"
1235
+ source_arn = "\${module.http_api.api_execution_arn}/authorizers/\${aws_apigatewayv2_authorizer.custom_authorizer.id}"
1236
+ }
1237
+
1238
+ # Lambda integration for HTTP API
1239
+ resource "aws_apigatewayv2_integration" "lambda_integration" {
1240
+ api_id = module.http_api.api_id
1241
+ integration_type = "AWS_PROXY"
1242
+ integration_uri = aws_lambda_alias.live.invoke_arn
1243
+
1244
+ payload_format_version = "2.0"
1245
+ timeout_milliseconds = 30000
1246
+
1247
+ depends_on = [aws_lambda_alias.live]
1248
+ }
1249
+
1250
+ # Route for proxy integration (catches all requests)
1251
+ resource "aws_apigatewayv2_route" "proxy_routes" {
1252
+ # NB: OPTIONS is omitted here since API Gateway manages responding to preflight requests
1253
+ # when cors settings are configured
1254
+ for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"])
1255
+
1256
+ api_id = module.http_api.api_id
1257
+ route_key = "\${each.key} /{proxy+}"
1258
+ target = "integrations/\${aws_apigatewayv2_integration.lambda_integration.id}"
1259
+
1260
+ authorization_type = "CUSTOM"
1261
+ authorizer_id = aws_apigatewayv2_authorizer.custom_authorizer.id
1262
+
1263
+ depends_on = [aws_apigatewayv2_integration.lambda_integration, aws_apigatewayv2_authorizer.custom_authorizer]
1264
+ }
1265
+
1266
+ # Add API url to runtime config
1267
+ module "add_url_to_runtime_config" {
1268
+ source = "../../../core/runtime-config/entry"
1269
+
1270
+ namespace = "connection"
1271
+ key = "apis"
1272
+ value = { "TestApi" = module.http_api.stage_invoke_url }
1273
+
1274
+ depends_on = [module.http_api]
1275
+ }
1276
+
1277
+ # Lambda permission for API Gateway to invoke the function via alias
1278
+ resource "aws_lambda_permission" "api_gateway_invoke" {
1279
+ statement_id = "AllowExecutionFromAPIGateway"
1280
+ action = "lambda:InvokeFunction"
1281
+ function_name = aws_lambda_function.api_lambda.function_name
1282
+ qualifier = aws_lambda_alias.live.name
1283
+ principal = "apigateway.amazonaws.com"
1284
+ source_arn = "\${module.http_api.api_execution_arn}/*/*"
1285
+
1286
+ depends_on = [module.http_api, aws_lambda_alias.live]
1287
+ }
1288
+
1289
+ # Outputs
1290
+
1291
+ # API Gateway Outputs (from core module)
1292
+ output "api_id" {
1293
+ description = "ID of the HTTP API Gateway"
1294
+ value = module.http_api.api_id
1295
+ }
1296
+
1297
+ output "api_arn" {
1298
+ description = "ARN of the HTTP API Gateway"
1299
+ value = module.http_api.api_arn
1300
+ }
1301
+
1302
+ output "api_endpoint" {
1303
+ description = "Base URL of the HTTP API Gateway"
1304
+ value = module.http_api.api_endpoint
1305
+ }
1306
+
1307
+ output "api_execution_arn" {
1308
+ description = "Execution ARN of the HTTP API Gateway"
1309
+ value = module.http_api.api_execution_arn
1310
+ }
1311
+
1312
+ output "stage_invoke_url" {
1313
+ description = "Invoke URL of the API Gateway stage"
1314
+ value = module.http_api.stage_invoke_url
1315
+ }
1316
+
1317
+ output "stage_arn" {
1318
+ description = "ARN of the API Gateway stage"
1319
+ value = module.http_api.stage_arn
1320
+ }
1321
+
1322
+ output "stage_execution_arn" {
1323
+ description = "Execution ARN of the API Gateway stage"
1324
+ value = module.http_api.stage_execution_arn
1325
+ }
1326
+
1327
+ # Lambda Function Outputs
1328
+ output "lambda_function_name" {
1329
+ description = "Name of the Lambda function"
1330
+ value = aws_lambda_function.api_lambda.function_name
1331
+ }
1332
+
1333
+ output "lambda_function_arn" {
1334
+ description = "ARN of the Lambda function"
1335
+ value = aws_lambda_function.api_lambda.arn
1336
+ }
1337
+
1338
+ output "lambda_invoke_arn" {
1339
+ description = "Invoke ARN of the Lambda function"
1340
+ value = aws_lambda_function.api_lambda.invoke_arn
1341
+ }
1342
+
1343
+ output "lambda_qualified_arn" {
1344
+ description = "Qualified ARN of the Lambda function"
1345
+ value = aws_lambda_function.api_lambda.qualified_arn
1346
+ }
1347
+
1348
+ output "lambda_version" {
1349
+ description = "Version of the Lambda function"
1350
+ value = aws_lambda_function.api_lambda.version
1351
+ }
1352
+
1353
+ output "lambda_source_code_hash" {
1354
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
1355
+ value = aws_lambda_function.api_lambda.source_code_hash
1356
+ }
1357
+
1358
+ output "lambda_source_code_size" {
1359
+ description = "Size of the Lambda deployment package in bytes"
1360
+ value = aws_lambda_function.api_lambda.source_code_size
1361
+ }
1362
+
1363
+ # IAM Role Outputs
1364
+ output "lambda_execution_role_arn" {
1365
+ description = "ARN of the Lambda execution role"
1366
+ value = aws_iam_role.lambda_execution_role.arn
1367
+ }
1368
+
1369
+ output "lambda_execution_role_name" {
1370
+ description = "Name of the Lambda execution role"
1371
+ value = aws_iam_role.lambda_execution_role.name
1372
+ }
1373
+
1374
+ # Integration Outputs
1375
+ output "integration_id" {
1376
+ description = "ID of the Lambda integration"
1377
+ value = aws_apigatewayv2_integration.lambda_integration.id
1378
+ }
1379
+
1380
+ # CloudWatch Log Groups
1381
+ output "lambda_log_group_name" {
1382
+ description = "Name of the Lambda CloudWatch log group"
1383
+ value = aws_cloudwatch_log_group.lambda_logs.name
1384
+ }
1385
+
1386
+ output "lambda_log_group_arn" {
1387
+ description = "ARN of the Lambda CloudWatch log group"
1388
+ value = aws_cloudwatch_log_group.lambda_logs.arn
1389
+ }
1390
+
1391
+ output "api_log_group_name" {
1392
+ description = "Name of the API Gateway CloudWatch log group"
1393
+ value = module.http_api.api_log_group_name
1394
+ }
1395
+
1396
+ output "api_log_group_arn" {
1397
+ description = "ARN of the API Gateway CloudWatch log group"
1398
+ value = module.http_api.api_log_group_arn
1399
+ }",
1400
+ }
1401
+ `;
1402
+
1403
+ exports[`fastapi project generator > terraform iac > should generate terraform files for HTTP API with IAM auth and snapshot them > terraform-http-iam-files 1`] = `
1404
+ {
1405
+ "http-api.tf": "# Core HTTP API Gateway module
1406
+ # This module creates the API Gateway HTTP API, stage, and logging resources
1407
+
1408
+ terraform {
1409
+ required_version = ">= 1.0"
1410
+
1411
+ required_providers {
1412
+ aws = {
1413
+ source = "hashicorp/aws"
1414
+ version = "6.52.0"
1415
+ }
1416
+ }
1417
+ }
1418
+
1419
+ # Core HTTP API Gateway Variables
1420
+
1421
+ variable "api_name" {
1422
+ description = "Name of the HTTP API Gateway"
1423
+ type = string
1424
+ }
1425
+
1426
+ variable "api_description" {
1427
+ description = "Description of the HTTP API Gateway"
1428
+ type = string
1429
+ default = "HTTP API Gateway"
1430
+ }
1431
+
1432
+ variable "stage_name" {
1433
+ description = "Name of the API Gateway stage"
1434
+ type = string
1435
+ default = "prod"
1436
+ }
1437
+
1438
+ variable "stage_auto_deploy" {
1439
+ description = "Whether to automatically deploy the API stage"
1440
+ type = bool
1441
+ default = true
1442
+ }
1443
+
1444
+ # CORS Configuration
1445
+
1446
+ variable "cors_allow_credentials" {
1447
+ description = "Whether to allow credentials in CORS requests"
1448
+ type = bool
1449
+ default = false
1450
+ }
1451
+
1452
+ variable "cors_allow_headers" {
1453
+ description = "List of allowed headers for CORS"
1454
+ type = list(string)
1455
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
1456
+ }
1457
+
1458
+ variable "cors_allow_methods" {
1459
+ description = "List of allowed HTTP methods for CORS"
1460
+ type = list(string)
1461
+ default = ["*"]
1462
+ }
1463
+
1464
+ variable "cors_allow_origins" {
1465
+ description = "List of allowed origins for CORS"
1466
+ type = list(string)
1467
+ default = ["*"]
1468
+ }
1469
+
1470
+ variable "cors_expose_headers" {
1471
+ description = "List of headers to expose in CORS responses"
1472
+ type = list(string)
1473
+ default = []
1474
+ }
1475
+
1476
+ variable "cors_max_age" {
1477
+ description = "Maximum age for CORS preflight requests in seconds"
1478
+ type = number
1479
+ default = 86400
1480
+ }
1481
+
1482
+ # Tags
1483
+
1484
+ variable "tags" {
1485
+ description = "Tags to apply to all resources"
1486
+ type = map(string)
1487
+ default = {}
1488
+ }
1489
+
1490
+ # Data sources
1491
+ data "aws_region" "current" {}
1492
+ data "aws_caller_identity" "current" {}
1493
+
1494
+ # Resources
1495
+
1496
+ # KMS key for CloudWatch log group encryption
1497
+ resource "aws_kms_key" "logs_key" {
1498
+ description = "KMS key for CloudWatch log group encryption"
1499
+ deletion_window_in_days = 7
1500
+ enable_key_rotation = true
1501
+
1502
+ policy = jsonencode({
1503
+ Version = "2012-10-17"
1504
+ Statement = [
1505
+ {
1506
+ Sid = "Enable IAM User Permissions"
1507
+ Effect = "Allow"
1508
+ Principal = {
1509
+ AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
1510
+ }
1511
+ Action = "kms:*"
1512
+ Resource = "*"
1513
+ },
1514
+ {
1515
+ Sid = "Allow CloudWatch Logs"
1516
+ Effect = "Allow"
1517
+ Principal = {
1518
+ Service = "logs.\${data.aws_region.current.region}.amazonaws.com"
1519
+ }
1520
+ Action = [
1521
+ "kms:Encrypt",
1522
+ "kms:Decrypt",
1523
+ "kms:ReEncrypt*",
1524
+ "kms:GenerateDataKey*",
1525
+ "kms:DescribeKey"
1526
+ ]
1527
+ Resource = "*"
1528
+ Condition = {
1529
+ ArnEquals = {
1530
+ "kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:log-group:/aws/apigateway/\${var.api_name}"
1531
+ }
1532
+ }
1533
+ }
1534
+ ]
1535
+ })
1536
+
1537
+ tags = var.tags
1538
+ }
1539
+
1540
+ resource "aws_kms_alias" "logs_key_alias" {
1541
+ name = "alias/\${var.api_name}-api-logs-encryption"
1542
+ target_key_id = aws_kms_key.logs_key.key_id
1543
+ }
1544
+
1545
+ # HTTP API Gateway
1546
+ resource "aws_apigatewayv2_api" "http_api" {
1547
+ name = var.api_name
1548
+ protocol_type = "HTTP"
1549
+ description = var.api_description
1550
+
1551
+ cors_configuration {
1552
+ allow_credentials = var.cors_allow_credentials
1553
+ allow_headers = var.cors_allow_headers
1554
+ allow_methods = var.cors_allow_methods
1555
+ allow_origins = var.cors_allow_origins
1556
+ expose_headers = var.cors_expose_headers
1557
+ max_age = var.cors_max_age
1558
+ }
1559
+
1560
+ tags = var.tags
1561
+ }
1562
+
1563
+ # API Gateway stage
1564
+ resource "aws_apigatewayv2_stage" "api_stage" {
1565
+ api_id = aws_apigatewayv2_api.http_api.id
1566
+ name = var.stage_name
1567
+ auto_deploy = var.stage_auto_deploy
1568
+
1569
+ access_log_settings {
1570
+ destination_arn = aws_cloudwatch_log_group.api_logs.arn
1571
+ format = jsonencode({
1572
+ requestId = "$context.requestId"
1573
+ ip = "$context.identity.sourceIp"
1574
+ requestTime = "$context.requestTime"
1575
+ httpMethod = "$context.httpMethod"
1576
+ routeKey = "$context.routeKey"
1577
+ status = "$context.status"
1578
+ protocol = "$context.protocol"
1579
+ responseLength = "$context.responseLength"
1580
+ error = "$context.error.message"
1581
+ integrationError = "$context.integrationErrorMessage"
1582
+ })
1583
+ }
1584
+
1585
+ default_route_settings {
1586
+ throttling_burst_limit = 5000
1587
+ throttling_rate_limit = 10000
1588
+ }
1589
+
1590
+ tags = var.tags
1591
+
1592
+ depends_on = [aws_cloudwatch_log_group.api_logs]
1593
+ }
1594
+
1595
+ # CloudWatch Log Group for API Gateway
1596
+ resource "aws_cloudwatch_log_group" "api_logs" {
1597
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
1598
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
1599
+ name = "/aws/apigateway/\${var.api_name}"
1600
+ kms_key_id = aws_kms_key.logs_key.arn
1601
+ tags = var.tags
1602
+ }
1603
+
1604
+ # Outputs
1605
+
1606
+ output "api_id" {
1607
+ description = "ID of the HTTP API Gateway"
1608
+ value = aws_apigatewayv2_api.http_api.id
1609
+ }
1610
+
1611
+ output "api_arn" {
1612
+ description = "ARN of the HTTP API Gateway"
1613
+ value = aws_apigatewayv2_api.http_api.arn
1614
+ }
1615
+
1616
+ output "api_endpoint" {
1617
+ description = "Base URL of the HTTP API Gateway"
1618
+ value = aws_apigatewayv2_api.http_api.api_endpoint
1619
+ }
1620
+
1621
+ output "api_execution_arn" {
1622
+ description = "Execution ARN of the HTTP API Gateway"
1623
+ value = aws_apigatewayv2_api.http_api.execution_arn
1624
+ }
1625
+
1626
+ output "stage_id" {
1627
+ description = "ID of the API Gateway stage"
1628
+ value = aws_apigatewayv2_stage.api_stage.id
1629
+ }
1630
+
1631
+ output "stage_arn" {
1632
+ description = "ARN of the API Gateway stage"
1633
+ value = aws_apigatewayv2_stage.api_stage.arn
1634
+ }
1635
+
1636
+ output "stage_execution_arn" {
1637
+ description = "Execution ARN of the API Gateway stage"
1638
+ value = aws_apigatewayv2_stage.api_stage.execution_arn
1639
+ }
1640
+
1641
+ output "stage_invoke_url" {
1642
+ description = "Invoke URL of the API Gateway stage"
1643
+ value = aws_apigatewayv2_stage.api_stage.invoke_url
1644
+ }
1645
+
1646
+ output "api_log_group_name" {
1647
+ description = "Name of the API Gateway CloudWatch log group"
1648
+ value = aws_cloudwatch_log_group.api_logs.name
1649
+ }
1650
+
1651
+ output "api_log_group_arn" {
1652
+ description = "ARN of the API Gateway CloudWatch log group"
1653
+ value = aws_cloudwatch_log_group.api_logs.arn
1654
+ }",
1655
+ "test-api.tf": "terraform {
1656
+ required_version = ">= 1.0"
1657
+
1658
+ required_providers {
1659
+ aws = {
1660
+ source = "hashicorp/aws"
1661
+ version = "6.52.0"
1662
+ }
1663
+ }
1664
+ }
1665
+
1666
+
1667
+ variable "env" {
1668
+ description = "Environment variables for the Lambda function"
1669
+ type = map(string)
1670
+ default = {}
1671
+ }
1672
+
1673
+ variable "additional_iam_policy_statements" {
1674
+ description = "Additional IAM policy statements for the Lambda function"
1675
+ type = list(object({
1676
+ Effect = string
1677
+ Action = list(string)
1678
+ Resource = list(string)
1679
+ }))
1680
+ default = []
1681
+ }
1682
+
1683
+ variable "asset_bucket_name" {
1684
+ 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."
1685
+ type = string
1686
+ }
1687
+
1688
+ # CORS Configuration (passed to core module)
1689
+ variable "cors_allow_credentials" {
1690
+ description = "Whether to allow credentials in CORS requests"
1691
+ type = bool
1692
+ default = false
1693
+ }
1694
+
1695
+ variable "cors_allow_headers" {
1696
+ description = "List of allowed headers for CORS"
1697
+ type = list(string)
1698
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
1699
+ }
1700
+
1701
+ variable "cors_allow_methods" {
1702
+ description = "List of allowed HTTP methods for CORS"
1703
+ type = list(string)
1704
+ default = ["*"]
1705
+ }
1706
+
1707
+ variable "cors_allow_origins" {
1708
+ description = "List of allowed origins for CORS"
1709
+ type = list(string)
1710
+ default = ["*"]
1711
+ }
1712
+
1713
+ variable "cors_expose_headers" {
1714
+ description = "List of headers to expose in CORS responses"
1715
+ type = list(string)
1716
+ default = []
1717
+ }
1718
+
1719
+ # Tags
1720
+ variable "tags" {
1721
+ description = "Tags to apply to all resources"
1722
+ type = map(string)
1723
+ default = {}
1724
+ }
1725
+
1726
+ # Get current AWS region and account ID
1727
+ data "aws_region" "current" {}
1728
+ data "aws_caller_identity" "current" {}
1729
+
1730
+ resource "random_string" "suffix" {
1731
+ length = 8
1732
+ special = false
1733
+ upper = false
1734
+ }
1735
+
1736
+ # Resources
1737
+
1738
+ # Create Lambda ZIP file from the bundle directory
1739
+ data "archive_file" "lambda_zip" {
1740
+ type = "zip"
1741
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
1742
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
1743
+ }
1744
+
1745
+ resource "aws_s3_object" "lambda_zip" {
1746
+ bucket = var.asset_bucket_name
1747
+ key = "apis/test-api/\${data.archive_file.lambda_zip.output_sha256}.zip"
1748
+ source = data.archive_file.lambda_zip.output_path
1749
+ source_hash = data.archive_file.lambda_zip.output_base64sha256
1750
+ etag = data.archive_file.lambda_zip.output_md5
1751
+ }
1752
+
1753
+
1754
+ # Use the core HTTP API module
1755
+ module "http_api" {
1756
+ source = "../../../core/api/http-api"
1757
+
1758
+ api_name = "TestApi-\${random_string.suffix.result}"
1759
+ api_description = "TestApi HTTP API"
1760
+ stage_name = "$default"
1761
+ stage_auto_deploy = true
1762
+
1763
+ # CORS Configuration
1764
+ cors_allow_credentials = var.cors_allow_credentials
1765
+ cors_allow_headers = var.cors_allow_headers
1766
+ cors_allow_methods = var.cors_allow_methods
1767
+ cors_allow_origins = var.cors_allow_origins
1768
+ cors_expose_headers = var.cors_expose_headers
1769
+
1770
+ # Tags
1771
+ tags = var.tags
1772
+ }
1773
+
1774
+ # Lambda function
1775
+ # This configures a single "router" lambda to serve all requests
1776
+ resource "aws_lambda_function" "api_lambda" {
1777
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
1778
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
1779
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
1780
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
1781
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
1782
+ s3_bucket = aws_s3_object.lambda_zip.bucket
1783
+ s3_key = aws_s3_object.lambda_zip.key
1784
+ s3_object_version = aws_s3_object.lambda_zip.version_id
1785
+ function_name = "TestApiHandler-\${random_string.suffix.result}"
1786
+ role = aws_iam_role.lambda_execution_role.arn
1787
+ handler = "run.sh"
1788
+ runtime = "python3.14"
1789
+ layers = ["arn:aws:lambda:\${data.aws_region.current.region}:753240598075:layer:LambdaAdapterLayerX86:24"]
1790
+ timeout = 30
1791
+ memory_size = 128
1792
+ publish = true
1793
+
1794
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
1795
+
1796
+ # Enable X-Ray tracing
1797
+ tracing_config {
1798
+ mode = "Active"
1799
+ }
1800
+
1801
+ # Enable SnapStart for faster cold starts
1802
+ snap_start {
1803
+ apply_on = "PublishedVersions"
1804
+ }
1805
+
1806
+ environment {
1807
+ variables = merge({
1808
+ PORT = "8000"
1809
+ AWS_LWA_INVOKE_MODE = "buffered"
1810
+ AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
1811
+ }, var.env)
1812
+ }
1813
+
1814
+ tags = var.tags
1815
+ }
1816
+
1817
+ # IAM role for Lambda execution
1818
+ resource "aws_iam_role" "lambda_execution_role" {
1819
+ name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
1820
+
1821
+ assume_role_policy = jsonencode({
1822
+ Version = "2012-10-17"
1823
+ Statement = [
1824
+ {
1825
+ Action = "sts:AssumeRole"
1826
+ Effect = "Allow"
1827
+ Principal = {
1828
+ Service = "lambda.amazonaws.com"
1829
+ }
1830
+ }
1831
+ ]
1832
+ })
1833
+
1834
+ tags = var.tags
1835
+ }
1836
+
1837
+ # Attach basic execution policy to Lambda role
1838
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
1839
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
1840
+ role = aws_iam_role.lambda_execution_role.name
1841
+ }
1842
+
1843
+ # Attach X-Ray tracing policy to Lambda role
1844
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
1845
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
1846
+ role = aws_iam_role.lambda_execution_role.name
1847
+ }
1848
+
1849
+ # Additional IAM policies for Lambda (if provided)
1850
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
1851
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
1852
+ name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
1853
+ role = aws_iam_role.lambda_execution_role.id
1854
+
1855
+ policy = jsonencode({
1856
+ Version = "2012-10-17"
1857
+ Statement = var.additional_iam_policy_statements
1858
+ })
1859
+ }
1860
+
1861
+ # CloudWatch Log Group for Lambda
1862
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
1863
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
1864
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
1865
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
1866
+ name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
1867
+ tags = var.tags
1868
+ }
1869
+
1870
+ # Lambda alias pointing to the latest published version for SnapStart
1871
+ resource "aws_lambda_alias" "live" {
1872
+ name = "live"
1873
+ function_name = aws_lambda_function.api_lambda.function_name
1874
+ function_version = aws_lambda_function.api_lambda.version
1875
+
1876
+ depends_on = [aws_lambda_function.api_lambda]
1877
+ }
1878
+
1879
+
1880
+ # Lambda integration for HTTP API
1881
+ resource "aws_apigatewayv2_integration" "lambda_integration" {
1882
+ api_id = module.http_api.api_id
1883
+ integration_type = "AWS_PROXY"
1884
+ integration_uri = aws_lambda_alias.live.invoke_arn
1885
+
1886
+ payload_format_version = "2.0"
1887
+ timeout_milliseconds = 30000
1888
+
1889
+ depends_on = [aws_lambda_alias.live]
1890
+ }
1891
+
1892
+ # Route for proxy integration (catches all requests)
1893
+ resource "aws_apigatewayv2_route" "proxy_routes" {
1894
+ # NB: OPTIONS is omitted here since API Gateway manages responding to preflight requests
1895
+ # when cors settings are configured
1896
+ for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"])
1897
+
1898
+ api_id = module.http_api.api_id
1899
+ route_key = "\${each.key} /{proxy+}"
1900
+ target = "integrations/\${aws_apigatewayv2_integration.lambda_integration.id}"
1901
+
1902
+ authorization_type = "AWS_IAM"
1903
+
1904
+ depends_on = [aws_apigatewayv2_integration.lambda_integration]
1905
+ }
1906
+
1907
+ # Add API url to runtime config
1908
+ module "add_url_to_runtime_config" {
1909
+ source = "../../../core/runtime-config/entry"
1910
+
1911
+ namespace = "connection"
1912
+ key = "apis"
1913
+ value = { "TestApi" = module.http_api.stage_invoke_url }
1914
+
1915
+ depends_on = [module.http_api]
1916
+ }
1917
+
1918
+ # Lambda permission for API Gateway to invoke the function via alias
1919
+ resource "aws_lambda_permission" "api_gateway_invoke" {
1920
+ statement_id = "AllowExecutionFromAPIGateway"
1921
+ action = "lambda:InvokeFunction"
1922
+ function_name = aws_lambda_function.api_lambda.function_name
1923
+ qualifier = aws_lambda_alias.live.name
1924
+ principal = "apigateway.amazonaws.com"
1925
+ source_arn = "\${module.http_api.api_execution_arn}/*/*"
1926
+
1927
+ depends_on = [module.http_api, aws_lambda_alias.live]
1928
+ }
1929
+
1930
+ # Outputs
1931
+
1932
+ # API Gateway Outputs (from core module)
1933
+ output "api_id" {
1934
+ description = "ID of the HTTP API Gateway"
1935
+ value = module.http_api.api_id
1936
+ }
1937
+
1938
+ output "api_arn" {
1939
+ description = "ARN of the HTTP API Gateway"
1940
+ value = module.http_api.api_arn
1941
+ }
1942
+
1943
+ output "api_endpoint" {
1944
+ description = "Base URL of the HTTP API Gateway"
1945
+ value = module.http_api.api_endpoint
1946
+ }
1947
+
1948
+ output "api_execution_arn" {
1949
+ description = "Execution ARN of the HTTP API Gateway"
1950
+ value = module.http_api.api_execution_arn
1951
+ }
1952
+
1953
+ output "stage_invoke_url" {
1954
+ description = "Invoke URL of the API Gateway stage"
1955
+ value = module.http_api.stage_invoke_url
1956
+ }
1957
+
1958
+ output "stage_arn" {
1959
+ description = "ARN of the API Gateway stage"
1960
+ value = module.http_api.stage_arn
1961
+ }
1962
+
1963
+ output "stage_execution_arn" {
1964
+ description = "Execution ARN of the API Gateway stage"
1965
+ value = module.http_api.stage_execution_arn
1966
+ }
1967
+
1968
+ # Lambda Function Outputs
1969
+ output "lambda_function_name" {
1970
+ description = "Name of the Lambda function"
1971
+ value = aws_lambda_function.api_lambda.function_name
1972
+ }
1973
+
1974
+ output "lambda_function_arn" {
1975
+ description = "ARN of the Lambda function"
1976
+ value = aws_lambda_function.api_lambda.arn
1977
+ }
1978
+
1979
+ output "lambda_invoke_arn" {
1980
+ description = "Invoke ARN of the Lambda function"
1981
+ value = aws_lambda_function.api_lambda.invoke_arn
1982
+ }
1983
+
1984
+ output "lambda_qualified_arn" {
1985
+ description = "Qualified ARN of the Lambda function"
1986
+ value = aws_lambda_function.api_lambda.qualified_arn
1987
+ }
1988
+
1989
+ output "lambda_version" {
1990
+ description = "Version of the Lambda function"
1991
+ value = aws_lambda_function.api_lambda.version
1992
+ }
1993
+
1994
+ output "lambda_source_code_hash" {
1995
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
1996
+ value = aws_lambda_function.api_lambda.source_code_hash
1997
+ }
1998
+
1999
+ output "lambda_source_code_size" {
2000
+ description = "Size of the Lambda deployment package in bytes"
2001
+ value = aws_lambda_function.api_lambda.source_code_size
2002
+ }
2003
+
2004
+ # IAM Role Outputs
2005
+ output "lambda_execution_role_arn" {
2006
+ description = "ARN of the Lambda execution role"
2007
+ value = aws_iam_role.lambda_execution_role.arn
2008
+ }
2009
+
2010
+ output "lambda_execution_role_name" {
2011
+ description = "Name of the Lambda execution role"
2012
+ value = aws_iam_role.lambda_execution_role.name
2013
+ }
2014
+
2015
+ # Integration Outputs
2016
+ output "integration_id" {
2017
+ description = "ID of the Lambda integration"
2018
+ value = aws_apigatewayv2_integration.lambda_integration.id
2019
+ }
2020
+
2021
+ # CloudWatch Log Groups
2022
+ output "lambda_log_group_name" {
2023
+ description = "Name of the Lambda CloudWatch log group"
2024
+ value = aws_cloudwatch_log_group.lambda_logs.name
2025
+ }
2026
+
2027
+ output "lambda_log_group_arn" {
2028
+ description = "ARN of the Lambda CloudWatch log group"
2029
+ value = aws_cloudwatch_log_group.lambda_logs.arn
2030
+ }
2031
+
2032
+ output "api_log_group_name" {
2033
+ description = "Name of the API Gateway CloudWatch log group"
2034
+ value = module.http_api.api_log_group_name
2035
+ }
2036
+
2037
+ output "api_log_group_arn" {
2038
+ description = "ARN of the API Gateway CloudWatch log group"
2039
+ value = module.http_api.api_log_group_arn
2040
+ }",
2041
+ }
2042
+ `;
2043
+
2044
+ exports[`fastapi project generator > terraform iac > should generate terraform files for REST API with Cognito auth and snapshot them > terraform-rest-cognito-files 1`] = `
2045
+ {
2046
+ "rest-api.tf": "# Core REST API Gateway module
2047
+ # This module creates the API Gateway REST API, deployment, stage, and logging resources
2048
+
2049
+ terraform {
2050
+ required_version = ">= 1.0"
2051
+
2052
+ required_providers {
2053
+ aws = {
2054
+ source = "hashicorp/aws"
2055
+ version = "6.52.0"
2056
+ }
2057
+ random = {
2058
+ source = "hashicorp/random"
2059
+ version = "3.9.0"
2060
+ }
2061
+ }
2062
+ }
2063
+
2064
+ # Core REST API Gateway Variables
2065
+
2066
+ variable "api_name" {
2067
+ description = "Name of the REST API Gateway"
2068
+ type = string
2069
+ }
2070
+
2071
+ variable "api_description" {
2072
+ description = "Description of the REST API Gateway"
2073
+ type = string
2074
+ default = "REST API Gateway"
2075
+ }
2076
+
2077
+ variable "stage_name" {
2078
+ description = "Name of the API Gateway stage"
2079
+ type = string
2080
+ default = "prod"
2081
+ }
2082
+
2083
+ variable "stage_auto_deploy" {
2084
+ description = "Whether to automatically deploy the API stage"
2085
+ type = bool
2086
+ default = true
2087
+ }
2088
+
2089
+ variable "enable_waf" {
2090
+ description = "Whether to enable AWS WAFv2 with the default managed ruleset (AWSManagedRulesCommonRuleSet and AWSManagedRulesKnownBadInputsRuleSet). The Web ACL is created here and associated with the stage in the consuming module."
2091
+ type = bool
2092
+ default = true
2093
+ }
2094
+
2095
+ # CORS Configuration
2096
+
2097
+ variable "cors_allow_headers" {
2098
+ description = "List of allowed headers for CORS"
2099
+ type = list(string)
2100
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
2101
+ }
2102
+
2103
+ variable "cors_allow_methods" {
2104
+ description = "List of allowed HTTP methods for CORS"
2105
+ type = list(string)
2106
+ default = ["*"]
2107
+ }
2108
+
2109
+ variable "cors_allow_origins" {
2110
+ description = "List of allowed origins for CORS"
2111
+ type = list(string)
2112
+ default = ["*"]
2113
+ }
2114
+
2115
+ # Tags
2116
+
2117
+ variable "tags" {
2118
+ description = "Tags to apply to all resources"
2119
+ type = map(string)
2120
+ default = {}
2121
+ }
2122
+
2123
+ # Data sources
2124
+ data "aws_region" "current" {}
2125
+ data "aws_caller_identity" "current" {}
2126
+
2127
+ # Resources
2128
+
2129
+ # REST API Gateway
2130
+ resource "aws_api_gateway_rest_api" "rest_api" {
2131
+ name = var.api_name
2132
+ description = var.api_description
2133
+
2134
+ endpoint_configuration {
2135
+ types = ["REGIONAL"]
2136
+ }
2137
+
2138
+ lifecycle {
2139
+ create_before_destroy = true
2140
+ }
2141
+
2142
+ tags = var.tags
2143
+ }
2144
+
2145
+ # Note: Deployment, stage and access logging are created in the consuming module
2146
+ # (e.g., foo-api.tf) after all methods and integrations are defined
2147
+
2148
+ # Gateway Response for CORS (4XX errors)
2149
+ resource "aws_api_gateway_gateway_response" "cors_4xx" {
2150
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
2151
+ response_type = "DEFAULT_4XX"
2152
+
2153
+ response_parameters = {
2154
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
2155
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
2156
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
2157
+ }
2158
+ }
2159
+
2160
+ # Gateway Response for CORS (5XX errors)
2161
+ resource "aws_api_gateway_gateway_response" "cors_5xx" {
2162
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
2163
+ response_type = "DEFAULT_5XX"
2164
+
2165
+ response_parameters = {
2166
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
2167
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
2168
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
2169
+ }
2170
+ }
2171
+
2172
+ resource "aws_wafv2_web_acl" "api_waf" {
2173
+ #checkov:skip=CKV2_AWS_31:Logging configuration is defined below in aws_wafv2_web_acl_logging_configuration.api_waf_logging; Checkov does not resolve the separate resource
2174
+ count = var.enable_waf ? 1 : 0
2175
+
2176
+ name = "\${var.api_name}-waf"
2177
+ scope = "REGIONAL"
2178
+
2179
+ default_action {
2180
+ allow {}
2181
+ }
2182
+
2183
+ rule {
2184
+ name = "CRSRule"
2185
+ priority = 0
2186
+
2187
+ override_action {
2188
+ none {}
2189
+ }
2190
+
2191
+ statement {
2192
+ managed_rule_group_statement {
2193
+ name = "AWSManagedRulesCommonRuleSet"
2194
+ vendor_name = "AWS"
2195
+
2196
+ # Count instead of Block: the CRS 8 KB body limit is too restrictive for most APIs.
2197
+ rule_action_override {
2198
+ name = "SizeRestrictions_BODY"
2199
+ action_to_use {
2200
+ count {}
2201
+ }
2202
+ }
2203
+ }
2204
+ }
2205
+
2206
+ visibility_config {
2207
+ cloudwatch_metrics_enabled = true
2208
+ metric_name = "\${var.api_name}WebAcl-CRS"
2209
+ sampled_requests_enabled = true
2210
+ }
2211
+ }
2212
+
2213
+ rule {
2214
+ name = "KnownBadInputsRule"
2215
+ priority = 1
2216
+
2217
+ override_action {
2218
+ none {}
2219
+ }
2220
+
2221
+ statement {
2222
+ managed_rule_group_statement {
2223
+ name = "AWSManagedRulesKnownBadInputsRuleSet"
2224
+ vendor_name = "AWS"
2225
+ }
2226
+ }
2227
+
2228
+ visibility_config {
2229
+ cloudwatch_metrics_enabled = true
2230
+ metric_name = "\${var.api_name}WebAcl-KnownBadInputs"
2231
+ sampled_requests_enabled = true
2232
+ }
2233
+ }
2234
+
2235
+ visibility_config {
2236
+ cloudwatch_metrics_enabled = true
2237
+ metric_name = "\${var.api_name}WebAcl"
2238
+ sampled_requests_enabled = true
2239
+ }
2240
+
2241
+ tags = var.tags
2242
+
2243
+ lifecycle {
2244
+ create_before_destroy = true
2245
+ }
2246
+ }
2247
+
2248
+ # CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
2249
+ resource "aws_cloudwatch_log_group" "api_waf_logs" {
2250
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
2251
+ #checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
2252
+ count = var.enable_waf ? 1 : 0
2253
+
2254
+ name = "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}"
2255
+ retention_in_days = 30
2256
+ tags = var.tags
2257
+ }
2258
+
2259
+ resource "random_id" "waf_log_suffix" {
2260
+ count = var.enable_waf ? 1 : 0
2261
+ byte_length = 4
2262
+ }
2263
+
2264
+ resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
2265
+ count = var.enable_waf ? 1 : 0
2266
+
2267
+ log_destination_configs = [aws_cloudwatch_log_group.api_waf_logs[0].arn]
2268
+ resource_arn = aws_wafv2_web_acl.api_waf[0].arn
2269
+ }
2270
+
2271
+
2272
+ # Outputs
2273
+
2274
+ output "api_id" {
2275
+ description = "ID of the REST API Gateway"
2276
+ value = aws_api_gateway_rest_api.rest_api.id
2277
+ }
2278
+
2279
+ output "api_arn" {
2280
+ description = "ARN of the REST API Gateway"
2281
+ value = aws_api_gateway_rest_api.rest_api.arn
2282
+ }
2283
+
2284
+ output "api_endpoint" {
2285
+ description = "Base URL of the REST API Gateway"
2286
+ value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
2287
+ }
2288
+
2289
+ output "api_execution_arn" {
2290
+ description = "Execution ARN of the REST API Gateway"
2291
+ value = aws_api_gateway_rest_api.rest_api.execution_arn
2292
+ }
2293
+
2294
+ output "api_root_resource_id" {
2295
+ description = "Root resource ID of the REST API Gateway"
2296
+ value = aws_api_gateway_rest_api.rest_api.root_resource_id
2297
+ }
2298
+
2299
+ output "waf_web_acl_arn" {
2300
+ description = "ARN of the WAFv2 Web ACL associated with the API, or null if WAF is disabled"
2301
+ value = var.enable_waf ? aws_wafv2_web_acl.api_waf[0].arn : null
2302
+ }
2303
+
2304
+ # Note: Stage, deployment and access log outputs are provided by the consuming module",
2305
+ "test-api.tf": "terraform {
2306
+ required_version = ">= 1.0"
2307
+
2308
+ required_providers {
2309
+ aws = {
2310
+ source = "hashicorp/aws"
2311
+ version = "6.52.0"
2312
+ }
2313
+ }
2314
+ }
2315
+
2316
+ # Authentication Configuration
2317
+ variable "user_pool_id" {
2318
+ description = "Cognito User Pool ID for authentication"
2319
+ type = string
2320
+ }
2321
+
2322
+ variable "user_pool_client_ids" {
2323
+ description = "List of Cognito User Pool Client IDs"
2324
+ type = list(string)
2325
+ }
2326
+
2327
+ variable "env" {
2328
+ description = "Environment variables for the Lambda function"
2329
+ type = map(string)
2330
+ default = {}
2331
+ }
2332
+
2333
+ variable "additional_iam_policy_statements" {
2334
+ description = "Additional IAM policy statements for the Lambda function"
2335
+ type = list(object({
2336
+ Effect = string
2337
+ Action = list(string)
2338
+ Resource = list(string)
2339
+ }))
2340
+ default = []
2341
+ }
2342
+
2343
+ variable "asset_bucket_name" {
2344
+ 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."
2345
+ type = string
2346
+ }
2347
+
2348
+ # CORS Configuration (passed to core module)
2349
+
2350
+ variable "cors_allow_headers" {
2351
+ description = "List of allowed headers for CORS"
2352
+ type = list(string)
2353
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
2354
+ }
2355
+
2356
+ variable "cors_allow_methods" {
2357
+ description = "List of allowed HTTP methods for CORS"
2358
+ type = list(string)
2359
+ default = ["*"]
2360
+ }
2361
+
2362
+ variable "cors_allow_origins" {
2363
+ description = "List of allowed origins for CORS"
2364
+ type = list(string)
2365
+ default = ["*"]
2366
+ }
2367
+
2368
+ # WAF Configuration
2369
+ variable "enable_waf" {
2370
+ description = "Whether to enable AWS WAFv2 with the default managed ruleset on the API stage"
2371
+ type = bool
2372
+ default = true
2373
+ }
2374
+
2375
+ # Tags
2376
+ variable "tags" {
2377
+ description = "Tags to apply to all resources"
2378
+ type = map(string)
2379
+ default = {}
2380
+ }
2381
+
2382
+ # Get current AWS region and account ID
2383
+ data "aws_region" "current" {}
2384
+ data "aws_caller_identity" "current" {}
2385
+
2386
+ resource "random_string" "suffix" {
2387
+ length = 8
2388
+ special = false
2389
+ upper = false
2390
+ }
2391
+
2392
+ # Resources
2393
+
2394
+ # Create Lambda ZIP file from the bundle directory
2395
+ data "archive_file" "lambda_zip" {
2396
+ type = "zip"
2397
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
2398
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
2399
+ }
2400
+
2401
+ resource "aws_s3_object" "lambda_zip" {
2402
+ bucket = var.asset_bucket_name
2403
+ key = "apis/test-api/\${data.archive_file.lambda_zip.output_sha256}.zip"
2404
+ source = data.archive_file.lambda_zip.output_path
2405
+ source_hash = data.archive_file.lambda_zip.output_base64sha256
2406
+ etag = data.archive_file.lambda_zip.output_md5
2407
+ }
2408
+
2409
+ # Use the core REST API module
2410
+ # Account-level CloudWatch role required for REST API access logging
2411
+ module "account" {
2412
+ source = "../../../core/api/api-gateway-account"
2413
+ }
2414
+
2415
+ module "rest_api" {
2416
+ source = "../../../core/api/rest-api"
2417
+
2418
+ api_name = "TestApi-\${random_string.suffix.result}"
2419
+ api_description = "TestApi REST API"
2420
+ stage_name = "prod"
2421
+ stage_auto_deploy = true
2422
+
2423
+ # WAF Configuration
2424
+ enable_waf = var.enable_waf
2425
+
2426
+ # CORS Configuration
2427
+ cors_allow_headers = var.cors_allow_headers
2428
+ cors_allow_methods = var.cors_allow_methods
2429
+ cors_allow_origins = var.cors_allow_origins
2430
+
2431
+ # Tags
2432
+ tags = var.tags
2433
+ }
2434
+
2435
+ resource "aws_wafv2_web_acl_association" "api_waf_association" {
2436
+ count = var.enable_waf ? 1 : 0
2437
+
2438
+ resource_arn = aws_api_gateway_stage.api_stage.arn
2439
+ web_acl_arn = module.rest_api.waf_web_acl_arn
2440
+
2441
+ depends_on = [aws_api_gateway_stage.api_stage]
2442
+ }
2443
+
2444
+ # Lambda function
2445
+ resource "aws_lambda_function" "api_lambda" {
2446
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
2447
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
2448
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
2449
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
2450
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
2451
+ s3_bucket = aws_s3_object.lambda_zip.bucket
2452
+ s3_key = aws_s3_object.lambda_zip.key
2453
+ s3_object_version = aws_s3_object.lambda_zip.version_id
2454
+ function_name = "TestApiHandler-\${random_string.suffix.result}"
2455
+ role = aws_iam_role.lambda_execution_role.arn
2456
+ handler = "run.sh"
2457
+ runtime = "python3.14"
2458
+ layers = ["arn:aws:lambda:\${data.aws_region.current.region}:753240598075:layer:LambdaAdapterLayerX86:24"]
2459
+ timeout = 30
2460
+ memory_size = 128
2461
+ publish = true
2462
+
2463
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
2464
+
2465
+ # Enable X-Ray tracing
2466
+ tracing_config {
2467
+ mode = "Active"
2468
+ }
2469
+
2470
+ # Enable SnapStart for faster cold starts
2471
+ snap_start {
2472
+ apply_on = "PublishedVersions"
2473
+ }
2474
+
2475
+ environment {
2476
+ variables = merge({
2477
+ PORT = "8000"
2478
+ AWS_LWA_INVOKE_MODE = "response_stream"
2479
+ AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
2480
+ }, var.env)
2481
+ }
2482
+
2483
+ tags = var.tags
2484
+ }
2485
+
2486
+ # IAM role for Lambda execution
2487
+ resource "aws_iam_role" "lambda_execution_role" {
2488
+ name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
2489
+
2490
+ assume_role_policy = jsonencode({
2491
+ Version = "2012-10-17"
2492
+ Statement = [
2493
+ {
2494
+ Action = "sts:AssumeRole"
2495
+ Effect = "Allow"
2496
+ Principal = {
2497
+ Service = "lambda.amazonaws.com"
2498
+ }
2499
+ }
2500
+ ]
2501
+ })
2502
+
2503
+ tags = var.tags
2504
+ }
2505
+
2506
+ # Attach basic execution policy to Lambda role
2507
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
2508
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
2509
+ role = aws_iam_role.lambda_execution_role.name
2510
+ }
2511
+
2512
+ # Attach X-Ray tracing policy to Lambda role
2513
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
2514
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
2515
+ role = aws_iam_role.lambda_execution_role.name
2516
+ }
2517
+
2518
+ # Additional IAM policies for Lambda (if provided)
2519
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
2520
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
2521
+ name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
2522
+ role = aws_iam_role.lambda_execution_role.id
2523
+
2524
+ policy = jsonencode({
2525
+ Version = "2012-10-17"
2526
+ Statement = var.additional_iam_policy_statements
2527
+ })
2528
+ }
2529
+
2530
+ # CloudWatch Log Group for Lambda
2531
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
2532
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
2533
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
2534
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
2535
+ name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
2536
+ tags = var.tags
2537
+ }
2538
+
2539
+ # Lambda alias pointing to the latest published version for SnapStart
2540
+ resource "aws_lambda_alias" "live" {
2541
+ name = "live"
2542
+ function_name = aws_lambda_function.api_lambda.function_name
2543
+ function_version = aws_lambda_function.api_lambda.version
2544
+
2545
+ depends_on = [aws_lambda_function.api_lambda]
2546
+ }
2547
+
2548
+ # Cognito User Pool Authorizer
2549
+ resource "aws_api_gateway_authorizer" "cognito_authorizer" {
2550
+ name = "TestApiAuthorizer-\${random_string.suffix.result}"
2551
+ rest_api_id = module.rest_api.api_id
2552
+ type = "COGNITO_USER_POOLS"
2553
+ provider_arns = ["arn:aws:cognito-idp:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:userpool/\${var.user_pool_id}"]
2554
+ identity_source = "method.request.header.Authorization"
2555
+ }
2556
+
2557
+ # Create proxy resource (captures all paths)
2558
+ resource "aws_api_gateway_resource" "proxy_resource" {
2559
+ rest_api_id = module.rest_api.api_id
2560
+ parent_id = module.rest_api.api_root_resource_id
2561
+ path_part = "{proxy+}"
2562
+ }
2563
+
2564
+ # Lambda integration for REST API
2565
+ resource "aws_api_gateway_integration" "lambda_integration" {
2566
+ rest_api_id = module.rest_api.api_id
2567
+ resource_id = aws_api_gateway_resource.proxy_resource.id
2568
+ http_method = aws_api_gateway_method.proxy_method.http_method
2569
+
2570
+ integration_http_method = "POST"
2571
+ type = "AWS_PROXY"
2572
+ # Use the response streaming invocation path with the alias ARN for SnapStart
2573
+ uri = "arn:aws:apigateway:\${data.aws_region.current.region}:lambda:path/2021-11-15/functions/\${aws_lambda_alias.live.arn}/response-streaming-invocations"
2574
+ response_transfer_mode = "STREAM"
2575
+
2576
+ depends_on = [aws_lambda_alias.live]
2577
+ }
2578
+
2579
+ # Method for proxy integration
2580
+ resource "aws_api_gateway_method" "proxy_method" {
2581
+ #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
2582
+ rest_api_id = module.rest_api.api_id
2583
+ resource_id = aws_api_gateway_resource.proxy_resource.id
2584
+ http_method = "ANY"
2585
+
2586
+ authorization = "COGNITO_USER_POOLS"
2587
+ authorizer_id = aws_api_gateway_authorizer.cognito_authorizer.id
2588
+ # Accept access tokens from both sign-in flows: 'openid' (Cognito hosted UI)
2589
+ # and 'aws.cognito.signin.user.admin' (Cognito admin/SRP auth APIs).
2590
+ authorization_scopes = ["openid", "aws.cognito.signin.user.admin"]
2591
+
2592
+ request_parameters = {
2593
+ "method.request.path.proxy" = true
2594
+ }
2595
+
2596
+ depends_on = [aws_api_gateway_authorizer.cognito_authorizer]
2597
+ }
2598
+
2599
+ # OPTIONS method for CORS preflight
2600
+ resource "aws_api_gateway_method" "options_method" {
2601
+ #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
2602
+ #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
2603
+ rest_api_id = module.rest_api.api_id
2604
+ resource_id = aws_api_gateway_resource.proxy_resource.id
2605
+ http_method = "OPTIONS"
2606
+ authorization = "NONE"
2607
+ }
2608
+
2609
+ # CORS integration for OPTIONS method
2610
+ resource "aws_api_gateway_integration" "options_integration" {
2611
+ rest_api_id = module.rest_api.api_id
2612
+ resource_id = aws_api_gateway_resource.proxy_resource.id
2613
+ http_method = aws_api_gateway_method.options_method.http_method
2614
+
2615
+ type = "MOCK"
2616
+ request_templates = {
2617
+ "application/json" = "{\\"statusCode\\": 204}"
2618
+ }
2619
+ }
2620
+
2621
+ # OPTIONS method response
2622
+ resource "aws_api_gateway_method_response" "options_response" {
2623
+ rest_api_id = module.rest_api.api_id
2624
+ resource_id = aws_api_gateway_resource.proxy_resource.id
2625
+ http_method = aws_api_gateway_method.options_method.http_method
2626
+ status_code = "204"
2627
+
2628
+ response_parameters = {
2629
+ "method.response.header.Access-Control-Allow-Headers" = true
2630
+ "method.response.header.Access-Control-Allow-Methods" = true
2631
+ "method.response.header.Access-Control-Allow-Origin" = true
2632
+ }
2633
+ }
2634
+
2635
+ # OPTIONS integration response
2636
+ resource "aws_api_gateway_integration_response" "options_integration_response" {
2637
+ rest_api_id = module.rest_api.api_id
2638
+ resource_id = aws_api_gateway_resource.proxy_resource.id
2639
+ http_method = aws_api_gateway_method.options_method.http_method
2640
+ status_code = aws_api_gateway_method_response.options_response.status_code
2641
+
2642
+ response_parameters = {
2643
+ "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
2644
+ "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
2645
+ "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
2646
+ }
2647
+ }
2648
+
2649
+ # API Gateway deployment
2650
+ resource "aws_api_gateway_deployment" "api_deployment" {
2651
+ rest_api_id = module.rest_api.api_id
2652
+
2653
+ triggers = {
2654
+ redeployment = sha1(jsonencode([
2655
+ aws_api_gateway_resource.proxy_resource.id,
2656
+ aws_api_gateway_method.proxy_method.id,
2657
+ aws_api_gateway_integration.lambda_integration.id,
2658
+ aws_api_gateway_method.options_method.id,
2659
+ aws_api_gateway_integration.options_integration.id,
2660
+ ]))
2661
+ }
2662
+
2663
+ lifecycle {
2664
+ create_before_destroy = true
2665
+ }
2666
+
2667
+ depends_on = [
2668
+ aws_api_gateway_method.proxy_method,
2669
+ aws_api_gateway_integration.lambda_integration,
2670
+ aws_api_gateway_method.options_method,
2671
+ aws_api_gateway_integration.options_integration,
2672
+ aws_api_gateway_method_response.options_response,
2673
+ aws_api_gateway_integration_response.options_integration_response,
2674
+ ]
2675
+ }
2676
+
2677
+ # KMS key for encrypting access logs at rest
2678
+ resource "aws_kms_key" "access_logs" {
2679
+ description = "TestApi API access log encryption"
2680
+ deletion_window_in_days = 7
2681
+ enable_key_rotation = true
2682
+
2683
+ policy = jsonencode({
2684
+ Version = "2012-10-17"
2685
+ Statement = [
2686
+ {
2687
+ Sid = "Enable IAM User Permissions"
2688
+ Effect = "Allow"
2689
+ Principal = { AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root" }
2690
+ Action = "kms:*"
2691
+ Resource = "*"
2692
+ },
2693
+ {
2694
+ Sid = "Allow CloudWatch Logs"
2695
+ Effect = "Allow"
2696
+ Principal = { Service = "logs.\${data.aws_region.current.region}.amazonaws.com" }
2697
+ Action = [
2698
+ "kms:Encrypt",
2699
+ "kms:Decrypt",
2700
+ "kms:ReEncrypt*",
2701
+ "kms:GenerateDataKey*",
2702
+ "kms:DescribeKey"
2703
+ ]
2704
+ Resource = "*"
2705
+ Condition = {
2706
+ ArnEquals = {
2707
+ "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"
2708
+ }
2709
+ }
2710
+ }
2711
+ ]
2712
+ })
2713
+
2714
+ tags = var.tags
2715
+ }
2716
+
2717
+ resource "aws_kms_alias" "access_logs" {
2718
+ name = "alias/test-api-\${random_string.suffix.result}-access-logs"
2719
+ target_key_id = aws_kms_key.access_logs.key_id
2720
+ }
2721
+
2722
+ # Access logs for the API Gateway stage
2723
+ resource "aws_cloudwatch_log_group" "access_logs" {
2724
+ name = "/aws/apigateway/test-api-\${random_string.suffix.result}/access"
2725
+ retention_in_days = 365
2726
+ kms_key_id = aws_kms_key.access_logs.arn
2727
+ tags = var.tags
2728
+ }
2729
+
2730
+ # API Gateway stage
2731
+ resource "aws_api_gateway_stage" "api_stage" {
2732
+ #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
2733
+ #checkov:skip=CKV2_AWS_4:Access logging is enabled below; verbose execution logging is intentionally not enabled
2734
+ #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
2735
+ #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
2736
+ deployment_id = aws_api_gateway_deployment.api_deployment.id
2737
+ rest_api_id = module.rest_api.api_id
2738
+ stage_name = "prod"
2739
+ xray_tracing_enabled = true
2740
+
2741
+ access_log_settings {
2742
+ destination_arn = aws_cloudwatch_log_group.access_logs.arn
2743
+ format = jsonencode({
2744
+ requestId = "$context.requestId"
2745
+ ip = "$context.identity.sourceIp"
2746
+ caller = "$context.identity.caller"
2747
+ user = "$context.identity.user"
2748
+ requestTime = "$context.requestTime"
2749
+ httpMethod = "$context.httpMethod"
2750
+ resourcePath = "$context.resourcePath"
2751
+ status = "$context.status"
2752
+ protocol = "$context.protocol"
2753
+ responseLength = "$context.responseLength"
2754
+ })
2755
+ }
2756
+
2757
+ tags = var.tags
2758
+
2759
+ # Ensure the account-level role is configured before the stage is created
2760
+ depends_on = [aws_api_gateway_deployment.api_deployment, module.account]
2761
+ }
2762
+
2763
+ # Default throttling applied to all resource paths and methods
2764
+ resource "aws_api_gateway_method_settings" "throttling" {
2765
+ #checkov:skip=CKV_AWS_225:API Gateway caching not required for this use case
2766
+ rest_api_id = module.rest_api.api_id
2767
+ stage_name = aws_api_gateway_stage.api_stage.stage_name
2768
+ method_path = "*/*"
2769
+
2770
+ settings {
2771
+ throttling_rate_limit = 10000
2772
+ throttling_burst_limit = 5000
2773
+ }
2774
+ }
2775
+
2776
+ # API Gateway Resource Policy
2777
+ resource "aws_api_gateway_rest_api_policy" "api_policy" {
2778
+ rest_api_id = module.rest_api.api_id
2779
+
2780
+ policy = jsonencode({
2781
+ Version = "2012-10-17"
2782
+ Statement = [
2783
+ {
2784
+ # Allow all callers to invoke the API in the resource policy, since auth is handled by Cognito
2785
+ Effect = "Allow"
2786
+ Principal = "*"
2787
+ Action = "execute-api:Invoke"
2788
+ Resource = "execute-api:/*"
2789
+ }
2790
+ ]
2791
+ })
2792
+ }
2793
+
2794
+ # Lambda permission for API Gateway to invoke the function via alias
2795
+ resource "aws_lambda_permission" "api_gateway_invoke" {
2796
+ statement_id = "AllowExecutionFromAPIGateway"
2797
+ action = "lambda:InvokeFunction"
2798
+ function_name = aws_lambda_function.api_lambda.function_name
2799
+ qualifier = aws_lambda_alias.live.name
2800
+ principal = "apigateway.amazonaws.com"
2801
+ source_arn = "\${module.rest_api.api_execution_arn}/*/*"
2802
+
2803
+ depends_on = [module.rest_api, aws_lambda_alias.live]
2804
+ }
2805
+
2806
+ # Lambda permission for API Gateway to invoke with response streaming via alias
2807
+ resource "aws_lambda_permission" "api_gateway_invoke_streaming" {
2808
+ statement_id = "AllowStreamingFromAPIGateway"
2809
+ action = "lambda:InvokeFunctionWithResponseStream"
2810
+ function_name = aws_lambda_function.api_lambda.function_name
2811
+ qualifier = aws_lambda_alias.live.name
2812
+ principal = "apigateway.amazonaws.com"
2813
+ source_arn = "\${module.rest_api.api_execution_arn}/*/*"
2814
+
2815
+ depends_on = [module.rest_api, aws_lambda_alias.live]
2816
+ }
2817
+
2818
+ # Add API url to runtime config
2819
+ module "add_url_to_runtime_config" {
2820
+ source = "../../../core/runtime-config/entry"
2821
+
2822
+ namespace = "connection"
2823
+ key = "apis"
2824
+ value = { "TestApi" = aws_api_gateway_stage.api_stage.invoke_url }
2825
+
2826
+ depends_on = [aws_api_gateway_stage.api_stage]
2827
+ }
2828
+
2829
+ # Outputs
2830
+
2831
+ # API Gateway Outputs (from core module)
2832
+ output "api_id" {
2833
+ description = "ID of the REST API Gateway"
2834
+ value = module.rest_api.api_id
2835
+ }
2836
+
2837
+ output "api_arn" {
2838
+ description = "ARN of the REST API Gateway"
2839
+ value = module.rest_api.api_arn
2840
+ }
2841
+
2842
+ output "api_endpoint" {
2843
+ description = "Base URL of the REST API Gateway"
2844
+ value = module.rest_api.api_endpoint
2845
+ }
2846
+
2847
+ output "api_execution_arn" {
2848
+ description = "Execution ARN of the REST API Gateway"
2849
+ value = module.rest_api.api_execution_arn
2850
+ }
2851
+
2852
+ output "stage_invoke_url" {
2853
+ description = "Invoke URL of the API Gateway stage"
2854
+ value = aws_api_gateway_stage.api_stage.invoke_url
2855
+ }
2856
+
2857
+ output "stage_arn" {
2858
+ description = "ARN of the API Gateway stage"
2859
+ value = aws_api_gateway_stage.api_stage.arn
2860
+ }
2861
+
2862
+ output "stage_execution_arn" {
2863
+ description = "Execution ARN of the API Gateway stage"
2864
+ value = aws_api_gateway_stage.api_stage.execution_arn
2865
+ }
2866
+
2867
+ output "deployment_id" {
2868
+ description = "ID of the API Gateway deployment"
2869
+ value = aws_api_gateway_deployment.api_deployment.id
2870
+ }
2871
+
2872
+ output "stage_id" {
2873
+ description = "ID of the API Gateway stage"
2874
+ value = aws_api_gateway_stage.api_stage.id
2875
+ }
2876
+
2877
+ # Lambda Function Outputs
2878
+ output "lambda_function_name" {
2879
+ description = "Name of the Lambda function"
2880
+ value = aws_lambda_function.api_lambda.function_name
2881
+ }
2882
+
2883
+ output "lambda_function_arn" {
2884
+ description = "ARN of the Lambda function"
2885
+ value = aws_lambda_function.api_lambda.arn
2886
+ }
2887
+
2888
+ output "lambda_invoke_arn" {
2889
+ description = "Invoke ARN of the Lambda function"
2890
+ value = aws_lambda_function.api_lambda.invoke_arn
2891
+ }
2892
+
2893
+ output "lambda_qualified_arn" {
2894
+ description = "Qualified ARN of the Lambda function"
2895
+ value = aws_lambda_function.api_lambda.qualified_arn
2896
+ }
2897
+
2898
+ output "lambda_version" {
2899
+ description = "Version of the Lambda function"
2900
+ value = aws_lambda_function.api_lambda.version
2901
+ }
2902
+
2903
+ output "lambda_source_code_hash" {
2904
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
2905
+ value = aws_lambda_function.api_lambda.source_code_hash
2906
+ }
2907
+
2908
+ output "lambda_source_code_size" {
2909
+ description = "Size of the Lambda deployment package in bytes"
2910
+ value = aws_lambda_function.api_lambda.source_code_size
2911
+ }
2912
+
2913
+ # IAM Role Outputs
2914
+ output "lambda_execution_role_arn" {
2915
+ description = "ARN of the Lambda execution role"
2916
+ value = aws_iam_role.lambda_execution_role.arn
2917
+ }
2918
+
2919
+ output "lambda_execution_role_name" {
2920
+ description = "Name of the Lambda execution role"
2921
+ value = aws_iam_role.lambda_execution_role.name
2922
+ }
2923
+
2924
+ # Integration Outputs
2925
+ output "integration_id" {
2926
+ description = "ID of the Lambda integration"
2927
+ value = aws_api_gateway_integration.lambda_integration.id
2928
+ }
2929
+
2930
+ output "proxy_resource_id" {
2931
+ description = "ID of the proxy resource"
2932
+ value = aws_api_gateway_resource.proxy_resource.id
2933
+ }
2934
+
2935
+ output "proxy_method_id" {
2936
+ description = "ID of the proxy method"
2937
+ value = aws_api_gateway_method.proxy_method.id
2938
+ }
2939
+
2940
+ # CloudWatch Log Groups
2941
+ output "lambda_log_group_name" {
2942
+ description = "Name of the Lambda CloudWatch log group"
2943
+ value = aws_cloudwatch_log_group.lambda_logs.name
2944
+ }
2945
+
2946
+ output "lambda_log_group_arn" {
2947
+ description = "ARN of the Lambda CloudWatch log group"
2948
+ value = aws_cloudwatch_log_group.lambda_logs.arn
2949
+ }
2950
+ ",
2951
+ }
2952
+ `;
2953
+
2954
+ exports[`fastapi project generator > terraform iac > should generate terraform files for REST API with IAM auth and snapshot them > terraform-rest-iam-files 1`] = `
2955
+ {
2956
+ "rest-api.tf": "# Core REST API Gateway module
2957
+ # This module creates the API Gateway REST API, deployment, stage, and logging resources
2958
+
2959
+ terraform {
2960
+ required_version = ">= 1.0"
2961
+
2962
+ required_providers {
2963
+ aws = {
2964
+ source = "hashicorp/aws"
2965
+ version = "6.52.0"
2966
+ }
2967
+ random = {
2968
+ source = "hashicorp/random"
2969
+ version = "3.9.0"
2970
+ }
2971
+ }
2972
+ }
2973
+
2974
+ # Core REST API Gateway Variables
2975
+
2976
+ variable "api_name" {
2977
+ description = "Name of the REST API Gateway"
2978
+ type = string
2979
+ }
2980
+
2981
+ variable "api_description" {
2982
+ description = "Description of the REST API Gateway"
2983
+ type = string
2984
+ default = "REST API Gateway"
2985
+ }
2986
+
2987
+ variable "stage_name" {
2988
+ description = "Name of the API Gateway stage"
2989
+ type = string
2990
+ default = "prod"
2991
+ }
2992
+
2993
+ variable "stage_auto_deploy" {
2994
+ description = "Whether to automatically deploy the API stage"
2995
+ type = bool
2996
+ default = true
2997
+ }
2998
+
2999
+ variable "enable_waf" {
3000
+ description = "Whether to enable AWS WAFv2 with the default managed ruleset (AWSManagedRulesCommonRuleSet and AWSManagedRulesKnownBadInputsRuleSet). The Web ACL is created here and associated with the stage in the consuming module."
3001
+ type = bool
3002
+ default = true
3003
+ }
3004
+
3005
+ # CORS Configuration
3006
+
3007
+ variable "cors_allow_headers" {
3008
+ description = "List of allowed headers for CORS"
3009
+ type = list(string)
3010
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
3011
+ }
3012
+
3013
+ variable "cors_allow_methods" {
3014
+ description = "List of allowed HTTP methods for CORS"
3015
+ type = list(string)
3016
+ default = ["*"]
3017
+ }
3018
+
3019
+ variable "cors_allow_origins" {
3020
+ description = "List of allowed origins for CORS"
3021
+ type = list(string)
3022
+ default = ["*"]
3023
+ }
3024
+
3025
+ # Tags
3026
+
3027
+ variable "tags" {
3028
+ description = "Tags to apply to all resources"
3029
+ type = map(string)
3030
+ default = {}
3031
+ }
3032
+
3033
+ # Data sources
3034
+ data "aws_region" "current" {}
3035
+ data "aws_caller_identity" "current" {}
3036
+
3037
+ # Resources
3038
+
3039
+ # REST API Gateway
3040
+ resource "aws_api_gateway_rest_api" "rest_api" {
3041
+ name = var.api_name
3042
+ description = var.api_description
3043
+
3044
+ endpoint_configuration {
3045
+ types = ["REGIONAL"]
3046
+ }
3047
+
3048
+ lifecycle {
3049
+ create_before_destroy = true
3050
+ }
3051
+
3052
+ tags = var.tags
3053
+ }
3054
+
3055
+ # Note: Deployment, stage and access logging are created in the consuming module
3056
+ # (e.g., foo-api.tf) after all methods and integrations are defined
3057
+
3058
+ # Gateway Response for CORS (4XX errors)
3059
+ resource "aws_api_gateway_gateway_response" "cors_4xx" {
3060
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
3061
+ response_type = "DEFAULT_4XX"
3062
+
3063
+ response_parameters = {
3064
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
3065
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
3066
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
3067
+ }
3068
+ }
3069
+
3070
+ # Gateway Response for CORS (5XX errors)
3071
+ resource "aws_api_gateway_gateway_response" "cors_5xx" {
3072
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
3073
+ response_type = "DEFAULT_5XX"
3074
+
3075
+ response_parameters = {
3076
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
3077
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
3078
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
3079
+ }
3080
+ }
3081
+
3082
+ resource "aws_wafv2_web_acl" "api_waf" {
3083
+ #checkov:skip=CKV2_AWS_31:Logging configuration is defined below in aws_wafv2_web_acl_logging_configuration.api_waf_logging; Checkov does not resolve the separate resource
3084
+ count = var.enable_waf ? 1 : 0
3085
+
3086
+ name = "\${var.api_name}-waf"
3087
+ scope = "REGIONAL"
3088
+
3089
+ default_action {
3090
+ allow {}
3091
+ }
3092
+
3093
+ rule {
3094
+ name = "CRSRule"
3095
+ priority = 0
3096
+
3097
+ override_action {
3098
+ none {}
3099
+ }
3100
+
3101
+ statement {
3102
+ managed_rule_group_statement {
3103
+ name = "AWSManagedRulesCommonRuleSet"
3104
+ vendor_name = "AWS"
3105
+
3106
+ # Count instead of Block: the CRS 8 KB body limit is too restrictive for most APIs.
3107
+ rule_action_override {
3108
+ name = "SizeRestrictions_BODY"
3109
+ action_to_use {
3110
+ count {}
3111
+ }
3112
+ }
3113
+ }
3114
+ }
3115
+
3116
+ visibility_config {
3117
+ cloudwatch_metrics_enabled = true
3118
+ metric_name = "\${var.api_name}WebAcl-CRS"
3119
+ sampled_requests_enabled = true
3120
+ }
3121
+ }
3122
+
3123
+ rule {
3124
+ name = "KnownBadInputsRule"
3125
+ priority = 1
3126
+
3127
+ override_action {
3128
+ none {}
3129
+ }
3130
+
3131
+ statement {
3132
+ managed_rule_group_statement {
3133
+ name = "AWSManagedRulesKnownBadInputsRuleSet"
3134
+ vendor_name = "AWS"
3135
+ }
3136
+ }
3137
+
3138
+ visibility_config {
3139
+ cloudwatch_metrics_enabled = true
3140
+ metric_name = "\${var.api_name}WebAcl-KnownBadInputs"
3141
+ sampled_requests_enabled = true
3142
+ }
3143
+ }
3144
+
3145
+ visibility_config {
3146
+ cloudwatch_metrics_enabled = true
3147
+ metric_name = "\${var.api_name}WebAcl"
3148
+ sampled_requests_enabled = true
3149
+ }
3150
+
3151
+ tags = var.tags
3152
+
3153
+ lifecycle {
3154
+ create_before_destroy = true
3155
+ }
3156
+ }
3157
+
3158
+ # CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
3159
+ resource "aws_cloudwatch_log_group" "api_waf_logs" {
3160
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
3161
+ #checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
3162
+ count = var.enable_waf ? 1 : 0
3163
+
3164
+ name = "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}"
3165
+ retention_in_days = 30
3166
+ tags = var.tags
3167
+ }
3168
+
3169
+ resource "random_id" "waf_log_suffix" {
3170
+ count = var.enable_waf ? 1 : 0
3171
+ byte_length = 4
3172
+ }
3173
+
3174
+ resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
3175
+ count = var.enable_waf ? 1 : 0
3176
+
3177
+ log_destination_configs = [aws_cloudwatch_log_group.api_waf_logs[0].arn]
3178
+ resource_arn = aws_wafv2_web_acl.api_waf[0].arn
3179
+ }
3180
+
3181
+
3182
+ # Outputs
3183
+
3184
+ output "api_id" {
3185
+ description = "ID of the REST API Gateway"
3186
+ value = aws_api_gateway_rest_api.rest_api.id
3187
+ }
3188
+
3189
+ output "api_arn" {
3190
+ description = "ARN of the REST API Gateway"
3191
+ value = aws_api_gateway_rest_api.rest_api.arn
3192
+ }
3193
+
3194
+ output "api_endpoint" {
3195
+ description = "Base URL of the REST API Gateway"
3196
+ value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
3197
+ }
3198
+
3199
+ output "api_execution_arn" {
3200
+ description = "Execution ARN of the REST API Gateway"
3201
+ value = aws_api_gateway_rest_api.rest_api.execution_arn
3202
+ }
3203
+
3204
+ output "api_root_resource_id" {
3205
+ description = "Root resource ID of the REST API Gateway"
3206
+ value = aws_api_gateway_rest_api.rest_api.root_resource_id
3207
+ }
3208
+
3209
+ output "waf_web_acl_arn" {
3210
+ description = "ARN of the WAFv2 Web ACL associated with the API, or null if WAF is disabled"
3211
+ value = var.enable_waf ? aws_wafv2_web_acl.api_waf[0].arn : null
3212
+ }
3213
+
3214
+ # Note: Stage, deployment and access log outputs are provided by the consuming module",
3215
+ "test-api.tf": "terraform {
3216
+ required_version = ">= 1.0"
3217
+
3218
+ required_providers {
3219
+ aws = {
3220
+ source = "hashicorp/aws"
3221
+ version = "6.52.0"
3222
+ }
3223
+ }
3224
+ }
3225
+
3226
+
3227
+ variable "env" {
3228
+ description = "Environment variables for the Lambda function"
3229
+ type = map(string)
3230
+ default = {}
3231
+ }
3232
+
3233
+ variable "additional_iam_policy_statements" {
3234
+ description = "Additional IAM policy statements for the Lambda function"
3235
+ type = list(object({
3236
+ Effect = string
3237
+ Action = list(string)
3238
+ Resource = list(string)
3239
+ }))
3240
+ default = []
3241
+ }
3242
+
3243
+ variable "asset_bucket_name" {
3244
+ 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."
3245
+ type = string
3246
+ }
3247
+
3248
+ # CORS Configuration (passed to core module)
3249
+
3250
+ variable "cors_allow_headers" {
3251
+ description = "List of allowed headers for CORS"
3252
+ type = list(string)
3253
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
3254
+ }
3255
+
3256
+ variable "cors_allow_methods" {
3257
+ description = "List of allowed HTTP methods for CORS"
3258
+ type = list(string)
3259
+ default = ["*"]
3260
+ }
3261
+
3262
+ variable "cors_allow_origins" {
3263
+ description = "List of allowed origins for CORS"
3264
+ type = list(string)
3265
+ default = ["*"]
3266
+ }
3267
+
3268
+ # WAF Configuration
3269
+ variable "enable_waf" {
3270
+ description = "Whether to enable AWS WAFv2 with the default managed ruleset on the API stage"
3271
+ type = bool
3272
+ default = true
3273
+ }
3274
+
3275
+ # Tags
3276
+ variable "tags" {
3277
+ description = "Tags to apply to all resources"
3278
+ type = map(string)
3279
+ default = {}
3280
+ }
3281
+
3282
+ # Get current AWS region and account ID
3283
+ data "aws_region" "current" {}
3284
+ data "aws_caller_identity" "current" {}
3285
+
3286
+ resource "random_string" "suffix" {
3287
+ length = 8
3288
+ special = false
3289
+ upper = false
3290
+ }
3291
+
3292
+ # Resources
3293
+
3294
+ # Create Lambda ZIP file from the bundle directory
3295
+ data "archive_file" "lambda_zip" {
3296
+ type = "zip"
3297
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
3298
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
3299
+ }
3300
+
3301
+ resource "aws_s3_object" "lambda_zip" {
3302
+ bucket = var.asset_bucket_name
3303
+ key = "apis/test-api/\${data.archive_file.lambda_zip.output_sha256}.zip"
3304
+ source = data.archive_file.lambda_zip.output_path
3305
+ source_hash = data.archive_file.lambda_zip.output_base64sha256
3306
+ etag = data.archive_file.lambda_zip.output_md5
3307
+ }
3308
+
3309
+ # Use the core REST API module
3310
+ # Account-level CloudWatch role required for REST API access logging
3311
+ module "account" {
3312
+ source = "../../../core/api/api-gateway-account"
3313
+ }
3314
+
3315
+ module "rest_api" {
3316
+ source = "../../../core/api/rest-api"
3317
+
3318
+ api_name = "TestApi-\${random_string.suffix.result}"
3319
+ api_description = "TestApi REST API"
3320
+ stage_name = "prod"
3321
+ stage_auto_deploy = true
3322
+
3323
+ # WAF Configuration
3324
+ enable_waf = var.enable_waf
3325
+
3326
+ # CORS Configuration
3327
+ cors_allow_headers = var.cors_allow_headers
3328
+ cors_allow_methods = var.cors_allow_methods
3329
+ cors_allow_origins = var.cors_allow_origins
3330
+
3331
+ # Tags
3332
+ tags = var.tags
3333
+ }
3334
+
3335
+ resource "aws_wafv2_web_acl_association" "api_waf_association" {
3336
+ count = var.enable_waf ? 1 : 0
3337
+
3338
+ resource_arn = aws_api_gateway_stage.api_stage.arn
3339
+ web_acl_arn = module.rest_api.waf_web_acl_arn
3340
+
3341
+ depends_on = [aws_api_gateway_stage.api_stage]
3342
+ }
3343
+
3344
+ # Lambda function
3345
+ resource "aws_lambda_function" "api_lambda" {
3346
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
3347
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
3348
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
3349
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
3350
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
3351
+ s3_bucket = aws_s3_object.lambda_zip.bucket
3352
+ s3_key = aws_s3_object.lambda_zip.key
3353
+ s3_object_version = aws_s3_object.lambda_zip.version_id
3354
+ function_name = "TestApiHandler-\${random_string.suffix.result}"
3355
+ role = aws_iam_role.lambda_execution_role.arn
3356
+ handler = "run.sh"
3357
+ runtime = "python3.14"
3358
+ layers = ["arn:aws:lambda:\${data.aws_region.current.region}:753240598075:layer:LambdaAdapterLayerX86:24"]
3359
+ timeout = 30
3360
+ memory_size = 128
3361
+ publish = true
3362
+
3363
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
3364
+
3365
+ # Enable X-Ray tracing
3366
+ tracing_config {
3367
+ mode = "Active"
3368
+ }
3369
+
3370
+ # Enable SnapStart for faster cold starts
3371
+ snap_start {
3372
+ apply_on = "PublishedVersions"
3373
+ }
3374
+
3375
+ environment {
3376
+ variables = merge({
3377
+ PORT = "8000"
3378
+ AWS_LWA_INVOKE_MODE = "response_stream"
3379
+ AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
3380
+ }, var.env)
3381
+ }
3382
+
3383
+ tags = var.tags
3384
+ }
3385
+
3386
+ # IAM role for Lambda execution
3387
+ resource "aws_iam_role" "lambda_execution_role" {
3388
+ name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
3389
+
3390
+ assume_role_policy = jsonencode({
3391
+ Version = "2012-10-17"
3392
+ Statement = [
3393
+ {
3394
+ Action = "sts:AssumeRole"
3395
+ Effect = "Allow"
3396
+ Principal = {
3397
+ Service = "lambda.amazonaws.com"
3398
+ }
3399
+ }
3400
+ ]
3401
+ })
3402
+
3403
+ tags = var.tags
3404
+ }
3405
+
3406
+ # Attach basic execution policy to Lambda role
3407
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
3408
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
3409
+ role = aws_iam_role.lambda_execution_role.name
3410
+ }
3411
+
3412
+ # Attach X-Ray tracing policy to Lambda role
3413
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
3414
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
3415
+ role = aws_iam_role.lambda_execution_role.name
3416
+ }
3417
+
3418
+ # Additional IAM policies for Lambda (if provided)
3419
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
3420
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
3421
+ name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
3422
+ role = aws_iam_role.lambda_execution_role.id
3423
+
3424
+ policy = jsonencode({
3425
+ Version = "2012-10-17"
3426
+ Statement = var.additional_iam_policy_statements
3427
+ })
3428
+ }
3429
+
3430
+ # CloudWatch Log Group for Lambda
3431
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
3432
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
3433
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
3434
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
3435
+ name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
3436
+ tags = var.tags
3437
+ }
3438
+
3439
+ # Lambda alias pointing to the latest published version for SnapStart
3440
+ resource "aws_lambda_alias" "live" {
3441
+ name = "live"
3442
+ function_name = aws_lambda_function.api_lambda.function_name
3443
+ function_version = aws_lambda_function.api_lambda.version
3444
+
3445
+ depends_on = [aws_lambda_function.api_lambda]
3446
+ }
3447
+
3448
+
3449
+ # Create proxy resource (captures all paths)
3450
+ resource "aws_api_gateway_resource" "proxy_resource" {
3451
+ rest_api_id = module.rest_api.api_id
3452
+ parent_id = module.rest_api.api_root_resource_id
3453
+ path_part = "{proxy+}"
3454
+ }
3455
+
3456
+ # Lambda integration for REST API
3457
+ resource "aws_api_gateway_integration" "lambda_integration" {
3458
+ rest_api_id = module.rest_api.api_id
3459
+ resource_id = aws_api_gateway_resource.proxy_resource.id
3460
+ http_method = aws_api_gateway_method.proxy_method.http_method
3461
+
3462
+ integration_http_method = "POST"
3463
+ type = "AWS_PROXY"
3464
+ # Use the response streaming invocation path with the alias ARN for SnapStart
3465
+ uri = "arn:aws:apigateway:\${data.aws_region.current.region}:lambda:path/2021-11-15/functions/\${aws_lambda_alias.live.arn}/response-streaming-invocations"
3466
+ response_transfer_mode = "STREAM"
3467
+
3468
+ depends_on = [aws_lambda_alias.live]
3469
+ }
3470
+
3471
+ # Method for proxy integration
3472
+ resource "aws_api_gateway_method" "proxy_method" {
3473
+ #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
3474
+ rest_api_id = module.rest_api.api_id
3475
+ resource_id = aws_api_gateway_resource.proxy_resource.id
3476
+ http_method = "ANY"
3477
+
3478
+ authorization = "AWS_IAM"
3479
+
3480
+ request_parameters = {
3481
+ "method.request.path.proxy" = true
3482
+ }
3483
+
3484
+ depends_on = []
3485
+ }
3486
+
3487
+ # OPTIONS method for CORS preflight
3488
+ resource "aws_api_gateway_method" "options_method" {
3489
+ #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
3490
+ #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
3491
+ rest_api_id = module.rest_api.api_id
3492
+ resource_id = aws_api_gateway_resource.proxy_resource.id
3493
+ http_method = "OPTIONS"
3494
+ authorization = "NONE"
3495
+ }
3496
+
3497
+ # CORS integration for OPTIONS method
3498
+ resource "aws_api_gateway_integration" "options_integration" {
3499
+ rest_api_id = module.rest_api.api_id
3500
+ resource_id = aws_api_gateway_resource.proxy_resource.id
3501
+ http_method = aws_api_gateway_method.options_method.http_method
3502
+
3503
+ type = "MOCK"
3504
+ request_templates = {
3505
+ "application/json" = "{\\"statusCode\\": 204}"
3506
+ }
3507
+ }
3508
+
3509
+ # OPTIONS method response
3510
+ resource "aws_api_gateway_method_response" "options_response" {
3511
+ rest_api_id = module.rest_api.api_id
3512
+ resource_id = aws_api_gateway_resource.proxy_resource.id
3513
+ http_method = aws_api_gateway_method.options_method.http_method
3514
+ status_code = "204"
3515
+
3516
+ response_parameters = {
3517
+ "method.response.header.Access-Control-Allow-Headers" = true
3518
+ "method.response.header.Access-Control-Allow-Methods" = true
3519
+ "method.response.header.Access-Control-Allow-Origin" = true
3520
+ }
3521
+ }
3522
+
3523
+ # OPTIONS integration response
3524
+ resource "aws_api_gateway_integration_response" "options_integration_response" {
3525
+ rest_api_id = module.rest_api.api_id
3526
+ resource_id = aws_api_gateway_resource.proxy_resource.id
3527
+ http_method = aws_api_gateway_method.options_method.http_method
3528
+ status_code = aws_api_gateway_method_response.options_response.status_code
3529
+
3530
+ response_parameters = {
3531
+ "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
3532
+ "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
3533
+ "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
3534
+ }
3535
+ }
3536
+
3537
+ # API Gateway deployment
3538
+ resource "aws_api_gateway_deployment" "api_deployment" {
3539
+ rest_api_id = module.rest_api.api_id
3540
+
3541
+ triggers = {
3542
+ redeployment = sha1(jsonencode([
3543
+ aws_api_gateway_resource.proxy_resource.id,
3544
+ aws_api_gateway_method.proxy_method.id,
3545
+ aws_api_gateway_integration.lambda_integration.id,
3546
+ aws_api_gateway_method.options_method.id,
3547
+ aws_api_gateway_integration.options_integration.id,
3548
+ ]))
3549
+ }
3550
+
3551
+ lifecycle {
3552
+ create_before_destroy = true
3553
+ }
3554
+
3555
+ depends_on = [
3556
+ aws_api_gateway_method.proxy_method,
3557
+ aws_api_gateway_integration.lambda_integration,
3558
+ aws_api_gateway_method.options_method,
3559
+ aws_api_gateway_integration.options_integration,
3560
+ aws_api_gateway_method_response.options_response,
3561
+ aws_api_gateway_integration_response.options_integration_response,
3562
+ ]
3563
+ }
3564
+
3565
+ # KMS key for encrypting access logs at rest
3566
+ resource "aws_kms_key" "access_logs" {
3567
+ description = "TestApi API access log encryption"
3568
+ deletion_window_in_days = 7
3569
+ enable_key_rotation = true
3570
+
3571
+ policy = jsonencode({
3572
+ Version = "2012-10-17"
3573
+ Statement = [
3574
+ {
3575
+ Sid = "Enable IAM User Permissions"
3576
+ Effect = "Allow"
3577
+ Principal = { AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root" }
3578
+ Action = "kms:*"
3579
+ Resource = "*"
3580
+ },
3581
+ {
3582
+ Sid = "Allow CloudWatch Logs"
3583
+ Effect = "Allow"
3584
+ Principal = { Service = "logs.\${data.aws_region.current.region}.amazonaws.com" }
3585
+ Action = [
3586
+ "kms:Encrypt",
3587
+ "kms:Decrypt",
3588
+ "kms:ReEncrypt*",
3589
+ "kms:GenerateDataKey*",
3590
+ "kms:DescribeKey"
3591
+ ]
3592
+ Resource = "*"
3593
+ Condition = {
3594
+ ArnEquals = {
3595
+ "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"
3596
+ }
3597
+ }
3598
+ }
3599
+ ]
3600
+ })
3601
+
3602
+ tags = var.tags
3603
+ }
3604
+
3605
+ resource "aws_kms_alias" "access_logs" {
3606
+ name = "alias/test-api-\${random_string.suffix.result}-access-logs"
3607
+ target_key_id = aws_kms_key.access_logs.key_id
3608
+ }
3609
+
3610
+ # Access logs for the API Gateway stage
3611
+ resource "aws_cloudwatch_log_group" "access_logs" {
3612
+ name = "/aws/apigateway/test-api-\${random_string.suffix.result}/access"
3613
+ retention_in_days = 365
3614
+ kms_key_id = aws_kms_key.access_logs.arn
3615
+ tags = var.tags
3616
+ }
3617
+
3618
+ # API Gateway stage
3619
+ resource "aws_api_gateway_stage" "api_stage" {
3620
+ #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
3621
+ #checkov:skip=CKV2_AWS_4:Access logging is enabled below; verbose execution logging is intentionally not enabled
3622
+ #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
3623
+ #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
3624
+ deployment_id = aws_api_gateway_deployment.api_deployment.id
3625
+ rest_api_id = module.rest_api.api_id
3626
+ stage_name = "prod"
3627
+ xray_tracing_enabled = true
3628
+
3629
+ access_log_settings {
3630
+ destination_arn = aws_cloudwatch_log_group.access_logs.arn
3631
+ format = jsonencode({
3632
+ requestId = "$context.requestId"
3633
+ ip = "$context.identity.sourceIp"
3634
+ caller = "$context.identity.caller"
3635
+ user = "$context.identity.user"
3636
+ requestTime = "$context.requestTime"
3637
+ httpMethod = "$context.httpMethod"
3638
+ resourcePath = "$context.resourcePath"
3639
+ status = "$context.status"
3640
+ protocol = "$context.protocol"
3641
+ responseLength = "$context.responseLength"
3642
+ })
3643
+ }
3644
+
3645
+ tags = var.tags
3646
+
3647
+ # Ensure the account-level role is configured before the stage is created
3648
+ depends_on = [aws_api_gateway_deployment.api_deployment, module.account]
3649
+ }
3650
+
3651
+ # Default throttling applied to all resource paths and methods
3652
+ resource "aws_api_gateway_method_settings" "throttling" {
3653
+ #checkov:skip=CKV_AWS_225:API Gateway caching not required for this use case
3654
+ rest_api_id = module.rest_api.api_id
3655
+ stage_name = aws_api_gateway_stage.api_stage.stage_name
3656
+ method_path = "*/*"
3657
+
3658
+ settings {
3659
+ throttling_rate_limit = 10000
3660
+ throttling_burst_limit = 5000
3661
+ }
3662
+ }
3663
+
3664
+ # API Gateway Resource Policy
3665
+ resource "aws_api_gateway_rest_api_policy" "api_policy" {
3666
+ rest_api_id = module.rest_api.api_id
3667
+
3668
+ policy = jsonencode({
3669
+ Version = "2012-10-17"
3670
+ Statement = [
3671
+ {
3672
+ # Grant any AWS credentials from the account to call the API
3673
+ # Machine to machine fine-grained access can be defined here using more specific principals
3674
+ # (eg roles or users) and resources (eg which api paths may be invoked by which principal) if required
3675
+ Effect = "Allow"
3676
+ Principal = {
3677
+ AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
3678
+ }
3679
+ Action = "execute-api:Invoke"
3680
+ Resource = "execute-api:/*"
3681
+ },
3682
+ {
3683
+ # Open up OPTIONS to allow browsers to make unauthenticated preflight requests
3684
+ Effect = "Allow"
3685
+ Principal = "*"
3686
+ Action = "execute-api:Invoke"
3687
+ Resource = "execute-api:/*/OPTIONS/*"
3688
+ }
3689
+ ]
3690
+ })
3691
+ }
3692
+
3693
+ # Lambda permission for API Gateway to invoke the function via alias
3694
+ resource "aws_lambda_permission" "api_gateway_invoke" {
3695
+ statement_id = "AllowExecutionFromAPIGateway"
3696
+ action = "lambda:InvokeFunction"
3697
+ function_name = aws_lambda_function.api_lambda.function_name
3698
+ qualifier = aws_lambda_alias.live.name
3699
+ principal = "apigateway.amazonaws.com"
3700
+ source_arn = "\${module.rest_api.api_execution_arn}/*/*"
3701
+
3702
+ depends_on = [module.rest_api, aws_lambda_alias.live]
3703
+ }
3704
+
3705
+ # Lambda permission for API Gateway to invoke with response streaming via alias
3706
+ resource "aws_lambda_permission" "api_gateway_invoke_streaming" {
3707
+ statement_id = "AllowStreamingFromAPIGateway"
3708
+ action = "lambda:InvokeFunctionWithResponseStream"
3709
+ function_name = aws_lambda_function.api_lambda.function_name
3710
+ qualifier = aws_lambda_alias.live.name
3711
+ principal = "apigateway.amazonaws.com"
3712
+ source_arn = "\${module.rest_api.api_execution_arn}/*/*"
3713
+
3714
+ depends_on = [module.rest_api, aws_lambda_alias.live]
3715
+ }
3716
+
3717
+ # Add API url to runtime config
3718
+ module "add_url_to_runtime_config" {
3719
+ source = "../../../core/runtime-config/entry"
3720
+
3721
+ namespace = "connection"
3722
+ key = "apis"
3723
+ value = { "TestApi" = aws_api_gateway_stage.api_stage.invoke_url }
3724
+
3725
+ depends_on = [aws_api_gateway_stage.api_stage]
3726
+ }
3727
+
3728
+ # Outputs
3729
+
3730
+ # API Gateway Outputs (from core module)
3731
+ output "api_id" {
3732
+ description = "ID of the REST API Gateway"
3733
+ value = module.rest_api.api_id
3734
+ }
3735
+
3736
+ output "api_arn" {
3737
+ description = "ARN of the REST API Gateway"
3738
+ value = module.rest_api.api_arn
3739
+ }
3740
+
3741
+ output "api_endpoint" {
3742
+ description = "Base URL of the REST API Gateway"
3743
+ value = module.rest_api.api_endpoint
3744
+ }
3745
+
3746
+ output "api_execution_arn" {
3747
+ description = "Execution ARN of the REST API Gateway"
3748
+ value = module.rest_api.api_execution_arn
3749
+ }
3750
+
3751
+ output "stage_invoke_url" {
3752
+ description = "Invoke URL of the API Gateway stage"
3753
+ value = aws_api_gateway_stage.api_stage.invoke_url
3754
+ }
3755
+
3756
+ output "stage_arn" {
3757
+ description = "ARN of the API Gateway stage"
3758
+ value = aws_api_gateway_stage.api_stage.arn
3759
+ }
3760
+
3761
+ output "stage_execution_arn" {
3762
+ description = "Execution ARN of the API Gateway stage"
3763
+ value = aws_api_gateway_stage.api_stage.execution_arn
3764
+ }
3765
+
3766
+ output "deployment_id" {
3767
+ description = "ID of the API Gateway deployment"
3768
+ value = aws_api_gateway_deployment.api_deployment.id
3769
+ }
3770
+
3771
+ output "stage_id" {
3772
+ description = "ID of the API Gateway stage"
3773
+ value = aws_api_gateway_stage.api_stage.id
3774
+ }
3775
+
3776
+ # Lambda Function Outputs
3777
+ output "lambda_function_name" {
3778
+ description = "Name of the Lambda function"
3779
+ value = aws_lambda_function.api_lambda.function_name
3780
+ }
3781
+
3782
+ output "lambda_function_arn" {
3783
+ description = "ARN of the Lambda function"
3784
+ value = aws_lambda_function.api_lambda.arn
3785
+ }
3786
+
3787
+ output "lambda_invoke_arn" {
3788
+ description = "Invoke ARN of the Lambda function"
3789
+ value = aws_lambda_function.api_lambda.invoke_arn
3790
+ }
3791
+
3792
+ output "lambda_qualified_arn" {
3793
+ description = "Qualified ARN of the Lambda function"
3794
+ value = aws_lambda_function.api_lambda.qualified_arn
3795
+ }
3796
+
3797
+ output "lambda_version" {
3798
+ description = "Version of the Lambda function"
3799
+ value = aws_lambda_function.api_lambda.version
3800
+ }
3801
+
3802
+ output "lambda_source_code_hash" {
3803
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
3804
+ value = aws_lambda_function.api_lambda.source_code_hash
3805
+ }
3806
+
3807
+ output "lambda_source_code_size" {
3808
+ description = "Size of the Lambda deployment package in bytes"
3809
+ value = aws_lambda_function.api_lambda.source_code_size
3810
+ }
3811
+
3812
+ # IAM Role Outputs
3813
+ output "lambda_execution_role_arn" {
3814
+ description = "ARN of the Lambda execution role"
3815
+ value = aws_iam_role.lambda_execution_role.arn
3816
+ }
3817
+
3818
+ output "lambda_execution_role_name" {
3819
+ description = "Name of the Lambda execution role"
3820
+ value = aws_iam_role.lambda_execution_role.name
3821
+ }
3822
+
3823
+ # Integration Outputs
3824
+ output "integration_id" {
3825
+ description = "ID of the Lambda integration"
3826
+ value = aws_api_gateway_integration.lambda_integration.id
3827
+ }
3828
+
3829
+ output "proxy_resource_id" {
3830
+ description = "ID of the proxy resource"
3831
+ value = aws_api_gateway_resource.proxy_resource.id
3832
+ }
3833
+
3834
+ output "proxy_method_id" {
3835
+ description = "ID of the proxy method"
3836
+ value = aws_api_gateway_method.proxy_method.id
3837
+ }
3838
+
3839
+ # CloudWatch Log Groups
3840
+ output "lambda_log_group_name" {
3841
+ description = "Name of the Lambda CloudWatch log group"
3842
+ value = aws_cloudwatch_log_group.lambda_logs.name
3843
+ }
3844
+
3845
+ output "lambda_log_group_arn" {
3846
+ description = "ARN of the Lambda CloudWatch log group"
3847
+ value = aws_cloudwatch_log_group.lambda_logs.arn
3848
+ }
3849
+ ",
3850
+ }
3851
+ `;
3852
+
3853
+ exports[`fastapi project generator > terraform iac > should generate terraform files for REST API with None auth and snapshot them > terraform-rest-none-files 1`] = `
3854
+ {
3855
+ "rest-api.tf": "# Core REST API Gateway module
3856
+ # This module creates the API Gateway REST API, deployment, stage, and logging resources
3857
+
3858
+ terraform {
3859
+ required_version = ">= 1.0"
3860
+
3861
+ required_providers {
3862
+ aws = {
3863
+ source = "hashicorp/aws"
3864
+ version = "6.52.0"
3865
+ }
3866
+ random = {
3867
+ source = "hashicorp/random"
3868
+ version = "3.9.0"
3869
+ }
3870
+ }
3871
+ }
3872
+
3873
+ # Core REST API Gateway Variables
3874
+
3875
+ variable "api_name" {
3876
+ description = "Name of the REST API Gateway"
3877
+ type = string
3878
+ }
3879
+
3880
+ variable "api_description" {
3881
+ description = "Description of the REST API Gateway"
3882
+ type = string
3883
+ default = "REST API Gateway"
3884
+ }
3885
+
3886
+ variable "stage_name" {
3887
+ description = "Name of the API Gateway stage"
3888
+ type = string
3889
+ default = "prod"
3890
+ }
3891
+
3892
+ variable "stage_auto_deploy" {
3893
+ description = "Whether to automatically deploy the API stage"
3894
+ type = bool
3895
+ default = true
3896
+ }
3897
+
3898
+ variable "enable_waf" {
3899
+ description = "Whether to enable AWS WAFv2 with the default managed ruleset (AWSManagedRulesCommonRuleSet and AWSManagedRulesKnownBadInputsRuleSet). The Web ACL is created here and associated with the stage in the consuming module."
3900
+ type = bool
3901
+ default = true
3902
+ }
3903
+
3904
+ # CORS Configuration
3905
+
3906
+ variable "cors_allow_headers" {
3907
+ description = "List of allowed headers for CORS"
3908
+ type = list(string)
3909
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
3910
+ }
3911
+
3912
+ variable "cors_allow_methods" {
3913
+ description = "List of allowed HTTP methods for CORS"
3914
+ type = list(string)
3915
+ default = ["*"]
3916
+ }
3917
+
3918
+ variable "cors_allow_origins" {
3919
+ description = "List of allowed origins for CORS"
3920
+ type = list(string)
3921
+ default = ["*"]
3922
+ }
3923
+
3924
+ # Tags
3925
+
3926
+ variable "tags" {
3927
+ description = "Tags to apply to all resources"
3928
+ type = map(string)
3929
+ default = {}
3930
+ }
3931
+
3932
+ # Data sources
3933
+ data "aws_region" "current" {}
3934
+ data "aws_caller_identity" "current" {}
3935
+
3936
+ # Resources
3937
+
3938
+ # REST API Gateway
3939
+ resource "aws_api_gateway_rest_api" "rest_api" {
3940
+ name = var.api_name
3941
+ description = var.api_description
3942
+
3943
+ endpoint_configuration {
3944
+ types = ["REGIONAL"]
3945
+ }
3946
+
3947
+ lifecycle {
3948
+ create_before_destroy = true
3949
+ }
3950
+
3951
+ tags = var.tags
3952
+ }
3953
+
3954
+ # Note: Deployment, stage and access logging are created in the consuming module
3955
+ # (e.g., foo-api.tf) after all methods and integrations are defined
3956
+
3957
+ # Gateway Response for CORS (4XX errors)
3958
+ resource "aws_api_gateway_gateway_response" "cors_4xx" {
3959
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
3960
+ response_type = "DEFAULT_4XX"
3961
+
3962
+ response_parameters = {
3963
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
3964
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
3965
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
3966
+ }
3967
+ }
3968
+
3969
+ # Gateway Response for CORS (5XX errors)
3970
+ resource "aws_api_gateway_gateway_response" "cors_5xx" {
3971
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
3972
+ response_type = "DEFAULT_5XX"
3973
+
3974
+ response_parameters = {
3975
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
3976
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
3977
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
3978
+ }
3979
+ }
3980
+
3981
+ resource "aws_wafv2_web_acl" "api_waf" {
3982
+ #checkov:skip=CKV2_AWS_31:Logging configuration is defined below in aws_wafv2_web_acl_logging_configuration.api_waf_logging; Checkov does not resolve the separate resource
3983
+ count = var.enable_waf ? 1 : 0
3984
+
3985
+ name = "\${var.api_name}-waf"
3986
+ scope = "REGIONAL"
3987
+
3988
+ default_action {
3989
+ allow {}
3990
+ }
3991
+
3992
+ rule {
3993
+ name = "CRSRule"
3994
+ priority = 0
3995
+
3996
+ override_action {
3997
+ none {}
3998
+ }
3999
+
4000
+ statement {
4001
+ managed_rule_group_statement {
4002
+ name = "AWSManagedRulesCommonRuleSet"
4003
+ vendor_name = "AWS"
4004
+
4005
+ # Count instead of Block: the CRS 8 KB body limit is too restrictive for most APIs.
4006
+ rule_action_override {
4007
+ name = "SizeRestrictions_BODY"
4008
+ action_to_use {
4009
+ count {}
4010
+ }
4011
+ }
4012
+ }
4013
+ }
4014
+
4015
+ visibility_config {
4016
+ cloudwatch_metrics_enabled = true
4017
+ metric_name = "\${var.api_name}WebAcl-CRS"
4018
+ sampled_requests_enabled = true
4019
+ }
4020
+ }
4021
+
4022
+ rule {
4023
+ name = "KnownBadInputsRule"
4024
+ priority = 1
4025
+
4026
+ override_action {
4027
+ none {}
4028
+ }
4029
+
4030
+ statement {
4031
+ managed_rule_group_statement {
4032
+ name = "AWSManagedRulesKnownBadInputsRuleSet"
4033
+ vendor_name = "AWS"
4034
+ }
4035
+ }
4036
+
4037
+ visibility_config {
4038
+ cloudwatch_metrics_enabled = true
4039
+ metric_name = "\${var.api_name}WebAcl-KnownBadInputs"
4040
+ sampled_requests_enabled = true
4041
+ }
4042
+ }
4043
+
4044
+ visibility_config {
4045
+ cloudwatch_metrics_enabled = true
4046
+ metric_name = "\${var.api_name}WebAcl"
4047
+ sampled_requests_enabled = true
4048
+ }
4049
+
4050
+ tags = var.tags
4051
+
4052
+ lifecycle {
4053
+ create_before_destroy = true
4054
+ }
4055
+ }
4056
+
4057
+ # CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
4058
+ resource "aws_cloudwatch_log_group" "api_waf_logs" {
4059
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
4060
+ #checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
4061
+ count = var.enable_waf ? 1 : 0
4062
+
4063
+ name = "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}"
4064
+ retention_in_days = 30
4065
+ tags = var.tags
4066
+ }
4067
+
4068
+ resource "random_id" "waf_log_suffix" {
4069
+ count = var.enable_waf ? 1 : 0
4070
+ byte_length = 4
4071
+ }
4072
+
4073
+ resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
4074
+ count = var.enable_waf ? 1 : 0
4075
+
4076
+ log_destination_configs = [aws_cloudwatch_log_group.api_waf_logs[0].arn]
4077
+ resource_arn = aws_wafv2_web_acl.api_waf[0].arn
4078
+ }
4079
+
4080
+
4081
+ # Outputs
4082
+
4083
+ output "api_id" {
4084
+ description = "ID of the REST API Gateway"
4085
+ value = aws_api_gateway_rest_api.rest_api.id
4086
+ }
4087
+
4088
+ output "api_arn" {
4089
+ description = "ARN of the REST API Gateway"
4090
+ value = aws_api_gateway_rest_api.rest_api.arn
4091
+ }
4092
+
4093
+ output "api_endpoint" {
4094
+ description = "Base URL of the REST API Gateway"
4095
+ value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
4096
+ }
4097
+
4098
+ output "api_execution_arn" {
4099
+ description = "Execution ARN of the REST API Gateway"
4100
+ value = aws_api_gateway_rest_api.rest_api.execution_arn
4101
+ }
4102
+
4103
+ output "api_root_resource_id" {
4104
+ description = "Root resource ID of the REST API Gateway"
4105
+ value = aws_api_gateway_rest_api.rest_api.root_resource_id
4106
+ }
4107
+
4108
+ output "waf_web_acl_arn" {
4109
+ description = "ARN of the WAFv2 Web ACL associated with the API, or null if WAF is disabled"
4110
+ value = var.enable_waf ? aws_wafv2_web_acl.api_waf[0].arn : null
4111
+ }
4112
+
4113
+ # Note: Stage, deployment and access log outputs are provided by the consuming module",
4114
+ "test-api.tf": "terraform {
4115
+ required_version = ">= 1.0"
4116
+
4117
+ required_providers {
4118
+ aws = {
4119
+ source = "hashicorp/aws"
4120
+ version = "6.52.0"
4121
+ }
4122
+ }
4123
+ }
4124
+
4125
+
4126
+ variable "env" {
4127
+ description = "Environment variables for the Lambda function"
4128
+ type = map(string)
4129
+ default = {}
4130
+ }
4131
+
4132
+ variable "additional_iam_policy_statements" {
4133
+ description = "Additional IAM policy statements for the Lambda function"
4134
+ type = list(object({
4135
+ Effect = string
4136
+ Action = list(string)
4137
+ Resource = list(string)
4138
+ }))
4139
+ default = []
4140
+ }
4141
+
4142
+ variable "asset_bucket_name" {
4143
+ 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."
4144
+ type = string
4145
+ }
4146
+
4147
+ # CORS Configuration (passed to core module)
4148
+
4149
+ variable "cors_allow_headers" {
4150
+ description = "List of allowed headers for CORS"
4151
+ type = list(string)
4152
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
4153
+ }
4154
+
4155
+ variable "cors_allow_methods" {
4156
+ description = "List of allowed HTTP methods for CORS"
4157
+ type = list(string)
4158
+ default = ["*"]
4159
+ }
4160
+
4161
+ variable "cors_allow_origins" {
4162
+ description = "List of allowed origins for CORS"
4163
+ type = list(string)
4164
+ default = ["*"]
4165
+ }
4166
+
4167
+ # WAF Configuration
4168
+ variable "enable_waf" {
4169
+ description = "Whether to enable AWS WAFv2 with the default managed ruleset on the API stage"
4170
+ type = bool
4171
+ default = true
4172
+ }
4173
+
4174
+ # Tags
4175
+ variable "tags" {
4176
+ description = "Tags to apply to all resources"
4177
+ type = map(string)
4178
+ default = {}
4179
+ }
4180
+
4181
+ # Get current AWS region and account ID
4182
+ data "aws_region" "current" {}
4183
+ data "aws_caller_identity" "current" {}
4184
+
4185
+ resource "random_string" "suffix" {
4186
+ length = 8
4187
+ special = false
4188
+ upper = false
4189
+ }
4190
+
4191
+ # Resources
4192
+
4193
+ # Create Lambda ZIP file from the bundle directory
4194
+ data "archive_file" "lambda_zip" {
4195
+ type = "zip"
4196
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
4197
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
4198
+ }
4199
+
4200
+ resource "aws_s3_object" "lambda_zip" {
4201
+ bucket = var.asset_bucket_name
4202
+ key = "apis/test-api/\${data.archive_file.lambda_zip.output_sha256}.zip"
4203
+ source = data.archive_file.lambda_zip.output_path
4204
+ source_hash = data.archive_file.lambda_zip.output_base64sha256
4205
+ etag = data.archive_file.lambda_zip.output_md5
4206
+ }
4207
+
4208
+ # Use the core REST API module
4209
+ # Account-level CloudWatch role required for REST API access logging
4210
+ module "account" {
4211
+ source = "../../../core/api/api-gateway-account"
4212
+ }
4213
+
4214
+ module "rest_api" {
4215
+ source = "../../../core/api/rest-api"
4216
+
4217
+ api_name = "TestApi-\${random_string.suffix.result}"
4218
+ api_description = "TestApi REST API"
4219
+ stage_name = "prod"
4220
+ stage_auto_deploy = true
4221
+
4222
+ # WAF Configuration
4223
+ enable_waf = var.enable_waf
4224
+
4225
+ # CORS Configuration
4226
+ cors_allow_headers = var.cors_allow_headers
4227
+ cors_allow_methods = var.cors_allow_methods
4228
+ cors_allow_origins = var.cors_allow_origins
4229
+
4230
+ # Tags
4231
+ tags = var.tags
4232
+ }
4233
+
4234
+ resource "aws_wafv2_web_acl_association" "api_waf_association" {
4235
+ count = var.enable_waf ? 1 : 0
4236
+
4237
+ resource_arn = aws_api_gateway_stage.api_stage.arn
4238
+ web_acl_arn = module.rest_api.waf_web_acl_arn
4239
+
4240
+ depends_on = [aws_api_gateway_stage.api_stage]
4241
+ }
4242
+
4243
+ # Lambda function
4244
+ resource "aws_lambda_function" "api_lambda" {
4245
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
4246
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
4247
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
4248
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
4249
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
4250
+ s3_bucket = aws_s3_object.lambda_zip.bucket
4251
+ s3_key = aws_s3_object.lambda_zip.key
4252
+ s3_object_version = aws_s3_object.lambda_zip.version_id
4253
+ function_name = "TestApiHandler-\${random_string.suffix.result}"
4254
+ role = aws_iam_role.lambda_execution_role.arn
4255
+ handler = "run.sh"
4256
+ runtime = "python3.14"
4257
+ layers = ["arn:aws:lambda:\${data.aws_region.current.region}:753240598075:layer:LambdaAdapterLayerX86:24"]
4258
+ timeout = 30
4259
+ memory_size = 128
4260
+ publish = true
4261
+
4262
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
4263
+
4264
+ # Enable X-Ray tracing
4265
+ tracing_config {
4266
+ mode = "Active"
4267
+ }
4268
+
4269
+ # Enable SnapStart for faster cold starts
4270
+ snap_start {
4271
+ apply_on = "PublishedVersions"
4272
+ }
4273
+
4274
+ environment {
4275
+ variables = merge({
4276
+ PORT = "8000"
4277
+ AWS_LWA_INVOKE_MODE = "response_stream"
4278
+ AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
4279
+ }, var.env)
4280
+ }
4281
+
4282
+ tags = var.tags
4283
+ }
4284
+
4285
+ # IAM role for Lambda execution
4286
+ resource "aws_iam_role" "lambda_execution_role" {
4287
+ name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
4288
+
4289
+ assume_role_policy = jsonencode({
4290
+ Version = "2012-10-17"
4291
+ Statement = [
4292
+ {
4293
+ Action = "sts:AssumeRole"
4294
+ Effect = "Allow"
4295
+ Principal = {
4296
+ Service = "lambda.amazonaws.com"
4297
+ }
4298
+ }
4299
+ ]
4300
+ })
4301
+
4302
+ tags = var.tags
4303
+ }
4304
+
4305
+ # Attach basic execution policy to Lambda role
4306
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
4307
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
4308
+ role = aws_iam_role.lambda_execution_role.name
4309
+ }
4310
+
4311
+ # Attach X-Ray tracing policy to Lambda role
4312
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
4313
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
4314
+ role = aws_iam_role.lambda_execution_role.name
4315
+ }
4316
+
4317
+ # Additional IAM policies for Lambda (if provided)
4318
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
4319
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
4320
+ name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
4321
+ role = aws_iam_role.lambda_execution_role.id
4322
+
4323
+ policy = jsonencode({
4324
+ Version = "2012-10-17"
4325
+ Statement = var.additional_iam_policy_statements
4326
+ })
4327
+ }
4328
+
4329
+ # CloudWatch Log Group for Lambda
4330
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
4331
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
4332
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
4333
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
4334
+ name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
4335
+ tags = var.tags
4336
+ }
4337
+
4338
+ # Lambda alias pointing to the latest published version for SnapStart
4339
+ resource "aws_lambda_alias" "live" {
4340
+ name = "live"
4341
+ function_name = aws_lambda_function.api_lambda.function_name
4342
+ function_version = aws_lambda_function.api_lambda.version
4343
+
4344
+ depends_on = [aws_lambda_function.api_lambda]
4345
+ }
4346
+
4347
+ # Custom Lambda Authorizer
4348
+ resource "aws_lambda_function" "authorizer_lambda" {
4349
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
4350
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for authorizer
4351
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
4352
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
4353
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
4354
+ s3_bucket = aws_s3_object.authorizer_zip.bucket
4355
+ s3_key = aws_s3_object.authorizer_zip.key
4356
+ s3_object_version = aws_s3_object.authorizer_zip.version_id
4357
+ function_name = "TestApiAuthorizer-\${random_string.suffix.result}"
4358
+ role = aws_iam_role.authorizer_execution_role.arn
4359
+ handler = "proj_test_api.authorizer.handler"
4360
+ runtime = "python3.14"
4361
+ timeout = 10
4362
+ memory_size = 128
4363
+
4364
+ source_code_hash = data.archive_file.authorizer_zip.output_base64sha256
4365
+
4366
+ tracing_config {
4367
+ mode = "Active"
4368
+ }
4369
+
4370
+ tags = var.tags
4371
+ }
4372
+
4373
+ resource "aws_iam_role" "authorizer_execution_role" {
4374
+ name = "TestApiAuthorizer-role-\${random_string.suffix.result}"
4375
+
4376
+ assume_role_policy = jsonencode({
4377
+ Version = "2012-10-17"
4378
+ Statement = [
4379
+ {
4380
+ Action = "sts:AssumeRole"
4381
+ Effect = "Allow"
4382
+ Principal = {
4383
+ Service = "lambda.amazonaws.com"
4384
+ }
4385
+ }
4386
+ ]
4387
+ })
4388
+
4389
+ tags = var.tags
4390
+ }
4391
+
4392
+ resource "aws_iam_role_policy_attachment" "authorizer_basic_execution" {
4393
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
4394
+ role = aws_iam_role.authorizer_execution_role.name
4395
+ }
4396
+
4397
+ resource "aws_iam_role_policy_attachment" "authorizer_xray_execution" {
4398
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
4399
+ role = aws_iam_role.authorizer_execution_role.name
4400
+ }
4401
+
4402
+ resource "aws_cloudwatch_log_group" "authorizer_logs" {
4403
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
4404
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
4405
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
4406
+ name = "/aws/lambda/TestApiAuthorizer-\${random_string.suffix.result}"
4407
+ tags = var.tags
4408
+ }
4409
+
4410
+ data "archive_file" "authorizer_zip" {
4411
+ type = "zip"
4412
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
4413
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/authorizer.zip"
4414
+ }
4415
+
4416
+ resource "aws_s3_object" "authorizer_zip" {
4417
+ bucket = var.asset_bucket_name
4418
+ key = "apis/test-api/authorizer-\${data.archive_file.authorizer_zip.output_sha256}.zip"
4419
+ source = data.archive_file.authorizer_zip.output_path
4420
+ source_hash = data.archive_file.authorizer_zip.output_base64sha256
4421
+ etag = data.archive_file.authorizer_zip.output_md5
4422
+ }
4423
+
4424
+ resource "aws_api_gateway_authorizer" "custom_authorizer" {
4425
+ name = "TestApiAuthorizer-\${random_string.suffix.result}"
4426
+ rest_api_id = module.rest_api.api_id
4427
+ type = "TOKEN"
4428
+ authorizer_uri = aws_lambda_function.authorizer_lambda.invoke_arn
4429
+ identity_source = "method.request.header.Authorization"
4430
+ }
4431
+
4432
+ resource "aws_lambda_permission" "authorizer_invoke" {
4433
+ statement_id = "AllowAPIGatewayInvokeAuthorizer"
4434
+ action = "lambda:InvokeFunction"
4435
+ function_name = aws_lambda_function.authorizer_lambda.function_name
4436
+ principal = "apigateway.amazonaws.com"
4437
+ source_arn = "\${module.rest_api.api_execution_arn}/authorizers/\${aws_api_gateway_authorizer.custom_authorizer.id}"
4438
+ }
4439
+
4440
+ # Create proxy resource (captures all paths)
4441
+ resource "aws_api_gateway_resource" "proxy_resource" {
4442
+ rest_api_id = module.rest_api.api_id
4443
+ parent_id = module.rest_api.api_root_resource_id
4444
+ path_part = "{proxy+}"
4445
+ }
4446
+
4447
+ # Lambda integration for REST API
4448
+ resource "aws_api_gateway_integration" "lambda_integration" {
4449
+ rest_api_id = module.rest_api.api_id
4450
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4451
+ http_method = aws_api_gateway_method.proxy_method.http_method
4452
+
4453
+ integration_http_method = "POST"
4454
+ type = "AWS_PROXY"
4455
+ # Use the response streaming invocation path with the alias ARN for SnapStart
4456
+ uri = "arn:aws:apigateway:\${data.aws_region.current.region}:lambda:path/2021-11-15/functions/\${aws_lambda_alias.live.arn}/response-streaming-invocations"
4457
+ response_transfer_mode = "STREAM"
4458
+
4459
+ depends_on = [aws_lambda_alias.live]
4460
+ }
4461
+
4462
+ # Method for proxy integration
4463
+ resource "aws_api_gateway_method" "proxy_method" {
4464
+ #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
4465
+ rest_api_id = module.rest_api.api_id
4466
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4467
+ http_method = "ANY"
4468
+
4469
+ authorization = "CUSTOM"
4470
+ authorizer_id = aws_api_gateway_authorizer.custom_authorizer.id
4471
+
4472
+ request_parameters = {
4473
+ "method.request.path.proxy" = true
4474
+ }
4475
+
4476
+ depends_on = [aws_api_gateway_authorizer.custom_authorizer]
4477
+ }
4478
+
4479
+ # OPTIONS method for CORS preflight
4480
+ resource "aws_api_gateway_method" "options_method" {
4481
+ #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
4482
+ #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
4483
+ rest_api_id = module.rest_api.api_id
4484
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4485
+ http_method = "OPTIONS"
4486
+ authorization = "NONE"
4487
+ }
4488
+
4489
+ # CORS integration for OPTIONS method
4490
+ resource "aws_api_gateway_integration" "options_integration" {
4491
+ rest_api_id = module.rest_api.api_id
4492
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4493
+ http_method = aws_api_gateway_method.options_method.http_method
4494
+
4495
+ type = "MOCK"
4496
+ request_templates = {
4497
+ "application/json" = "{\\"statusCode\\": 204}"
4498
+ }
4499
+ }
4500
+
4501
+ # OPTIONS method response
4502
+ resource "aws_api_gateway_method_response" "options_response" {
4503
+ rest_api_id = module.rest_api.api_id
4504
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4505
+ http_method = aws_api_gateway_method.options_method.http_method
4506
+ status_code = "204"
4507
+
4508
+ response_parameters = {
4509
+ "method.response.header.Access-Control-Allow-Headers" = true
4510
+ "method.response.header.Access-Control-Allow-Methods" = true
4511
+ "method.response.header.Access-Control-Allow-Origin" = true
4512
+ }
4513
+ }
4514
+
4515
+ # OPTIONS integration response
4516
+ resource "aws_api_gateway_integration_response" "options_integration_response" {
4517
+ rest_api_id = module.rest_api.api_id
4518
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4519
+ http_method = aws_api_gateway_method.options_method.http_method
4520
+ status_code = aws_api_gateway_method_response.options_response.status_code
4521
+
4522
+ response_parameters = {
4523
+ "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
4524
+ "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
4525
+ "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
4526
+ }
4527
+ }
4528
+
4529
+ # API Gateway deployment
4530
+ resource "aws_api_gateway_deployment" "api_deployment" {
4531
+ rest_api_id = module.rest_api.api_id
4532
+
4533
+ triggers = {
4534
+ redeployment = sha1(jsonencode([
4535
+ aws_api_gateway_resource.proxy_resource.id,
4536
+ aws_api_gateway_method.proxy_method.id,
4537
+ aws_api_gateway_integration.lambda_integration.id,
4538
+ aws_api_gateway_method.options_method.id,
4539
+ aws_api_gateway_integration.options_integration.id,
4540
+ ]))
4541
+ }
4542
+
4543
+ lifecycle {
4544
+ create_before_destroy = true
4545
+ }
4546
+
4547
+ depends_on = [
4548
+ aws_api_gateway_method.proxy_method,
4549
+ aws_api_gateway_integration.lambda_integration,
4550
+ aws_api_gateway_method.options_method,
4551
+ aws_api_gateway_integration.options_integration,
4552
+ aws_api_gateway_method_response.options_response,
4553
+ aws_api_gateway_integration_response.options_integration_response,
4554
+ aws_api_gateway_authorizer.custom_authorizer,
4555
+ ]
4556
+ }
4557
+
4558
+ # KMS key for encrypting access logs at rest
4559
+ resource "aws_kms_key" "access_logs" {
4560
+ description = "TestApi API access log encryption"
4561
+ deletion_window_in_days = 7
4562
+ enable_key_rotation = true
4563
+
4564
+ policy = jsonencode({
4565
+ Version = "2012-10-17"
4566
+ Statement = [
4567
+ {
4568
+ Sid = "Enable IAM User Permissions"
4569
+ Effect = "Allow"
4570
+ Principal = { AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root" }
4571
+ Action = "kms:*"
4572
+ Resource = "*"
4573
+ },
4574
+ {
4575
+ Sid = "Allow CloudWatch Logs"
4576
+ Effect = "Allow"
4577
+ Principal = { Service = "logs.\${data.aws_region.current.region}.amazonaws.com" }
4578
+ Action = [
4579
+ "kms:Encrypt",
4580
+ "kms:Decrypt",
4581
+ "kms:ReEncrypt*",
4582
+ "kms:GenerateDataKey*",
4583
+ "kms:DescribeKey"
4584
+ ]
4585
+ Resource = "*"
4586
+ Condition = {
4587
+ ArnEquals = {
4588
+ "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"
4589
+ }
4590
+ }
4591
+ }
4592
+ ]
4593
+ })
4594
+
4595
+ tags = var.tags
4596
+ }
4597
+
4598
+ resource "aws_kms_alias" "access_logs" {
4599
+ name = "alias/test-api-\${random_string.suffix.result}-access-logs"
4600
+ target_key_id = aws_kms_key.access_logs.key_id
4601
+ }
4602
+
4603
+ # Access logs for the API Gateway stage
4604
+ resource "aws_cloudwatch_log_group" "access_logs" {
4605
+ name = "/aws/apigateway/test-api-\${random_string.suffix.result}/access"
4606
+ retention_in_days = 365
4607
+ kms_key_id = aws_kms_key.access_logs.arn
4608
+ tags = var.tags
4609
+ }
4610
+
4611
+ # API Gateway stage
4612
+ resource "aws_api_gateway_stage" "api_stage" {
4613
+ #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
4614
+ #checkov:skip=CKV2_AWS_4:Access logging is enabled below; verbose execution logging is intentionally not enabled
4615
+ #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
4616
+ #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
4617
+ deployment_id = aws_api_gateway_deployment.api_deployment.id
4618
+ rest_api_id = module.rest_api.api_id
4619
+ stage_name = "prod"
4620
+ xray_tracing_enabled = true
4621
+
4622
+ access_log_settings {
4623
+ destination_arn = aws_cloudwatch_log_group.access_logs.arn
4624
+ format = jsonencode({
4625
+ requestId = "$context.requestId"
4626
+ ip = "$context.identity.sourceIp"
4627
+ caller = "$context.identity.caller"
4628
+ user = "$context.identity.user"
4629
+ requestTime = "$context.requestTime"
4630
+ httpMethod = "$context.httpMethod"
4631
+ resourcePath = "$context.resourcePath"
4632
+ status = "$context.status"
4633
+ protocol = "$context.protocol"
4634
+ responseLength = "$context.responseLength"
4635
+ })
4636
+ }
4637
+
4638
+ tags = var.tags
4639
+
4640
+ # Ensure the account-level role is configured before the stage is created
4641
+ depends_on = [aws_api_gateway_deployment.api_deployment, module.account]
4642
+ }
4643
+
4644
+ # Default throttling applied to all resource paths and methods
4645
+ resource "aws_api_gateway_method_settings" "throttling" {
4646
+ #checkov:skip=CKV_AWS_225:API Gateway caching not required for this use case
4647
+ rest_api_id = module.rest_api.api_id
4648
+ stage_name = aws_api_gateway_stage.api_stage.stage_name
4649
+ method_path = "*/*"
4650
+
4651
+ settings {
4652
+ throttling_rate_limit = 10000
4653
+ throttling_burst_limit = 5000
4654
+ }
4655
+ }
4656
+
4657
+ # API Gateway Resource Policy
4658
+ resource "aws_api_gateway_rest_api_policy" "api_policy" {
4659
+ rest_api_id = module.rest_api.api_id
4660
+
4661
+ policy = jsonencode({
4662
+ Version = "2012-10-17"
4663
+ Statement = [
4664
+ {
4665
+ # Allow all callers to invoke the API in the resource policy, since auth is handled by the Lambda Authorizer
4666
+ Effect = "Allow"
4667
+ Principal = "*"
4668
+ Action = "execute-api:Invoke"
4669
+ Resource = "execute-api:/*"
4670
+ }
4671
+ ]
4672
+ })
4673
+ }
4674
+
4675
+ # Lambda permission for API Gateway to invoke the function via alias
4676
+ resource "aws_lambda_permission" "api_gateway_invoke" {
4677
+ statement_id = "AllowExecutionFromAPIGateway"
4678
+ action = "lambda:InvokeFunction"
4679
+ function_name = aws_lambda_function.api_lambda.function_name
4680
+ qualifier = aws_lambda_alias.live.name
4681
+ principal = "apigateway.amazonaws.com"
4682
+ source_arn = "\${module.rest_api.api_execution_arn}/*/*"
4683
+
4684
+ depends_on = [module.rest_api, aws_lambda_alias.live]
4685
+ }
4686
+
4687
+ # Lambda permission for API Gateway to invoke with response streaming via alias
4688
+ resource "aws_lambda_permission" "api_gateway_invoke_streaming" {
4689
+ statement_id = "AllowStreamingFromAPIGateway"
4690
+ action = "lambda:InvokeFunctionWithResponseStream"
4691
+ function_name = aws_lambda_function.api_lambda.function_name
4692
+ qualifier = aws_lambda_alias.live.name
4693
+ principal = "apigateway.amazonaws.com"
4694
+ source_arn = "\${module.rest_api.api_execution_arn}/*/*"
4695
+
4696
+ depends_on = [module.rest_api, aws_lambda_alias.live]
4697
+ }
4698
+
4699
+ # Add API url to runtime config
4700
+ module "add_url_to_runtime_config" {
4701
+ source = "../../../core/runtime-config/entry"
4702
+
4703
+ namespace = "connection"
4704
+ key = "apis"
4705
+ value = { "TestApi" = aws_api_gateway_stage.api_stage.invoke_url }
4706
+
4707
+ depends_on = [aws_api_gateway_stage.api_stage]
4708
+ }
4709
+
4710
+ # Outputs
4711
+
4712
+ # API Gateway Outputs (from core module)
4713
+ output "api_id" {
4714
+ description = "ID of the REST API Gateway"
4715
+ value = module.rest_api.api_id
4716
+ }
4717
+
4718
+ output "api_arn" {
4719
+ description = "ARN of the REST API Gateway"
4720
+ value = module.rest_api.api_arn
4721
+ }
4722
+
4723
+ output "api_endpoint" {
4724
+ description = "Base URL of the REST API Gateway"
4725
+ value = module.rest_api.api_endpoint
4726
+ }
4727
+
4728
+ output "api_execution_arn" {
4729
+ description = "Execution ARN of the REST API Gateway"
4730
+ value = module.rest_api.api_execution_arn
4731
+ }
4732
+
4733
+ output "stage_invoke_url" {
4734
+ description = "Invoke URL of the API Gateway stage"
4735
+ value = aws_api_gateway_stage.api_stage.invoke_url
4736
+ }
4737
+
4738
+ output "stage_arn" {
4739
+ description = "ARN of the API Gateway stage"
4740
+ value = aws_api_gateway_stage.api_stage.arn
4741
+ }
4742
+
4743
+ output "stage_execution_arn" {
4744
+ description = "Execution ARN of the API Gateway stage"
4745
+ value = aws_api_gateway_stage.api_stage.execution_arn
4746
+ }
4747
+
4748
+ output "deployment_id" {
4749
+ description = "ID of the API Gateway deployment"
4750
+ value = aws_api_gateway_deployment.api_deployment.id
4751
+ }
4752
+
4753
+ output "stage_id" {
4754
+ description = "ID of the API Gateway stage"
4755
+ value = aws_api_gateway_stage.api_stage.id
4756
+ }
4757
+
4758
+ # Lambda Function Outputs
4759
+ output "lambda_function_name" {
4760
+ description = "Name of the Lambda function"
4761
+ value = aws_lambda_function.api_lambda.function_name
4762
+ }
4763
+
4764
+ output "lambda_function_arn" {
4765
+ description = "ARN of the Lambda function"
4766
+ value = aws_lambda_function.api_lambda.arn
4767
+ }
4768
+
4769
+ output "lambda_invoke_arn" {
4770
+ description = "Invoke ARN of the Lambda function"
4771
+ value = aws_lambda_function.api_lambda.invoke_arn
4772
+ }
4773
+
4774
+ output "lambda_qualified_arn" {
4775
+ description = "Qualified ARN of the Lambda function"
4776
+ value = aws_lambda_function.api_lambda.qualified_arn
4777
+ }
4778
+
4779
+ output "lambda_version" {
4780
+ description = "Version of the Lambda function"
4781
+ value = aws_lambda_function.api_lambda.version
4782
+ }
4783
+
4784
+ output "lambda_source_code_hash" {
4785
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
4786
+ value = aws_lambda_function.api_lambda.source_code_hash
4787
+ }
4788
+
4789
+ output "lambda_source_code_size" {
4790
+ description = "Size of the Lambda deployment package in bytes"
4791
+ value = aws_lambda_function.api_lambda.source_code_size
4792
+ }
4793
+
4794
+ # IAM Role Outputs
4795
+ output "lambda_execution_role_arn" {
4796
+ description = "ARN of the Lambda execution role"
4797
+ value = aws_iam_role.lambda_execution_role.arn
4798
+ }
4799
+
4800
+ output "lambda_execution_role_name" {
4801
+ description = "Name of the Lambda execution role"
4802
+ value = aws_iam_role.lambda_execution_role.name
4803
+ }
4804
+
4805
+ # Integration Outputs
4806
+ output "integration_id" {
4807
+ description = "ID of the Lambda integration"
4808
+ value = aws_api_gateway_integration.lambda_integration.id
4809
+ }
4810
+
4811
+ output "proxy_resource_id" {
4812
+ description = "ID of the proxy resource"
4813
+ value = aws_api_gateway_resource.proxy_resource.id
4814
+ }
4815
+
4816
+ output "proxy_method_id" {
4817
+ description = "ID of the proxy method"
4818
+ value = aws_api_gateway_method.proxy_method.id
4819
+ }
4820
+
4821
+ # CloudWatch Log Groups
4822
+ output "lambda_log_group_name" {
4823
+ description = "Name of the Lambda CloudWatch log group"
4824
+ value = aws_cloudwatch_log_group.lambda_logs.name
4825
+ }
4826
+
4827
+ output "lambda_log_group_arn" {
4828
+ description = "ARN of the Lambda CloudWatch log group"
4829
+ value = aws_cloudwatch_log_group.lambda_logs.arn
4830
+ }
4831
+ ",
4832
+ }
4833
+ `;