@aws/nx-plugin 1.0.0-rc.22 → 1.0.0-rc.24

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 (657) hide show
  1. package/LICENSE-THIRD-PARTY +2088 -971
  2. package/package.json +25 -10
  3. package/src/agentcore-gateway/attach-upstream.d.ts +7 -7
  4. package/src/agentcore-gateway/attach-upstream.js +19 -22
  5. package/src/agentcore-gateway/attach-upstream.js.map +1 -1
  6. package/src/agentcore-gateway/gateway-connection/generator.d.ts +3 -3
  7. package/src/agentcore-gateway/gateway-connection/generator.js +42 -45
  8. package/src/agentcore-gateway/gateway-connection/generator.js.map +1 -1
  9. package/src/agentcore-gateway/gateway-connection/schema.d.js +6 -0
  10. package/src/agentcore-gateway/gateway-connection/schema.d.js.map +1 -0
  11. package/src/agentcore-gateway/generator.js +89 -72
  12. package/src/agentcore-gateway/generator.js.map +1 -1
  13. package/src/agentcore-gateway/mcp-connection/generator.d.ts +2 -2
  14. package/src/agentcore-gateway/mcp-connection/generator.js +24 -30
  15. package/src/agentcore-gateway/mcp-connection/generator.js.map +1 -1
  16. package/src/agentcore-gateway/mcp-connection/schema.d.js +6 -0
  17. package/src/agentcore-gateway/mcp-connection/schema.d.js.map +1 -0
  18. package/src/agentcore-gateway/schema.d.js +6 -0
  19. package/src/agentcore-gateway/schema.d.js.map +1 -0
  20. package/src/bin/aws-nx-mcp.js +14 -0
  21. package/src/bin/aws-nx-mcp.js.map +1 -0
  22. package/src/connection/{agent-serve-local.d.ts → agent-local-dev.d.ts} +5 -5
  23. package/src/connection/agent-local-dev.js +42 -0
  24. package/src/connection/agent-local-dev.js.map +1 -0
  25. package/src/connection/agent-runtime-config.js +9 -14
  26. package/src/connection/agent-runtime-config.js.map +1 -1
  27. package/src/connection/generator.js +262 -207
  28. package/src/connection/generator.js.map +1 -1
  29. package/src/connection/{serve-local.d.ts → local-dev.d.ts} +3 -3
  30. package/src/connection/local-dev.js +37 -0
  31. package/src/connection/local-dev.js.map +1 -0
  32. package/src/connection/schema.d.js +6 -0
  33. package/src/connection/schema.d.js.map +1 -0
  34. package/src/index.js +2 -5
  35. package/src/index.js.map +1 -1
  36. package/src/infra/app/__snapshots__/generator.spec.ts.snap +4 -26
  37. package/src/infra/app/generator.js +133 -123
  38. package/src/infra/app/generator.js.map +1 -1
  39. package/src/infra/app/schema.d.js +6 -0
  40. package/src/infra/app/schema.d.js.map +1 -0
  41. package/src/license/config-types.js +5 -3
  42. package/src/license/config-types.js.map +1 -1
  43. package/src/license/config.d.ts +1 -1
  44. package/src/license/config.js +267 -242
  45. package/src/license/config.js.map +1 -1
  46. package/src/license/dependency-check/check.js +51 -46
  47. package/src/license/dependency-check/check.js.map +1 -1
  48. package/src/license/dependency-check/collectors/collector.js +41 -71
  49. package/src/license/dependency-check/collectors/collector.js.map +1 -1
  50. package/src/license/dependency-check/collectors/npm-collector.js +34 -41
  51. package/src/license/dependency-check/collectors/npm-collector.js.map +1 -1
  52. package/src/license/dependency-check/collectors/python-collector.js +55 -62
  53. package/src/license/dependency-check/collectors/python-collector.js.map +1 -1
  54. package/src/license/dependency-check/evaluator.js +40 -68
  55. package/src/license/dependency-check/evaluator.js.map +1 -1
  56. package/src/license/dependency-check/executor.js +30 -22
  57. package/src/license/dependency-check/executor.js.map +1 -1
  58. package/src/license/dependency-check/pre-approved.js +949 -336
  59. package/src/license/dependency-check/pre-approved.js.map +1 -1
  60. package/src/license/dependency-check/types.js +2 -3
  61. package/src/license/dependency-check/types.js.map +1 -1
  62. package/src/license/generator.js +55 -60
  63. package/src/license/generator.js.map +1 -1
  64. package/src/license/index.js +3 -9
  65. package/src/license/index.js.map +1 -1
  66. package/src/license/known-exceptions.js +14 -13
  67. package/src/license/known-exceptions.js.map +1 -1
  68. package/src/license/schema.d.js +6 -0
  69. package/src/license/schema.d.js.map +1 -0
  70. package/src/license/sync/generator.js +94 -119
  71. package/src/license/sync/generator.js.map +1 -1
  72. package/src/license/sync/project-file-sync.js +28 -35
  73. package/src/license/sync/project-file-sync.js.map +1 -1
  74. package/src/mcp-server/generator-info.js +130 -160
  75. package/src/mcp-server/generator-info.js.map +1 -1
  76. package/src/mcp-server/guide-pipeline.js +184 -206
  77. package/src/mcp-server/guide-pipeline.js.map +1 -1
  78. package/src/mcp-server/guide-render.js +10 -25
  79. package/src/mcp-server/guide-render.js.map +1 -1
  80. package/src/mcp-server/index.js +6 -10
  81. package/src/mcp-server/index.js.map +1 -1
  82. package/src/mcp-server/mdx-ast.js +39 -57
  83. package/src/mcp-server/mdx-ast.js.map +1 -1
  84. package/src/mcp-server/option-filter.js +33 -47
  85. package/src/mcp-server/option-filter.js.map +1 -1
  86. package/src/mcp-server/schema-registry.js +27 -40
  87. package/src/mcp-server/schema-registry.js.map +1 -1
  88. package/src/mcp-server/schema.js +9 -7
  89. package/src/mcp-server/schema.js.map +1 -1
  90. package/src/mcp-server/server.js +20 -24
  91. package/src/mcp-server/server.js.map +1 -1
  92. package/src/mcp-server/tools/create-workspace-command.js +23 -24
  93. package/src/mcp-server/tools/create-workspace-command.js.map +1 -1
  94. package/src/mcp-server/tools/general-guidance.js +28 -25
  95. package/src/mcp-server/tools/general-guidance.js.map +1 -1
  96. package/src/mcp-server/tools/generator-guide.js +29 -37
  97. package/src/mcp-server/tools/generator-guide.js.map +1 -1
  98. package/src/mcp-server/tools/list-generators.js +19 -25
  99. package/src/mcp-server/tools/list-generators.js.map +1 -1
  100. package/src/open-api/ts-client/generator.js +16 -26
  101. package/src/open-api/ts-client/generator.js.map +1 -1
  102. package/src/open-api/ts-client/schema.d.js +6 -0
  103. package/src/open-api/ts-client/schema.d.js.map +1 -0
  104. package/src/open-api/ts-hooks/generator.js +13 -21
  105. package/src/open-api/ts-hooks/generator.js.map +1 -1
  106. package/src/open-api/ts-hooks/schema.d.js +6 -0
  107. package/src/open-api/ts-hooks/schema.d.js.map +1 -0
  108. package/src/open-api/ts-metadata/generator.js +12 -20
  109. package/src/open-api/ts-metadata/generator.js.map +1 -1
  110. package/src/open-api/ts-metadata/schema.d.js +6 -0
  111. package/src/open-api/ts-metadata/schema.d.js.map +1 -0
  112. package/src/open-api/utils/codegen-data/languages.js +42 -60
  113. package/src/open-api/utils/codegen-data/languages.js.map +1 -1
  114. package/src/open-api/utils/codegen-data/types.js +31 -30
  115. package/src/open-api/utils/codegen-data/types.js.map +1 -1
  116. package/src/open-api/utils/codegen-data.js +253 -269
  117. package/src/open-api/utils/codegen-data.js.map +1 -1
  118. package/src/open-api/utils/normalise.js +242 -241
  119. package/src/open-api/utils/normalise.js.map +1 -1
  120. package/src/open-api/utils/parse.js +8 -14
  121. package/src/open-api/utils/parse.js.map +1 -1
  122. package/src/open-api/utils/refs.js +12 -22
  123. package/src/open-api/utils/refs.js.map +1 -1
  124. package/src/open-api/utils/types.js +5 -2
  125. package/src/open-api/utils/types.js.map +1 -1
  126. package/src/preset/__snapshots__/generator.spec.ts.snap +31 -6
  127. package/src/preset/generator.d.ts +1 -1
  128. package/src/preset/generator.js +130 -112
  129. package/src/preset/generator.js.map +1 -1
  130. package/src/preset/schema.d.js +6 -0
  131. package/src/preset/schema.d.js.map +1 -0
  132. package/src/preset/schema.d.ts +0 -1
  133. package/src/preset/schema.json +0 -5
  134. package/src/py/agent/__snapshots__/generator.constructs.spec.ts.snap +304 -0
  135. package/src/py/agent/__snapshots__/{generator.spec.ts.snap → generator.frameworks.spec.ts.snap} +18 -845
  136. package/src/py/agent/__snapshots__/generator.protocols.spec.ts.snap +535 -0
  137. package/src/py/agent/a2a-connection/__snapshots__/generator.spec.ts.snap +1 -2
  138. package/src/py/agent/a2a-connection/files/agent-connection/app/__targetAgentSnakeCase___client_strands.py.template +1 -1
  139. package/src/py/agent/a2a-connection/files/langchain/agent-connection/app/__targetAgentSnakeCase___client_langchain.py.template +1 -1
  140. package/src/py/agent/a2a-connection/generator.js +70 -75
  141. package/src/py/agent/a2a-connection/generator.js.map +1 -1
  142. package/src/py/agent/a2a-connection/schema.d.js +8 -0
  143. package/src/py/agent/a2a-connection/schema.d.js.map +1 -0
  144. package/src/py/agent/files/ag-ui-langchain/main.py.template +10 -0
  145. package/src/py/agent/gateway-connection/__snapshots__/generator.spec.ts.snap +6 -8
  146. package/src/py/agent/gateway-connection/files/agent-connection/app/__gatewaySnakeCase___client_strands.py.template +3 -3
  147. package/src/py/agent/gateway-connection/files/langchain/agent-connection/app/__gatewaySnakeCase___client_langchain.py.template +3 -3
  148. package/src/py/agent/gateway-connection/generator.js +61 -58
  149. package/src/py/agent/gateway-connection/generator.js.map +1 -1
  150. package/src/py/agent/gateway-connection/schema.d.js +6 -0
  151. package/src/py/agent/gateway-connection/schema.d.js.map +1 -0
  152. package/src/py/agent/generator.js +227 -194
  153. package/src/py/agent/generator.js.map +1 -1
  154. package/src/py/agent/mcp-connection/__snapshots__/generator.spec.ts.snap +5 -12
  155. package/src/py/agent/mcp-connection/files/agent-connection/app/__mcpServerSnakeCase___client_strands.py.template +1 -1
  156. package/src/py/agent/mcp-connection/files/langchain/agent-connection/app/__mcpServerSnakeCase___client_langchain.py.template +1 -1
  157. package/src/py/agent/mcp-connection/generator.js +53 -52
  158. package/src/py/agent/mcp-connection/generator.js.map +1 -1
  159. package/src/py/agent/mcp-connection/schema.d.js +8 -0
  160. package/src/py/agent/mcp-connection/schema.d.js.map +1 -0
  161. package/src/py/agent/react-connection/__snapshots__/generator.spec.ts.snap +3 -2
  162. package/src/py/agent/react-connection/generator.js +51 -58
  163. package/src/py/agent/react-connection/generator.js.map +1 -1
  164. package/src/py/agent/react-connection/{serve-local.d.ts → local-dev.d.ts} +4 -4
  165. package/src/py/agent/react-connection/local-dev.js +36 -0
  166. package/src/py/agent/react-connection/local-dev.js.map +1 -0
  167. package/src/py/agent/react-connection/schema.d.js +8 -0
  168. package/src/py/agent/react-connection/schema.d.js.map +1 -0
  169. package/src/py/agent/schema.d.js +6 -0
  170. package/src/py/agent/schema.d.js.map +1 -0
  171. package/src/py/api/generator.js +12 -12
  172. package/src/py/api/generator.js.map +1 -1
  173. package/src/py/api/schema.d.js +6 -0
  174. package/src/py/api/schema.d.js.map +1 -0
  175. package/src/py/dynamodb/__snapshots__/generator.spec.ts.snap +68 -28
  176. package/src/py/dynamodb/agent-connection/__snapshots__/generator.spec.ts.snap +5 -5
  177. package/src/py/dynamodb/agent-connection/generator.js +27 -29
  178. package/src/py/dynamodb/agent-connection/generator.js.map +1 -1
  179. package/src/py/dynamodb/agent-connection/schema.d.js +8 -0
  180. package/src/py/dynamodb/agent-connection/schema.d.js.map +1 -0
  181. package/src/py/dynamodb/fast-api-connection/__snapshots__/generator.spec.ts.snap +3 -3
  182. package/src/py/dynamodb/fast-api-connection/generator.js +26 -28
  183. package/src/py/dynamodb/fast-api-connection/generator.js.map +1 -1
  184. package/src/py/dynamodb/fast-api-connection/schema.d.js +8 -0
  185. package/src/py/dynamodb/fast-api-connection/schema.d.js.map +1 -0
  186. package/src/py/dynamodb/files/__name__/client.py.template +5 -5
  187. package/src/py/dynamodb/files/__name__/entities/base.py.template +2 -2
  188. package/src/py/dynamodb/files/config.json.template +1 -1
  189. package/src/py/dynamodb/generator.js +63 -62
  190. package/src/py/dynamodb/generator.js.map +1 -1
  191. package/src/py/dynamodb/mcp-server-connection/__snapshots__/generator.spec.ts.snap +5 -5
  192. package/src/py/dynamodb/mcp-server-connection/generator.js +27 -29
  193. package/src/py/dynamodb/mcp-server-connection/generator.js.map +1 -1
  194. package/src/py/dynamodb/mcp-server-connection/schema.d.js +8 -0
  195. package/src/py/dynamodb/mcp-server-connection/schema.d.js.map +1 -0
  196. package/src/py/dynamodb/schema.d.js +6 -0
  197. package/src/py/dynamodb/schema.d.js.map +1 -0
  198. package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +45 -4632
  199. package/src/py/fast-api/__snapshots__/generator.terraform.spec.ts.snap +4833 -0
  200. package/src/py/fast-api/generator.js +79 -85
  201. package/src/py/fast-api/generator.js.map +1 -1
  202. package/src/py/fast-api/react/__snapshots__/generator.spec.ts.snap +3 -2
  203. package/src/py/fast-api/react/generator.js +22 -24
  204. package/src/py/fast-api/react/generator.js.map +1 -1
  205. package/src/py/fast-api/react/open-api.js +28 -29
  206. package/src/py/fast-api/react/open-api.js.map +1 -1
  207. package/src/py/fast-api/react/schema.d.js +6 -0
  208. package/src/py/fast-api/react/schema.d.js.map +1 -0
  209. package/src/py/fast-api/schema.d.js +6 -0
  210. package/src/py/fast-api/schema.d.js.map +1 -0
  211. package/src/py/lambda-function/__snapshots__/generator.spec.ts.snap +4 -4
  212. package/src/py/lambda-function/generator.js +57 -58
  213. package/src/py/lambda-function/generator.js.map +1 -1
  214. package/src/py/lambda-function/schema.d.js +6 -0
  215. package/src/py/lambda-function/schema.d.js.map +1 -0
  216. package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +5 -5
  217. package/src/py/mcp-server/generator.js +149 -131
  218. package/src/py/mcp-server/generator.js.map +1 -1
  219. package/src/py/mcp-server/schema.d.js +6 -0
  220. package/src/py/mcp-server/schema.d.js.map +1 -0
  221. package/src/py/project/generator.d.ts +14 -1
  222. package/src/py/project/generator.js +137 -88
  223. package/src/py/project/generator.js.map +1 -1
  224. package/src/py/project/schema.d.js +6 -0
  225. package/src/py/project/schema.d.js.map +1 -0
  226. package/src/sdk/agentcore-gateway.d.ts +6 -0
  227. package/src/sdk/agentcore-gateway.js +7 -0
  228. package/src/sdk/agentcore-gateway.js.map +1 -0
  229. package/src/sdk/connection.d.ts +6 -0
  230. package/src/sdk/connection.js +6 -0
  231. package/src/sdk/connection.js.map +1 -0
  232. package/{sdk → src/sdk}/license.d.ts +4 -4
  233. package/src/sdk/license.js +7 -0
  234. package/src/sdk/license.js.map +1 -0
  235. package/src/sdk/open-api.d.ts +10 -0
  236. package/src/sdk/open-api.js +8 -0
  237. package/src/sdk/open-api.js.map +1 -0
  238. package/src/sdk/py.d.ts +14 -0
  239. package/src/sdk/py.js +10 -0
  240. package/src/sdk/py.js.map +1 -0
  241. package/src/sdk/terraform.d.ts +6 -0
  242. package/src/sdk/terraform.js +7 -0
  243. package/src/sdk/terraform.js.map +1 -0
  244. package/src/sdk/ts.d.ts +33 -0
  245. package/src/sdk/ts.js +35 -0
  246. package/src/sdk/ts.js.map +1 -0
  247. package/{sdk → src/sdk}/utils/ast.d.ts +2 -2
  248. package/src/sdk/utils/ast.js +7 -0
  249. package/src/sdk/utils/ast.js.map +1 -0
  250. package/{sdk → src/sdk}/utils/format.d.ts +1 -1
  251. package/src/sdk/utils/format.js +6 -0
  252. package/src/sdk/utils/format.js.map +1 -0
  253. package/{sdk → src/sdk}/utils/test.d.ts +1 -1
  254. package/src/sdk/utils/test.js +6 -0
  255. package/src/sdk/utils/test.js.map +1 -0
  256. package/src/smithy/project/__snapshots__/generator.spec.ts.snap +2 -8
  257. package/src/smithy/project/files/build.Dockerfile.template +1 -4
  258. package/src/smithy/project/generator.js +44 -42
  259. package/src/smithy/project/generator.js.map +1 -1
  260. package/src/smithy/project/schema.d.js +6 -0
  261. package/src/smithy/project/schema.d.js.map +1 -0
  262. package/src/smithy/react-connection/generator.js +35 -46
  263. package/src/smithy/react-connection/generator.js.map +1 -1
  264. package/src/smithy/react-connection/schema.d.js +6 -0
  265. package/src/smithy/react-connection/schema.d.js.map +1 -0
  266. package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +395 -72
  267. package/src/smithy/ts/api/generator.js +121 -111
  268. package/src/smithy/ts/api/generator.js.map +1 -1
  269. package/src/smithy/ts/api/schema.d.js +6 -0
  270. package/src/smithy/ts/api/schema.d.js.map +1 -0
  271. package/src/terraform/project/files/application/src/providers.tf.template +1 -1
  272. package/src/terraform/project/generator.js +141 -103
  273. package/src/terraform/project/generator.js.map +1 -1
  274. package/src/terraform/project/schema.d.js +9 -0
  275. package/src/terraform/project/schema.d.js.map +1 -0
  276. package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +336 -92
  277. package/src/trpc/backend/generator.js +93 -86
  278. package/src/trpc/backend/generator.js.map +1 -1
  279. package/src/trpc/backend/schema.d.js +6 -0
  280. package/src/trpc/backend/schema.d.js.map +1 -0
  281. package/src/trpc/react/__snapshots__/generator.spec.ts.snap +1 -1
  282. package/src/trpc/react/generator.js +59 -58
  283. package/src/trpc/react/generator.js.map +1 -1
  284. package/src/trpc/react/schema.d.js +6 -0
  285. package/src/trpc/react/schema.d.js.map +1 -0
  286. package/src/ts/agent/__snapshots__/generator.spec.ts.snap +11 -11
  287. package/src/ts/agent/a2a-connection/__snapshots__/generator.spec.ts.snap +1 -1
  288. package/src/ts/agent/a2a-connection/files/agent-connection/app/__targetAgentKebabCase__-client-strands.ts.template +1 -1
  289. package/src/ts/agent/a2a-connection/generator.js +58 -54
  290. package/src/ts/agent/a2a-connection/generator.js.map +1 -1
  291. package/src/ts/agent/a2a-connection/schema.d.js +8 -0
  292. package/src/ts/agent/a2a-connection/schema.d.js.map +1 -0
  293. package/src/ts/agent/gateway-connection/__snapshots__/generator.spec.ts.snap +5 -5
  294. package/src/ts/agent/gateway-connection/files/agent-connection/app/__gatewayKebabCase__-client-strands.ts.template +3 -3
  295. package/src/ts/agent/gateway-connection/generator.js +57 -52
  296. package/src/ts/agent/gateway-connection/generator.js.map +1 -1
  297. package/src/ts/agent/gateway-connection/schema.d.js +6 -0
  298. package/src/ts/agent/gateway-connection/schema.d.js.map +1 -0
  299. package/src/ts/agent/generator.js +165 -136
  300. package/src/ts/agent/generator.js.map +1 -1
  301. package/src/ts/agent/mcp-connection/__snapshots__/generator.spec.ts.snap +1 -1
  302. package/src/ts/agent/mcp-connection/files/agent-connection/app/__mcpServerKebabCase__-client-strands.ts.template +1 -1
  303. package/src/ts/agent/mcp-connection/generator.js +48 -46
  304. package/src/ts/agent/mcp-connection/generator.js.map +1 -1
  305. package/src/ts/agent/mcp-connection/schema.d.js +8 -0
  306. package/src/ts/agent/mcp-connection/schema.d.js.map +1 -0
  307. package/src/ts/agent/react-connection/generator.js +79 -79
  308. package/src/ts/agent/react-connection/generator.js.map +1 -1
  309. package/src/ts/agent/react-connection/{serve-local.d.ts → local-dev.d.ts} +2 -2
  310. package/src/ts/agent/react-connection/local-dev.js +17 -0
  311. package/src/ts/agent/react-connection/local-dev.js.map +1 -0
  312. package/src/ts/agent/react-connection/schema.d.js +8 -0
  313. package/src/ts/agent/react-connection/schema.d.js.map +1 -0
  314. package/src/ts/agent/schema.d.js +6 -0
  315. package/src/ts/agent/schema.d.js.map +1 -0
  316. package/src/ts/api/generator.js +16 -18
  317. package/src/ts/api/generator.js.map +1 -1
  318. package/src/ts/api/schema.d.js +6 -0
  319. package/src/ts/api/schema.d.js.map +1 -0
  320. package/src/ts/astro-docs/__snapshots__/generator.spec.ts.snap +8 -8
  321. package/src/ts/astro-docs/generator.js +59 -48
  322. package/src/ts/astro-docs/generator.js.map +1 -1
  323. package/src/ts/astro-docs/schema.d.js +6 -0
  324. package/src/ts/astro-docs/schema.d.js.map +1 -0
  325. package/src/ts/docs/generator.js +10 -10
  326. package/src/ts/docs/generator.js.map +1 -1
  327. package/src/ts/docs/schema.d.js +6 -0
  328. package/src/ts/docs/schema.d.js.map +1 -0
  329. package/src/ts/dynamodb/__snapshots__/generator.spec.ts.snap +68 -30
  330. package/src/ts/dynamodb/agent-connection/__snapshots__/generator.spec.ts.snap +5 -5
  331. package/src/ts/dynamodb/agent-connection/generator.js +21 -23
  332. package/src/ts/dynamodb/agent-connection/generator.js.map +1 -1
  333. package/src/ts/dynamodb/agent-connection/schema.d.js +8 -0
  334. package/src/ts/dynamodb/agent-connection/schema.d.js.map +1 -0
  335. package/src/ts/dynamodb/files/config.json.template +1 -1
  336. package/src/ts/dynamodb/files/src/client.ts.template +7 -7
  337. package/src/ts/dynamodb/generator.js +64 -63
  338. package/src/ts/dynamodb/generator.js.map +1 -1
  339. package/src/ts/dynamodb/mcp-server-connection/__snapshots__/generator.spec.ts.snap +5 -5
  340. package/src/ts/dynamodb/mcp-server-connection/generator.js +21 -23
  341. package/src/ts/dynamodb/mcp-server-connection/generator.js.map +1 -1
  342. package/src/ts/dynamodb/mcp-server-connection/schema.d.js +8 -0
  343. package/src/ts/dynamodb/mcp-server-connection/schema.d.js.map +1 -0
  344. package/src/ts/dynamodb/schema.d.js +6 -0
  345. package/src/ts/dynamodb/schema.d.js.map +1 -0
  346. package/src/ts/dynamodb/smithy-connection/__snapshots__/generator.spec.ts.snap +3 -3
  347. package/src/ts/dynamodb/smithy-connection/generator.js +20 -22
  348. package/src/ts/dynamodb/smithy-connection/generator.js.map +1 -1
  349. package/src/ts/dynamodb/smithy-connection/schema.d.js +8 -0
  350. package/src/ts/dynamodb/smithy-connection/schema.d.js.map +1 -0
  351. package/src/ts/dynamodb/trpc-connection/__snapshots__/generator.spec.ts.snap +3 -3
  352. package/src/ts/dynamodb/trpc-connection/generator.js +20 -22
  353. package/src/ts/dynamodb/trpc-connection/generator.js.map +1 -1
  354. package/src/ts/dynamodb/trpc-connection/schema.d.js +8 -0
  355. package/src/ts/dynamodb/trpc-connection/schema.d.js.map +1 -0
  356. package/src/ts/lambda-function/__snapshots__/generator.spec.ts.snap +4 -4
  357. package/src/ts/lambda-function/generator.js +55 -55
  358. package/src/ts/lambda-function/generator.js.map +1 -1
  359. package/src/ts/lambda-function/io.js +83 -52
  360. package/src/ts/lambda-function/io.js.map +1 -1
  361. package/src/ts/lambda-function/schema.d.js +6 -0
  362. package/src/ts/lambda-function/schema.d.js.map +1 -0
  363. package/src/ts/lib/__snapshots__/generator.spec.ts.snap +23 -36
  364. package/src/ts/lib/biome.js +37 -37
  365. package/src/ts/lib/biome.js.map +1 -1
  366. package/src/ts/lib/generator.js +75 -77
  367. package/src/ts/lib/generator.js.map +1 -1
  368. package/src/ts/lib/schema.d.js +6 -0
  369. package/src/ts/lib/schema.d.js.map +1 -0
  370. package/src/ts/lib/ts-project-utils.js +65 -70
  371. package/src/ts/lib/ts-project-utils.js.map +1 -1
  372. package/src/ts/lib/types.js +5 -2
  373. package/src/ts/lib/types.js.map +1 -1
  374. package/src/ts/lib/vitest.js +29 -26
  375. package/src/ts/lib/vitest.js.map +1 -1
  376. package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +5 -5
  377. package/src/ts/mcp-server/generator.js +157 -138
  378. package/src/ts/mcp-server/generator.js.map +1 -1
  379. package/src/ts/mcp-server/schema.d.js +6 -0
  380. package/src/ts/mcp-server/schema.d.js.map +1 -0
  381. package/src/ts/nx-generator/__snapshots__/generator.spec.ts.snap +14 -9
  382. package/src/ts/nx-generator/files/local/generator.ts.template +3 -3
  383. package/src/ts/nx-generator/files/nx-plugin-for-aws/generator/generator.ts.template +4 -4
  384. package/src/ts/nx-generator/generator.js +84 -83
  385. package/src/ts/nx-generator/generator.js.map +1 -1
  386. package/src/ts/nx-generator/schema.d.js +6 -0
  387. package/src/ts/nx-generator/schema.d.js.map +1 -0
  388. package/src/ts/nx-plugin/__snapshots__/generator.spec.ts.snap +30 -18
  389. package/src/ts/nx-plugin/files/mcp-server/server.ts.template +5 -5
  390. package/src/ts/nx-plugin/files/mcp-server/tools/create-workspace-command.ts.template +2 -2
  391. package/src/ts/nx-plugin/files/mcp-server/tools/general-guidance.ts.template +1 -1
  392. package/src/ts/nx-plugin/files/mcp-server/tools/generator-guide.ts.template +2 -2
  393. package/src/ts/nx-plugin/files/mcp-server/tools/list-generators.ts.template +2 -2
  394. package/src/ts/nx-plugin/files/mcp-server/utils.ts.template +3 -3
  395. package/src/ts/nx-plugin/generator.js +49 -48
  396. package/src/ts/nx-plugin/generator.js.map +1 -1
  397. package/src/ts/nx-plugin/schema.d.js +9 -0
  398. package/src/ts/nx-plugin/schema.d.js.map +1 -0
  399. package/src/ts/nx-plugin/utils.js +32 -44
  400. package/src/ts/nx-plugin/utils.js.map +1 -1
  401. package/src/ts/rdb/__snapshots__/generator.spec.ts.snap +34 -30
  402. package/src/ts/rdb/agent-connection/__snapshots__/generator.spec.ts.snap +6 -6
  403. package/src/ts/rdb/agent-connection/generator.js +38 -39
  404. package/src/ts/rdb/agent-connection/generator.js.map +1 -1
  405. package/src/ts/rdb/agent-connection/schema.d.js +8 -0
  406. package/src/ts/rdb/agent-connection/schema.d.js.map +1 -0
  407. package/src/ts/rdb/files/config.json.template +1 -1
  408. package/src/ts/rdb/files/prisma.config.ts.template +2 -2
  409. package/src/ts/rdb/files/src/prisma.ts.template +7 -7
  410. package/src/ts/rdb/files/src/utils.ts.template +3 -0
  411. package/src/ts/rdb/generator.js +130 -117
  412. package/src/ts/rdb/generator.js.map +1 -1
  413. package/src/ts/rdb/mcp-server-connection/__snapshots__/generator.spec.ts.snap +6 -6
  414. package/src/ts/rdb/mcp-server-connection/generator.js +36 -37
  415. package/src/ts/rdb/mcp-server-connection/generator.js.map +1 -1
  416. package/src/ts/rdb/mcp-server-connection/schema.d.js +8 -0
  417. package/src/ts/rdb/mcp-server-connection/schema.d.js.map +1 -0
  418. package/src/ts/rdb/schema.d.js +6 -0
  419. package/src/ts/rdb/schema.d.js.map +1 -0
  420. package/src/ts/rdb/smithy-connection/__snapshots__/generator.spec.ts.snap +3 -3
  421. package/src/ts/rdb/smithy-connection/generator.js +46 -43
  422. package/src/ts/rdb/smithy-connection/generator.js.map +1 -1
  423. package/src/ts/rdb/smithy-connection/schema.d.js +8 -0
  424. package/src/ts/rdb/smithy-connection/schema.d.js.map +1 -0
  425. package/src/ts/rdb/trpc-connection/__snapshots__/generator.spec.ts.snap +3 -3
  426. package/src/ts/rdb/trpc-connection/generator.js +36 -31
  427. package/src/ts/rdb/trpc-connection/generator.js.map +1 -1
  428. package/src/ts/rdb/trpc-connection/schema.d.js +8 -0
  429. package/src/ts/rdb/trpc-connection/schema.d.js.map +1 -0
  430. package/src/ts/rdb/utils.js +9 -11
  431. package/src/ts/rdb/utils.js.map +1 -1
  432. package/src/ts/react-website/agui/generator.js +69 -61
  433. package/src/ts/react-website/agui/generator.js.map +1 -1
  434. package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +179 -332
  435. package/src/ts/react-website/app/generator.js +197 -188
  436. package/src/ts/react-website/app/generator.js.map +1 -1
  437. package/src/ts/react-website/app/schema.d.js +6 -0
  438. package/src/ts/react-website/app/schema.d.js.map +1 -0
  439. package/src/ts/react-website/app/schema.json +6 -6
  440. package/src/ts/react-website/cognito-auth/__snapshots__/generator.spec.ts.snap +118 -4
  441. package/src/ts/react-website/cognito-auth/__snapshots__/generator.terraform.spec.ts.snap +27 -7
  442. package/src/ts/react-website/cognito-auth/files/app/components/CognitoAuth/index.tsx.template +2 -2
  443. package/src/ts/react-website/cognito-auth/generator.js +60 -60
  444. package/src/ts/react-website/cognito-auth/generator.js.map +1 -1
  445. package/src/ts/react-website/cognito-auth/schema.d.js +6 -0
  446. package/src/ts/react-website/cognito-auth/schema.d.js.map +1 -0
  447. package/src/ts/react-website/cognito-auth/utils.js +34 -32
  448. package/src/ts/react-website/cognito-auth/utils.js.map +1 -1
  449. package/src/ts/react-website/runtime-config/__snapshots__/generator.spec.ts.snap +2 -2
  450. package/src/ts/react-website/runtime-config/files/app/components/RuntimeConfig/index.tsx.template +2 -2
  451. package/src/ts/react-website/runtime-config/generator.js +29 -31
  452. package/src/ts/react-website/runtime-config/generator.js.map +1 -1
  453. package/src/ts/react-website/runtime-config/schema.d.js +6 -0
  454. package/src/ts/react-website/runtime-config/schema.d.js.map +1 -0
  455. package/src/ts/sync/generator.js +60 -59
  456. package/src/ts/sync/generator.js.map +1 -1
  457. package/src/ts/website/app/generator.js +10 -10
  458. package/src/ts/website/app/generator.js.map +1 -1
  459. package/src/ts/website/app/schema.d.js +6 -0
  460. package/src/ts/website/app/schema.d.js.map +1 -0
  461. package/src/ts/website/app/schema.json +6 -6
  462. package/src/ts/website/auth/generator.js +11 -11
  463. package/src/ts/website/auth/generator.js.map +1 -1
  464. package/src/ts/website/auth/schema.d.js +6 -0
  465. package/src/ts/website/auth/schema.d.js.map +1 -0
  466. package/src/utils/agent-chat/agent-chat.d.ts +1 -1
  467. package/src/utils/agent-chat/agent-chat.js +11 -12
  468. package/src/utils/agent-chat/agent-chat.js.map +1 -1
  469. package/src/utils/agent-chat/files/a2a/chat.ts.template +1 -1
  470. package/src/utils/agent-chat/files/ag-ui/chat.ts.template +1 -1
  471. package/src/utils/agent-chat/files/http-py/chat.ts.template +1 -1
  472. package/src/utils/agent-chat/files/http-ts/chat.ts.template +1 -1
  473. package/src/utils/agent-connection/agent-connection.d.ts +9 -3
  474. package/src/utils/agent-connection/agent-connection.js +133 -147
  475. package/src/utils/agent-connection/agent-connection.js.map +1 -1
  476. package/src/utils/agent-connection/files/core-gateway/agentcore-gateway-mcp-transport.ts.template +1 -1
  477. package/src/utils/agent-connection/files/core-strands/gateway/agentcore-gateway-mcp-client-strands.ts.template +1 -1
  478. package/src/utils/agent-connection/files/py-core-gateway/agentcore_gateway_mcp_transport.py.template +1 -1
  479. package/src/utils/agent-connection/files/py-core-langchain/gateway/agentcore_gateway_mcp_client_langchain.py.template +1 -1
  480. package/src/utils/agent-connection/files/py-core-strands/gateway/agentcore_gateway_mcp_client_strands.py.template +1 -1
  481. package/src/utils/agent-core-constructs/agent-core-constructs.js +53 -50
  482. package/src/utils/agent-core-constructs/agent-core-constructs.js.map +1 -1
  483. package/src/utils/agent-core-constructs/files/terraform/app/agentcore-gateway/__nameKebabCase__/__nameKebabCase__.tf.template +4 -4
  484. package/src/utils/agent-core-constructs/files/terraform/core/agent-core/runtime.tf.template +5 -5
  485. package/src/utils/agent-core-constructs/files/terraform/core/agentcore-gateway/gateway.tf.template +2 -2
  486. package/src/utils/api-constructs/api-constructs.js +30 -31
  487. package/src/utils/api-constructs/api-constructs.js.map +1 -1
  488. package/src/utils/api-constructs/files/cdk/core/api/http/http-api.ts.template +14 -6
  489. package/src/utils/api-constructs/files/cdk/core/api/rest/api-gateway-account/index.js.template +58 -0
  490. package/src/utils/api-constructs/files/cdk/core/api/rest/api-gateway-account.ts.template +82 -0
  491. package/src/utils/api-constructs/files/cdk/core/api/rest/rest-api.ts.template +28 -14
  492. package/src/utils/api-constructs/files/terraform/app/apis/http/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +1 -8
  493. package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +91 -4
  494. package/src/utils/api-constructs/files/terraform/core/api/http/http-api/http-api.tf.template +1 -1
  495. package/src/utils/api-constructs/files/terraform/core/api/rest/api-gateway-account/api-gateway-account.tf.template +83 -0
  496. package/src/utils/api-constructs/files/terraform/core/api/rest/rest-api/rest-api.tf.template +5 -11
  497. package/src/utils/api-constructs/open-api-metadata.js +22 -22
  498. package/src/utils/api-constructs/open-api-metadata.js.map +1 -1
  499. package/src/utils/ast/website.js +17 -19
  500. package/src/utils/ast/website.js.map +1 -1
  501. package/src/utils/ast.js +72 -97
  502. package/src/utils/ast.js.map +1 -1
  503. package/src/utils/bundle/bundle.js +50 -47
  504. package/src/utils/bundle/bundle.js.map +1 -1
  505. package/src/utils/commands.js +47 -26
  506. package/src/utils/commands.js.map +1 -1
  507. package/src/utils/config/index.d.ts +2 -2
  508. package/src/utils/config/index.js +5 -4
  509. package/src/utils/config/index.js.map +1 -1
  510. package/src/utils/config/utils.js +48 -58
  511. package/src/utils/config/utils.js.map +1 -1
  512. package/src/utils/connection/open-api/react.d.ts +5 -5
  513. package/src/utils/connection/open-api/react.js +81 -77
  514. package/src/utils/connection/open-api/react.js.map +1 -1
  515. package/src/utils/containers.js +22 -23
  516. package/src/utils/containers.js.map +1 -1
  517. package/src/utils/dynamodb-constructs/dynamodb-constructs.js +32 -33
  518. package/src/utils/dynamodb-constructs/dynamodb-constructs.js.map +1 -1
  519. package/src/utils/dynamodb-constructs/files/terraform/app/dynamodb/__nameKebabCase__/__nameKebabCase__.tf.template +12 -2
  520. package/src/utils/dynamodb-constructs/files/terraform/core/dynamodb/dynamodb.tf.template +1 -1
  521. package/src/utils/files/common/scripts/src/dynamodb/create-local-table.ts.template +2 -2
  522. package/src/utils/files/common/scripts/src/dynamodb/pull-image.ts.template +1 -1
  523. package/src/utils/files/common/scripts/src/dynamodb/start-container.ts.template +17 -2
  524. package/src/utils/files/common/scripts/src/rdb/mysql/wait-for-mysql-db.ts.template +32 -0
  525. package/src/utils/files/common/scripts/src/rdb/postgres/wait-for-postgres-db.ts.template +32 -0
  526. package/src/utils/files/common/scripts/src/rdb/{pull-image.ts.template → shared/pull-image.ts.template} +1 -1
  527. package/src/utils/files/common/scripts/src/rdb/{start-container.ts.template → shared/start-container.ts.template} +16 -1
  528. package/src/utils/files/terraform/src/core/asset-bucket/asset-bucket.tf.template +2 -2
  529. package/src/utils/files/terraform/src/core/runtime-config/appconfig/appconfig.tf.template +1 -1
  530. package/src/utils/files/terraform/src/core/runtime-config/appconfig-deployment/appconfig-deployment.tf.template +3 -3
  531. package/src/utils/files/terraform/src/core/runtime-config/entry/entry.tf.template +1 -1
  532. package/src/utils/files/terraform/src/core/runtime-config/read/read.tf.template +2 -2
  533. package/src/utils/files/website/hooks/sigv4/useSigV4.tsx.template +1 -1
  534. package/src/utils/format.js +153 -99
  535. package/src/utils/format.js.map +1 -1
  536. package/src/utils/fs.js +14 -13
  537. package/src/utils/fs.js.map +1 -1
  538. package/src/utils/function-constructs/files/terraform/app/lambda-functions/__nameKebabCase__/__nameKebabCase__.tf.template +2 -2
  539. package/src/utils/function-constructs/function-constructs.js +21 -26
  540. package/src/utils/function-constructs/function-constructs.js.map +1 -1
  541. package/src/utils/generators.js +18 -20
  542. package/src/utils/generators.js.map +1 -1
  543. package/src/utils/git.js +19 -34
  544. package/src/utils/git.js.map +1 -1
  545. package/src/utils/iac-providers.js +5 -5
  546. package/src/utils/iac-providers.js.map +1 -1
  547. package/src/utils/iac.js +12 -14
  548. package/src/utils/iac.js.map +1 -1
  549. package/src/utils/identity-constructs/files/cdk/core/user-identity.ts.template +112 -2
  550. package/src/utils/identity-constructs/files/terraform/core/user-identity/add-callback-url/add-callback-url.tf.template +19 -6
  551. package/src/utils/identity-constructs/files/terraform/core/user-identity/identity/identity.tf.template +104 -2
  552. package/src/utils/identity-constructs/files/terraform/core/user-identity/main.tf.template +7 -0
  553. package/src/utils/identity-constructs/identity-constructs.js +24 -28
  554. package/src/utils/identity-constructs/identity-constructs.js.map +1 -1
  555. package/src/utils/js.js +27 -20
  556. package/src/utils/js.js.map +1 -1
  557. package/src/utils/mcp.js +34 -41
  558. package/src/utils/mcp.js.map +1 -1
  559. package/src/utils/metrics.js +30 -45
  560. package/src/utils/metrics.js.map +1 -1
  561. package/src/utils/mock-project-graph.js +8 -12
  562. package/src/utils/mock-project-graph.js.map +1 -1
  563. package/src/utils/names.d.ts +16 -0
  564. package/src/utils/names.js +39 -48
  565. package/src/utils/names.js.map +1 -1
  566. package/src/utils/npm-scope.js +9 -17
  567. package/src/utils/npm-scope.js.map +1 -1
  568. package/src/utils/nx.d.ts +18 -2
  569. package/src/utils/nx.js +111 -74
  570. package/src/utils/nx.js.map +1 -1
  571. package/src/utils/nxlv-python.js +27 -15
  572. package/src/utils/nxlv-python.js.map +1 -1
  573. package/src/utils/object.js +5 -11
  574. package/src/utils/object.js.map +1 -1
  575. package/src/utils/paths.js +11 -15
  576. package/src/utils/paths.js.map +1 -1
  577. package/src/utils/pkg-manager.js +4 -9
  578. package/src/utils/pkg-manager.js.map +1 -1
  579. package/src/utils/pnpm-workspace.js +22 -22
  580. package/src/utils/pnpm-workspace.js.map +1 -1
  581. package/src/utils/port.js +26 -36
  582. package/src/utils/port.js.map +1 -1
  583. package/src/utils/py.d.ts +14 -1
  584. package/src/utils/py.js +63 -45
  585. package/src/utils/py.js.map +1 -1
  586. package/src/utils/rdb-constructs/files/terraform/app/dbs/__nameKebabCase__/__nameKebabCase__.tf.template +4 -4
  587. package/src/utils/rdb-constructs/files/terraform/core/rdb/aurora/aurora.tf.template +3 -3
  588. package/src/utils/rdb-constructs/rdb-constructs.js +31 -33
  589. package/src/utils/rdb-constructs/rdb-constructs.js.map +1 -1
  590. package/src/utils/shared-constructs-constants.js +16 -16
  591. package/src/utils/shared-constructs-constants.js.map +1 -1
  592. package/src/utils/shared-constructs.js +57 -54
  593. package/src/utils/shared-constructs.js.map +1 -1
  594. package/src/utils/shared-dynamodb-scripts.js +16 -14
  595. package/src/utils/shared-dynamodb-scripts.js.map +1 -1
  596. package/src/utils/shared-infra-config.js +30 -31
  597. package/src/utils/shared-infra-config.js.map +1 -1
  598. package/src/utils/shared-infra-scripts.js +30 -25
  599. package/src/utils/shared-infra-scripts.js.map +1 -1
  600. package/src/utils/shared-rdb-scripts.d.ts +8 -3
  601. package/src/utils/shared-rdb-scripts.js +21 -13
  602. package/src/utils/shared-rdb-scripts.js.map +1 -1
  603. package/src/utils/shared-scripts.js +12 -17
  604. package/src/utils/shared-scripts.js.map +1 -1
  605. package/src/utils/shared-shadcn.js +64 -60
  606. package/src/utils/shared-shadcn.js.map +1 -1
  607. package/src/utils/test.d.ts +8 -0
  608. package/src/utils/test.js +33 -25
  609. package/src/utils/test.js.map +1 -1
  610. package/src/utils/toml.js +7 -15
  611. package/src/utils/toml.js.map +1 -1
  612. package/src/utils/versions.d.ts +31 -4
  613. package/src/utils/versions.js +40 -23
  614. package/src/utils/versions.js.map +1 -1
  615. package/src/utils/website-constructs/files/terraform/app/static-websites/__websiteNameKebabCase__/__websiteNameKebabCase__.tf.template +1 -1
  616. package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template +4 -4
  617. package/src/utils/website-constructs/website-constructs.js +31 -30
  618. package/src/utils/website-constructs/website-constructs.js.map +1 -1
  619. package/bin/aws-nx-mcp.js +0 -17
  620. package/bin/aws-nx-mcp.js.map +0 -1
  621. package/sdk/agentcore-gateway.d.ts +0 -6
  622. package/sdk/agentcore-gateway.js +0 -11
  623. package/sdk/agentcore-gateway.js.map +0 -1
  624. package/sdk/connection.d.ts +0 -6
  625. package/sdk/connection.js +0 -10
  626. package/sdk/connection.js.map +0 -1
  627. package/sdk/license.js +0 -14
  628. package/sdk/license.js.map +0 -1
  629. package/sdk/open-api.d.ts +0 -10
  630. package/sdk/open-api.js +0 -15
  631. package/sdk/open-api.js.map +0 -1
  632. package/sdk/py.d.ts +0 -14
  633. package/sdk/py.js +0 -18
  634. package/sdk/py.js.map +0 -1
  635. package/sdk/terraform.d.ts +0 -6
  636. package/sdk/terraform.js +0 -11
  637. package/sdk/terraform.js.map +0 -1
  638. package/sdk/ts.d.ts +0 -33
  639. package/sdk/ts.js +0 -53
  640. package/sdk/ts.js.map +0 -1
  641. package/sdk/utils/ast.js +0 -12
  642. package/sdk/utils/ast.js.map +0 -1
  643. package/sdk/utils/format.js +0 -10
  644. package/sdk/utils/format.js.map +0 -1
  645. package/sdk/utils/test.js +0 -10
  646. package/sdk/utils/test.js.map +0 -1
  647. package/src/connection/agent-serve-local.js +0 -48
  648. package/src/connection/agent-serve-local.js.map +0 -1
  649. package/src/connection/serve-local.js +0 -41
  650. package/src/connection/serve-local.js.map +0 -1
  651. package/src/py/agent/react-connection/serve-local.js +0 -38
  652. package/src/py/agent/react-connection/serve-local.js.map +0 -1
  653. package/src/ts/agent/react-connection/serve-local.js +0 -19
  654. package/src/ts/agent/react-connection/serve-local.js.map +0 -1
  655. package/src/utils/files/common/scripts/src/rdb/wait-for-db.ts.template +0 -59
  656. /package/src/agentcore-gateway/files/project/{serve-local.ts.template → local-dev.ts.template} +0 -0
  657. /package/{bin → src/bin}/aws-nx-mcp.d.ts +0 -0
@@ -213,11 +213,12 @@ def echo(message: Annotated[str, Query(max_length=1024)]) -> EchoOutput:
213
213
  if __name__ == "__main__":
214
214
  uvicorn.run("proj_test_api.main:app", port=8000)
215
215
  ",
216
- "apps/test_api/scripts/generate_open_api.py": "from proj_test_api.main import app
217
- import json
216
+ "apps/test_api/scripts/generate_open_api.py": "import json
218
217
  import os
219
218
  import sys
220
219
 
220
+ from proj_test_api.main import app
221
+
221
222
  os.makedirs(os.path.dirname(sys.argv[1]), exist_ok=True)
222
223
  with open(sys.argv[1], "w") as f:
223
224
  json.dump(app.openapi(), f)
@@ -249,7 +250,7 @@ import {
249
250
  HttpApiIntegration,
250
251
  OperationDetails,
251
252
  } from './utils.js';
252
- import { CfnOutput } from 'aws-cdk-lib';
253
+ import { CfnOutput, Stack } from 'aws-cdk-lib';
253
254
  import {
254
255
  HttpApi as _HttpApi,
255
256
  HttpApiProps as _HttpApiProps,
@@ -259,6 +260,8 @@ import {
259
260
  ThrottleSettings,
260
261
  } from 'aws-cdk-lib/aws-apigatewayv2';
261
262
  import { LogGroup } from 'aws-cdk-lib/aws-logs';
263
+ import { Key } from 'aws-cdk-lib/aws-kms';
264
+ import { ServicePrincipal } from 'aws-cdk-lib/aws-iam';
262
265
  import { suppressRules } from '../checkov.js';
263
266
 
264
267
  /**
@@ -335,12 +338,18 @@ export class HttpApi<
335
338
  ...props,
336
339
  });
337
340
 
338
- const accessLogGroup = new LogGroup(this, 'AccessLogs');
339
- suppressRules(
340
- accessLogGroup,
341
- ['CKV_AWS_158'],
342
- 'Using default CloudWatch log encryption',
341
+ // Customer-managed key for access log encryption at rest
342
+ const logsKey = new Key(this, 'AccessLogsKey', {
343
+ enableKeyRotation: true,
344
+ });
345
+ const stack = Stack.of(this);
346
+ logsKey.grantEncryptDecrypt(
347
+ new ServicePrincipal(\`logs.\${stack.region}.amazonaws.com\`),
343
348
  );
349
+
350
+ const accessLogGroup = new LogGroup(this, 'AccessLogs', {
351
+ encryptionKey: logsKey,
352
+ });
344
353
  suppressRules(
345
354
  accessLogGroup,
346
355
  ['CKV_AWS_66', 'CKV_AWS_338'],
@@ -1068,7 +1077,9 @@ export class IntegrationBuilder<
1068
1077
  exports[`fastapi project generator > should set up shared constructs for rest > rest-api.ts 1`] = `
1069
1078
  "import { Construct, IConstruct } from 'constructs';
1070
1079
  import {
1080
+ AccessLogFormat,
1071
1081
  Cors,
1082
+ LogGroupLogDestination,
1072
1083
  Resource,
1073
1084
  RestApi as _RestApi,
1074
1085
  RestApiProps as _RestApiProps,
@@ -1076,14 +1087,17 @@ import {
1076
1087
  Stage,
1077
1088
  ThrottleSettings,
1078
1089
  } from 'aws-cdk-lib/aws-apigateway';
1079
- import { IAspect, RemovalPolicy } from 'aws-cdk-lib';
1090
+ import { IAspect, Stack } from 'aws-cdk-lib';
1080
1091
  import {
1081
1092
  CfnLoggingConfiguration,
1082
1093
  CfnWebACL,
1083
1094
  CfnWebACLAssociation,
1084
1095
  } from 'aws-cdk-lib/aws-wafv2';
1085
1096
  import { LogGroup, RetentionDays } from 'aws-cdk-lib/aws-logs';
1097
+ import { Key } from 'aws-cdk-lib/aws-kms';
1098
+ import { ServicePrincipal } from 'aws-cdk-lib/aws-iam';
1086
1099
  import { RuntimeConfig } from '../runtime-config.js';
1100
+ import { ApiGatewayAccount } from './api-gateway-account.js';
1087
1101
  import {
1088
1102
  ApiIntegrations,
1089
1103
  OperationDetails,
@@ -1168,10 +1182,28 @@ export class RestApi<
1168
1182
  super(scope, id);
1169
1183
  this.integrations = integrations;
1170
1184
 
1185
+ // Account-level CloudWatch role required for REST API access logging
1186
+ const account = ApiGatewayAccount.ensure(this);
1187
+
1188
+ // KMS key for encrypting logs at rest, usable by CloudWatch Logs
1189
+ const logsKey = new Key(this, 'LogsKey', {
1190
+ enableKeyRotation: true,
1191
+ });
1192
+ logsKey.grantEncryptDecrypt(
1193
+ new ServicePrincipal(\`logs.\${Stack.of(this).region}.amazonaws.com\`),
1194
+ );
1195
+
1196
+ const accessLogs = new LogGroup(this, 'AccessLogs', {
1197
+ retention: RetentionDays.ONE_YEAR,
1198
+ encryptionKey: logsKey,
1199
+ });
1200
+
1171
1201
  // Create the API Gateway REST API
1172
1202
  this.api = new _RestApi(this, 'Api', {
1173
1203
  ...props,
1174
1204
  deployOptions: {
1205
+ accessLogDestination: new LogGroupLogDestination(accessLogs),
1206
+ accessLogFormat: AccessLogFormat.jsonWithStandardFields(),
1175
1207
  ...props.deployOptions,
1176
1208
  methodOptions: {
1177
1209
  ...props.deployOptions?.methodOptions,
@@ -1184,6 +1216,8 @@ export class RestApi<
1184
1216
  },
1185
1217
  },
1186
1218
  });
1219
+ // Ensure the account-level role is configured before the stage is created
1220
+ this.api.deploymentStage.node.addDependency(account.resource);
1187
1221
 
1188
1222
  if (enableWaf) {
1189
1223
  this.webAcl = new CfnWebACL(this, 'WebAcl', {
@@ -1250,14 +1284,9 @@ export class RestApi<
1250
1284
  // \`aws-waf-logs-\` to satisfy the WAFv2 logging destination requirement.
1251
1285
  const wafLogGroup = new LogGroup(this, 'WebAclLogs', {
1252
1286
  logGroupName: \`aws-waf-logs-\${apiName}-\${this.node.addr.slice(-8)}\`,
1253
- retention: RetentionDays.ONE_MONTH,
1254
- removalPolicy: RemovalPolicy.DESTROY,
1287
+ retention: RetentionDays.ONE_YEAR,
1288
+ encryptionKey: logsKey,
1255
1289
  });
1256
- suppressRules(
1257
- wafLogGroup,
1258
- ['CKV_AWS_158'],
1259
- 'Using default CloudWatch log encryption for WAF logs',
1260
- );
1261
1290
 
1262
1291
  new CfnLoggingConfiguration(this, 'WebAclLoggingConfig', {
1263
1292
  resourceArn: this.webAcl.attrArn,
@@ -1271,12 +1300,6 @@ export class RestApi<
1271
1300
  'Caching not required for this use case',
1272
1301
  (c) => c instanceof Stage,
1273
1302
  );
1274
- suppressRules(
1275
- this.api,
1276
- ['CKV_AWS_76'],
1277
- 'API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement',
1278
- (c) => c instanceof Stage,
1279
- );
1280
1303
 
1281
1304
  // Resolve the integration for a given operation.
1282
1305
  // If the operation has a dedicated integration, use it; otherwise fall back to $router.
@@ -2039,4613 +2062,3 @@ export class IntegrationBuilder<
2039
2062
  }
2040
2063
  "
2041
2064
  `;
2042
-
2043
- exports[`fastapi project generator > terraform iac > should generate terraform files for HTTP API with Cognito auth and snapshot them > terraform-http-cognito-files 1`] = `
2044
- {
2045
- "http-api.tf": "# Core HTTP API Gateway module
2046
- # This module creates the API Gateway HTTP API, stage, and logging resources
2047
-
2048
- terraform {
2049
- required_version = ">= 1.0"
2050
-
2051
- required_providers {
2052
- aws = {
2053
- source = "hashicorp/aws"
2054
- version = "~> 6.0"
2055
- }
2056
- }
2057
- }
2058
-
2059
- # Core HTTP API Gateway Variables
2060
-
2061
- variable "api_name" {
2062
- description = "Name of the HTTP API Gateway"
2063
- type = string
2064
- }
2065
-
2066
- variable "api_description" {
2067
- description = "Description of the HTTP API Gateway"
2068
- type = string
2069
- default = "HTTP API Gateway"
2070
- }
2071
-
2072
- variable "stage_name" {
2073
- description = "Name of the API Gateway stage"
2074
- type = string
2075
- default = "prod"
2076
- }
2077
-
2078
- variable "stage_auto_deploy" {
2079
- description = "Whether to automatically deploy the API stage"
2080
- type = bool
2081
- default = true
2082
- }
2083
-
2084
- # CORS Configuration
2085
-
2086
- variable "cors_allow_credentials" {
2087
- description = "Whether to allow credentials in CORS requests"
2088
- type = bool
2089
- default = false
2090
- }
2091
-
2092
- variable "cors_allow_headers" {
2093
- description = "List of allowed headers for CORS"
2094
- type = list(string)
2095
- default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
2096
- }
2097
-
2098
- variable "cors_allow_methods" {
2099
- description = "List of allowed HTTP methods for CORS"
2100
- type = list(string)
2101
- default = ["*"]
2102
- }
2103
-
2104
- variable "cors_allow_origins" {
2105
- description = "List of allowed origins for CORS"
2106
- type = list(string)
2107
- default = ["*"]
2108
- }
2109
-
2110
- variable "cors_expose_headers" {
2111
- description = "List of headers to expose in CORS responses"
2112
- type = list(string)
2113
- default = []
2114
- }
2115
-
2116
- variable "cors_max_age" {
2117
- description = "Maximum age for CORS preflight requests in seconds"
2118
- type = number
2119
- default = 86400
2120
- }
2121
-
2122
- # Tags
2123
-
2124
- variable "tags" {
2125
- description = "Tags to apply to all resources"
2126
- type = map(string)
2127
- default = {}
2128
- }
2129
-
2130
- # Data sources
2131
- data "aws_region" "current" {}
2132
- data "aws_caller_identity" "current" {}
2133
-
2134
- # Resources
2135
-
2136
- # KMS key for CloudWatch log group encryption
2137
- resource "aws_kms_key" "logs_key" {
2138
- description = "KMS key for CloudWatch log group encryption"
2139
- deletion_window_in_days = 7
2140
- enable_key_rotation = true
2141
-
2142
- policy = jsonencode({
2143
- Version = "2012-10-17"
2144
- Statement = [
2145
- {
2146
- Sid = "Enable IAM User Permissions"
2147
- Effect = "Allow"
2148
- Principal = {
2149
- AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
2150
- }
2151
- Action = "kms:*"
2152
- Resource = "*"
2153
- },
2154
- {
2155
- Sid = "Allow CloudWatch Logs"
2156
- Effect = "Allow"
2157
- Principal = {
2158
- Service = "logs.\${data.aws_region.current.region}.amazonaws.com"
2159
- }
2160
- Action = [
2161
- "kms:Encrypt",
2162
- "kms:Decrypt",
2163
- "kms:ReEncrypt*",
2164
- "kms:GenerateDataKey*",
2165
- "kms:DescribeKey"
2166
- ]
2167
- Resource = "*"
2168
- Condition = {
2169
- ArnEquals = {
2170
- "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}"
2171
- }
2172
- }
2173
- }
2174
- ]
2175
- })
2176
-
2177
- tags = var.tags
2178
- }
2179
-
2180
- resource "aws_kms_alias" "logs_key_alias" {
2181
- name = "alias/\${var.api_name}-api-logs-encryption"
2182
- target_key_id = aws_kms_key.logs_key.key_id
2183
- }
2184
-
2185
- # HTTP API Gateway
2186
- resource "aws_apigatewayv2_api" "http_api" {
2187
- name = var.api_name
2188
- protocol_type = "HTTP"
2189
- description = var.api_description
2190
-
2191
- cors_configuration {
2192
- allow_credentials = var.cors_allow_credentials
2193
- allow_headers = var.cors_allow_headers
2194
- allow_methods = var.cors_allow_methods
2195
- allow_origins = var.cors_allow_origins
2196
- expose_headers = var.cors_expose_headers
2197
- max_age = var.cors_max_age
2198
- }
2199
-
2200
- tags = var.tags
2201
- }
2202
-
2203
- # API Gateway stage
2204
- resource "aws_apigatewayv2_stage" "api_stage" {
2205
- api_id = aws_apigatewayv2_api.http_api.id
2206
- name = var.stage_name
2207
- auto_deploy = var.stage_auto_deploy
2208
-
2209
- access_log_settings {
2210
- destination_arn = aws_cloudwatch_log_group.api_logs.arn
2211
- format = jsonencode({
2212
- requestId = "$context.requestId"
2213
- ip = "$context.identity.sourceIp"
2214
- requestTime = "$context.requestTime"
2215
- httpMethod = "$context.httpMethod"
2216
- routeKey = "$context.routeKey"
2217
- status = "$context.status"
2218
- protocol = "$context.protocol"
2219
- responseLength = "$context.responseLength"
2220
- error = "$context.error.message"
2221
- integrationError = "$context.integrationErrorMessage"
2222
- })
2223
- }
2224
-
2225
- default_route_settings {
2226
- throttling_burst_limit = 5000
2227
- throttling_rate_limit = 10000
2228
- }
2229
-
2230
- tags = var.tags
2231
-
2232
- depends_on = [aws_cloudwatch_log_group.api_logs]
2233
- }
2234
-
2235
- # CloudWatch Log Group for API Gateway
2236
- resource "aws_cloudwatch_log_group" "api_logs" {
2237
- #checkov:skip=CKV_AWS_338:Log retention set to forever
2238
- #checkov:skip=CKV_AWS_66:Log retention set to forever
2239
- name = "/aws/apigateway/\${var.api_name}"
2240
- kms_key_id = aws_kms_key.logs_key.arn
2241
- tags = var.tags
2242
- }
2243
-
2244
- # Outputs
2245
-
2246
- output "api_id" {
2247
- description = "ID of the HTTP API Gateway"
2248
- value = aws_apigatewayv2_api.http_api.id
2249
- }
2250
-
2251
- output "api_arn" {
2252
- description = "ARN of the HTTP API Gateway"
2253
- value = aws_apigatewayv2_api.http_api.arn
2254
- }
2255
-
2256
- output "api_endpoint" {
2257
- description = "Base URL of the HTTP API Gateway"
2258
- value = aws_apigatewayv2_api.http_api.api_endpoint
2259
- }
2260
-
2261
- output "api_execution_arn" {
2262
- description = "Execution ARN of the HTTP API Gateway"
2263
- value = aws_apigatewayv2_api.http_api.execution_arn
2264
- }
2265
-
2266
- output "stage_id" {
2267
- description = "ID of the API Gateway stage"
2268
- value = aws_apigatewayv2_stage.api_stage.id
2269
- }
2270
-
2271
- output "stage_arn" {
2272
- description = "ARN of the API Gateway stage"
2273
- value = aws_apigatewayv2_stage.api_stage.arn
2274
- }
2275
-
2276
- output "stage_execution_arn" {
2277
- description = "Execution ARN of the API Gateway stage"
2278
- value = aws_apigatewayv2_stage.api_stage.execution_arn
2279
- }
2280
-
2281
- output "stage_invoke_url" {
2282
- description = "Invoke URL of the API Gateway stage"
2283
- value = aws_apigatewayv2_stage.api_stage.invoke_url
2284
- }
2285
-
2286
- output "api_log_group_name" {
2287
- description = "Name of the API Gateway CloudWatch log group"
2288
- value = aws_cloudwatch_log_group.api_logs.name
2289
- }
2290
-
2291
- output "api_log_group_arn" {
2292
- description = "ARN of the API Gateway CloudWatch log group"
2293
- value = aws_cloudwatch_log_group.api_logs.arn
2294
- }",
2295
- "test-api.tf": "terraform {
2296
- required_version = ">= 1.0"
2297
-
2298
- required_providers {
2299
- aws = {
2300
- source = "hashicorp/aws"
2301
- version = "~> 6.0"
2302
- }
2303
- }
2304
- }
2305
-
2306
- # Authentication Configuration
2307
- variable "user_pool_id" {
2308
- description = "Cognito User Pool ID for authentication"
2309
- type = string
2310
- }
2311
-
2312
- variable "user_pool_client_ids" {
2313
- description = "List of Cognito User Pool Client IDs"
2314
- type = list(string)
2315
- }
2316
-
2317
- variable "env" {
2318
- description = "Environment variables for the Lambda function"
2319
- type = map(string)
2320
- default = {}
2321
- }
2322
-
2323
- variable "additional_iam_policy_statements" {
2324
- description = "Additional IAM policy statements for the Lambda function"
2325
- type = list(object({
2326
- Effect = string
2327
- Action = list(string)
2328
- Resource = list(string)
2329
- }))
2330
- default = []
2331
- }
2332
-
2333
- variable "asset_bucket_name" {
2334
- 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."
2335
- type = string
2336
- }
2337
-
2338
- # CORS Configuration (passed to core module)
2339
- variable "cors_allow_credentials" {
2340
- description = "Whether to allow credentials in CORS requests"
2341
- type = bool
2342
- default = false
2343
- }
2344
-
2345
- variable "cors_allow_headers" {
2346
- description = "List of allowed headers for CORS"
2347
- type = list(string)
2348
- default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
2349
- }
2350
-
2351
- variable "cors_allow_methods" {
2352
- description = "List of allowed HTTP methods for CORS"
2353
- type = list(string)
2354
- default = ["*"]
2355
- }
2356
-
2357
- variable "cors_allow_origins" {
2358
- description = "List of allowed origins for CORS"
2359
- type = list(string)
2360
- default = ["*"]
2361
- }
2362
-
2363
- variable "cors_expose_headers" {
2364
- description = "List of headers to expose in CORS responses"
2365
- type = list(string)
2366
- default = []
2367
- }
2368
-
2369
- variable "cors_max_age" {
2370
- description = "Maximum age for CORS preflight requests in seconds"
2371
- type = number
2372
- default = 0
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
-
2410
- # Use the core HTTP API module
2411
- module "http_api" {
2412
- source = "../../../core/api/http-api"
2413
-
2414
- api_name = "TestApi-\${random_string.suffix.result}"
2415
- api_description = "TestApi HTTP API"
2416
- stage_name = "$default"
2417
- stage_auto_deploy = true
2418
-
2419
- # CORS Configuration
2420
- cors_allow_credentials = var.cors_allow_credentials
2421
- cors_allow_headers = var.cors_allow_headers
2422
- cors_allow_methods = var.cors_allow_methods
2423
- cors_allow_origins = var.cors_allow_origins
2424
- cors_expose_headers = var.cors_expose_headers
2425
- cors_max_age = var.cors_max_age
2426
-
2427
- # Tags
2428
- tags = var.tags
2429
- }
2430
-
2431
- # Lambda function
2432
- # This configures a single "router" lambda to serve all requests
2433
- resource "aws_lambda_function" "api_lambda" {
2434
- #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
2435
- #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
2436
- #checkov:skip=CKV_AWS_272:Code signing not required for this use case
2437
- #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
2438
- #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
2439
- s3_bucket = aws_s3_object.lambda_zip.bucket
2440
- s3_key = aws_s3_object.lambda_zip.key
2441
- s3_object_version = aws_s3_object.lambda_zip.version_id
2442
- function_name = "TestApiHandler-\${random_string.suffix.result}"
2443
- role = aws_iam_role.lambda_execution_role.arn
2444
- handler = "run.sh"
2445
- runtime = "python3.14"
2446
- layers = ["arn:aws:lambda:\${data.aws_region.current.region}:753240598075:layer:LambdaAdapterLayerX86:24"]
2447
- timeout = 30
2448
- memory_size = 128
2449
- publish = true
2450
-
2451
- source_code_hash = data.archive_file.lambda_zip.output_base64sha256
2452
-
2453
- # Enable X-Ray tracing
2454
- tracing_config {
2455
- mode = "Active"
2456
- }
2457
-
2458
- # Enable SnapStart for faster cold starts
2459
- snap_start {
2460
- apply_on = "PublishedVersions"
2461
- }
2462
-
2463
- environment {
2464
- variables = merge({
2465
- PORT = "8000"
2466
- AWS_LWA_INVOKE_MODE = "buffered"
2467
- AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
2468
- }, var.env)
2469
- }
2470
-
2471
- tags = var.tags
2472
- }
2473
-
2474
- # IAM role for Lambda execution
2475
- resource "aws_iam_role" "lambda_execution_role" {
2476
- name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
2477
-
2478
- assume_role_policy = jsonencode({
2479
- Version = "2012-10-17"
2480
- Statement = [
2481
- {
2482
- Action = "sts:AssumeRole"
2483
- Effect = "Allow"
2484
- Principal = {
2485
- Service = "lambda.amazonaws.com"
2486
- }
2487
- }
2488
- ]
2489
- })
2490
-
2491
- tags = var.tags
2492
- }
2493
-
2494
- # Attach basic execution policy to Lambda role
2495
- resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
2496
- policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
2497
- role = aws_iam_role.lambda_execution_role.name
2498
- }
2499
-
2500
- # Attach X-Ray tracing policy to Lambda role
2501
- resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
2502
- policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
2503
- role = aws_iam_role.lambda_execution_role.name
2504
- }
2505
-
2506
- # Additional IAM policies for Lambda (if provided)
2507
- resource "aws_iam_role_policy" "lambda_additional_policies" {
2508
- count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
2509
- name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
2510
- role = aws_iam_role.lambda_execution_role.id
2511
-
2512
- policy = jsonencode({
2513
- Version = "2012-10-17"
2514
- Statement = var.additional_iam_policy_statements
2515
- })
2516
- }
2517
-
2518
- # CloudWatch Log Group for Lambda
2519
- resource "aws_cloudwatch_log_group" "lambda_logs" {
2520
- #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
2521
- #checkov:skip=CKV_AWS_338:Log retention set to forever
2522
- #checkov:skip=CKV_AWS_66:Log retention set to forever
2523
- name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
2524
- tags = var.tags
2525
- }
2526
-
2527
- # Lambda alias pointing to the latest published version for SnapStart
2528
- resource "aws_lambda_alias" "live" {
2529
- name = "live"
2530
- function_name = aws_lambda_function.api_lambda.function_name
2531
- function_version = aws_lambda_function.api_lambda.version
2532
-
2533
- depends_on = [aws_lambda_function.api_lambda]
2534
- }
2535
-
2536
- # Cognito User Pool Authorizer
2537
- resource "aws_apigatewayv2_authorizer" "cognito_authorizer" {
2538
- api_id = module.http_api.api_id
2539
- authorizer_type = "JWT"
2540
- identity_sources = ["$request.header.Authorization"]
2541
- name = "TestApiAuthorizer-\${random_string.suffix.result}"
2542
-
2543
- jwt_configuration {
2544
- audience = var.user_pool_client_ids
2545
- issuer = "https://cognito-idp.\${data.aws_region.current.region}.amazonaws.com/\${var.user_pool_id}"
2546
- }
2547
- }
2548
-
2549
- # Lambda integration for HTTP API
2550
- resource "aws_apigatewayv2_integration" "lambda_integration" {
2551
- api_id = module.http_api.api_id
2552
- integration_type = "AWS_PROXY"
2553
- integration_uri = aws_lambda_alias.live.invoke_arn
2554
-
2555
- payload_format_version = "2.0"
2556
- timeout_milliseconds = 30000
2557
-
2558
- depends_on = [aws_lambda_alias.live]
2559
- }
2560
-
2561
- # Route for proxy integration (catches all requests)
2562
- resource "aws_apigatewayv2_route" "proxy_routes" {
2563
- # NB: OPTIONS is omitted here since API Gateway manages responding to preflight requests
2564
- # when cors settings are configured
2565
- for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"])
2566
-
2567
- api_id = module.http_api.api_id
2568
- route_key = "\${each.key} /{proxy+}"
2569
- target = "integrations/\${aws_apigatewayv2_integration.lambda_integration.id}"
2570
-
2571
- authorization_type = "JWT"
2572
- authorizer_id = aws_apigatewayv2_authorizer.cognito_authorizer.id
2573
-
2574
- depends_on = [aws_apigatewayv2_integration.lambda_integration, aws_apigatewayv2_authorizer.cognito_authorizer]
2575
- }
2576
-
2577
- # Add API url to runtime config
2578
- module "add_url_to_runtime_config" {
2579
- source = "../../../core/runtime-config/entry"
2580
-
2581
- namespace = "connection"
2582
- key = "apis"
2583
- value = { "TestApi" = module.http_api.stage_invoke_url }
2584
-
2585
- depends_on = [module.http_api]
2586
- }
2587
-
2588
- # Lambda permission for API Gateway to invoke the function via alias
2589
- resource "aws_lambda_permission" "api_gateway_invoke" {
2590
- statement_id = "AllowExecutionFromAPIGateway"
2591
- action = "lambda:InvokeFunction"
2592
- function_name = aws_lambda_function.api_lambda.function_name
2593
- qualifier = aws_lambda_alias.live.name
2594
- principal = "apigateway.amazonaws.com"
2595
- source_arn = "\${module.http_api.api_execution_arn}/*/*"
2596
-
2597
- depends_on = [module.http_api, aws_lambda_alias.live]
2598
- }
2599
-
2600
- # Outputs
2601
-
2602
- # API Gateway Outputs (from core module)
2603
- output "api_id" {
2604
- description = "ID of the HTTP API Gateway"
2605
- value = module.http_api.api_id
2606
- }
2607
-
2608
- output "api_arn" {
2609
- description = "ARN of the HTTP API Gateway"
2610
- value = module.http_api.api_arn
2611
- }
2612
-
2613
- output "api_endpoint" {
2614
- description = "Base URL of the HTTP API Gateway"
2615
- value = module.http_api.api_endpoint
2616
- }
2617
-
2618
- output "api_execution_arn" {
2619
- description = "Execution ARN of the HTTP API Gateway"
2620
- value = module.http_api.api_execution_arn
2621
- }
2622
-
2623
- output "stage_invoke_url" {
2624
- description = "Invoke URL of the API Gateway stage"
2625
- value = module.http_api.stage_invoke_url
2626
- }
2627
-
2628
- output "stage_arn" {
2629
- description = "ARN of the API Gateway stage"
2630
- value = module.http_api.stage_arn
2631
- }
2632
-
2633
- output "stage_execution_arn" {
2634
- description = "Execution ARN of the API Gateway stage"
2635
- value = module.http_api.stage_execution_arn
2636
- }
2637
-
2638
- # Lambda Function Outputs
2639
- output "lambda_function_name" {
2640
- description = "Name of the Lambda function"
2641
- value = aws_lambda_function.api_lambda.function_name
2642
- }
2643
-
2644
- output "lambda_function_arn" {
2645
- description = "ARN of the Lambda function"
2646
- value = aws_lambda_function.api_lambda.arn
2647
- }
2648
-
2649
- output "lambda_invoke_arn" {
2650
- description = "Invoke ARN of the Lambda function"
2651
- value = aws_lambda_function.api_lambda.invoke_arn
2652
- }
2653
-
2654
- output "lambda_qualified_arn" {
2655
- description = "Qualified ARN of the Lambda function"
2656
- value = aws_lambda_function.api_lambda.qualified_arn
2657
- }
2658
-
2659
- output "lambda_version" {
2660
- description = "Version of the Lambda function"
2661
- value = aws_lambda_function.api_lambda.version
2662
- }
2663
-
2664
- output "lambda_source_code_hash" {
2665
- description = "Base64-encoded SHA256 hash of the Lambda deployment package"
2666
- value = aws_lambda_function.api_lambda.source_code_hash
2667
- }
2668
-
2669
- output "lambda_source_code_size" {
2670
- description = "Size of the Lambda deployment package in bytes"
2671
- value = aws_lambda_function.api_lambda.source_code_size
2672
- }
2673
-
2674
- # IAM Role Outputs
2675
- output "lambda_execution_role_arn" {
2676
- description = "ARN of the Lambda execution role"
2677
- value = aws_iam_role.lambda_execution_role.arn
2678
- }
2679
-
2680
- output "lambda_execution_role_name" {
2681
- description = "Name of the Lambda execution role"
2682
- value = aws_iam_role.lambda_execution_role.name
2683
- }
2684
-
2685
- # Integration Outputs
2686
- output "integration_id" {
2687
- description = "ID of the Lambda integration"
2688
- value = aws_apigatewayv2_integration.lambda_integration.id
2689
- }
2690
-
2691
- # CloudWatch Log Groups
2692
- output "lambda_log_group_name" {
2693
- description = "Name of the Lambda CloudWatch log group"
2694
- value = aws_cloudwatch_log_group.lambda_logs.name
2695
- }
2696
-
2697
- output "lambda_log_group_arn" {
2698
- description = "ARN of the Lambda CloudWatch log group"
2699
- value = aws_cloudwatch_log_group.lambda_logs.arn
2700
- }
2701
-
2702
- output "api_log_group_name" {
2703
- description = "Name of the API Gateway CloudWatch log group"
2704
- value = module.http_api.api_log_group_name
2705
- }
2706
-
2707
- output "api_log_group_arn" {
2708
- description = "ARN of the API Gateway CloudWatch log group"
2709
- value = module.http_api.api_log_group_arn
2710
- }",
2711
- }
2712
- `;
2713
-
2714
- exports[`fastapi project generator > terraform iac > should generate terraform files for HTTP API with Custom auth and snapshot them > terraform-http-custom-files 1`] = `
2715
- {
2716
- "http-api.tf": "# Core HTTP API Gateway module
2717
- # This module creates the API Gateway HTTP API, stage, and logging resources
2718
-
2719
- terraform {
2720
- required_version = ">= 1.0"
2721
-
2722
- required_providers {
2723
- aws = {
2724
- source = "hashicorp/aws"
2725
- version = "~> 6.0"
2726
- }
2727
- }
2728
- }
2729
-
2730
- # Core HTTP API Gateway Variables
2731
-
2732
- variable "api_name" {
2733
- description = "Name of the HTTP API Gateway"
2734
- type = string
2735
- }
2736
-
2737
- variable "api_description" {
2738
- description = "Description of the HTTP API Gateway"
2739
- type = string
2740
- default = "HTTP API Gateway"
2741
- }
2742
-
2743
- variable "stage_name" {
2744
- description = "Name of the API Gateway stage"
2745
- type = string
2746
- default = "prod"
2747
- }
2748
-
2749
- variable "stage_auto_deploy" {
2750
- description = "Whether to automatically deploy the API stage"
2751
- type = bool
2752
- default = true
2753
- }
2754
-
2755
- # CORS Configuration
2756
-
2757
- variable "cors_allow_credentials" {
2758
- description = "Whether to allow credentials in CORS requests"
2759
- type = bool
2760
- default = false
2761
- }
2762
-
2763
- variable "cors_allow_headers" {
2764
- description = "List of allowed headers for CORS"
2765
- type = list(string)
2766
- default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
2767
- }
2768
-
2769
- variable "cors_allow_methods" {
2770
- description = "List of allowed HTTP methods for CORS"
2771
- type = list(string)
2772
- default = ["*"]
2773
- }
2774
-
2775
- variable "cors_allow_origins" {
2776
- description = "List of allowed origins for CORS"
2777
- type = list(string)
2778
- default = ["*"]
2779
- }
2780
-
2781
- variable "cors_expose_headers" {
2782
- description = "List of headers to expose in CORS responses"
2783
- type = list(string)
2784
- default = []
2785
- }
2786
-
2787
- variable "cors_max_age" {
2788
- description = "Maximum age for CORS preflight requests in seconds"
2789
- type = number
2790
- default = 86400
2791
- }
2792
-
2793
- # Tags
2794
-
2795
- variable "tags" {
2796
- description = "Tags to apply to all resources"
2797
- type = map(string)
2798
- default = {}
2799
- }
2800
-
2801
- # Data sources
2802
- data "aws_region" "current" {}
2803
- data "aws_caller_identity" "current" {}
2804
-
2805
- # Resources
2806
-
2807
- # KMS key for CloudWatch log group encryption
2808
- resource "aws_kms_key" "logs_key" {
2809
- description = "KMS key for CloudWatch log group encryption"
2810
- deletion_window_in_days = 7
2811
- enable_key_rotation = true
2812
-
2813
- policy = jsonencode({
2814
- Version = "2012-10-17"
2815
- Statement = [
2816
- {
2817
- Sid = "Enable IAM User Permissions"
2818
- Effect = "Allow"
2819
- Principal = {
2820
- AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
2821
- }
2822
- Action = "kms:*"
2823
- Resource = "*"
2824
- },
2825
- {
2826
- Sid = "Allow CloudWatch Logs"
2827
- Effect = "Allow"
2828
- Principal = {
2829
- Service = "logs.\${data.aws_region.current.region}.amazonaws.com"
2830
- }
2831
- Action = [
2832
- "kms:Encrypt",
2833
- "kms:Decrypt",
2834
- "kms:ReEncrypt*",
2835
- "kms:GenerateDataKey*",
2836
- "kms:DescribeKey"
2837
- ]
2838
- Resource = "*"
2839
- Condition = {
2840
- ArnEquals = {
2841
- "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}"
2842
- }
2843
- }
2844
- }
2845
- ]
2846
- })
2847
-
2848
- tags = var.tags
2849
- }
2850
-
2851
- resource "aws_kms_alias" "logs_key_alias" {
2852
- name = "alias/\${var.api_name}-api-logs-encryption"
2853
- target_key_id = aws_kms_key.logs_key.key_id
2854
- }
2855
-
2856
- # HTTP API Gateway
2857
- resource "aws_apigatewayv2_api" "http_api" {
2858
- name = var.api_name
2859
- protocol_type = "HTTP"
2860
- description = var.api_description
2861
-
2862
- cors_configuration {
2863
- allow_credentials = var.cors_allow_credentials
2864
- allow_headers = var.cors_allow_headers
2865
- allow_methods = var.cors_allow_methods
2866
- allow_origins = var.cors_allow_origins
2867
- expose_headers = var.cors_expose_headers
2868
- max_age = var.cors_max_age
2869
- }
2870
-
2871
- tags = var.tags
2872
- }
2873
-
2874
- # API Gateway stage
2875
- resource "aws_apigatewayv2_stage" "api_stage" {
2876
- api_id = aws_apigatewayv2_api.http_api.id
2877
- name = var.stage_name
2878
- auto_deploy = var.stage_auto_deploy
2879
-
2880
- access_log_settings {
2881
- destination_arn = aws_cloudwatch_log_group.api_logs.arn
2882
- format = jsonencode({
2883
- requestId = "$context.requestId"
2884
- ip = "$context.identity.sourceIp"
2885
- requestTime = "$context.requestTime"
2886
- httpMethod = "$context.httpMethod"
2887
- routeKey = "$context.routeKey"
2888
- status = "$context.status"
2889
- protocol = "$context.protocol"
2890
- responseLength = "$context.responseLength"
2891
- error = "$context.error.message"
2892
- integrationError = "$context.integrationErrorMessage"
2893
- })
2894
- }
2895
-
2896
- default_route_settings {
2897
- throttling_burst_limit = 5000
2898
- throttling_rate_limit = 10000
2899
- }
2900
-
2901
- tags = var.tags
2902
-
2903
- depends_on = [aws_cloudwatch_log_group.api_logs]
2904
- }
2905
-
2906
- # CloudWatch Log Group for API Gateway
2907
- resource "aws_cloudwatch_log_group" "api_logs" {
2908
- #checkov:skip=CKV_AWS_338:Log retention set to forever
2909
- #checkov:skip=CKV_AWS_66:Log retention set to forever
2910
- name = "/aws/apigateway/\${var.api_name}"
2911
- kms_key_id = aws_kms_key.logs_key.arn
2912
- tags = var.tags
2913
- }
2914
-
2915
- # Outputs
2916
-
2917
- output "api_id" {
2918
- description = "ID of the HTTP API Gateway"
2919
- value = aws_apigatewayv2_api.http_api.id
2920
- }
2921
-
2922
- output "api_arn" {
2923
- description = "ARN of the HTTP API Gateway"
2924
- value = aws_apigatewayv2_api.http_api.arn
2925
- }
2926
-
2927
- output "api_endpoint" {
2928
- description = "Base URL of the HTTP API Gateway"
2929
- value = aws_apigatewayv2_api.http_api.api_endpoint
2930
- }
2931
-
2932
- output "api_execution_arn" {
2933
- description = "Execution ARN of the HTTP API Gateway"
2934
- value = aws_apigatewayv2_api.http_api.execution_arn
2935
- }
2936
-
2937
- output "stage_id" {
2938
- description = "ID of the API Gateway stage"
2939
- value = aws_apigatewayv2_stage.api_stage.id
2940
- }
2941
-
2942
- output "stage_arn" {
2943
- description = "ARN of the API Gateway stage"
2944
- value = aws_apigatewayv2_stage.api_stage.arn
2945
- }
2946
-
2947
- output "stage_execution_arn" {
2948
- description = "Execution ARN of the API Gateway stage"
2949
- value = aws_apigatewayv2_stage.api_stage.execution_arn
2950
- }
2951
-
2952
- output "stage_invoke_url" {
2953
- description = "Invoke URL of the API Gateway stage"
2954
- value = aws_apigatewayv2_stage.api_stage.invoke_url
2955
- }
2956
-
2957
- output "api_log_group_name" {
2958
- description = "Name of the API Gateway CloudWatch log group"
2959
- value = aws_cloudwatch_log_group.api_logs.name
2960
- }
2961
-
2962
- output "api_log_group_arn" {
2963
- description = "ARN of the API Gateway CloudWatch log group"
2964
- value = aws_cloudwatch_log_group.api_logs.arn
2965
- }",
2966
- "test-api.tf": "terraform {
2967
- required_version = ">= 1.0"
2968
-
2969
- required_providers {
2970
- aws = {
2971
- source = "hashicorp/aws"
2972
- version = "~> 6.0"
2973
- }
2974
- }
2975
- }
2976
-
2977
-
2978
- variable "env" {
2979
- description = "Environment variables for the Lambda function"
2980
- type = map(string)
2981
- default = {}
2982
- }
2983
-
2984
- variable "additional_iam_policy_statements" {
2985
- description = "Additional IAM policy statements for the Lambda function"
2986
- type = list(object({
2987
- Effect = string
2988
- Action = list(string)
2989
- Resource = list(string)
2990
- }))
2991
- default = []
2992
- }
2993
-
2994
- variable "asset_bucket_name" {
2995
- 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."
2996
- type = string
2997
- }
2998
-
2999
- # CORS Configuration (passed to core module)
3000
- variable "cors_allow_credentials" {
3001
- description = "Whether to allow credentials in CORS requests"
3002
- type = bool
3003
- default = false
3004
- }
3005
-
3006
- variable "cors_allow_headers" {
3007
- description = "List of allowed headers for CORS"
3008
- type = list(string)
3009
- default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
3010
- }
3011
-
3012
- variable "cors_allow_methods" {
3013
- description = "List of allowed HTTP methods for CORS"
3014
- type = list(string)
3015
- default = ["*"]
3016
- }
3017
-
3018
- variable "cors_allow_origins" {
3019
- description = "List of allowed origins for CORS"
3020
- type = list(string)
3021
- default = ["*"]
3022
- }
3023
-
3024
- variable "cors_expose_headers" {
3025
- description = "List of headers to expose in CORS responses"
3026
- type = list(string)
3027
- default = []
3028
- }
3029
-
3030
- variable "cors_max_age" {
3031
- description = "Maximum age for CORS preflight requests in seconds"
3032
- type = number
3033
- default = 0
3034
- }
3035
-
3036
- # Tags
3037
- variable "tags" {
3038
- description = "Tags to apply to all resources"
3039
- type = map(string)
3040
- default = {}
3041
- }
3042
-
3043
- # Get current AWS region and account ID
3044
- data "aws_region" "current" {}
3045
- data "aws_caller_identity" "current" {}
3046
-
3047
- resource "random_string" "suffix" {
3048
- length = 8
3049
- special = false
3050
- upper = false
3051
- }
3052
-
3053
- # Resources
3054
-
3055
- # Create Lambda ZIP file from the bundle directory
3056
- data "archive_file" "lambda_zip" {
3057
- type = "zip"
3058
- source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
3059
- output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
3060
- }
3061
-
3062
- resource "aws_s3_object" "lambda_zip" {
3063
- bucket = var.asset_bucket_name
3064
- key = "apis/test-api/\${data.archive_file.lambda_zip.output_sha256}.zip"
3065
- source = data.archive_file.lambda_zip.output_path
3066
- source_hash = data.archive_file.lambda_zip.output_base64sha256
3067
- etag = data.archive_file.lambda_zip.output_md5
3068
- }
3069
-
3070
-
3071
- # Use the core HTTP API module
3072
- module "http_api" {
3073
- source = "../../../core/api/http-api"
3074
-
3075
- api_name = "TestApi-\${random_string.suffix.result}"
3076
- api_description = "TestApi HTTP API"
3077
- stage_name = "$default"
3078
- stage_auto_deploy = true
3079
-
3080
- # CORS Configuration
3081
- cors_allow_credentials = var.cors_allow_credentials
3082
- cors_allow_headers = var.cors_allow_headers
3083
- cors_allow_methods = var.cors_allow_methods
3084
- cors_allow_origins = var.cors_allow_origins
3085
- cors_expose_headers = var.cors_expose_headers
3086
- cors_max_age = var.cors_max_age
3087
-
3088
- # Tags
3089
- tags = var.tags
3090
- }
3091
-
3092
- # Lambda function
3093
- # This configures a single "router" lambda to serve all requests
3094
- resource "aws_lambda_function" "api_lambda" {
3095
- #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
3096
- #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
3097
- #checkov:skip=CKV_AWS_272:Code signing not required for this use case
3098
- #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
3099
- #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
3100
- s3_bucket = aws_s3_object.lambda_zip.bucket
3101
- s3_key = aws_s3_object.lambda_zip.key
3102
- s3_object_version = aws_s3_object.lambda_zip.version_id
3103
- function_name = "TestApiHandler-\${random_string.suffix.result}"
3104
- role = aws_iam_role.lambda_execution_role.arn
3105
- handler = "run.sh"
3106
- runtime = "python3.14"
3107
- layers = ["arn:aws:lambda:\${data.aws_region.current.region}:753240598075:layer:LambdaAdapterLayerX86:24"]
3108
- timeout = 30
3109
- memory_size = 128
3110
- publish = true
3111
-
3112
- source_code_hash = data.archive_file.lambda_zip.output_base64sha256
3113
-
3114
- # Enable X-Ray tracing
3115
- tracing_config {
3116
- mode = "Active"
3117
- }
3118
-
3119
- # Enable SnapStart for faster cold starts
3120
- snap_start {
3121
- apply_on = "PublishedVersions"
3122
- }
3123
-
3124
- environment {
3125
- variables = merge({
3126
- PORT = "8000"
3127
- AWS_LWA_INVOKE_MODE = "buffered"
3128
- AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
3129
- }, var.env)
3130
- }
3131
-
3132
- tags = var.tags
3133
- }
3134
-
3135
- # IAM role for Lambda execution
3136
- resource "aws_iam_role" "lambda_execution_role" {
3137
- name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
3138
-
3139
- assume_role_policy = jsonencode({
3140
- Version = "2012-10-17"
3141
- Statement = [
3142
- {
3143
- Action = "sts:AssumeRole"
3144
- Effect = "Allow"
3145
- Principal = {
3146
- Service = "lambda.amazonaws.com"
3147
- }
3148
- }
3149
- ]
3150
- })
3151
-
3152
- tags = var.tags
3153
- }
3154
-
3155
- # Attach basic execution policy to Lambda role
3156
- resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
3157
- policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
3158
- role = aws_iam_role.lambda_execution_role.name
3159
- }
3160
-
3161
- # Attach X-Ray tracing policy to Lambda role
3162
- resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
3163
- policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
3164
- role = aws_iam_role.lambda_execution_role.name
3165
- }
3166
-
3167
- # Additional IAM policies for Lambda (if provided)
3168
- resource "aws_iam_role_policy" "lambda_additional_policies" {
3169
- count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
3170
- name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
3171
- role = aws_iam_role.lambda_execution_role.id
3172
-
3173
- policy = jsonencode({
3174
- Version = "2012-10-17"
3175
- Statement = var.additional_iam_policy_statements
3176
- })
3177
- }
3178
-
3179
- # CloudWatch Log Group for Lambda
3180
- resource "aws_cloudwatch_log_group" "lambda_logs" {
3181
- #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
3182
- #checkov:skip=CKV_AWS_338:Log retention set to forever
3183
- #checkov:skip=CKV_AWS_66:Log retention set to forever
3184
- name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
3185
- tags = var.tags
3186
- }
3187
-
3188
- # Lambda alias pointing to the latest published version for SnapStart
3189
- resource "aws_lambda_alias" "live" {
3190
- name = "live"
3191
- function_name = aws_lambda_function.api_lambda.function_name
3192
- function_version = aws_lambda_function.api_lambda.version
3193
-
3194
- depends_on = [aws_lambda_function.api_lambda]
3195
- }
3196
-
3197
- # Custom Lambda Authorizer
3198
- resource "aws_lambda_function" "authorizer_lambda" {
3199
- #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
3200
- #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for authorizer
3201
- #checkov:skip=CKV_AWS_272:Code signing not required for this use case
3202
- #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
3203
- #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
3204
- s3_bucket = aws_s3_object.authorizer_zip.bucket
3205
- s3_key = aws_s3_object.authorizer_zip.key
3206
- s3_object_version = aws_s3_object.authorizer_zip.version_id
3207
- function_name = "TestApiAuthorizer-\${random_string.suffix.result}"
3208
- role = aws_iam_role.authorizer_execution_role.arn
3209
- handler = "proj_test_api.authorizer.handler"
3210
- runtime = "python3.14"
3211
- timeout = 10
3212
- memory_size = 128
3213
-
3214
- source_code_hash = data.archive_file.authorizer_zip.output_base64sha256
3215
-
3216
- tracing_config {
3217
- mode = "Active"
3218
- }
3219
-
3220
- tags = var.tags
3221
- }
3222
-
3223
- resource "aws_iam_role" "authorizer_execution_role" {
3224
- name = "TestApiAuthorizer-role-\${random_string.suffix.result}"
3225
-
3226
- assume_role_policy = jsonencode({
3227
- Version = "2012-10-17"
3228
- Statement = [
3229
- {
3230
- Action = "sts:AssumeRole"
3231
- Effect = "Allow"
3232
- Principal = {
3233
- Service = "lambda.amazonaws.com"
3234
- }
3235
- }
3236
- ]
3237
- })
3238
-
3239
- tags = var.tags
3240
- }
3241
-
3242
- resource "aws_iam_role_policy_attachment" "authorizer_basic_execution" {
3243
- policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
3244
- role = aws_iam_role.authorizer_execution_role.name
3245
- }
3246
-
3247
- resource "aws_iam_role_policy_attachment" "authorizer_xray_execution" {
3248
- policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
3249
- role = aws_iam_role.authorizer_execution_role.name
3250
- }
3251
-
3252
- resource "aws_cloudwatch_log_group" "authorizer_logs" {
3253
- #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
3254
- #checkov:skip=CKV_AWS_338:Log retention set to forever
3255
- #checkov:skip=CKV_AWS_66:Log retention set to forever
3256
- name = "/aws/lambda/TestApiAuthorizer-\${random_string.suffix.result}"
3257
- tags = var.tags
3258
- }
3259
-
3260
- data "archive_file" "authorizer_zip" {
3261
- type = "zip"
3262
- source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
3263
- output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/authorizer.zip"
3264
- }
3265
-
3266
- resource "aws_s3_object" "authorizer_zip" {
3267
- bucket = var.asset_bucket_name
3268
- key = "apis/test-api/authorizer-\${data.archive_file.authorizer_zip.output_sha256}.zip"
3269
- source = data.archive_file.authorizer_zip.output_path
3270
- source_hash = data.archive_file.authorizer_zip.output_base64sha256
3271
- etag = data.archive_file.authorizer_zip.output_md5
3272
- }
3273
-
3274
- resource "aws_apigatewayv2_authorizer" "custom_authorizer" {
3275
- api_id = module.http_api.api_id
3276
- authorizer_type = "REQUEST"
3277
- authorizer_uri = aws_lambda_function.authorizer_lambda.invoke_arn
3278
- authorizer_payload_format_version = "2.0"
3279
- authorizer_result_ttl_in_seconds = 0
3280
- enable_simple_responses = true
3281
- name = "TestApiAuthorizer-\${random_string.suffix.result}"
3282
- }
3283
-
3284
- resource "aws_lambda_permission" "authorizer_invoke" {
3285
- statement_id = "AllowAPIGatewayInvokeAuthorizer"
3286
- action = "lambda:InvokeFunction"
3287
- function_name = aws_lambda_function.authorizer_lambda.function_name
3288
- principal = "apigateway.amazonaws.com"
3289
- source_arn = "\${module.http_api.api_execution_arn}/authorizers/\${aws_apigatewayv2_authorizer.custom_authorizer.id}"
3290
- }
3291
-
3292
- # Lambda integration for HTTP API
3293
- resource "aws_apigatewayv2_integration" "lambda_integration" {
3294
- api_id = module.http_api.api_id
3295
- integration_type = "AWS_PROXY"
3296
- integration_uri = aws_lambda_alias.live.invoke_arn
3297
-
3298
- payload_format_version = "2.0"
3299
- timeout_milliseconds = 30000
3300
-
3301
- depends_on = [aws_lambda_alias.live]
3302
- }
3303
-
3304
- # Route for proxy integration (catches all requests)
3305
- resource "aws_apigatewayv2_route" "proxy_routes" {
3306
- # NB: OPTIONS is omitted here since API Gateway manages responding to preflight requests
3307
- # when cors settings are configured
3308
- for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"])
3309
-
3310
- api_id = module.http_api.api_id
3311
- route_key = "\${each.key} /{proxy+}"
3312
- target = "integrations/\${aws_apigatewayv2_integration.lambda_integration.id}"
3313
-
3314
- authorization_type = "CUSTOM"
3315
- authorizer_id = aws_apigatewayv2_authorizer.custom_authorizer.id
3316
-
3317
- depends_on = [aws_apigatewayv2_integration.lambda_integration, aws_apigatewayv2_authorizer.custom_authorizer]
3318
- }
3319
-
3320
- # Add API url to runtime config
3321
- module "add_url_to_runtime_config" {
3322
- source = "../../../core/runtime-config/entry"
3323
-
3324
- namespace = "connection"
3325
- key = "apis"
3326
- value = { "TestApi" = module.http_api.stage_invoke_url }
3327
-
3328
- depends_on = [module.http_api]
3329
- }
3330
-
3331
- # Lambda permission for API Gateway to invoke the function via alias
3332
- resource "aws_lambda_permission" "api_gateway_invoke" {
3333
- statement_id = "AllowExecutionFromAPIGateway"
3334
- action = "lambda:InvokeFunction"
3335
- function_name = aws_lambda_function.api_lambda.function_name
3336
- qualifier = aws_lambda_alias.live.name
3337
- principal = "apigateway.amazonaws.com"
3338
- source_arn = "\${module.http_api.api_execution_arn}/*/*"
3339
-
3340
- depends_on = [module.http_api, aws_lambda_alias.live]
3341
- }
3342
-
3343
- # Outputs
3344
-
3345
- # API Gateway Outputs (from core module)
3346
- output "api_id" {
3347
- description = "ID of the HTTP API Gateway"
3348
- value = module.http_api.api_id
3349
- }
3350
-
3351
- output "api_arn" {
3352
- description = "ARN of the HTTP API Gateway"
3353
- value = module.http_api.api_arn
3354
- }
3355
-
3356
- output "api_endpoint" {
3357
- description = "Base URL of the HTTP API Gateway"
3358
- value = module.http_api.api_endpoint
3359
- }
3360
-
3361
- output "api_execution_arn" {
3362
- description = "Execution ARN of the HTTP API Gateway"
3363
- value = module.http_api.api_execution_arn
3364
- }
3365
-
3366
- output "stage_invoke_url" {
3367
- description = "Invoke URL of the API Gateway stage"
3368
- value = module.http_api.stage_invoke_url
3369
- }
3370
-
3371
- output "stage_arn" {
3372
- description = "ARN of the API Gateway stage"
3373
- value = module.http_api.stage_arn
3374
- }
3375
-
3376
- output "stage_execution_arn" {
3377
- description = "Execution ARN of the API Gateway stage"
3378
- value = module.http_api.stage_execution_arn
3379
- }
3380
-
3381
- # Lambda Function Outputs
3382
- output "lambda_function_name" {
3383
- description = "Name of the Lambda function"
3384
- value = aws_lambda_function.api_lambda.function_name
3385
- }
3386
-
3387
- output "lambda_function_arn" {
3388
- description = "ARN of the Lambda function"
3389
- value = aws_lambda_function.api_lambda.arn
3390
- }
3391
-
3392
- output "lambda_invoke_arn" {
3393
- description = "Invoke ARN of the Lambda function"
3394
- value = aws_lambda_function.api_lambda.invoke_arn
3395
- }
3396
-
3397
- output "lambda_qualified_arn" {
3398
- description = "Qualified ARN of the Lambda function"
3399
- value = aws_lambda_function.api_lambda.qualified_arn
3400
- }
3401
-
3402
- output "lambda_version" {
3403
- description = "Version of the Lambda function"
3404
- value = aws_lambda_function.api_lambda.version
3405
- }
3406
-
3407
- output "lambda_source_code_hash" {
3408
- description = "Base64-encoded SHA256 hash of the Lambda deployment package"
3409
- value = aws_lambda_function.api_lambda.source_code_hash
3410
- }
3411
-
3412
- output "lambda_source_code_size" {
3413
- description = "Size of the Lambda deployment package in bytes"
3414
- value = aws_lambda_function.api_lambda.source_code_size
3415
- }
3416
-
3417
- # IAM Role Outputs
3418
- output "lambda_execution_role_arn" {
3419
- description = "ARN of the Lambda execution role"
3420
- value = aws_iam_role.lambda_execution_role.arn
3421
- }
3422
-
3423
- output "lambda_execution_role_name" {
3424
- description = "Name of the Lambda execution role"
3425
- value = aws_iam_role.lambda_execution_role.name
3426
- }
3427
-
3428
- # Integration Outputs
3429
- output "integration_id" {
3430
- description = "ID of the Lambda integration"
3431
- value = aws_apigatewayv2_integration.lambda_integration.id
3432
- }
3433
-
3434
- # CloudWatch Log Groups
3435
- output "lambda_log_group_name" {
3436
- description = "Name of the Lambda CloudWatch log group"
3437
- value = aws_cloudwatch_log_group.lambda_logs.name
3438
- }
3439
-
3440
- output "lambda_log_group_arn" {
3441
- description = "ARN of the Lambda CloudWatch log group"
3442
- value = aws_cloudwatch_log_group.lambda_logs.arn
3443
- }
3444
-
3445
- output "api_log_group_name" {
3446
- description = "Name of the API Gateway CloudWatch log group"
3447
- value = module.http_api.api_log_group_name
3448
- }
3449
-
3450
- output "api_log_group_arn" {
3451
- description = "ARN of the API Gateway CloudWatch log group"
3452
- value = module.http_api.api_log_group_arn
3453
- }",
3454
- }
3455
- `;
3456
-
3457
- exports[`fastapi project generator > terraform iac > should generate terraform files for HTTP API with IAM auth and snapshot them > terraform-http-iam-files 1`] = `
3458
- {
3459
- "http-api.tf": "# Core HTTP API Gateway module
3460
- # This module creates the API Gateway HTTP API, stage, and logging resources
3461
-
3462
- terraform {
3463
- required_version = ">= 1.0"
3464
-
3465
- required_providers {
3466
- aws = {
3467
- source = "hashicorp/aws"
3468
- version = "~> 6.0"
3469
- }
3470
- }
3471
- }
3472
-
3473
- # Core HTTP API Gateway Variables
3474
-
3475
- variable "api_name" {
3476
- description = "Name of the HTTP API Gateway"
3477
- type = string
3478
- }
3479
-
3480
- variable "api_description" {
3481
- description = "Description of the HTTP API Gateway"
3482
- type = string
3483
- default = "HTTP API Gateway"
3484
- }
3485
-
3486
- variable "stage_name" {
3487
- description = "Name of the API Gateway stage"
3488
- type = string
3489
- default = "prod"
3490
- }
3491
-
3492
- variable "stage_auto_deploy" {
3493
- description = "Whether to automatically deploy the API stage"
3494
- type = bool
3495
- default = true
3496
- }
3497
-
3498
- # CORS Configuration
3499
-
3500
- variable "cors_allow_credentials" {
3501
- description = "Whether to allow credentials in CORS requests"
3502
- type = bool
3503
- default = false
3504
- }
3505
-
3506
- variable "cors_allow_headers" {
3507
- description = "List of allowed headers for CORS"
3508
- type = list(string)
3509
- default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
3510
- }
3511
-
3512
- variable "cors_allow_methods" {
3513
- description = "List of allowed HTTP methods for CORS"
3514
- type = list(string)
3515
- default = ["*"]
3516
- }
3517
-
3518
- variable "cors_allow_origins" {
3519
- description = "List of allowed origins for CORS"
3520
- type = list(string)
3521
- default = ["*"]
3522
- }
3523
-
3524
- variable "cors_expose_headers" {
3525
- description = "List of headers to expose in CORS responses"
3526
- type = list(string)
3527
- default = []
3528
- }
3529
-
3530
- variable "cors_max_age" {
3531
- description = "Maximum age for CORS preflight requests in seconds"
3532
- type = number
3533
- default = 86400
3534
- }
3535
-
3536
- # Tags
3537
-
3538
- variable "tags" {
3539
- description = "Tags to apply to all resources"
3540
- type = map(string)
3541
- default = {}
3542
- }
3543
-
3544
- # Data sources
3545
- data "aws_region" "current" {}
3546
- data "aws_caller_identity" "current" {}
3547
-
3548
- # Resources
3549
-
3550
- # KMS key for CloudWatch log group encryption
3551
- resource "aws_kms_key" "logs_key" {
3552
- description = "KMS key for CloudWatch log group encryption"
3553
- deletion_window_in_days = 7
3554
- enable_key_rotation = true
3555
-
3556
- policy = jsonencode({
3557
- Version = "2012-10-17"
3558
- Statement = [
3559
- {
3560
- Sid = "Enable IAM User Permissions"
3561
- Effect = "Allow"
3562
- Principal = {
3563
- AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
3564
- }
3565
- Action = "kms:*"
3566
- Resource = "*"
3567
- },
3568
- {
3569
- Sid = "Allow CloudWatch Logs"
3570
- Effect = "Allow"
3571
- Principal = {
3572
- Service = "logs.\${data.aws_region.current.region}.amazonaws.com"
3573
- }
3574
- Action = [
3575
- "kms:Encrypt",
3576
- "kms:Decrypt",
3577
- "kms:ReEncrypt*",
3578
- "kms:GenerateDataKey*",
3579
- "kms:DescribeKey"
3580
- ]
3581
- Resource = "*"
3582
- Condition = {
3583
- ArnEquals = {
3584
- "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}"
3585
- }
3586
- }
3587
- }
3588
- ]
3589
- })
3590
-
3591
- tags = var.tags
3592
- }
3593
-
3594
- resource "aws_kms_alias" "logs_key_alias" {
3595
- name = "alias/\${var.api_name}-api-logs-encryption"
3596
- target_key_id = aws_kms_key.logs_key.key_id
3597
- }
3598
-
3599
- # HTTP API Gateway
3600
- resource "aws_apigatewayv2_api" "http_api" {
3601
- name = var.api_name
3602
- protocol_type = "HTTP"
3603
- description = var.api_description
3604
-
3605
- cors_configuration {
3606
- allow_credentials = var.cors_allow_credentials
3607
- allow_headers = var.cors_allow_headers
3608
- allow_methods = var.cors_allow_methods
3609
- allow_origins = var.cors_allow_origins
3610
- expose_headers = var.cors_expose_headers
3611
- max_age = var.cors_max_age
3612
- }
3613
-
3614
- tags = var.tags
3615
- }
3616
-
3617
- # API Gateway stage
3618
- resource "aws_apigatewayv2_stage" "api_stage" {
3619
- api_id = aws_apigatewayv2_api.http_api.id
3620
- name = var.stage_name
3621
- auto_deploy = var.stage_auto_deploy
3622
-
3623
- access_log_settings {
3624
- destination_arn = aws_cloudwatch_log_group.api_logs.arn
3625
- format = jsonencode({
3626
- requestId = "$context.requestId"
3627
- ip = "$context.identity.sourceIp"
3628
- requestTime = "$context.requestTime"
3629
- httpMethod = "$context.httpMethod"
3630
- routeKey = "$context.routeKey"
3631
- status = "$context.status"
3632
- protocol = "$context.protocol"
3633
- responseLength = "$context.responseLength"
3634
- error = "$context.error.message"
3635
- integrationError = "$context.integrationErrorMessage"
3636
- })
3637
- }
3638
-
3639
- default_route_settings {
3640
- throttling_burst_limit = 5000
3641
- throttling_rate_limit = 10000
3642
- }
3643
-
3644
- tags = var.tags
3645
-
3646
- depends_on = [aws_cloudwatch_log_group.api_logs]
3647
- }
3648
-
3649
- # CloudWatch Log Group for API Gateway
3650
- resource "aws_cloudwatch_log_group" "api_logs" {
3651
- #checkov:skip=CKV_AWS_338:Log retention set to forever
3652
- #checkov:skip=CKV_AWS_66:Log retention set to forever
3653
- name = "/aws/apigateway/\${var.api_name}"
3654
- kms_key_id = aws_kms_key.logs_key.arn
3655
- tags = var.tags
3656
- }
3657
-
3658
- # Outputs
3659
-
3660
- output "api_id" {
3661
- description = "ID of the HTTP API Gateway"
3662
- value = aws_apigatewayv2_api.http_api.id
3663
- }
3664
-
3665
- output "api_arn" {
3666
- description = "ARN of the HTTP API Gateway"
3667
- value = aws_apigatewayv2_api.http_api.arn
3668
- }
3669
-
3670
- output "api_endpoint" {
3671
- description = "Base URL of the HTTP API Gateway"
3672
- value = aws_apigatewayv2_api.http_api.api_endpoint
3673
- }
3674
-
3675
- output "api_execution_arn" {
3676
- description = "Execution ARN of the HTTP API Gateway"
3677
- value = aws_apigatewayv2_api.http_api.execution_arn
3678
- }
3679
-
3680
- output "stage_id" {
3681
- description = "ID of the API Gateway stage"
3682
- value = aws_apigatewayv2_stage.api_stage.id
3683
- }
3684
-
3685
- output "stage_arn" {
3686
- description = "ARN of the API Gateway stage"
3687
- value = aws_apigatewayv2_stage.api_stage.arn
3688
- }
3689
-
3690
- output "stage_execution_arn" {
3691
- description = "Execution ARN of the API Gateway stage"
3692
- value = aws_apigatewayv2_stage.api_stage.execution_arn
3693
- }
3694
-
3695
- output "stage_invoke_url" {
3696
- description = "Invoke URL of the API Gateway stage"
3697
- value = aws_apigatewayv2_stage.api_stage.invoke_url
3698
- }
3699
-
3700
- output "api_log_group_name" {
3701
- description = "Name of the API Gateway CloudWatch log group"
3702
- value = aws_cloudwatch_log_group.api_logs.name
3703
- }
3704
-
3705
- output "api_log_group_arn" {
3706
- description = "ARN of the API Gateway CloudWatch log group"
3707
- value = aws_cloudwatch_log_group.api_logs.arn
3708
- }",
3709
- "test-api.tf": "terraform {
3710
- required_version = ">= 1.0"
3711
-
3712
- required_providers {
3713
- aws = {
3714
- source = "hashicorp/aws"
3715
- version = "~> 6.0"
3716
- }
3717
- }
3718
- }
3719
-
3720
-
3721
- variable "env" {
3722
- description = "Environment variables for the Lambda function"
3723
- type = map(string)
3724
- default = {}
3725
- }
3726
-
3727
- variable "additional_iam_policy_statements" {
3728
- description = "Additional IAM policy statements for the Lambda function"
3729
- type = list(object({
3730
- Effect = string
3731
- Action = list(string)
3732
- Resource = list(string)
3733
- }))
3734
- default = []
3735
- }
3736
-
3737
- variable "asset_bucket_name" {
3738
- 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."
3739
- type = string
3740
- }
3741
-
3742
- # CORS Configuration (passed to core module)
3743
- variable "cors_allow_credentials" {
3744
- description = "Whether to allow credentials in CORS requests"
3745
- type = bool
3746
- default = false
3747
- }
3748
-
3749
- variable "cors_allow_headers" {
3750
- description = "List of allowed headers for CORS"
3751
- type = list(string)
3752
- default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
3753
- }
3754
-
3755
- variable "cors_allow_methods" {
3756
- description = "List of allowed HTTP methods for CORS"
3757
- type = list(string)
3758
- default = ["*"]
3759
- }
3760
-
3761
- variable "cors_allow_origins" {
3762
- description = "List of allowed origins for CORS"
3763
- type = list(string)
3764
- default = ["*"]
3765
- }
3766
-
3767
- variable "cors_expose_headers" {
3768
- description = "List of headers to expose in CORS responses"
3769
- type = list(string)
3770
- default = []
3771
- }
3772
-
3773
- variable "cors_max_age" {
3774
- description = "Maximum age for CORS preflight requests in seconds"
3775
- type = number
3776
- default = 0
3777
- }
3778
-
3779
- # Tags
3780
- variable "tags" {
3781
- description = "Tags to apply to all resources"
3782
- type = map(string)
3783
- default = {}
3784
- }
3785
-
3786
- # Get current AWS region and account ID
3787
- data "aws_region" "current" {}
3788
- data "aws_caller_identity" "current" {}
3789
-
3790
- resource "random_string" "suffix" {
3791
- length = 8
3792
- special = false
3793
- upper = false
3794
- }
3795
-
3796
- # Resources
3797
-
3798
- # Create Lambda ZIP file from the bundle directory
3799
- data "archive_file" "lambda_zip" {
3800
- type = "zip"
3801
- source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
3802
- output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
3803
- }
3804
-
3805
- resource "aws_s3_object" "lambda_zip" {
3806
- bucket = var.asset_bucket_name
3807
- key = "apis/test-api/\${data.archive_file.lambda_zip.output_sha256}.zip"
3808
- source = data.archive_file.lambda_zip.output_path
3809
- source_hash = data.archive_file.lambda_zip.output_base64sha256
3810
- etag = data.archive_file.lambda_zip.output_md5
3811
- }
3812
-
3813
-
3814
- # Use the core HTTP API module
3815
- module "http_api" {
3816
- source = "../../../core/api/http-api"
3817
-
3818
- api_name = "TestApi-\${random_string.suffix.result}"
3819
- api_description = "TestApi HTTP API"
3820
- stage_name = "$default"
3821
- stage_auto_deploy = true
3822
-
3823
- # CORS Configuration
3824
- cors_allow_credentials = var.cors_allow_credentials
3825
- cors_allow_headers = var.cors_allow_headers
3826
- cors_allow_methods = var.cors_allow_methods
3827
- cors_allow_origins = var.cors_allow_origins
3828
- cors_expose_headers = var.cors_expose_headers
3829
- cors_max_age = var.cors_max_age
3830
-
3831
- # Tags
3832
- tags = var.tags
3833
- }
3834
-
3835
- # Lambda function
3836
- # This configures a single "router" lambda to serve all requests
3837
- resource "aws_lambda_function" "api_lambda" {
3838
- #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
3839
- #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
3840
- #checkov:skip=CKV_AWS_272:Code signing not required for this use case
3841
- #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
3842
- #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
3843
- s3_bucket = aws_s3_object.lambda_zip.bucket
3844
- s3_key = aws_s3_object.lambda_zip.key
3845
- s3_object_version = aws_s3_object.lambda_zip.version_id
3846
- function_name = "TestApiHandler-\${random_string.suffix.result}"
3847
- role = aws_iam_role.lambda_execution_role.arn
3848
- handler = "run.sh"
3849
- runtime = "python3.14"
3850
- layers = ["arn:aws:lambda:\${data.aws_region.current.region}:753240598075:layer:LambdaAdapterLayerX86:24"]
3851
- timeout = 30
3852
- memory_size = 128
3853
- publish = true
3854
-
3855
- source_code_hash = data.archive_file.lambda_zip.output_base64sha256
3856
-
3857
- # Enable X-Ray tracing
3858
- tracing_config {
3859
- mode = "Active"
3860
- }
3861
-
3862
- # Enable SnapStart for faster cold starts
3863
- snap_start {
3864
- apply_on = "PublishedVersions"
3865
- }
3866
-
3867
- environment {
3868
- variables = merge({
3869
- PORT = "8000"
3870
- AWS_LWA_INVOKE_MODE = "buffered"
3871
- AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
3872
- }, var.env)
3873
- }
3874
-
3875
- tags = var.tags
3876
- }
3877
-
3878
- # IAM role for Lambda execution
3879
- resource "aws_iam_role" "lambda_execution_role" {
3880
- name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
3881
-
3882
- assume_role_policy = jsonencode({
3883
- Version = "2012-10-17"
3884
- Statement = [
3885
- {
3886
- Action = "sts:AssumeRole"
3887
- Effect = "Allow"
3888
- Principal = {
3889
- Service = "lambda.amazonaws.com"
3890
- }
3891
- }
3892
- ]
3893
- })
3894
-
3895
- tags = var.tags
3896
- }
3897
-
3898
- # Attach basic execution policy to Lambda role
3899
- resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
3900
- policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
3901
- role = aws_iam_role.lambda_execution_role.name
3902
- }
3903
-
3904
- # Attach X-Ray tracing policy to Lambda role
3905
- resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
3906
- policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
3907
- role = aws_iam_role.lambda_execution_role.name
3908
- }
3909
-
3910
- # Additional IAM policies for Lambda (if provided)
3911
- resource "aws_iam_role_policy" "lambda_additional_policies" {
3912
- count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
3913
- name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
3914
- role = aws_iam_role.lambda_execution_role.id
3915
-
3916
- policy = jsonencode({
3917
- Version = "2012-10-17"
3918
- Statement = var.additional_iam_policy_statements
3919
- })
3920
- }
3921
-
3922
- # CloudWatch Log Group for Lambda
3923
- resource "aws_cloudwatch_log_group" "lambda_logs" {
3924
- #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
3925
- #checkov:skip=CKV_AWS_338:Log retention set to forever
3926
- #checkov:skip=CKV_AWS_66:Log retention set to forever
3927
- name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
3928
- tags = var.tags
3929
- }
3930
-
3931
- # Lambda alias pointing to the latest published version for SnapStart
3932
- resource "aws_lambda_alias" "live" {
3933
- name = "live"
3934
- function_name = aws_lambda_function.api_lambda.function_name
3935
- function_version = aws_lambda_function.api_lambda.version
3936
-
3937
- depends_on = [aws_lambda_function.api_lambda]
3938
- }
3939
-
3940
-
3941
- # Lambda integration for HTTP API
3942
- resource "aws_apigatewayv2_integration" "lambda_integration" {
3943
- api_id = module.http_api.api_id
3944
- integration_type = "AWS_PROXY"
3945
- integration_uri = aws_lambda_alias.live.invoke_arn
3946
-
3947
- payload_format_version = "2.0"
3948
- timeout_milliseconds = 30000
3949
-
3950
- depends_on = [aws_lambda_alias.live]
3951
- }
3952
-
3953
- # Route for proxy integration (catches all requests)
3954
- resource "aws_apigatewayv2_route" "proxy_routes" {
3955
- # NB: OPTIONS is omitted here since API Gateway manages responding to preflight requests
3956
- # when cors settings are configured
3957
- for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"])
3958
-
3959
- api_id = module.http_api.api_id
3960
- route_key = "\${each.key} /{proxy+}"
3961
- target = "integrations/\${aws_apigatewayv2_integration.lambda_integration.id}"
3962
-
3963
- authorization_type = "AWS_IAM"
3964
-
3965
- depends_on = [aws_apigatewayv2_integration.lambda_integration]
3966
- }
3967
-
3968
- # Add API url to runtime config
3969
- module "add_url_to_runtime_config" {
3970
- source = "../../../core/runtime-config/entry"
3971
-
3972
- namespace = "connection"
3973
- key = "apis"
3974
- value = { "TestApi" = module.http_api.stage_invoke_url }
3975
-
3976
- depends_on = [module.http_api]
3977
- }
3978
-
3979
- # Lambda permission for API Gateway to invoke the function via alias
3980
- resource "aws_lambda_permission" "api_gateway_invoke" {
3981
- statement_id = "AllowExecutionFromAPIGateway"
3982
- action = "lambda:InvokeFunction"
3983
- function_name = aws_lambda_function.api_lambda.function_name
3984
- qualifier = aws_lambda_alias.live.name
3985
- principal = "apigateway.amazonaws.com"
3986
- source_arn = "\${module.http_api.api_execution_arn}/*/*"
3987
-
3988
- depends_on = [module.http_api, aws_lambda_alias.live]
3989
- }
3990
-
3991
- # Outputs
3992
-
3993
- # API Gateway Outputs (from core module)
3994
- output "api_id" {
3995
- description = "ID of the HTTP API Gateway"
3996
- value = module.http_api.api_id
3997
- }
3998
-
3999
- output "api_arn" {
4000
- description = "ARN of the HTTP API Gateway"
4001
- value = module.http_api.api_arn
4002
- }
4003
-
4004
- output "api_endpoint" {
4005
- description = "Base URL of the HTTP API Gateway"
4006
- value = module.http_api.api_endpoint
4007
- }
4008
-
4009
- output "api_execution_arn" {
4010
- description = "Execution ARN of the HTTP API Gateway"
4011
- value = module.http_api.api_execution_arn
4012
- }
4013
-
4014
- output "stage_invoke_url" {
4015
- description = "Invoke URL of the API Gateway stage"
4016
- value = module.http_api.stage_invoke_url
4017
- }
4018
-
4019
- output "stage_arn" {
4020
- description = "ARN of the API Gateway stage"
4021
- value = module.http_api.stage_arn
4022
- }
4023
-
4024
- output "stage_execution_arn" {
4025
- description = "Execution ARN of the API Gateway stage"
4026
- value = module.http_api.stage_execution_arn
4027
- }
4028
-
4029
- # Lambda Function Outputs
4030
- output "lambda_function_name" {
4031
- description = "Name of the Lambda function"
4032
- value = aws_lambda_function.api_lambda.function_name
4033
- }
4034
-
4035
- output "lambda_function_arn" {
4036
- description = "ARN of the Lambda function"
4037
- value = aws_lambda_function.api_lambda.arn
4038
- }
4039
-
4040
- output "lambda_invoke_arn" {
4041
- description = "Invoke ARN of the Lambda function"
4042
- value = aws_lambda_function.api_lambda.invoke_arn
4043
- }
4044
-
4045
- output "lambda_qualified_arn" {
4046
- description = "Qualified ARN of the Lambda function"
4047
- value = aws_lambda_function.api_lambda.qualified_arn
4048
- }
4049
-
4050
- output "lambda_version" {
4051
- description = "Version of the Lambda function"
4052
- value = aws_lambda_function.api_lambda.version
4053
- }
4054
-
4055
- output "lambda_source_code_hash" {
4056
- description = "Base64-encoded SHA256 hash of the Lambda deployment package"
4057
- value = aws_lambda_function.api_lambda.source_code_hash
4058
- }
4059
-
4060
- output "lambda_source_code_size" {
4061
- description = "Size of the Lambda deployment package in bytes"
4062
- value = aws_lambda_function.api_lambda.source_code_size
4063
- }
4064
-
4065
- # IAM Role Outputs
4066
- output "lambda_execution_role_arn" {
4067
- description = "ARN of the Lambda execution role"
4068
- value = aws_iam_role.lambda_execution_role.arn
4069
- }
4070
-
4071
- output "lambda_execution_role_name" {
4072
- description = "Name of the Lambda execution role"
4073
- value = aws_iam_role.lambda_execution_role.name
4074
- }
4075
-
4076
- # Integration Outputs
4077
- output "integration_id" {
4078
- description = "ID of the Lambda integration"
4079
- value = aws_apigatewayv2_integration.lambda_integration.id
4080
- }
4081
-
4082
- # CloudWatch Log Groups
4083
- output "lambda_log_group_name" {
4084
- description = "Name of the Lambda CloudWatch log group"
4085
- value = aws_cloudwatch_log_group.lambda_logs.name
4086
- }
4087
-
4088
- output "lambda_log_group_arn" {
4089
- description = "ARN of the Lambda CloudWatch log group"
4090
- value = aws_cloudwatch_log_group.lambda_logs.arn
4091
- }
4092
-
4093
- output "api_log_group_name" {
4094
- description = "Name of the API Gateway CloudWatch log group"
4095
- value = module.http_api.api_log_group_name
4096
- }
4097
-
4098
- output "api_log_group_arn" {
4099
- description = "ARN of the API Gateway CloudWatch log group"
4100
- value = module.http_api.api_log_group_arn
4101
- }",
4102
- }
4103
- `;
4104
-
4105
- exports[`fastapi project generator > terraform iac > should generate terraform files for REST API with Cognito auth and snapshot them > terraform-rest-cognito-files 1`] = `
4106
- {
4107
- "rest-api.tf": "# Core REST API Gateway module
4108
- # This module creates the API Gateway REST API, deployment, stage, and logging resources
4109
-
4110
- terraform {
4111
- required_version = ">= 1.0"
4112
-
4113
- required_providers {
4114
- aws = {
4115
- source = "hashicorp/aws"
4116
- version = "~> 6.33"
4117
- }
4118
- random = {
4119
- source = "hashicorp/random"
4120
- version = "~> 3.1"
4121
- }
4122
- }
4123
- }
4124
-
4125
- # Core REST API Gateway Variables
4126
-
4127
- variable "api_name" {
4128
- description = "Name of the REST API Gateway"
4129
- type = string
4130
- }
4131
-
4132
- variable "api_description" {
4133
- description = "Description of the REST API Gateway"
4134
- type = string
4135
- default = "REST API Gateway"
4136
- }
4137
-
4138
- variable "stage_name" {
4139
- description = "Name of the API Gateway stage"
4140
- type = string
4141
- default = "prod"
4142
- }
4143
-
4144
- variable "stage_auto_deploy" {
4145
- description = "Whether to automatically deploy the API stage"
4146
- type = bool
4147
- default = true
4148
- }
4149
-
4150
- variable "enable_waf" {
4151
- 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."
4152
- type = bool
4153
- default = true
4154
- }
4155
-
4156
- # CORS Configuration
4157
-
4158
- variable "cors_allow_headers" {
4159
- description = "List of allowed headers for CORS"
4160
- type = list(string)
4161
- default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
4162
- }
4163
-
4164
- variable "cors_allow_methods" {
4165
- description = "List of allowed HTTP methods for CORS"
4166
- type = list(string)
4167
- default = ["*"]
4168
- }
4169
-
4170
- variable "cors_allow_origins" {
4171
- description = "List of allowed origins for CORS"
4172
- type = list(string)
4173
- default = ["*"]
4174
- }
4175
-
4176
- # Tags
4177
-
4178
- variable "tags" {
4179
- description = "Tags to apply to all resources"
4180
- type = map(string)
4181
- default = {}
4182
- }
4183
-
4184
- # Data sources
4185
- data "aws_region" "current" {}
4186
- data "aws_caller_identity" "current" {}
4187
-
4188
- # Resources
4189
-
4190
- # Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
4191
-
4192
- # REST API Gateway
4193
- resource "aws_api_gateway_rest_api" "rest_api" {
4194
- name = var.api_name
4195
- description = var.api_description
4196
-
4197
- endpoint_configuration {
4198
- types = ["REGIONAL"]
4199
- }
4200
-
4201
- lifecycle {
4202
- create_before_destroy = true
4203
- }
4204
-
4205
- tags = var.tags
4206
- }
4207
-
4208
- # Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
4209
- # after all methods and integrations are defined
4210
-
4211
- # Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
4212
-
4213
- # Gateway Response for CORS (4XX errors)
4214
- resource "aws_api_gateway_gateway_response" "cors_4xx" {
4215
- rest_api_id = aws_api_gateway_rest_api.rest_api.id
4216
- response_type = "DEFAULT_4XX"
4217
-
4218
- response_parameters = {
4219
- "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
4220
- "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
4221
- "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
4222
- }
4223
- }
4224
-
4225
- # Gateway Response for CORS (5XX errors)
4226
- resource "aws_api_gateway_gateway_response" "cors_5xx" {
4227
- rest_api_id = aws_api_gateway_rest_api.rest_api.id
4228
- response_type = "DEFAULT_5XX"
4229
-
4230
- response_parameters = {
4231
- "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
4232
- "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
4233
- "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
4234
- }
4235
- }
4236
-
4237
- resource "aws_wafv2_web_acl" "api_waf" {
4238
- #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
4239
- count = var.enable_waf ? 1 : 0
4240
-
4241
- name = "\${var.api_name}-waf"
4242
- scope = "REGIONAL"
4243
-
4244
- default_action {
4245
- allow {}
4246
- }
4247
-
4248
- rule {
4249
- name = "CRSRule"
4250
- priority = 0
4251
-
4252
- override_action {
4253
- none {}
4254
- }
4255
-
4256
- statement {
4257
- managed_rule_group_statement {
4258
- name = "AWSManagedRulesCommonRuleSet"
4259
- vendor_name = "AWS"
4260
-
4261
- # Count instead of Block: the CRS 8 KB body limit is too restrictive for most APIs.
4262
- rule_action_override {
4263
- name = "SizeRestrictions_BODY"
4264
- action_to_use {
4265
- count {}
4266
- }
4267
- }
4268
- }
4269
- }
4270
-
4271
- visibility_config {
4272
- cloudwatch_metrics_enabled = true
4273
- metric_name = "\${var.api_name}WebAcl-CRS"
4274
- sampled_requests_enabled = true
4275
- }
4276
- }
4277
-
4278
- rule {
4279
- name = "KnownBadInputsRule"
4280
- priority = 1
4281
-
4282
- override_action {
4283
- none {}
4284
- }
4285
-
4286
- statement {
4287
- managed_rule_group_statement {
4288
- name = "AWSManagedRulesKnownBadInputsRuleSet"
4289
- vendor_name = "AWS"
4290
- }
4291
- }
4292
-
4293
- visibility_config {
4294
- cloudwatch_metrics_enabled = true
4295
- metric_name = "\${var.api_name}WebAcl-KnownBadInputs"
4296
- sampled_requests_enabled = true
4297
- }
4298
- }
4299
-
4300
- visibility_config {
4301
- cloudwatch_metrics_enabled = true
4302
- metric_name = "\${var.api_name}WebAcl"
4303
- sampled_requests_enabled = true
4304
- }
4305
-
4306
- tags = var.tags
4307
-
4308
- lifecycle {
4309
- create_before_destroy = true
4310
- }
4311
- }
4312
-
4313
- # CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
4314
- resource "aws_cloudwatch_log_group" "api_waf_logs" {
4315
- #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
4316
- #checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
4317
- count = var.enable_waf ? 1 : 0
4318
-
4319
- name = "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}"
4320
- retention_in_days = 30
4321
- tags = var.tags
4322
- }
4323
-
4324
- resource "random_id" "waf_log_suffix" {
4325
- count = var.enable_waf ? 1 : 0
4326
- byte_length = 4
4327
- }
4328
-
4329
- resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
4330
- count = var.enable_waf ? 1 : 0
4331
-
4332
- log_destination_configs = [aws_cloudwatch_log_group.api_waf_logs[0].arn]
4333
- resource_arn = aws_wafv2_web_acl.api_waf[0].arn
4334
- }
4335
-
4336
-
4337
- # Outputs
4338
-
4339
- output "api_id" {
4340
- description = "ID of the REST API Gateway"
4341
- value = aws_api_gateway_rest_api.rest_api.id
4342
- }
4343
-
4344
- output "api_arn" {
4345
- description = "ARN of the REST API Gateway"
4346
- value = aws_api_gateway_rest_api.rest_api.arn
4347
- }
4348
-
4349
- output "api_endpoint" {
4350
- description = "Base URL of the REST API Gateway"
4351
- value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
4352
- }
4353
-
4354
- output "api_execution_arn" {
4355
- description = "Execution ARN of the REST API Gateway"
4356
- value = aws_api_gateway_rest_api.rest_api.execution_arn
4357
- }
4358
-
4359
- output "api_root_resource_id" {
4360
- description = "Root resource ID of the REST API Gateway"
4361
- value = aws_api_gateway_rest_api.rest_api.root_resource_id
4362
- }
4363
-
4364
- output "waf_web_acl_arn" {
4365
- description = "ARN of the WAFv2 Web ACL associated with the API, or null if WAF is disabled"
4366
- value = var.enable_waf ? aws_wafv2_web_acl.api_waf[0].arn : null
4367
- }
4368
-
4369
- # Note: Stage and deployment outputs are provided by the consuming module
4370
-
4371
- # Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
4372
- "test-api.tf": "terraform {
4373
- required_version = ">= 1.0"
4374
-
4375
- required_providers {
4376
- aws = {
4377
- source = "hashicorp/aws"
4378
- version = "~> 6.33"
4379
- }
4380
- }
4381
- }
4382
-
4383
- # Authentication Configuration
4384
- variable "user_pool_id" {
4385
- description = "Cognito User Pool ID for authentication"
4386
- type = string
4387
- }
4388
-
4389
- variable "user_pool_client_ids" {
4390
- description = "List of Cognito User Pool Client IDs"
4391
- type = list(string)
4392
- }
4393
-
4394
- variable "env" {
4395
- description = "Environment variables for the Lambda function"
4396
- type = map(string)
4397
- default = {}
4398
- }
4399
-
4400
- variable "additional_iam_policy_statements" {
4401
- description = "Additional IAM policy statements for the Lambda function"
4402
- type = list(object({
4403
- Effect = string
4404
- Action = list(string)
4405
- Resource = list(string)
4406
- }))
4407
- default = []
4408
- }
4409
-
4410
- variable "asset_bucket_name" {
4411
- 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."
4412
- type = string
4413
- }
4414
-
4415
- # CORS Configuration (passed to core module)
4416
-
4417
- variable "cors_allow_headers" {
4418
- description = "List of allowed headers for CORS"
4419
- type = list(string)
4420
- default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
4421
- }
4422
-
4423
- variable "cors_allow_methods" {
4424
- description = "List of allowed HTTP methods for CORS"
4425
- type = list(string)
4426
- default = ["*"]
4427
- }
4428
-
4429
- variable "cors_allow_origins" {
4430
- description = "List of allowed origins for CORS"
4431
- type = list(string)
4432
- default = ["*"]
4433
- }
4434
-
4435
- # WAF Configuration
4436
- variable "enable_waf" {
4437
- description = "Whether to enable AWS WAFv2 with the default managed ruleset on the API stage"
4438
- type = bool
4439
- default = true
4440
- }
4441
-
4442
- # Tags
4443
- variable "tags" {
4444
- description = "Tags to apply to all resources"
4445
- type = map(string)
4446
- default = {}
4447
- }
4448
-
4449
- # Get current AWS region and account ID
4450
- data "aws_region" "current" {}
4451
- data "aws_caller_identity" "current" {}
4452
-
4453
- resource "random_string" "suffix" {
4454
- length = 8
4455
- special = false
4456
- upper = false
4457
- }
4458
-
4459
- # Resources
4460
-
4461
- # Create Lambda ZIP file from the bundle directory
4462
- data "archive_file" "lambda_zip" {
4463
- type = "zip"
4464
- source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
4465
- output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
4466
- }
4467
-
4468
- resource "aws_s3_object" "lambda_zip" {
4469
- bucket = var.asset_bucket_name
4470
- key = "apis/test-api/\${data.archive_file.lambda_zip.output_sha256}.zip"
4471
- source = data.archive_file.lambda_zip.output_path
4472
- source_hash = data.archive_file.lambda_zip.output_base64sha256
4473
- etag = data.archive_file.lambda_zip.output_md5
4474
- }
4475
-
4476
- # Use the core REST API module
4477
- module "rest_api" {
4478
- source = "../../../core/api/rest-api"
4479
-
4480
- api_name = "TestApi-\${random_string.suffix.result}"
4481
- api_description = "TestApi REST API"
4482
- stage_name = "prod"
4483
- stage_auto_deploy = true
4484
-
4485
- # WAF Configuration
4486
- enable_waf = var.enable_waf
4487
-
4488
- # CORS Configuration
4489
- cors_allow_headers = var.cors_allow_headers
4490
- cors_allow_methods = var.cors_allow_methods
4491
- cors_allow_origins = var.cors_allow_origins
4492
-
4493
- # Tags
4494
- tags = var.tags
4495
- }
4496
-
4497
- resource "aws_wafv2_web_acl_association" "api_waf_association" {
4498
- count = var.enable_waf ? 1 : 0
4499
-
4500
- resource_arn = aws_api_gateway_stage.api_stage.arn
4501
- web_acl_arn = module.rest_api.waf_web_acl_arn
4502
-
4503
- depends_on = [aws_api_gateway_stage.api_stage]
4504
- }
4505
-
4506
- # Lambda function
4507
- resource "aws_lambda_function" "api_lambda" {
4508
- #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
4509
- #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
4510
- #checkov:skip=CKV_AWS_272:Code signing not required for this use case
4511
- #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
4512
- #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
4513
- s3_bucket = aws_s3_object.lambda_zip.bucket
4514
- s3_key = aws_s3_object.lambda_zip.key
4515
- s3_object_version = aws_s3_object.lambda_zip.version_id
4516
- function_name = "TestApiHandler-\${random_string.suffix.result}"
4517
- role = aws_iam_role.lambda_execution_role.arn
4518
- handler = "run.sh"
4519
- runtime = "python3.14"
4520
- layers = ["arn:aws:lambda:\${data.aws_region.current.region}:753240598075:layer:LambdaAdapterLayerX86:24"]
4521
- timeout = 30
4522
- memory_size = 128
4523
- publish = true
4524
-
4525
- source_code_hash = data.archive_file.lambda_zip.output_base64sha256
4526
-
4527
- # Enable X-Ray tracing
4528
- tracing_config {
4529
- mode = "Active"
4530
- }
4531
-
4532
- # Enable SnapStart for faster cold starts
4533
- snap_start {
4534
- apply_on = "PublishedVersions"
4535
- }
4536
-
4537
- environment {
4538
- variables = merge({
4539
- PORT = "8000"
4540
- AWS_LWA_INVOKE_MODE = "response_stream"
4541
- AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
4542
- }, var.env)
4543
- }
4544
-
4545
- tags = var.tags
4546
- }
4547
-
4548
- # IAM role for Lambda execution
4549
- resource "aws_iam_role" "lambda_execution_role" {
4550
- name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
4551
-
4552
- assume_role_policy = jsonencode({
4553
- Version = "2012-10-17"
4554
- Statement = [
4555
- {
4556
- Action = "sts:AssumeRole"
4557
- Effect = "Allow"
4558
- Principal = {
4559
- Service = "lambda.amazonaws.com"
4560
- }
4561
- }
4562
- ]
4563
- })
4564
-
4565
- tags = var.tags
4566
- }
4567
-
4568
- # Attach basic execution policy to Lambda role
4569
- resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
4570
- policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
4571
- role = aws_iam_role.lambda_execution_role.name
4572
- }
4573
-
4574
- # Attach X-Ray tracing policy to Lambda role
4575
- resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
4576
- policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
4577
- role = aws_iam_role.lambda_execution_role.name
4578
- }
4579
-
4580
- # Additional IAM policies for Lambda (if provided)
4581
- resource "aws_iam_role_policy" "lambda_additional_policies" {
4582
- count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
4583
- name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
4584
- role = aws_iam_role.lambda_execution_role.id
4585
-
4586
- policy = jsonencode({
4587
- Version = "2012-10-17"
4588
- Statement = var.additional_iam_policy_statements
4589
- })
4590
- }
4591
-
4592
- # CloudWatch Log Group for Lambda
4593
- resource "aws_cloudwatch_log_group" "lambda_logs" {
4594
- #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
4595
- #checkov:skip=CKV_AWS_338:Log retention set to forever
4596
- #checkov:skip=CKV_AWS_66:Log retention set to forever
4597
- name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
4598
- tags = var.tags
4599
- }
4600
-
4601
- # Lambda alias pointing to the latest published version for SnapStart
4602
- resource "aws_lambda_alias" "live" {
4603
- name = "live"
4604
- function_name = aws_lambda_function.api_lambda.function_name
4605
- function_version = aws_lambda_function.api_lambda.version
4606
-
4607
- depends_on = [aws_lambda_function.api_lambda]
4608
- }
4609
-
4610
- # Cognito User Pool Authorizer
4611
- resource "aws_api_gateway_authorizer" "cognito_authorizer" {
4612
- name = "TestApiAuthorizer-\${random_string.suffix.result}"
4613
- rest_api_id = module.rest_api.api_id
4614
- type = "COGNITO_USER_POOLS"
4615
- provider_arns = ["arn:aws:cognito-idp:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:userpool/\${var.user_pool_id}"]
4616
- identity_source = "method.request.header.Authorization"
4617
- }
4618
-
4619
- # Create proxy resource (captures all paths)
4620
- resource "aws_api_gateway_resource" "proxy_resource" {
4621
- rest_api_id = module.rest_api.api_id
4622
- parent_id = module.rest_api.api_root_resource_id
4623
- path_part = "{proxy+}"
4624
- }
4625
-
4626
- # Lambda integration for REST API
4627
- resource "aws_api_gateway_integration" "lambda_integration" {
4628
- rest_api_id = module.rest_api.api_id
4629
- resource_id = aws_api_gateway_resource.proxy_resource.id
4630
- http_method = aws_api_gateway_method.proxy_method.http_method
4631
-
4632
- integration_http_method = "POST"
4633
- type = "AWS_PROXY"
4634
- # Use the response streaming invocation path with the alias ARN for SnapStart
4635
- uri = "arn:aws:apigateway:\${data.aws_region.current.region}:lambda:path/2021-11-15/functions/\${aws_lambda_alias.live.arn}/response-streaming-invocations"
4636
- response_transfer_mode = "STREAM"
4637
-
4638
- depends_on = [aws_lambda_alias.live]
4639
- }
4640
-
4641
- # Method for proxy integration
4642
- resource "aws_api_gateway_method" "proxy_method" {
4643
- #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
4644
- rest_api_id = module.rest_api.api_id
4645
- resource_id = aws_api_gateway_resource.proxy_resource.id
4646
- http_method = "ANY"
4647
-
4648
- authorization = "COGNITO_USER_POOLS"
4649
- authorizer_id = aws_api_gateway_authorizer.cognito_authorizer.id
4650
- # Accept access tokens from both sign-in flows: 'openid' (Cognito hosted UI)
4651
- # and 'aws.cognito.signin.user.admin' (Cognito admin/SRP auth APIs).
4652
- authorization_scopes = ["openid", "aws.cognito.signin.user.admin"]
4653
-
4654
- request_parameters = {
4655
- "method.request.path.proxy" = true
4656
- }
4657
-
4658
- depends_on = [aws_api_gateway_authorizer.cognito_authorizer]
4659
- }
4660
-
4661
- # OPTIONS method for CORS preflight
4662
- resource "aws_api_gateway_method" "options_method" {
4663
- #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
4664
- #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
4665
- rest_api_id = module.rest_api.api_id
4666
- resource_id = aws_api_gateway_resource.proxy_resource.id
4667
- http_method = "OPTIONS"
4668
- authorization = "NONE"
4669
- }
4670
-
4671
- # CORS integration for OPTIONS method
4672
- resource "aws_api_gateway_integration" "options_integration" {
4673
- rest_api_id = module.rest_api.api_id
4674
- resource_id = aws_api_gateway_resource.proxy_resource.id
4675
- http_method = aws_api_gateway_method.options_method.http_method
4676
-
4677
- type = "MOCK"
4678
- request_templates = {
4679
- "application/json" = "{\\"statusCode\\": 204}"
4680
- }
4681
- }
4682
-
4683
- # OPTIONS method response
4684
- resource "aws_api_gateway_method_response" "options_response" {
4685
- rest_api_id = module.rest_api.api_id
4686
- resource_id = aws_api_gateway_resource.proxy_resource.id
4687
- http_method = aws_api_gateway_method.options_method.http_method
4688
- status_code = "204"
4689
-
4690
- response_parameters = {
4691
- "method.response.header.Access-Control-Allow-Headers" = true
4692
- "method.response.header.Access-Control-Allow-Methods" = true
4693
- "method.response.header.Access-Control-Allow-Origin" = true
4694
- }
4695
- }
4696
-
4697
- # OPTIONS integration response
4698
- resource "aws_api_gateway_integration_response" "options_integration_response" {
4699
- rest_api_id = module.rest_api.api_id
4700
- resource_id = aws_api_gateway_resource.proxy_resource.id
4701
- http_method = aws_api_gateway_method.options_method.http_method
4702
- status_code = aws_api_gateway_method_response.options_response.status_code
4703
-
4704
- response_parameters = {
4705
- "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
4706
- "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
4707
- "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
4708
- }
4709
- }
4710
-
4711
- # API Gateway deployment
4712
- resource "aws_api_gateway_deployment" "api_deployment" {
4713
- rest_api_id = module.rest_api.api_id
4714
-
4715
- triggers = {
4716
- redeployment = sha1(jsonencode([
4717
- aws_api_gateway_resource.proxy_resource.id,
4718
- aws_api_gateway_method.proxy_method.id,
4719
- aws_api_gateway_integration.lambda_integration.id,
4720
- aws_api_gateway_method.options_method.id,
4721
- aws_api_gateway_integration.options_integration.id,
4722
- ]))
4723
- }
4724
-
4725
- lifecycle {
4726
- create_before_destroy = true
4727
- }
4728
-
4729
- depends_on = [
4730
- aws_api_gateway_method.proxy_method,
4731
- aws_api_gateway_integration.lambda_integration,
4732
- aws_api_gateway_method.options_method,
4733
- aws_api_gateway_integration.options_integration,
4734
- aws_api_gateway_method_response.options_response,
4735
- aws_api_gateway_integration_response.options_integration_response,
4736
- ]
4737
- }
4738
-
4739
- # API Gateway stage
4740
- resource "aws_api_gateway_stage" "api_stage" {
4741
- #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
4742
- #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
4743
- #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
4744
- #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
4745
- #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
4746
- deployment_id = aws_api_gateway_deployment.api_deployment.id
4747
- rest_api_id = module.rest_api.api_id
4748
- stage_name = "prod"
4749
- xray_tracing_enabled = true
4750
-
4751
- tags = var.tags
4752
-
4753
- depends_on = [aws_api_gateway_deployment.api_deployment]
4754
- }
4755
-
4756
- # API Gateway Resource Policy
4757
- resource "aws_api_gateway_rest_api_policy" "api_policy" {
4758
- rest_api_id = module.rest_api.api_id
4759
-
4760
- policy = jsonencode({
4761
- Version = "2012-10-17"
4762
- Statement = [
4763
- {
4764
- # Allow all callers to invoke the API in the resource policy, since auth is handled by Cognito
4765
- Effect = "Allow"
4766
- Principal = "*"
4767
- Action = "execute-api:Invoke"
4768
- Resource = "execute-api:/*"
4769
- }
4770
- ]
4771
- })
4772
- }
4773
-
4774
- # Lambda permission for API Gateway to invoke the function via alias
4775
- resource "aws_lambda_permission" "api_gateway_invoke" {
4776
- statement_id = "AllowExecutionFromAPIGateway"
4777
- action = "lambda:InvokeFunction"
4778
- function_name = aws_lambda_function.api_lambda.function_name
4779
- qualifier = aws_lambda_alias.live.name
4780
- principal = "apigateway.amazonaws.com"
4781
- source_arn = "\${module.rest_api.api_execution_arn}/*/*"
4782
-
4783
- depends_on = [module.rest_api, aws_lambda_alias.live]
4784
- }
4785
-
4786
- # Lambda permission for API Gateway to invoke with response streaming via alias
4787
- resource "aws_lambda_permission" "api_gateway_invoke_streaming" {
4788
- statement_id = "AllowStreamingFromAPIGateway"
4789
- action = "lambda:InvokeFunctionWithResponseStream"
4790
- function_name = aws_lambda_function.api_lambda.function_name
4791
- qualifier = aws_lambda_alias.live.name
4792
- principal = "apigateway.amazonaws.com"
4793
- source_arn = "\${module.rest_api.api_execution_arn}/*/*"
4794
-
4795
- depends_on = [module.rest_api, aws_lambda_alias.live]
4796
- }
4797
-
4798
- # Add API url to runtime config
4799
- module "add_url_to_runtime_config" {
4800
- source = "../../../core/runtime-config/entry"
4801
-
4802
- namespace = "connection"
4803
- key = "apis"
4804
- value = { "TestApi" = aws_api_gateway_stage.api_stage.invoke_url }
4805
-
4806
- depends_on = [aws_api_gateway_stage.api_stage]
4807
- }
4808
-
4809
- # Outputs
4810
-
4811
- # API Gateway Outputs (from core module)
4812
- output "api_id" {
4813
- description = "ID of the REST API Gateway"
4814
- value = module.rest_api.api_id
4815
- }
4816
-
4817
- output "api_arn" {
4818
- description = "ARN of the REST API Gateway"
4819
- value = module.rest_api.api_arn
4820
- }
4821
-
4822
- output "api_endpoint" {
4823
- description = "Base URL of the REST API Gateway"
4824
- value = module.rest_api.api_endpoint
4825
- }
4826
-
4827
- output "api_execution_arn" {
4828
- description = "Execution ARN of the REST API Gateway"
4829
- value = module.rest_api.api_execution_arn
4830
- }
4831
-
4832
- output "stage_invoke_url" {
4833
- description = "Invoke URL of the API Gateway stage"
4834
- value = aws_api_gateway_stage.api_stage.invoke_url
4835
- }
4836
-
4837
- output "stage_arn" {
4838
- description = "ARN of the API Gateway stage"
4839
- value = aws_api_gateway_stage.api_stage.arn
4840
- }
4841
-
4842
- output "stage_execution_arn" {
4843
- description = "Execution ARN of the API Gateway stage"
4844
- value = aws_api_gateway_stage.api_stage.execution_arn
4845
- }
4846
-
4847
- output "deployment_id" {
4848
- description = "ID of the API Gateway deployment"
4849
- value = aws_api_gateway_deployment.api_deployment.id
4850
- }
4851
-
4852
- output "stage_id" {
4853
- description = "ID of the API Gateway stage"
4854
- value = aws_api_gateway_stage.api_stage.id
4855
- }
4856
-
4857
- # Lambda Function Outputs
4858
- output "lambda_function_name" {
4859
- description = "Name of the Lambda function"
4860
- value = aws_lambda_function.api_lambda.function_name
4861
- }
4862
-
4863
- output "lambda_function_arn" {
4864
- description = "ARN of the Lambda function"
4865
- value = aws_lambda_function.api_lambda.arn
4866
- }
4867
-
4868
- output "lambda_invoke_arn" {
4869
- description = "Invoke ARN of the Lambda function"
4870
- value = aws_lambda_function.api_lambda.invoke_arn
4871
- }
4872
-
4873
- output "lambda_qualified_arn" {
4874
- description = "Qualified ARN of the Lambda function"
4875
- value = aws_lambda_function.api_lambda.qualified_arn
4876
- }
4877
-
4878
- output "lambda_version" {
4879
- description = "Version of the Lambda function"
4880
- value = aws_lambda_function.api_lambda.version
4881
- }
4882
-
4883
- output "lambda_source_code_hash" {
4884
- description = "Base64-encoded SHA256 hash of the Lambda deployment package"
4885
- value = aws_lambda_function.api_lambda.source_code_hash
4886
- }
4887
-
4888
- output "lambda_source_code_size" {
4889
- description = "Size of the Lambda deployment package in bytes"
4890
- value = aws_lambda_function.api_lambda.source_code_size
4891
- }
4892
-
4893
- # IAM Role Outputs
4894
- output "lambda_execution_role_arn" {
4895
- description = "ARN of the Lambda execution role"
4896
- value = aws_iam_role.lambda_execution_role.arn
4897
- }
4898
-
4899
- output "lambda_execution_role_name" {
4900
- description = "Name of the Lambda execution role"
4901
- value = aws_iam_role.lambda_execution_role.name
4902
- }
4903
-
4904
- # Integration Outputs
4905
- output "integration_id" {
4906
- description = "ID of the Lambda integration"
4907
- value = aws_api_gateway_integration.lambda_integration.id
4908
- }
4909
-
4910
- output "proxy_resource_id" {
4911
- description = "ID of the proxy resource"
4912
- value = aws_api_gateway_resource.proxy_resource.id
4913
- }
4914
-
4915
- output "proxy_method_id" {
4916
- description = "ID of the proxy method"
4917
- value = aws_api_gateway_method.proxy_method.id
4918
- }
4919
-
4920
- # CloudWatch Log Groups
4921
- output "lambda_log_group_name" {
4922
- description = "Name of the Lambda CloudWatch log group"
4923
- value = aws_cloudwatch_log_group.lambda_logs.name
4924
- }
4925
-
4926
- output "lambda_log_group_arn" {
4927
- description = "ARN of the Lambda CloudWatch log group"
4928
- value = aws_cloudwatch_log_group.lambda_logs.arn
4929
- }
4930
- ",
4931
- }
4932
- `;
4933
-
4934
- exports[`fastapi project generator > terraform iac > should generate terraform files for REST API with IAM auth and snapshot them > terraform-rest-iam-files 1`] = `
4935
- {
4936
- "rest-api.tf": "# Core REST API Gateway module
4937
- # This module creates the API Gateway REST API, deployment, stage, and logging resources
4938
-
4939
- terraform {
4940
- required_version = ">= 1.0"
4941
-
4942
- required_providers {
4943
- aws = {
4944
- source = "hashicorp/aws"
4945
- version = "~> 6.33"
4946
- }
4947
- random = {
4948
- source = "hashicorp/random"
4949
- version = "~> 3.1"
4950
- }
4951
- }
4952
- }
4953
-
4954
- # Core REST API Gateway Variables
4955
-
4956
- variable "api_name" {
4957
- description = "Name of the REST API Gateway"
4958
- type = string
4959
- }
4960
-
4961
- variable "api_description" {
4962
- description = "Description of the REST API Gateway"
4963
- type = string
4964
- default = "REST API Gateway"
4965
- }
4966
-
4967
- variable "stage_name" {
4968
- description = "Name of the API Gateway stage"
4969
- type = string
4970
- default = "prod"
4971
- }
4972
-
4973
- variable "stage_auto_deploy" {
4974
- description = "Whether to automatically deploy the API stage"
4975
- type = bool
4976
- default = true
4977
- }
4978
-
4979
- variable "enable_waf" {
4980
- 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."
4981
- type = bool
4982
- default = true
4983
- }
4984
-
4985
- # CORS Configuration
4986
-
4987
- variable "cors_allow_headers" {
4988
- description = "List of allowed headers for CORS"
4989
- type = list(string)
4990
- default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
4991
- }
4992
-
4993
- variable "cors_allow_methods" {
4994
- description = "List of allowed HTTP methods for CORS"
4995
- type = list(string)
4996
- default = ["*"]
4997
- }
4998
-
4999
- variable "cors_allow_origins" {
5000
- description = "List of allowed origins for CORS"
5001
- type = list(string)
5002
- default = ["*"]
5003
- }
5004
-
5005
- # Tags
5006
-
5007
- variable "tags" {
5008
- description = "Tags to apply to all resources"
5009
- type = map(string)
5010
- default = {}
5011
- }
5012
-
5013
- # Data sources
5014
- data "aws_region" "current" {}
5015
- data "aws_caller_identity" "current" {}
5016
-
5017
- # Resources
5018
-
5019
- # Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
5020
-
5021
- # REST API Gateway
5022
- resource "aws_api_gateway_rest_api" "rest_api" {
5023
- name = var.api_name
5024
- description = var.api_description
5025
-
5026
- endpoint_configuration {
5027
- types = ["REGIONAL"]
5028
- }
5029
-
5030
- lifecycle {
5031
- create_before_destroy = true
5032
- }
5033
-
5034
- tags = var.tags
5035
- }
5036
-
5037
- # Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
5038
- # after all methods and integrations are defined
5039
-
5040
- # Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
5041
-
5042
- # Gateway Response for CORS (4XX errors)
5043
- resource "aws_api_gateway_gateway_response" "cors_4xx" {
5044
- rest_api_id = aws_api_gateway_rest_api.rest_api.id
5045
- response_type = "DEFAULT_4XX"
5046
-
5047
- response_parameters = {
5048
- "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
5049
- "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
5050
- "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
5051
- }
5052
- }
5053
-
5054
- # Gateway Response for CORS (5XX errors)
5055
- resource "aws_api_gateway_gateway_response" "cors_5xx" {
5056
- rest_api_id = aws_api_gateway_rest_api.rest_api.id
5057
- response_type = "DEFAULT_5XX"
5058
-
5059
- response_parameters = {
5060
- "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
5061
- "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
5062
- "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
5063
- }
5064
- }
5065
-
5066
- resource "aws_wafv2_web_acl" "api_waf" {
5067
- #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
5068
- count = var.enable_waf ? 1 : 0
5069
-
5070
- name = "\${var.api_name}-waf"
5071
- scope = "REGIONAL"
5072
-
5073
- default_action {
5074
- allow {}
5075
- }
5076
-
5077
- rule {
5078
- name = "CRSRule"
5079
- priority = 0
5080
-
5081
- override_action {
5082
- none {}
5083
- }
5084
-
5085
- statement {
5086
- managed_rule_group_statement {
5087
- name = "AWSManagedRulesCommonRuleSet"
5088
- vendor_name = "AWS"
5089
-
5090
- # Count instead of Block: the CRS 8 KB body limit is too restrictive for most APIs.
5091
- rule_action_override {
5092
- name = "SizeRestrictions_BODY"
5093
- action_to_use {
5094
- count {}
5095
- }
5096
- }
5097
- }
5098
- }
5099
-
5100
- visibility_config {
5101
- cloudwatch_metrics_enabled = true
5102
- metric_name = "\${var.api_name}WebAcl-CRS"
5103
- sampled_requests_enabled = true
5104
- }
5105
- }
5106
-
5107
- rule {
5108
- name = "KnownBadInputsRule"
5109
- priority = 1
5110
-
5111
- override_action {
5112
- none {}
5113
- }
5114
-
5115
- statement {
5116
- managed_rule_group_statement {
5117
- name = "AWSManagedRulesKnownBadInputsRuleSet"
5118
- vendor_name = "AWS"
5119
- }
5120
- }
5121
-
5122
- visibility_config {
5123
- cloudwatch_metrics_enabled = true
5124
- metric_name = "\${var.api_name}WebAcl-KnownBadInputs"
5125
- sampled_requests_enabled = true
5126
- }
5127
- }
5128
-
5129
- visibility_config {
5130
- cloudwatch_metrics_enabled = true
5131
- metric_name = "\${var.api_name}WebAcl"
5132
- sampled_requests_enabled = true
5133
- }
5134
-
5135
- tags = var.tags
5136
-
5137
- lifecycle {
5138
- create_before_destroy = true
5139
- }
5140
- }
5141
-
5142
- # CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
5143
- resource "aws_cloudwatch_log_group" "api_waf_logs" {
5144
- #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
5145
- #checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
5146
- count = var.enable_waf ? 1 : 0
5147
-
5148
- name = "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}"
5149
- retention_in_days = 30
5150
- tags = var.tags
5151
- }
5152
-
5153
- resource "random_id" "waf_log_suffix" {
5154
- count = var.enable_waf ? 1 : 0
5155
- byte_length = 4
5156
- }
5157
-
5158
- resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
5159
- count = var.enable_waf ? 1 : 0
5160
-
5161
- log_destination_configs = [aws_cloudwatch_log_group.api_waf_logs[0].arn]
5162
- resource_arn = aws_wafv2_web_acl.api_waf[0].arn
5163
- }
5164
-
5165
-
5166
- # Outputs
5167
-
5168
- output "api_id" {
5169
- description = "ID of the REST API Gateway"
5170
- value = aws_api_gateway_rest_api.rest_api.id
5171
- }
5172
-
5173
- output "api_arn" {
5174
- description = "ARN of the REST API Gateway"
5175
- value = aws_api_gateway_rest_api.rest_api.arn
5176
- }
5177
-
5178
- output "api_endpoint" {
5179
- description = "Base URL of the REST API Gateway"
5180
- value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
5181
- }
5182
-
5183
- output "api_execution_arn" {
5184
- description = "Execution ARN of the REST API Gateway"
5185
- value = aws_api_gateway_rest_api.rest_api.execution_arn
5186
- }
5187
-
5188
- output "api_root_resource_id" {
5189
- description = "Root resource ID of the REST API Gateway"
5190
- value = aws_api_gateway_rest_api.rest_api.root_resource_id
5191
- }
5192
-
5193
- output "waf_web_acl_arn" {
5194
- description = "ARN of the WAFv2 Web ACL associated with the API, or null if WAF is disabled"
5195
- value = var.enable_waf ? aws_wafv2_web_acl.api_waf[0].arn : null
5196
- }
5197
-
5198
- # Note: Stage and deployment outputs are provided by the consuming module
5199
-
5200
- # Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
5201
- "test-api.tf": "terraform {
5202
- required_version = ">= 1.0"
5203
-
5204
- required_providers {
5205
- aws = {
5206
- source = "hashicorp/aws"
5207
- version = "~> 6.33"
5208
- }
5209
- }
5210
- }
5211
-
5212
-
5213
- variable "env" {
5214
- description = "Environment variables for the Lambda function"
5215
- type = map(string)
5216
- default = {}
5217
- }
5218
-
5219
- variable "additional_iam_policy_statements" {
5220
- description = "Additional IAM policy statements for the Lambda function"
5221
- type = list(object({
5222
- Effect = string
5223
- Action = list(string)
5224
- Resource = list(string)
5225
- }))
5226
- default = []
5227
- }
5228
-
5229
- variable "asset_bucket_name" {
5230
- 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."
5231
- type = string
5232
- }
5233
-
5234
- # CORS Configuration (passed to core module)
5235
-
5236
- variable "cors_allow_headers" {
5237
- description = "List of allowed headers for CORS"
5238
- type = list(string)
5239
- default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
5240
- }
5241
-
5242
- variable "cors_allow_methods" {
5243
- description = "List of allowed HTTP methods for CORS"
5244
- type = list(string)
5245
- default = ["*"]
5246
- }
5247
-
5248
- variable "cors_allow_origins" {
5249
- description = "List of allowed origins for CORS"
5250
- type = list(string)
5251
- default = ["*"]
5252
- }
5253
-
5254
- # WAF Configuration
5255
- variable "enable_waf" {
5256
- description = "Whether to enable AWS WAFv2 with the default managed ruleset on the API stage"
5257
- type = bool
5258
- default = true
5259
- }
5260
-
5261
- # Tags
5262
- variable "tags" {
5263
- description = "Tags to apply to all resources"
5264
- type = map(string)
5265
- default = {}
5266
- }
5267
-
5268
- # Get current AWS region and account ID
5269
- data "aws_region" "current" {}
5270
- data "aws_caller_identity" "current" {}
5271
-
5272
- resource "random_string" "suffix" {
5273
- length = 8
5274
- special = false
5275
- upper = false
5276
- }
5277
-
5278
- # Resources
5279
-
5280
- # Create Lambda ZIP file from the bundle directory
5281
- data "archive_file" "lambda_zip" {
5282
- type = "zip"
5283
- source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
5284
- output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
5285
- }
5286
-
5287
- resource "aws_s3_object" "lambda_zip" {
5288
- bucket = var.asset_bucket_name
5289
- key = "apis/test-api/\${data.archive_file.lambda_zip.output_sha256}.zip"
5290
- source = data.archive_file.lambda_zip.output_path
5291
- source_hash = data.archive_file.lambda_zip.output_base64sha256
5292
- etag = data.archive_file.lambda_zip.output_md5
5293
- }
5294
-
5295
- # Use the core REST API module
5296
- module "rest_api" {
5297
- source = "../../../core/api/rest-api"
5298
-
5299
- api_name = "TestApi-\${random_string.suffix.result}"
5300
- api_description = "TestApi REST API"
5301
- stage_name = "prod"
5302
- stage_auto_deploy = true
5303
-
5304
- # WAF Configuration
5305
- enable_waf = var.enable_waf
5306
-
5307
- # CORS Configuration
5308
- cors_allow_headers = var.cors_allow_headers
5309
- cors_allow_methods = var.cors_allow_methods
5310
- cors_allow_origins = var.cors_allow_origins
5311
-
5312
- # Tags
5313
- tags = var.tags
5314
- }
5315
-
5316
- resource "aws_wafv2_web_acl_association" "api_waf_association" {
5317
- count = var.enable_waf ? 1 : 0
5318
-
5319
- resource_arn = aws_api_gateway_stage.api_stage.arn
5320
- web_acl_arn = module.rest_api.waf_web_acl_arn
5321
-
5322
- depends_on = [aws_api_gateway_stage.api_stage]
5323
- }
5324
-
5325
- # Lambda function
5326
- resource "aws_lambda_function" "api_lambda" {
5327
- #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
5328
- #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
5329
- #checkov:skip=CKV_AWS_272:Code signing not required for this use case
5330
- #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
5331
- #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
5332
- s3_bucket = aws_s3_object.lambda_zip.bucket
5333
- s3_key = aws_s3_object.lambda_zip.key
5334
- s3_object_version = aws_s3_object.lambda_zip.version_id
5335
- function_name = "TestApiHandler-\${random_string.suffix.result}"
5336
- role = aws_iam_role.lambda_execution_role.arn
5337
- handler = "run.sh"
5338
- runtime = "python3.14"
5339
- layers = ["arn:aws:lambda:\${data.aws_region.current.region}:753240598075:layer:LambdaAdapterLayerX86:24"]
5340
- timeout = 30
5341
- memory_size = 128
5342
- publish = true
5343
-
5344
- source_code_hash = data.archive_file.lambda_zip.output_base64sha256
5345
-
5346
- # Enable X-Ray tracing
5347
- tracing_config {
5348
- mode = "Active"
5349
- }
5350
-
5351
- # Enable SnapStart for faster cold starts
5352
- snap_start {
5353
- apply_on = "PublishedVersions"
5354
- }
5355
-
5356
- environment {
5357
- variables = merge({
5358
- PORT = "8000"
5359
- AWS_LWA_INVOKE_MODE = "response_stream"
5360
- AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
5361
- }, var.env)
5362
- }
5363
-
5364
- tags = var.tags
5365
- }
5366
-
5367
- # IAM role for Lambda execution
5368
- resource "aws_iam_role" "lambda_execution_role" {
5369
- name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
5370
-
5371
- assume_role_policy = jsonencode({
5372
- Version = "2012-10-17"
5373
- Statement = [
5374
- {
5375
- Action = "sts:AssumeRole"
5376
- Effect = "Allow"
5377
- Principal = {
5378
- Service = "lambda.amazonaws.com"
5379
- }
5380
- }
5381
- ]
5382
- })
5383
-
5384
- tags = var.tags
5385
- }
5386
-
5387
- # Attach basic execution policy to Lambda role
5388
- resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
5389
- policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
5390
- role = aws_iam_role.lambda_execution_role.name
5391
- }
5392
-
5393
- # Attach X-Ray tracing policy to Lambda role
5394
- resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
5395
- policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
5396
- role = aws_iam_role.lambda_execution_role.name
5397
- }
5398
-
5399
- # Additional IAM policies for Lambda (if provided)
5400
- resource "aws_iam_role_policy" "lambda_additional_policies" {
5401
- count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
5402
- name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
5403
- role = aws_iam_role.lambda_execution_role.id
5404
-
5405
- policy = jsonencode({
5406
- Version = "2012-10-17"
5407
- Statement = var.additional_iam_policy_statements
5408
- })
5409
- }
5410
-
5411
- # CloudWatch Log Group for Lambda
5412
- resource "aws_cloudwatch_log_group" "lambda_logs" {
5413
- #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
5414
- #checkov:skip=CKV_AWS_338:Log retention set to forever
5415
- #checkov:skip=CKV_AWS_66:Log retention set to forever
5416
- name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
5417
- tags = var.tags
5418
- }
5419
-
5420
- # Lambda alias pointing to the latest published version for SnapStart
5421
- resource "aws_lambda_alias" "live" {
5422
- name = "live"
5423
- function_name = aws_lambda_function.api_lambda.function_name
5424
- function_version = aws_lambda_function.api_lambda.version
5425
-
5426
- depends_on = [aws_lambda_function.api_lambda]
5427
- }
5428
-
5429
-
5430
- # Create proxy resource (captures all paths)
5431
- resource "aws_api_gateway_resource" "proxy_resource" {
5432
- rest_api_id = module.rest_api.api_id
5433
- parent_id = module.rest_api.api_root_resource_id
5434
- path_part = "{proxy+}"
5435
- }
5436
-
5437
- # Lambda integration for REST API
5438
- resource "aws_api_gateway_integration" "lambda_integration" {
5439
- rest_api_id = module.rest_api.api_id
5440
- resource_id = aws_api_gateway_resource.proxy_resource.id
5441
- http_method = aws_api_gateway_method.proxy_method.http_method
5442
-
5443
- integration_http_method = "POST"
5444
- type = "AWS_PROXY"
5445
- # Use the response streaming invocation path with the alias ARN for SnapStart
5446
- uri = "arn:aws:apigateway:\${data.aws_region.current.region}:lambda:path/2021-11-15/functions/\${aws_lambda_alias.live.arn}/response-streaming-invocations"
5447
- response_transfer_mode = "STREAM"
5448
-
5449
- depends_on = [aws_lambda_alias.live]
5450
- }
5451
-
5452
- # Method for proxy integration
5453
- resource "aws_api_gateway_method" "proxy_method" {
5454
- #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
5455
- rest_api_id = module.rest_api.api_id
5456
- resource_id = aws_api_gateway_resource.proxy_resource.id
5457
- http_method = "ANY"
5458
-
5459
- authorization = "AWS_IAM"
5460
-
5461
- request_parameters = {
5462
- "method.request.path.proxy" = true
5463
- }
5464
-
5465
- depends_on = []
5466
- }
5467
-
5468
- # OPTIONS method for CORS preflight
5469
- resource "aws_api_gateway_method" "options_method" {
5470
- #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
5471
- #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
5472
- rest_api_id = module.rest_api.api_id
5473
- resource_id = aws_api_gateway_resource.proxy_resource.id
5474
- http_method = "OPTIONS"
5475
- authorization = "NONE"
5476
- }
5477
-
5478
- # CORS integration for OPTIONS method
5479
- resource "aws_api_gateway_integration" "options_integration" {
5480
- rest_api_id = module.rest_api.api_id
5481
- resource_id = aws_api_gateway_resource.proxy_resource.id
5482
- http_method = aws_api_gateway_method.options_method.http_method
5483
-
5484
- type = "MOCK"
5485
- request_templates = {
5486
- "application/json" = "{\\"statusCode\\": 204}"
5487
- }
5488
- }
5489
-
5490
- # OPTIONS method response
5491
- resource "aws_api_gateway_method_response" "options_response" {
5492
- rest_api_id = module.rest_api.api_id
5493
- resource_id = aws_api_gateway_resource.proxy_resource.id
5494
- http_method = aws_api_gateway_method.options_method.http_method
5495
- status_code = "204"
5496
-
5497
- response_parameters = {
5498
- "method.response.header.Access-Control-Allow-Headers" = true
5499
- "method.response.header.Access-Control-Allow-Methods" = true
5500
- "method.response.header.Access-Control-Allow-Origin" = true
5501
- }
5502
- }
5503
-
5504
- # OPTIONS integration response
5505
- resource "aws_api_gateway_integration_response" "options_integration_response" {
5506
- rest_api_id = module.rest_api.api_id
5507
- resource_id = aws_api_gateway_resource.proxy_resource.id
5508
- http_method = aws_api_gateway_method.options_method.http_method
5509
- status_code = aws_api_gateway_method_response.options_response.status_code
5510
-
5511
- response_parameters = {
5512
- "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
5513
- "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
5514
- "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
5515
- }
5516
- }
5517
-
5518
- # API Gateway deployment
5519
- resource "aws_api_gateway_deployment" "api_deployment" {
5520
- rest_api_id = module.rest_api.api_id
5521
-
5522
- triggers = {
5523
- redeployment = sha1(jsonencode([
5524
- aws_api_gateway_resource.proxy_resource.id,
5525
- aws_api_gateway_method.proxy_method.id,
5526
- aws_api_gateway_integration.lambda_integration.id,
5527
- aws_api_gateway_method.options_method.id,
5528
- aws_api_gateway_integration.options_integration.id,
5529
- ]))
5530
- }
5531
-
5532
- lifecycle {
5533
- create_before_destroy = true
5534
- }
5535
-
5536
- depends_on = [
5537
- aws_api_gateway_method.proxy_method,
5538
- aws_api_gateway_integration.lambda_integration,
5539
- aws_api_gateway_method.options_method,
5540
- aws_api_gateway_integration.options_integration,
5541
- aws_api_gateway_method_response.options_response,
5542
- aws_api_gateway_integration_response.options_integration_response,
5543
- ]
5544
- }
5545
-
5546
- # API Gateway stage
5547
- resource "aws_api_gateway_stage" "api_stage" {
5548
- #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
5549
- #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
5550
- #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
5551
- #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
5552
- #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
5553
- deployment_id = aws_api_gateway_deployment.api_deployment.id
5554
- rest_api_id = module.rest_api.api_id
5555
- stage_name = "prod"
5556
- xray_tracing_enabled = true
5557
-
5558
- tags = var.tags
5559
-
5560
- depends_on = [aws_api_gateway_deployment.api_deployment]
5561
- }
5562
-
5563
- # API Gateway Resource Policy
5564
- resource "aws_api_gateway_rest_api_policy" "api_policy" {
5565
- rest_api_id = module.rest_api.api_id
5566
-
5567
- policy = jsonencode({
5568
- Version = "2012-10-17"
5569
- Statement = [
5570
- {
5571
- # Grant any AWS credentials from the account to call the API
5572
- # Machine to machine fine-grained access can be defined here using more specific principals
5573
- # (eg roles or users) and resources (eg which api paths may be invoked by which principal) if required
5574
- Effect = "Allow"
5575
- Principal = {
5576
- AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
5577
- }
5578
- Action = "execute-api:Invoke"
5579
- Resource = "execute-api:/*"
5580
- },
5581
- {
5582
- # Open up OPTIONS to allow browsers to make unauthenticated preflight requests
5583
- Effect = "Allow"
5584
- Principal = "*"
5585
- Action = "execute-api:Invoke"
5586
- Resource = "execute-api:/*/OPTIONS/*"
5587
- }
5588
- ]
5589
- })
5590
- }
5591
-
5592
- # Lambda permission for API Gateway to invoke the function via alias
5593
- resource "aws_lambda_permission" "api_gateway_invoke" {
5594
- statement_id = "AllowExecutionFromAPIGateway"
5595
- action = "lambda:InvokeFunction"
5596
- function_name = aws_lambda_function.api_lambda.function_name
5597
- qualifier = aws_lambda_alias.live.name
5598
- principal = "apigateway.amazonaws.com"
5599
- source_arn = "\${module.rest_api.api_execution_arn}/*/*"
5600
-
5601
- depends_on = [module.rest_api, aws_lambda_alias.live]
5602
- }
5603
-
5604
- # Lambda permission for API Gateway to invoke with response streaming via alias
5605
- resource "aws_lambda_permission" "api_gateway_invoke_streaming" {
5606
- statement_id = "AllowStreamingFromAPIGateway"
5607
- action = "lambda:InvokeFunctionWithResponseStream"
5608
- function_name = aws_lambda_function.api_lambda.function_name
5609
- qualifier = aws_lambda_alias.live.name
5610
- principal = "apigateway.amazonaws.com"
5611
- source_arn = "\${module.rest_api.api_execution_arn}/*/*"
5612
-
5613
- depends_on = [module.rest_api, aws_lambda_alias.live]
5614
- }
5615
-
5616
- # Add API url to runtime config
5617
- module "add_url_to_runtime_config" {
5618
- source = "../../../core/runtime-config/entry"
5619
-
5620
- namespace = "connection"
5621
- key = "apis"
5622
- value = { "TestApi" = aws_api_gateway_stage.api_stage.invoke_url }
5623
-
5624
- depends_on = [aws_api_gateway_stage.api_stage]
5625
- }
5626
-
5627
- # Outputs
5628
-
5629
- # API Gateway Outputs (from core module)
5630
- output "api_id" {
5631
- description = "ID of the REST API Gateway"
5632
- value = module.rest_api.api_id
5633
- }
5634
-
5635
- output "api_arn" {
5636
- description = "ARN of the REST API Gateway"
5637
- value = module.rest_api.api_arn
5638
- }
5639
-
5640
- output "api_endpoint" {
5641
- description = "Base URL of the REST API Gateway"
5642
- value = module.rest_api.api_endpoint
5643
- }
5644
-
5645
- output "api_execution_arn" {
5646
- description = "Execution ARN of the REST API Gateway"
5647
- value = module.rest_api.api_execution_arn
5648
- }
5649
-
5650
- output "stage_invoke_url" {
5651
- description = "Invoke URL of the API Gateway stage"
5652
- value = aws_api_gateway_stage.api_stage.invoke_url
5653
- }
5654
-
5655
- output "stage_arn" {
5656
- description = "ARN of the API Gateway stage"
5657
- value = aws_api_gateway_stage.api_stage.arn
5658
- }
5659
-
5660
- output "stage_execution_arn" {
5661
- description = "Execution ARN of the API Gateway stage"
5662
- value = aws_api_gateway_stage.api_stage.execution_arn
5663
- }
5664
-
5665
- output "deployment_id" {
5666
- description = "ID of the API Gateway deployment"
5667
- value = aws_api_gateway_deployment.api_deployment.id
5668
- }
5669
-
5670
- output "stage_id" {
5671
- description = "ID of the API Gateway stage"
5672
- value = aws_api_gateway_stage.api_stage.id
5673
- }
5674
-
5675
- # Lambda Function Outputs
5676
- output "lambda_function_name" {
5677
- description = "Name of the Lambda function"
5678
- value = aws_lambda_function.api_lambda.function_name
5679
- }
5680
-
5681
- output "lambda_function_arn" {
5682
- description = "ARN of the Lambda function"
5683
- value = aws_lambda_function.api_lambda.arn
5684
- }
5685
-
5686
- output "lambda_invoke_arn" {
5687
- description = "Invoke ARN of the Lambda function"
5688
- value = aws_lambda_function.api_lambda.invoke_arn
5689
- }
5690
-
5691
- output "lambda_qualified_arn" {
5692
- description = "Qualified ARN of the Lambda function"
5693
- value = aws_lambda_function.api_lambda.qualified_arn
5694
- }
5695
-
5696
- output "lambda_version" {
5697
- description = "Version of the Lambda function"
5698
- value = aws_lambda_function.api_lambda.version
5699
- }
5700
-
5701
- output "lambda_source_code_hash" {
5702
- description = "Base64-encoded SHA256 hash of the Lambda deployment package"
5703
- value = aws_lambda_function.api_lambda.source_code_hash
5704
- }
5705
-
5706
- output "lambda_source_code_size" {
5707
- description = "Size of the Lambda deployment package in bytes"
5708
- value = aws_lambda_function.api_lambda.source_code_size
5709
- }
5710
-
5711
- # IAM Role Outputs
5712
- output "lambda_execution_role_arn" {
5713
- description = "ARN of the Lambda execution role"
5714
- value = aws_iam_role.lambda_execution_role.arn
5715
- }
5716
-
5717
- output "lambda_execution_role_name" {
5718
- description = "Name of the Lambda execution role"
5719
- value = aws_iam_role.lambda_execution_role.name
5720
- }
5721
-
5722
- # Integration Outputs
5723
- output "integration_id" {
5724
- description = "ID of the Lambda integration"
5725
- value = aws_api_gateway_integration.lambda_integration.id
5726
- }
5727
-
5728
- output "proxy_resource_id" {
5729
- description = "ID of the proxy resource"
5730
- value = aws_api_gateway_resource.proxy_resource.id
5731
- }
5732
-
5733
- output "proxy_method_id" {
5734
- description = "ID of the proxy method"
5735
- value = aws_api_gateway_method.proxy_method.id
5736
- }
5737
-
5738
- # CloudWatch Log Groups
5739
- output "lambda_log_group_name" {
5740
- description = "Name of the Lambda CloudWatch log group"
5741
- value = aws_cloudwatch_log_group.lambda_logs.name
5742
- }
5743
-
5744
- output "lambda_log_group_arn" {
5745
- description = "ARN of the Lambda CloudWatch log group"
5746
- value = aws_cloudwatch_log_group.lambda_logs.arn
5747
- }
5748
- ",
5749
- }
5750
- `;
5751
-
5752
- exports[`fastapi project generator > terraform iac > should generate terraform files for REST API with None auth and snapshot them > terraform-rest-none-files 1`] = `
5753
- {
5754
- "rest-api.tf": "# Core REST API Gateway module
5755
- # This module creates the API Gateway REST API, deployment, stage, and logging resources
5756
-
5757
- terraform {
5758
- required_version = ">= 1.0"
5759
-
5760
- required_providers {
5761
- aws = {
5762
- source = "hashicorp/aws"
5763
- version = "~> 6.33"
5764
- }
5765
- random = {
5766
- source = "hashicorp/random"
5767
- version = "~> 3.1"
5768
- }
5769
- }
5770
- }
5771
-
5772
- # Core REST API Gateway Variables
5773
-
5774
- variable "api_name" {
5775
- description = "Name of the REST API Gateway"
5776
- type = string
5777
- }
5778
-
5779
- variable "api_description" {
5780
- description = "Description of the REST API Gateway"
5781
- type = string
5782
- default = "REST API Gateway"
5783
- }
5784
-
5785
- variable "stage_name" {
5786
- description = "Name of the API Gateway stage"
5787
- type = string
5788
- default = "prod"
5789
- }
5790
-
5791
- variable "stage_auto_deploy" {
5792
- description = "Whether to automatically deploy the API stage"
5793
- type = bool
5794
- default = true
5795
- }
5796
-
5797
- variable "enable_waf" {
5798
- 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."
5799
- type = bool
5800
- default = true
5801
- }
5802
-
5803
- # CORS Configuration
5804
-
5805
- variable "cors_allow_headers" {
5806
- description = "List of allowed headers for CORS"
5807
- type = list(string)
5808
- default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
5809
- }
5810
-
5811
- variable "cors_allow_methods" {
5812
- description = "List of allowed HTTP methods for CORS"
5813
- type = list(string)
5814
- default = ["*"]
5815
- }
5816
-
5817
- variable "cors_allow_origins" {
5818
- description = "List of allowed origins for CORS"
5819
- type = list(string)
5820
- default = ["*"]
5821
- }
5822
-
5823
- # Tags
5824
-
5825
- variable "tags" {
5826
- description = "Tags to apply to all resources"
5827
- type = map(string)
5828
- default = {}
5829
- }
5830
-
5831
- # Data sources
5832
- data "aws_region" "current" {}
5833
- data "aws_caller_identity" "current" {}
5834
-
5835
- # Resources
5836
-
5837
- # Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
5838
-
5839
- # REST API Gateway
5840
- resource "aws_api_gateway_rest_api" "rest_api" {
5841
- name = var.api_name
5842
- description = var.api_description
5843
-
5844
- endpoint_configuration {
5845
- types = ["REGIONAL"]
5846
- }
5847
-
5848
- lifecycle {
5849
- create_before_destroy = true
5850
- }
5851
-
5852
- tags = var.tags
5853
- }
5854
-
5855
- # Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
5856
- # after all methods and integrations are defined
5857
-
5858
- # Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
5859
-
5860
- # Gateway Response for CORS (4XX errors)
5861
- resource "aws_api_gateway_gateway_response" "cors_4xx" {
5862
- rest_api_id = aws_api_gateway_rest_api.rest_api.id
5863
- response_type = "DEFAULT_4XX"
5864
-
5865
- response_parameters = {
5866
- "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
5867
- "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
5868
- "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
5869
- }
5870
- }
5871
-
5872
- # Gateway Response for CORS (5XX errors)
5873
- resource "aws_api_gateway_gateway_response" "cors_5xx" {
5874
- rest_api_id = aws_api_gateway_rest_api.rest_api.id
5875
- response_type = "DEFAULT_5XX"
5876
-
5877
- response_parameters = {
5878
- "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
5879
- "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
5880
- "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
5881
- }
5882
- }
5883
-
5884
- resource "aws_wafv2_web_acl" "api_waf" {
5885
- #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
5886
- count = var.enable_waf ? 1 : 0
5887
-
5888
- name = "\${var.api_name}-waf"
5889
- scope = "REGIONAL"
5890
-
5891
- default_action {
5892
- allow {}
5893
- }
5894
-
5895
- rule {
5896
- name = "CRSRule"
5897
- priority = 0
5898
-
5899
- override_action {
5900
- none {}
5901
- }
5902
-
5903
- statement {
5904
- managed_rule_group_statement {
5905
- name = "AWSManagedRulesCommonRuleSet"
5906
- vendor_name = "AWS"
5907
-
5908
- # Count instead of Block: the CRS 8 KB body limit is too restrictive for most APIs.
5909
- rule_action_override {
5910
- name = "SizeRestrictions_BODY"
5911
- action_to_use {
5912
- count {}
5913
- }
5914
- }
5915
- }
5916
- }
5917
-
5918
- visibility_config {
5919
- cloudwatch_metrics_enabled = true
5920
- metric_name = "\${var.api_name}WebAcl-CRS"
5921
- sampled_requests_enabled = true
5922
- }
5923
- }
5924
-
5925
- rule {
5926
- name = "KnownBadInputsRule"
5927
- priority = 1
5928
-
5929
- override_action {
5930
- none {}
5931
- }
5932
-
5933
- statement {
5934
- managed_rule_group_statement {
5935
- name = "AWSManagedRulesKnownBadInputsRuleSet"
5936
- vendor_name = "AWS"
5937
- }
5938
- }
5939
-
5940
- visibility_config {
5941
- cloudwatch_metrics_enabled = true
5942
- metric_name = "\${var.api_name}WebAcl-KnownBadInputs"
5943
- sampled_requests_enabled = true
5944
- }
5945
- }
5946
-
5947
- visibility_config {
5948
- cloudwatch_metrics_enabled = true
5949
- metric_name = "\${var.api_name}WebAcl"
5950
- sampled_requests_enabled = true
5951
- }
5952
-
5953
- tags = var.tags
5954
-
5955
- lifecycle {
5956
- create_before_destroy = true
5957
- }
5958
- }
5959
-
5960
- # CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
5961
- resource "aws_cloudwatch_log_group" "api_waf_logs" {
5962
- #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
5963
- #checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
5964
- count = var.enable_waf ? 1 : 0
5965
-
5966
- name = "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}"
5967
- retention_in_days = 30
5968
- tags = var.tags
5969
- }
5970
-
5971
- resource "random_id" "waf_log_suffix" {
5972
- count = var.enable_waf ? 1 : 0
5973
- byte_length = 4
5974
- }
5975
-
5976
- resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
5977
- count = var.enable_waf ? 1 : 0
5978
-
5979
- log_destination_configs = [aws_cloudwatch_log_group.api_waf_logs[0].arn]
5980
- resource_arn = aws_wafv2_web_acl.api_waf[0].arn
5981
- }
5982
-
5983
-
5984
- # Outputs
5985
-
5986
- output "api_id" {
5987
- description = "ID of the REST API Gateway"
5988
- value = aws_api_gateway_rest_api.rest_api.id
5989
- }
5990
-
5991
- output "api_arn" {
5992
- description = "ARN of the REST API Gateway"
5993
- value = aws_api_gateway_rest_api.rest_api.arn
5994
- }
5995
-
5996
- output "api_endpoint" {
5997
- description = "Base URL of the REST API Gateway"
5998
- value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
5999
- }
6000
-
6001
- output "api_execution_arn" {
6002
- description = "Execution ARN of the REST API Gateway"
6003
- value = aws_api_gateway_rest_api.rest_api.execution_arn
6004
- }
6005
-
6006
- output "api_root_resource_id" {
6007
- description = "Root resource ID of the REST API Gateway"
6008
- value = aws_api_gateway_rest_api.rest_api.root_resource_id
6009
- }
6010
-
6011
- output "waf_web_acl_arn" {
6012
- description = "ARN of the WAFv2 Web ACL associated with the API, or null if WAF is disabled"
6013
- value = var.enable_waf ? aws_wafv2_web_acl.api_waf[0].arn : null
6014
- }
6015
-
6016
- # Note: Stage and deployment outputs are provided by the consuming module
6017
-
6018
- # Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
6019
- "test-api.tf": "terraform {
6020
- required_version = ">= 1.0"
6021
-
6022
- required_providers {
6023
- aws = {
6024
- source = "hashicorp/aws"
6025
- version = "~> 6.33"
6026
- }
6027
- }
6028
- }
6029
-
6030
-
6031
- variable "env" {
6032
- description = "Environment variables for the Lambda function"
6033
- type = map(string)
6034
- default = {}
6035
- }
6036
-
6037
- variable "additional_iam_policy_statements" {
6038
- description = "Additional IAM policy statements for the Lambda function"
6039
- type = list(object({
6040
- Effect = string
6041
- Action = list(string)
6042
- Resource = list(string)
6043
- }))
6044
- default = []
6045
- }
6046
-
6047
- variable "asset_bucket_name" {
6048
- 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."
6049
- type = string
6050
- }
6051
-
6052
- # CORS Configuration (passed to core module)
6053
-
6054
- variable "cors_allow_headers" {
6055
- description = "List of allowed headers for CORS"
6056
- type = list(string)
6057
- default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
6058
- }
6059
-
6060
- variable "cors_allow_methods" {
6061
- description = "List of allowed HTTP methods for CORS"
6062
- type = list(string)
6063
- default = ["*"]
6064
- }
6065
-
6066
- variable "cors_allow_origins" {
6067
- description = "List of allowed origins for CORS"
6068
- type = list(string)
6069
- default = ["*"]
6070
- }
6071
-
6072
- # WAF Configuration
6073
- variable "enable_waf" {
6074
- description = "Whether to enable AWS WAFv2 with the default managed ruleset on the API stage"
6075
- type = bool
6076
- default = true
6077
- }
6078
-
6079
- # Tags
6080
- variable "tags" {
6081
- description = "Tags to apply to all resources"
6082
- type = map(string)
6083
- default = {}
6084
- }
6085
-
6086
- # Get current AWS region and account ID
6087
- data "aws_region" "current" {}
6088
- data "aws_caller_identity" "current" {}
6089
-
6090
- resource "random_string" "suffix" {
6091
- length = 8
6092
- special = false
6093
- upper = false
6094
- }
6095
-
6096
- # Resources
6097
-
6098
- # Create Lambda ZIP file from the bundle directory
6099
- data "archive_file" "lambda_zip" {
6100
- type = "zip"
6101
- source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
6102
- output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
6103
- }
6104
-
6105
- resource "aws_s3_object" "lambda_zip" {
6106
- bucket = var.asset_bucket_name
6107
- key = "apis/test-api/\${data.archive_file.lambda_zip.output_sha256}.zip"
6108
- source = data.archive_file.lambda_zip.output_path
6109
- source_hash = data.archive_file.lambda_zip.output_base64sha256
6110
- etag = data.archive_file.lambda_zip.output_md5
6111
- }
6112
-
6113
- # Use the core REST API module
6114
- module "rest_api" {
6115
- source = "../../../core/api/rest-api"
6116
-
6117
- api_name = "TestApi-\${random_string.suffix.result}"
6118
- api_description = "TestApi REST API"
6119
- stage_name = "prod"
6120
- stage_auto_deploy = true
6121
-
6122
- # WAF Configuration
6123
- enable_waf = var.enable_waf
6124
-
6125
- # CORS Configuration
6126
- cors_allow_headers = var.cors_allow_headers
6127
- cors_allow_methods = var.cors_allow_methods
6128
- cors_allow_origins = var.cors_allow_origins
6129
-
6130
- # Tags
6131
- tags = var.tags
6132
- }
6133
-
6134
- resource "aws_wafv2_web_acl_association" "api_waf_association" {
6135
- count = var.enable_waf ? 1 : 0
6136
-
6137
- resource_arn = aws_api_gateway_stage.api_stage.arn
6138
- web_acl_arn = module.rest_api.waf_web_acl_arn
6139
-
6140
- depends_on = [aws_api_gateway_stage.api_stage]
6141
- }
6142
-
6143
- # Lambda function
6144
- resource "aws_lambda_function" "api_lambda" {
6145
- #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
6146
- #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
6147
- #checkov:skip=CKV_AWS_272:Code signing not required for this use case
6148
- #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
6149
- #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
6150
- s3_bucket = aws_s3_object.lambda_zip.bucket
6151
- s3_key = aws_s3_object.lambda_zip.key
6152
- s3_object_version = aws_s3_object.lambda_zip.version_id
6153
- function_name = "TestApiHandler-\${random_string.suffix.result}"
6154
- role = aws_iam_role.lambda_execution_role.arn
6155
- handler = "run.sh"
6156
- runtime = "python3.14"
6157
- layers = ["arn:aws:lambda:\${data.aws_region.current.region}:753240598075:layer:LambdaAdapterLayerX86:24"]
6158
- timeout = 30
6159
- memory_size = 128
6160
- publish = true
6161
-
6162
- source_code_hash = data.archive_file.lambda_zip.output_base64sha256
6163
-
6164
- # Enable X-Ray tracing
6165
- tracing_config {
6166
- mode = "Active"
6167
- }
6168
-
6169
- # Enable SnapStart for faster cold starts
6170
- snap_start {
6171
- apply_on = "PublishedVersions"
6172
- }
6173
-
6174
- environment {
6175
- variables = merge({
6176
- PORT = "8000"
6177
- AWS_LWA_INVOKE_MODE = "response_stream"
6178
- AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
6179
- }, var.env)
6180
- }
6181
-
6182
- tags = var.tags
6183
- }
6184
-
6185
- # IAM role for Lambda execution
6186
- resource "aws_iam_role" "lambda_execution_role" {
6187
- name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
6188
-
6189
- assume_role_policy = jsonencode({
6190
- Version = "2012-10-17"
6191
- Statement = [
6192
- {
6193
- Action = "sts:AssumeRole"
6194
- Effect = "Allow"
6195
- Principal = {
6196
- Service = "lambda.amazonaws.com"
6197
- }
6198
- }
6199
- ]
6200
- })
6201
-
6202
- tags = var.tags
6203
- }
6204
-
6205
- # Attach basic execution policy to Lambda role
6206
- resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
6207
- policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
6208
- role = aws_iam_role.lambda_execution_role.name
6209
- }
6210
-
6211
- # Attach X-Ray tracing policy to Lambda role
6212
- resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
6213
- policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
6214
- role = aws_iam_role.lambda_execution_role.name
6215
- }
6216
-
6217
- # Additional IAM policies for Lambda (if provided)
6218
- resource "aws_iam_role_policy" "lambda_additional_policies" {
6219
- count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
6220
- name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
6221
- role = aws_iam_role.lambda_execution_role.id
6222
-
6223
- policy = jsonencode({
6224
- Version = "2012-10-17"
6225
- Statement = var.additional_iam_policy_statements
6226
- })
6227
- }
6228
-
6229
- # CloudWatch Log Group for Lambda
6230
- resource "aws_cloudwatch_log_group" "lambda_logs" {
6231
- #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
6232
- #checkov:skip=CKV_AWS_338:Log retention set to forever
6233
- #checkov:skip=CKV_AWS_66:Log retention set to forever
6234
- name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
6235
- tags = var.tags
6236
- }
6237
-
6238
- # Lambda alias pointing to the latest published version for SnapStart
6239
- resource "aws_lambda_alias" "live" {
6240
- name = "live"
6241
- function_name = aws_lambda_function.api_lambda.function_name
6242
- function_version = aws_lambda_function.api_lambda.version
6243
-
6244
- depends_on = [aws_lambda_function.api_lambda]
6245
- }
6246
-
6247
- # Custom Lambda Authorizer
6248
- resource "aws_lambda_function" "authorizer_lambda" {
6249
- #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
6250
- #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for authorizer
6251
- #checkov:skip=CKV_AWS_272:Code signing not required for this use case
6252
- #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
6253
- #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
6254
- s3_bucket = aws_s3_object.authorizer_zip.bucket
6255
- s3_key = aws_s3_object.authorizer_zip.key
6256
- s3_object_version = aws_s3_object.authorizer_zip.version_id
6257
- function_name = "TestApiAuthorizer-\${random_string.suffix.result}"
6258
- role = aws_iam_role.authorizer_execution_role.arn
6259
- handler = "proj_test_api.authorizer.handler"
6260
- runtime = "python3.14"
6261
- timeout = 10
6262
- memory_size = 128
6263
-
6264
- source_code_hash = data.archive_file.authorizer_zip.output_base64sha256
6265
-
6266
- tracing_config {
6267
- mode = "Active"
6268
- }
6269
-
6270
- tags = var.tags
6271
- }
6272
-
6273
- resource "aws_iam_role" "authorizer_execution_role" {
6274
- name = "TestApiAuthorizer-role-\${random_string.suffix.result}"
6275
-
6276
- assume_role_policy = jsonencode({
6277
- Version = "2012-10-17"
6278
- Statement = [
6279
- {
6280
- Action = "sts:AssumeRole"
6281
- Effect = "Allow"
6282
- Principal = {
6283
- Service = "lambda.amazonaws.com"
6284
- }
6285
- }
6286
- ]
6287
- })
6288
-
6289
- tags = var.tags
6290
- }
6291
-
6292
- resource "aws_iam_role_policy_attachment" "authorizer_basic_execution" {
6293
- policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
6294
- role = aws_iam_role.authorizer_execution_role.name
6295
- }
6296
-
6297
- resource "aws_iam_role_policy_attachment" "authorizer_xray_execution" {
6298
- policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
6299
- role = aws_iam_role.authorizer_execution_role.name
6300
- }
6301
-
6302
- resource "aws_cloudwatch_log_group" "authorizer_logs" {
6303
- #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
6304
- #checkov:skip=CKV_AWS_338:Log retention set to forever
6305
- #checkov:skip=CKV_AWS_66:Log retention set to forever
6306
- name = "/aws/lambda/TestApiAuthorizer-\${random_string.suffix.result}"
6307
- tags = var.tags
6308
- }
6309
-
6310
- data "archive_file" "authorizer_zip" {
6311
- type = "zip"
6312
- source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
6313
- output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/authorizer.zip"
6314
- }
6315
-
6316
- resource "aws_s3_object" "authorizer_zip" {
6317
- bucket = var.asset_bucket_name
6318
- key = "apis/test-api/authorizer-\${data.archive_file.authorizer_zip.output_sha256}.zip"
6319
- source = data.archive_file.authorizer_zip.output_path
6320
- source_hash = data.archive_file.authorizer_zip.output_base64sha256
6321
- etag = data.archive_file.authorizer_zip.output_md5
6322
- }
6323
-
6324
- resource "aws_api_gateway_authorizer" "custom_authorizer" {
6325
- name = "TestApiAuthorizer-\${random_string.suffix.result}"
6326
- rest_api_id = module.rest_api.api_id
6327
- type = "TOKEN"
6328
- authorizer_uri = aws_lambda_function.authorizer_lambda.invoke_arn
6329
- identity_source = "method.request.header.Authorization"
6330
- }
6331
-
6332
- resource "aws_lambda_permission" "authorizer_invoke" {
6333
- statement_id = "AllowAPIGatewayInvokeAuthorizer"
6334
- action = "lambda:InvokeFunction"
6335
- function_name = aws_lambda_function.authorizer_lambda.function_name
6336
- principal = "apigateway.amazonaws.com"
6337
- source_arn = "\${module.rest_api.api_execution_arn}/authorizers/\${aws_api_gateway_authorizer.custom_authorizer.id}"
6338
- }
6339
-
6340
- # Create proxy resource (captures all paths)
6341
- resource "aws_api_gateway_resource" "proxy_resource" {
6342
- rest_api_id = module.rest_api.api_id
6343
- parent_id = module.rest_api.api_root_resource_id
6344
- path_part = "{proxy+}"
6345
- }
6346
-
6347
- # Lambda integration for REST API
6348
- resource "aws_api_gateway_integration" "lambda_integration" {
6349
- rest_api_id = module.rest_api.api_id
6350
- resource_id = aws_api_gateway_resource.proxy_resource.id
6351
- http_method = aws_api_gateway_method.proxy_method.http_method
6352
-
6353
- integration_http_method = "POST"
6354
- type = "AWS_PROXY"
6355
- # Use the response streaming invocation path with the alias ARN for SnapStart
6356
- uri = "arn:aws:apigateway:\${data.aws_region.current.region}:lambda:path/2021-11-15/functions/\${aws_lambda_alias.live.arn}/response-streaming-invocations"
6357
- response_transfer_mode = "STREAM"
6358
-
6359
- depends_on = [aws_lambda_alias.live]
6360
- }
6361
-
6362
- # Method for proxy integration
6363
- resource "aws_api_gateway_method" "proxy_method" {
6364
- #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
6365
- rest_api_id = module.rest_api.api_id
6366
- resource_id = aws_api_gateway_resource.proxy_resource.id
6367
- http_method = "ANY"
6368
-
6369
- authorization = "CUSTOM"
6370
- authorizer_id = aws_api_gateway_authorizer.custom_authorizer.id
6371
-
6372
- request_parameters = {
6373
- "method.request.path.proxy" = true
6374
- }
6375
-
6376
- depends_on = [aws_api_gateway_authorizer.custom_authorizer]
6377
- }
6378
-
6379
- # OPTIONS method for CORS preflight
6380
- resource "aws_api_gateway_method" "options_method" {
6381
- #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
6382
- #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
6383
- rest_api_id = module.rest_api.api_id
6384
- resource_id = aws_api_gateway_resource.proxy_resource.id
6385
- http_method = "OPTIONS"
6386
- authorization = "NONE"
6387
- }
6388
-
6389
- # CORS integration for OPTIONS method
6390
- resource "aws_api_gateway_integration" "options_integration" {
6391
- rest_api_id = module.rest_api.api_id
6392
- resource_id = aws_api_gateway_resource.proxy_resource.id
6393
- http_method = aws_api_gateway_method.options_method.http_method
6394
-
6395
- type = "MOCK"
6396
- request_templates = {
6397
- "application/json" = "{\\"statusCode\\": 204}"
6398
- }
6399
- }
6400
-
6401
- # OPTIONS method response
6402
- resource "aws_api_gateway_method_response" "options_response" {
6403
- rest_api_id = module.rest_api.api_id
6404
- resource_id = aws_api_gateway_resource.proxy_resource.id
6405
- http_method = aws_api_gateway_method.options_method.http_method
6406
- status_code = "204"
6407
-
6408
- response_parameters = {
6409
- "method.response.header.Access-Control-Allow-Headers" = true
6410
- "method.response.header.Access-Control-Allow-Methods" = true
6411
- "method.response.header.Access-Control-Allow-Origin" = true
6412
- }
6413
- }
6414
-
6415
- # OPTIONS integration response
6416
- resource "aws_api_gateway_integration_response" "options_integration_response" {
6417
- rest_api_id = module.rest_api.api_id
6418
- resource_id = aws_api_gateway_resource.proxy_resource.id
6419
- http_method = aws_api_gateway_method.options_method.http_method
6420
- status_code = aws_api_gateway_method_response.options_response.status_code
6421
-
6422
- response_parameters = {
6423
- "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
6424
- "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
6425
- "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
6426
- }
6427
- }
6428
-
6429
- # API Gateway deployment
6430
- resource "aws_api_gateway_deployment" "api_deployment" {
6431
- rest_api_id = module.rest_api.api_id
6432
-
6433
- triggers = {
6434
- redeployment = sha1(jsonencode([
6435
- aws_api_gateway_resource.proxy_resource.id,
6436
- aws_api_gateway_method.proxy_method.id,
6437
- aws_api_gateway_integration.lambda_integration.id,
6438
- aws_api_gateway_method.options_method.id,
6439
- aws_api_gateway_integration.options_integration.id,
6440
- ]))
6441
- }
6442
-
6443
- lifecycle {
6444
- create_before_destroy = true
6445
- }
6446
-
6447
- depends_on = [
6448
- aws_api_gateway_method.proxy_method,
6449
- aws_api_gateway_integration.lambda_integration,
6450
- aws_api_gateway_method.options_method,
6451
- aws_api_gateway_integration.options_integration,
6452
- aws_api_gateway_method_response.options_response,
6453
- aws_api_gateway_integration_response.options_integration_response,
6454
- aws_api_gateway_authorizer.custom_authorizer,
6455
- ]
6456
- }
6457
-
6458
- # API Gateway stage
6459
- resource "aws_api_gateway_stage" "api_stage" {
6460
- #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
6461
- #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
6462
- #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
6463
- #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
6464
- #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
6465
- deployment_id = aws_api_gateway_deployment.api_deployment.id
6466
- rest_api_id = module.rest_api.api_id
6467
- stage_name = "prod"
6468
- xray_tracing_enabled = true
6469
-
6470
- tags = var.tags
6471
-
6472
- depends_on = [aws_api_gateway_deployment.api_deployment]
6473
- }
6474
-
6475
- # API Gateway Resource Policy
6476
- resource "aws_api_gateway_rest_api_policy" "api_policy" {
6477
- rest_api_id = module.rest_api.api_id
6478
-
6479
- policy = jsonencode({
6480
- Version = "2012-10-17"
6481
- Statement = [
6482
- {
6483
- # Allow all callers to invoke the API in the resource policy, since auth is handled by the Lambda Authorizer
6484
- Effect = "Allow"
6485
- Principal = "*"
6486
- Action = "execute-api:Invoke"
6487
- Resource = "execute-api:/*"
6488
- }
6489
- ]
6490
- })
6491
- }
6492
-
6493
- # Lambda permission for API Gateway to invoke the function via alias
6494
- resource "aws_lambda_permission" "api_gateway_invoke" {
6495
- statement_id = "AllowExecutionFromAPIGateway"
6496
- action = "lambda:InvokeFunction"
6497
- function_name = aws_lambda_function.api_lambda.function_name
6498
- qualifier = aws_lambda_alias.live.name
6499
- principal = "apigateway.amazonaws.com"
6500
- source_arn = "\${module.rest_api.api_execution_arn}/*/*"
6501
-
6502
- depends_on = [module.rest_api, aws_lambda_alias.live]
6503
- }
6504
-
6505
- # Lambda permission for API Gateway to invoke with response streaming via alias
6506
- resource "aws_lambda_permission" "api_gateway_invoke_streaming" {
6507
- statement_id = "AllowStreamingFromAPIGateway"
6508
- action = "lambda:InvokeFunctionWithResponseStream"
6509
- function_name = aws_lambda_function.api_lambda.function_name
6510
- qualifier = aws_lambda_alias.live.name
6511
- principal = "apigateway.amazonaws.com"
6512
- source_arn = "\${module.rest_api.api_execution_arn}/*/*"
6513
-
6514
- depends_on = [module.rest_api, aws_lambda_alias.live]
6515
- }
6516
-
6517
- # Add API url to runtime config
6518
- module "add_url_to_runtime_config" {
6519
- source = "../../../core/runtime-config/entry"
6520
-
6521
- namespace = "connection"
6522
- key = "apis"
6523
- value = { "TestApi" = aws_api_gateway_stage.api_stage.invoke_url }
6524
-
6525
- depends_on = [aws_api_gateway_stage.api_stage]
6526
- }
6527
-
6528
- # Outputs
6529
-
6530
- # API Gateway Outputs (from core module)
6531
- output "api_id" {
6532
- description = "ID of the REST API Gateway"
6533
- value = module.rest_api.api_id
6534
- }
6535
-
6536
- output "api_arn" {
6537
- description = "ARN of the REST API Gateway"
6538
- value = module.rest_api.api_arn
6539
- }
6540
-
6541
- output "api_endpoint" {
6542
- description = "Base URL of the REST API Gateway"
6543
- value = module.rest_api.api_endpoint
6544
- }
6545
-
6546
- output "api_execution_arn" {
6547
- description = "Execution ARN of the REST API Gateway"
6548
- value = module.rest_api.api_execution_arn
6549
- }
6550
-
6551
- output "stage_invoke_url" {
6552
- description = "Invoke URL of the API Gateway stage"
6553
- value = aws_api_gateway_stage.api_stage.invoke_url
6554
- }
6555
-
6556
- output "stage_arn" {
6557
- description = "ARN of the API Gateway stage"
6558
- value = aws_api_gateway_stage.api_stage.arn
6559
- }
6560
-
6561
- output "stage_execution_arn" {
6562
- description = "Execution ARN of the API Gateway stage"
6563
- value = aws_api_gateway_stage.api_stage.execution_arn
6564
- }
6565
-
6566
- output "deployment_id" {
6567
- description = "ID of the API Gateway deployment"
6568
- value = aws_api_gateway_deployment.api_deployment.id
6569
- }
6570
-
6571
- output "stage_id" {
6572
- description = "ID of the API Gateway stage"
6573
- value = aws_api_gateway_stage.api_stage.id
6574
- }
6575
-
6576
- # Lambda Function Outputs
6577
- output "lambda_function_name" {
6578
- description = "Name of the Lambda function"
6579
- value = aws_lambda_function.api_lambda.function_name
6580
- }
6581
-
6582
- output "lambda_function_arn" {
6583
- description = "ARN of the Lambda function"
6584
- value = aws_lambda_function.api_lambda.arn
6585
- }
6586
-
6587
- output "lambda_invoke_arn" {
6588
- description = "Invoke ARN of the Lambda function"
6589
- value = aws_lambda_function.api_lambda.invoke_arn
6590
- }
6591
-
6592
- output "lambda_qualified_arn" {
6593
- description = "Qualified ARN of the Lambda function"
6594
- value = aws_lambda_function.api_lambda.qualified_arn
6595
- }
6596
-
6597
- output "lambda_version" {
6598
- description = "Version of the Lambda function"
6599
- value = aws_lambda_function.api_lambda.version
6600
- }
6601
-
6602
- output "lambda_source_code_hash" {
6603
- description = "Base64-encoded SHA256 hash of the Lambda deployment package"
6604
- value = aws_lambda_function.api_lambda.source_code_hash
6605
- }
6606
-
6607
- output "lambda_source_code_size" {
6608
- description = "Size of the Lambda deployment package in bytes"
6609
- value = aws_lambda_function.api_lambda.source_code_size
6610
- }
6611
-
6612
- # IAM Role Outputs
6613
- output "lambda_execution_role_arn" {
6614
- description = "ARN of the Lambda execution role"
6615
- value = aws_iam_role.lambda_execution_role.arn
6616
- }
6617
-
6618
- output "lambda_execution_role_name" {
6619
- description = "Name of the Lambda execution role"
6620
- value = aws_iam_role.lambda_execution_role.name
6621
- }
6622
-
6623
- # Integration Outputs
6624
- output "integration_id" {
6625
- description = "ID of the Lambda integration"
6626
- value = aws_api_gateway_integration.lambda_integration.id
6627
- }
6628
-
6629
- output "proxy_resource_id" {
6630
- description = "ID of the proxy resource"
6631
- value = aws_api_gateway_resource.proxy_resource.id
6632
- }
6633
-
6634
- output "proxy_method_id" {
6635
- description = "ID of the proxy method"
6636
- value = aws_api_gateway_method.proxy_method.id
6637
- }
6638
-
6639
- # CloudWatch Log Groups
6640
- output "lambda_log_group_name" {
6641
- description = "Name of the Lambda CloudWatch log group"
6642
- value = aws_cloudwatch_log_group.lambda_logs.name
6643
- }
6644
-
6645
- output "lambda_log_group_arn" {
6646
- description = "ARN of the Lambda CloudWatch log group"
6647
- value = aws_cloudwatch_log_group.lambda_logs.arn
6648
- }
6649
- ",
6650
- }
6651
- `;