@aws/nx-plugin 0.49.0 → 0.50.0

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 (137) hide show
  1. package/generators.json +22 -1
  2. package/package.json +1 -1
  3. package/sdk/ts.d.ts +2 -0
  4. package/sdk/ts.js +6 -3
  5. package/sdk/ts.js.map +1 -1
  6. package/src/api-connection/generator.d.ts +2 -2
  7. package/src/api-connection/generator.js +20 -0
  8. package/src/api-connection/generator.js.map +1 -1
  9. package/src/infra/app/__snapshots__/generator.spec.ts.snap +124 -14
  10. package/src/infra/app/files/app/README.md.template +5 -5
  11. package/src/infra/app/files/app/checkov.yml.template +12 -0
  12. package/src/infra/app/files/app/src/main.ts.template +2 -4
  13. package/src/infra/app/generator.js +13 -8
  14. package/src/infra/app/generator.js.map +1 -1
  15. package/src/infra/app/schema.d.ts +0 -8
  16. package/src/infra/app/schema.json +0 -16
  17. package/src/license/config.js +3 -3
  18. package/src/license/config.js.map +1 -1
  19. package/src/open-api/ts-hooks/__snapshots__/generator.spec.tsx.snap +114 -0
  20. package/src/open-api/ts-hooks/generator.spec.tsx +176 -0
  21. package/src/open-api/utils/codegen-data.js +42 -5
  22. package/src/open-api/utils/codegen-data.js.map +1 -1
  23. package/src/preset/__snapshots__/generator.spec.ts.snap +2 -0
  24. package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +55 -2
  25. package/src/py/fast-api/generator.js +8 -55
  26. package/src/py/fast-api/generator.js.map +1 -1
  27. package/src/py/fast-api/react/generator.js +9 -111
  28. package/src/py/fast-api/react/generator.js.map +1 -1
  29. package/src/py/lambda-function/generator.js +1 -1
  30. package/src/py/lambda-function/generator.js.map +1 -1
  31. package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +3 -2
  32. package/src/py/mcp-server/generator.js +1 -1
  33. package/src/py/mcp-server/generator.js.map +1 -1
  34. package/src/py/strands-agent/__snapshots__/generator.spec.ts.snap +3 -2
  35. package/src/py/strands-agent/generator.js +1 -1
  36. package/src/py/strands-agent/generator.js.map +1 -1
  37. package/src/smithy/project/__snapshots__/generator.spec.ts.snap +576 -0
  38. package/src/smithy/project/files/build.Dockerfile.template +97 -0
  39. package/src/smithy/project/files/smithy-build.json.template +25 -0
  40. package/src/smithy/project/files/src/main.smithy.template +19 -0
  41. package/src/smithy/project/files/src/operations/echo.smithy.template +18 -0
  42. package/src/smithy/project/generator.d.ts +10 -0
  43. package/src/smithy/project/generator.js +70 -0
  44. package/src/smithy/project/generator.js.map +1 -0
  45. package/src/smithy/project/schema.d.ts +11 -0
  46. package/src/smithy/project/schema.json +42 -0
  47. package/src/smithy/react-connection/__snapshots__/generator.spec.ts.snap +270 -0
  48. package/src/smithy/react-connection/files/model/extensions.smithy.template +33 -0
  49. package/src/smithy/react-connection/generator.d.ts +10 -0
  50. package/src/smithy/react-connection/generator.js +100 -0
  51. package/src/smithy/react-connection/generator.js.map +1 -0
  52. package/src/smithy/react-connection/schema.d.ts +8 -0
  53. package/src/smithy/react-connection/schema.json +26 -0
  54. package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +3023 -0
  55. package/src/smithy/ts/api/files/context.ts.template +12 -0
  56. package/src/smithy/ts/api/files/handler.ts.template +50 -0
  57. package/src/smithy/ts/api/files/index.ts.template +0 -0
  58. package/src/smithy/ts/api/files/local-server.ts.template +41 -0
  59. package/src/smithy/ts/api/files/operations/echo.ts.template +7 -0
  60. package/src/smithy/ts/api/files/service.ts.template +8 -0
  61. package/src/smithy/ts/api/generator.d.ts +10 -0
  62. package/src/smithy/ts/api/generator.js +154 -0
  63. package/src/smithy/ts/api/generator.js.map +1 -0
  64. package/src/smithy/ts/api/schema.d.ts +14 -0
  65. package/src/smithy/ts/api/schema.json +56 -0
  66. package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +61 -2
  67. package/src/trpc/backend/generator.js +6 -20
  68. package/src/trpc/backend/generator.js.map +1 -1
  69. package/src/trpc/backend/schema.d.ts +2 -1
  70. package/src/ts/lambda-function/__snapshots__/generator.spec.ts.snap +3 -3
  71. package/src/ts/lambda-function/generator.js +10 -10
  72. package/src/ts/lambda-function/generator.js.map +1 -1
  73. package/src/ts/lib/eslint.d.ts +7 -0
  74. package/src/ts/lib/eslint.js +37 -29
  75. package/src/ts/lib/eslint.js.map +1 -1
  76. package/src/ts/lib/generator.js +2 -2
  77. package/src/ts/lib/generator.js.map +1 -1
  78. package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +3 -2
  79. package/src/ts/mcp-server/files/Dockerfile.template +1 -1
  80. package/src/ts/mcp-server/generator.js +20 -14
  81. package/src/ts/mcp-server/generator.js.map +1 -1
  82. package/src/ts/nx-generator/__snapshots__/generator.spec.ts.snap +6 -6
  83. package/src/ts/nx-generator/generator.js +3 -2
  84. package/src/ts/nx-generator/generator.js.map +1 -1
  85. package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +264 -3
  86. package/src/ts/react-website/cognito-auth/__snapshots__/generator.spec.ts.snap +10 -0
  87. package/src/utils/__snapshots__/shared-constructs.spec.ts.snap +49 -0
  88. package/src/utils/agent-core-constructs/files/cdk/app/agent-core/__nameKebabCase__/__nameKebabCase__.ts.template +1 -1
  89. package/src/utils/agent-core-constructs/files/terraform/core/agent-core/runtime.tf.template +1 -1
  90. package/src/utils/api-connection/open-api/react.d.ts +43 -0
  91. package/src/utils/api-connection/open-api/react.js +132 -0
  92. package/src/utils/api-connection/open-api/react.js.map +1 -0
  93. package/src/utils/api-constructs/api-constructs.d.ts +6 -2
  94. package/src/utils/api-constructs/api-constructs.js.map +1 -1
  95. package/src/utils/api-constructs/files/cdk/app/apis/http/__apiNameKebabCase__.ts.template +4 -4
  96. package/src/utils/api-constructs/files/cdk/app/apis/rest/__apiNameKebabCase__.ts.template +7 -4
  97. package/src/utils/api-constructs/files/cdk/core/api/http/http-api.ts.template +37 -2
  98. package/src/utils/api-constructs/files/cdk/core/api/rest/rest-api.ts.template +15 -0
  99. package/src/utils/api-constructs/files/terraform/app/apis/http/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +1 -1
  100. package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +1 -1
  101. package/src/utils/api-constructs/open-api-metadata.d.ts +17 -0
  102. package/src/utils/api-constructs/open-api-metadata.js +68 -0
  103. package/src/utils/api-constructs/open-api-metadata.js.map +1 -0
  104. package/src/utils/bundle/bundle.d.ts +35 -0
  105. package/src/utils/bundle/bundle.js +107 -0
  106. package/src/utils/bundle/bundle.js.map +1 -0
  107. package/src/utils/bundle/files/ts/rolldown.config.ts.template +3 -0
  108. package/src/utils/files/common/constructs/src/core/checkov.ts.template +44 -0
  109. package/src/utils/files/common/constructs/src/core/index.ts.template +1 -0
  110. package/src/utils/fs.d.ts +16 -0
  111. package/src/utils/fs.js +32 -0
  112. package/src/utils/fs.js.map +1 -0
  113. package/src/utils/identity-constructs/files/cdk/core/user-identity.ts.template +8 -0
  114. package/src/utils/nx.d.ts +10 -3
  115. package/src/utils/nx.js +18 -3
  116. package/src/utils/nx.js.map +1 -1
  117. package/src/utils/versions.d.ts +6 -2
  118. package/src/utils/versions.js +5 -1
  119. package/src/utils/versions.js.map +1 -1
  120. package/src/utils/website-constructs/files/cdk/core/static-website.ts.template +56 -2
  121. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/aws-prototyping.guard +0 -1282
  122. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/cfn-nag.guard +0 -6839
  123. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/hipaa-security.guard +0 -2807
  124. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/nist-csf.guard +0 -2585
  125. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/pci-dss-3-2-1.guard +0 -2236
  126. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/wa-reliability-pillar.guard +0 -885
  127. package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/wa-security-pillar.guard +0 -2205
  128. package/src/infra/app/files/common/constructs/src/core/cfn-guard.ts.template +0 -67
  129. package/src/utils/bundle.d.ts +0 -16
  130. package/src/utils/bundle.js +0 -48
  131. package/src/utils/bundle.js.map +0 -1
  132. package/src/utils/esbuild.d.ts +0 -15
  133. package/src/utils/esbuild.js +0 -46
  134. package/src/utils/esbuild.js.map +0 -1
  135. /package/src/{py/fast-api/react/files/website → utils/api-connection/open-api/files}/components/__apiNameClassName__Provider.tsx.template +0 -0
  136. /package/src/{py/fast-api/react/files/website → utils/api-connection/open-api/files}/hooks/use__apiNameClassName__.tsx.template +0 -0
  137. /package/src/{py/fast-api/react/files/website → utils/api-connection/open-api/files}/hooks/use__apiNameClassName__Client.tsx.template +0 -0
@@ -0,0 +1,3023 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`tsSmithyApiGenerator > should configure git and eslint ignores for generated code > eslint.config.mjs 1`] = `
4
+ "import baseConfig from '../../eslint.config.mjs';
5
+
6
+ export default [
7
+ ...baseConfig,
8
+ {
9
+ files: ['**/*.json'],
10
+ rules: {
11
+ '@nx/dependency-checks': [
12
+ 'warn',
13
+ {
14
+ ignoredFiles: [
15
+ '{projectRoot}/eslint.config.{js,cjs,mjs,ts,cts,mts}',
16
+ '{projectRoot}/vite.config.{js,ts,mjs,mts}',
17
+ ],
18
+ },
19
+ ],
20
+ },
21
+ languageOptions: {
22
+ parser: await import('jsonc-eslint-parser'),
23
+ },
24
+ },
25
+ {
26
+ ignores: ['**/generated'],
27
+ },
28
+ ];
29
+ "
30
+ `;
31
+
32
+ exports[`tsSmithyApiGenerator > should configure git and eslint ignores for generated code > gitignore 1`] = `"src/generated"`;
33
+
34
+ exports[`tsSmithyApiGenerator > should generate smithy ts api with Cognito auth > cognito-auth-infra.ts 1`] = `
35
+ "import { Construct } from 'constructs';
36
+ import * as url from 'url';
37
+ import {
38
+ Code,
39
+ Runtime,
40
+ Function,
41
+ FunctionProps,
42
+ Tracing,
43
+ } from 'aws-cdk-lib/aws-lambda';
44
+ import {
45
+ AuthorizationType,
46
+ Cors,
47
+ LambdaIntegration,
48
+ CognitoUserPoolsAuthorizer,
49
+ } from 'aws-cdk-lib/aws-apigateway';
50
+ import { Duration } from 'aws-cdk-lib';
51
+ import {
52
+ PolicyDocument,
53
+ PolicyStatement,
54
+ Effect,
55
+ AnyPrincipal,
56
+ } from 'aws-cdk-lib/aws-iam';
57
+ import { IUserPool } from 'aws-cdk-lib/aws-cognito';
58
+ import {
59
+ IntegrationBuilder,
60
+ RestApiIntegration,
61
+ } from '../../core/api/utils.js';
62
+ import { RestApi } from '../../core/api/rest-api.js';
63
+ import {
64
+ OPERATION_DETAILS,
65
+ Operations,
66
+ } from '../../generated/test-api/metadata.gen.js';
67
+
68
+ /**
69
+ * Properties for creating a TestApi construct
70
+ *
71
+ * @template TIntegrations - Map of operation names to their integrations
72
+ */
73
+ export interface TestApiProps<
74
+ TIntegrations extends Record<Operations, RestApiIntegration>,
75
+ > {
76
+ /**
77
+ * Map of operation names to their API Gateway integrations
78
+ */
79
+ integrations: TIntegrations;
80
+ /**
81
+ * Identity details for Cognito Authentication
82
+ */
83
+ identity: {
84
+ userPool: IUserPool;
85
+ };
86
+ }
87
+
88
+ /**
89
+ * A CDK construct that creates and configures an AWS API Gateway REST API
90
+ * specifically for TestApi.
91
+ * @template TIntegrations - Map of operation names to their integrations
92
+ */
93
+ export class TestApi<
94
+ TIntegrations extends Record<Operations, RestApiIntegration>,
95
+ > extends RestApi<Operations, TIntegrations> {
96
+ /**
97
+ * Creates default integrations for all operations, which implement each operation as
98
+ * its own individual lambda function.
99
+ *
100
+ * @param scope - The CDK construct scope
101
+ * @returns An IntegrationBuilder with default lambda integrations
102
+ */
103
+ public static defaultIntegrations = (scope: Construct) => {
104
+ return IntegrationBuilder.rest({
105
+ operations: OPERATION_DETAILS,
106
+ defaultIntegrationOptions: {
107
+ runtime: Runtime.NODEJS_LATEST,
108
+ handler: 'index.handler',
109
+ code: Code.fromAsset(
110
+ url.fileURLToPath(
111
+ new URL(
112
+ '../../../../../../dist/test-api/backend/bundle',
113
+ import.meta.url,
114
+ ),
115
+ ),
116
+ ),
117
+ timeout: Duration.seconds(30),
118
+ tracing: Tracing.ACTIVE,
119
+ environment: {
120
+ AWS_CONNECTION_REUSE_ENABLED: '1',
121
+ },
122
+ } satisfies FunctionProps,
123
+ buildDefaultIntegration: (op, props: FunctionProps) => {
124
+ const handler = new Function(scope, \`TestApi\${op}Handler\`, props);
125
+ return { handler, integration: new LambdaIntegration(handler) };
126
+ },
127
+ });
128
+ };
129
+
130
+ constructor(
131
+ scope: Construct,
132
+ id: string,
133
+ props: TestApiProps<TIntegrations>,
134
+ ) {
135
+ super(scope, id, {
136
+ apiName: 'TestApi',
137
+ defaultMethodOptions: {
138
+ authorizationType: AuthorizationType.COGNITO,
139
+ authorizer: new CognitoUserPoolsAuthorizer(scope, 'TestApiAuthorizer', {
140
+ cognitoUserPools: [props.identity.userPool],
141
+ }),
142
+ },
143
+ defaultCorsPreflightOptions: {
144
+ allowOrigins: Cors.ALL_ORIGINS,
145
+ allowMethods: Cors.ALL_METHODS,
146
+ },
147
+ deployOptions: {
148
+ tracingEnabled: true,
149
+ },
150
+ policy: new PolicyDocument({
151
+ statements: [
152
+ // Allow all callers to invoke the API in the resource policy, since auth is handled by Cognito
153
+ new PolicyStatement({
154
+ effect: Effect.ALLOW,
155
+ principals: [new AnyPrincipal()],
156
+ actions: ['execute-api:Invoke'],
157
+ resources: ['execute-api:/*'],
158
+ }),
159
+ ],
160
+ }),
161
+ operations: OPERATION_DETAILS,
162
+ ...props,
163
+ });
164
+ }
165
+ }
166
+ "
167
+ `;
168
+
169
+ exports[`tsSmithyApiGenerator > should generate smithy ts api with IAM auth > iam-auth-infra.ts 1`] = `
170
+ "import { Construct } from 'constructs';
171
+ import * as url from 'url';
172
+ import {
173
+ Code,
174
+ Runtime,
175
+ Function,
176
+ FunctionProps,
177
+ Tracing,
178
+ } from 'aws-cdk-lib/aws-lambda';
179
+ import {
180
+ AuthorizationType,
181
+ Cors,
182
+ LambdaIntegration,
183
+ } from 'aws-cdk-lib/aws-apigateway';
184
+ import { Duration, Stack } from 'aws-cdk-lib';
185
+ import {
186
+ PolicyDocument,
187
+ PolicyStatement,
188
+ Effect,
189
+ AnyPrincipal,
190
+ AccountPrincipal,
191
+ IGrantable,
192
+ Grant,
193
+ } from 'aws-cdk-lib/aws-iam';
194
+ import {
195
+ IntegrationBuilder,
196
+ RestApiIntegration,
197
+ } from '../../core/api/utils.js';
198
+ import { RestApi } from '../../core/api/rest-api.js';
199
+ import {
200
+ OPERATION_DETAILS,
201
+ Operations,
202
+ } from '../../generated/test-api/metadata.gen.js';
203
+
204
+ /**
205
+ * Properties for creating a TestApi construct
206
+ *
207
+ * @template TIntegrations - Map of operation names to their integrations
208
+ */
209
+ export interface TestApiProps<
210
+ TIntegrations extends Record<Operations, RestApiIntegration>,
211
+ > {
212
+ /**
213
+ * Map of operation names to their API Gateway integrations
214
+ */
215
+ integrations: TIntegrations;
216
+ }
217
+
218
+ /**
219
+ * A CDK construct that creates and configures an AWS API Gateway REST API
220
+ * specifically for TestApi.
221
+ * @template TIntegrations - Map of operation names to their integrations
222
+ */
223
+ export class TestApi<
224
+ TIntegrations extends Record<Operations, RestApiIntegration>,
225
+ > extends RestApi<Operations, TIntegrations> {
226
+ /**
227
+ * Creates default integrations for all operations, which implement each operation as
228
+ * its own individual lambda function.
229
+ *
230
+ * @param scope - The CDK construct scope
231
+ * @returns An IntegrationBuilder with default lambda integrations
232
+ */
233
+ public static defaultIntegrations = (scope: Construct) => {
234
+ return IntegrationBuilder.rest({
235
+ operations: OPERATION_DETAILS,
236
+ defaultIntegrationOptions: {
237
+ runtime: Runtime.NODEJS_LATEST,
238
+ handler: 'index.handler',
239
+ code: Code.fromAsset(
240
+ url.fileURLToPath(
241
+ new URL(
242
+ '../../../../../../dist/test-api/backend/bundle',
243
+ import.meta.url,
244
+ ),
245
+ ),
246
+ ),
247
+ timeout: Duration.seconds(30),
248
+ tracing: Tracing.ACTIVE,
249
+ environment: {
250
+ AWS_CONNECTION_REUSE_ENABLED: '1',
251
+ },
252
+ } satisfies FunctionProps,
253
+ buildDefaultIntegration: (op, props: FunctionProps) => {
254
+ const handler = new Function(scope, \`TestApi\${op}Handler\`, props);
255
+ return { handler, integration: new LambdaIntegration(handler) };
256
+ },
257
+ });
258
+ };
259
+
260
+ constructor(
261
+ scope: Construct,
262
+ id: string,
263
+ props: TestApiProps<TIntegrations>,
264
+ ) {
265
+ super(scope, id, {
266
+ apiName: 'TestApi',
267
+ defaultMethodOptions: {
268
+ authorizationType: AuthorizationType.IAM,
269
+ },
270
+ defaultCorsPreflightOptions: {
271
+ allowOrigins: Cors.ALL_ORIGINS,
272
+ allowMethods: Cors.ALL_METHODS,
273
+ },
274
+ deployOptions: {
275
+ tracingEnabled: true,
276
+ },
277
+ policy: new PolicyDocument({
278
+ statements: [
279
+ // Here we grant any AWS credentials from the account that the project is deployed in to call the api.
280
+ // Machine to machine fine-grained access can be defined here using more specific principals (eg roles or
281
+ // users) and resources (eg which api paths may be invoked by which principal) if required.
282
+ new PolicyStatement({
283
+ effect: Effect.ALLOW,
284
+ principals: [new AccountPrincipal(Stack.of(scope).account)],
285
+ actions: ['execute-api:Invoke'],
286
+ resources: ['execute-api:/*'],
287
+ }),
288
+ // Open up OPTIONS to allow browsers to make unauthenticated preflight requests
289
+ new PolicyStatement({
290
+ effect: Effect.ALLOW,
291
+ principals: [new AnyPrincipal()],
292
+ actions: ['execute-api:Invoke'],
293
+ resources: ['execute-api:/*/OPTIONS/*'],
294
+ }),
295
+ ],
296
+ }),
297
+ operations: OPERATION_DETAILS,
298
+ ...props,
299
+ });
300
+ }
301
+
302
+ /**
303
+ * Grants IAM permissions to invoke any method on this API.
304
+ *
305
+ * @param grantee - The IAM principal to grant permissions to
306
+ */
307
+ public grantInvokeAccess(grantee: IGrantable) {
308
+ Grant.addToPrincipal({
309
+ grantee,
310
+ actions: ['execute-api:Invoke'],
311
+ resourceArns: [this.api.arnForExecuteApi('*', '/*', '*')],
312
+ });
313
+ }
314
+ }
315
+ "
316
+ `;
317
+
318
+ exports[`tsSmithyApiGenerator > should generate smithy ts api with Terraform provider > terraform-infra.tf 1`] = `
319
+ "# Core REST API Gateway module
320
+ # This module creates the API Gateway REST API, deployment, stage, and logging resources
321
+
322
+ terraform {
323
+ required_version = ">= 1.0"
324
+
325
+ required_providers {
326
+ aws = {
327
+ source = "hashicorp/aws"
328
+ version = "~> 6.0"
329
+ }
330
+ }
331
+ }
332
+
333
+ # Core REST API Gateway Variables
334
+
335
+ variable "api_name" {
336
+ description = "Name of the REST API Gateway"
337
+ type = string
338
+ }
339
+
340
+ variable "api_description" {
341
+ description = "Description of the REST API Gateway"
342
+ type = string
343
+ default = "REST API Gateway"
344
+ }
345
+
346
+ variable "stage_name" {
347
+ description = "Name of the API Gateway stage"
348
+ type = string
349
+ default = "prod"
350
+ }
351
+
352
+ variable "stage_auto_deploy" {
353
+ description = "Whether to automatically deploy the API stage"
354
+ type = bool
355
+ default = true
356
+ }
357
+
358
+ # CORS Configuration
359
+
360
+ variable "cors_allow_headers" {
361
+ description = "List of allowed headers for CORS"
362
+ type = list(string)
363
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
364
+ }
365
+
366
+ variable "cors_allow_methods" {
367
+ description = "List of allowed HTTP methods for CORS"
368
+ type = list(string)
369
+ default = ["*"]
370
+ }
371
+
372
+ variable "cors_allow_origins" {
373
+ description = "List of allowed origins for CORS"
374
+ type = list(string)
375
+ default = ["*"]
376
+ }
377
+
378
+ # Tags
379
+
380
+ variable "tags" {
381
+ description = "Tags to apply to all resources"
382
+ type = map(string)
383
+ default = {}
384
+ }
385
+
386
+ # Data sources
387
+ data "aws_region" "current" {}
388
+ data "aws_caller_identity" "current" {}
389
+
390
+ # Resources
391
+
392
+ # Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
393
+
394
+ # REST API Gateway
395
+ resource "aws_api_gateway_rest_api" "rest_api" {
396
+ name = var.api_name
397
+ description = var.api_description
398
+
399
+ endpoint_configuration {
400
+ types = ["REGIONAL"]
401
+ }
402
+
403
+ lifecycle {
404
+ create_before_destroy = true
405
+ }
406
+
407
+ tags = var.tags
408
+ }
409
+
410
+ # Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
411
+ # after all methods and integrations are defined
412
+
413
+ # Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
414
+
415
+ # Gateway Response for CORS (4XX errors)
416
+ resource "aws_api_gateway_gateway_response" "cors_4xx" {
417
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
418
+ response_type = "DEFAULT_4XX"
419
+
420
+ response_parameters = {
421
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
422
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
423
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
424
+ }
425
+ }
426
+
427
+ # Gateway Response for CORS (5XX errors)
428
+ resource "aws_api_gateway_gateway_response" "cors_5xx" {
429
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
430
+ response_type = "DEFAULT_5XX"
431
+
432
+ response_parameters = {
433
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
434
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
435
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
436
+ }
437
+ }
438
+
439
+ # Outputs
440
+
441
+ output "api_id" {
442
+ description = "ID of the REST API Gateway"
443
+ value = aws_api_gateway_rest_api.rest_api.id
444
+ }
445
+
446
+ output "api_arn" {
447
+ description = "ARN of the REST API Gateway"
448
+ value = aws_api_gateway_rest_api.rest_api.arn
449
+ }
450
+
451
+ output "api_endpoint" {
452
+ description = "Base URL of the REST API Gateway"
453
+ value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
454
+ }
455
+
456
+ output "api_execution_arn" {
457
+ description = "Execution ARN of the REST API Gateway"
458
+ value = aws_api_gateway_rest_api.rest_api.execution_arn
459
+ }
460
+
461
+ output "api_root_resource_id" {
462
+ description = "Root resource ID of the REST API Gateway"
463
+ value = aws_api_gateway_rest_api.rest_api.root_resource_id
464
+ }
465
+
466
+ # Note: Stage and deployment outputs are provided by the consuming module
467
+
468
+ # Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement"
469
+ `;
470
+
471
+ exports[`tsSmithyApiGenerator > should generate smithy ts api with Terraform provider and None auth > none-auth-terraform.tf 1`] = `
472
+ "terraform {
473
+ required_version = ">= 1.0"
474
+
475
+ required_providers {
476
+ aws = {
477
+ source = "hashicorp/aws"
478
+ version = "~> 6.0"
479
+ }
480
+ }
481
+ }
482
+
483
+
484
+ variable "env" {
485
+ description = "Environment variables for the Lambda function"
486
+ type = map(string)
487
+ default = {}
488
+ }
489
+
490
+ variable "additional_iam_policy_statements" {
491
+ description = "Additional IAM policy statements for the Lambda function"
492
+ type = list(object({
493
+ Effect = string
494
+ Action = list(string)
495
+ Resource = list(string)
496
+ }))
497
+ default = []
498
+ }
499
+
500
+ # CORS Configuration (passed to core module)
501
+
502
+ variable "cors_allow_headers" {
503
+ description = "List of allowed headers for CORS"
504
+ type = list(string)
505
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
506
+ }
507
+
508
+ variable "cors_allow_methods" {
509
+ description = "List of allowed HTTP methods for CORS"
510
+ type = list(string)
511
+ default = ["*"]
512
+ }
513
+
514
+ variable "cors_allow_origins" {
515
+ description = "List of allowed origins for CORS"
516
+ type = list(string)
517
+ default = ["*"]
518
+ }
519
+
520
+ # Tags
521
+ variable "tags" {
522
+ description = "Tags to apply to all resources"
523
+ type = map(string)
524
+ default = {}
525
+ }
526
+
527
+ # Get current AWS region and account ID
528
+ data "aws_region" "current" {}
529
+ data "aws_caller_identity" "current" {}
530
+
531
+ # Resources
532
+
533
+ # Create Lambda ZIP file from the FastAPI bundle directory
534
+ data "archive_file" "lambda_zip" {
535
+ type = "zip"
536
+ source_dir = "\${path.module}/../../../../../../../dist/test-api/backend/bundle"
537
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
538
+ }
539
+
540
+ # Use the core REST API module
541
+ module "rest_api" {
542
+ source = "../../../core/api/rest-api"
543
+
544
+ api_name = "TestApi"
545
+ api_description = "TestApi REST API"
546
+ stage_name = "prod"
547
+ stage_auto_deploy = true
548
+
549
+ # CORS Configuration
550
+ cors_allow_headers = var.cors_allow_headers
551
+ cors_allow_methods = var.cors_allow_methods
552
+ cors_allow_origins = var.cors_allow_origins
553
+
554
+ # Tags
555
+ tags = var.tags
556
+ }
557
+
558
+ # Lambda function
559
+ resource "aws_lambda_function" "api_lambda" {
560
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
561
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
562
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
563
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
564
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
565
+ filename = data.archive_file.lambda_zip.output_path
566
+ function_name = "TestApiHandler"
567
+ role = aws_iam_role.lambda_execution_role.arn
568
+ handler = "index.handler"
569
+ runtime = "nodejs22.x"
570
+ timeout = 30
571
+ memory_size = 128
572
+
573
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
574
+
575
+ # Enable X-Ray tracing
576
+ tracing_config {
577
+ mode = "Active"
578
+ }
579
+
580
+ environment {
581
+ variables = merge({
582
+ AWS_CONNECTION_REUSE_ENABLED = "1"
583
+ }, var.env)
584
+ }
585
+
586
+ tags = var.tags
587
+ }
588
+
589
+ # IAM role for Lambda execution
590
+ resource "aws_iam_role" "lambda_execution_role" {
591
+ name = "TestApiHandler-execution-role"
592
+
593
+ assume_role_policy = jsonencode({
594
+ Version = "2012-10-17"
595
+ Statement = [
596
+ {
597
+ Action = "sts:AssumeRole"
598
+ Effect = "Allow"
599
+ Principal = {
600
+ Service = "lambda.amazonaws.com"
601
+ }
602
+ }
603
+ ]
604
+ })
605
+
606
+ tags = var.tags
607
+ }
608
+
609
+ # Attach basic execution policy to Lambda role
610
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
611
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
612
+ role = aws_iam_role.lambda_execution_role.name
613
+ }
614
+
615
+ # Attach X-Ray tracing policy to Lambda role
616
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
617
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
618
+ role = aws_iam_role.lambda_execution_role.name
619
+ }
620
+
621
+ # Additional IAM policies for Lambda (if provided)
622
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
623
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
624
+ name = "TestApiHandler-additional-policies"
625
+ role = aws_iam_role.lambda_execution_role.id
626
+
627
+ policy = jsonencode({
628
+ Version = "2012-10-17"
629
+ Statement = var.additional_iam_policy_statements
630
+ })
631
+ }
632
+
633
+ # CloudWatch Log Group for Lambda
634
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
635
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
636
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
637
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
638
+ name = "/aws/lambda/TestApiHandler"
639
+ tags = var.tags
640
+ }
641
+
642
+
643
+ # Create proxy resource (captures all paths)
644
+ resource "aws_api_gateway_resource" "proxy_resource" {
645
+ rest_api_id = module.rest_api.api_id
646
+ parent_id = module.rest_api.api_root_resource_id
647
+ path_part = "{proxy+}"
648
+ }
649
+
650
+ # Lambda integration for REST API
651
+ resource "aws_api_gateway_integration" "lambda_integration" {
652
+ rest_api_id = module.rest_api.api_id
653
+ resource_id = aws_api_gateway_resource.proxy_resource.id
654
+ http_method = aws_api_gateway_method.proxy_method.http_method
655
+
656
+ integration_http_method = "POST"
657
+ type = "AWS_PROXY"
658
+ uri = aws_lambda_function.api_lambda.invoke_arn
659
+
660
+ depends_on = [aws_lambda_function.api_lambda]
661
+ }
662
+
663
+ # Method for proxy integration
664
+ resource "aws_api_gateway_method" "proxy_method" {
665
+ #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
666
+ rest_api_id = module.rest_api.api_id
667
+ resource_id = aws_api_gateway_resource.proxy_resource.id
668
+ http_method = "ANY"
669
+
670
+ # Note: you may wish to suppress the checkov rule CKV_AWS_59 if you are absolutely sure you
671
+ # need a public API without authentication
672
+ authorization = "NONE"
673
+
674
+ request_parameters = {
675
+ "method.request.path.proxy" = true
676
+ }
677
+
678
+ depends_on = []
679
+ }
680
+
681
+ # OPTIONS method for CORS preflight
682
+ resource "aws_api_gateway_method" "options_method" {
683
+ #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
684
+ #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
685
+ rest_api_id = module.rest_api.api_id
686
+ resource_id = aws_api_gateway_resource.proxy_resource.id
687
+ http_method = "OPTIONS"
688
+ authorization = "NONE"
689
+ }
690
+
691
+ # CORS integration for OPTIONS method
692
+ resource "aws_api_gateway_integration" "options_integration" {
693
+ rest_api_id = module.rest_api.api_id
694
+ resource_id = aws_api_gateway_resource.proxy_resource.id
695
+ http_method = aws_api_gateway_method.options_method.http_method
696
+
697
+ type = "MOCK"
698
+ request_templates = {
699
+ "application/json" = "{\\"statusCode\\": 204}"
700
+ }
701
+ }
702
+
703
+ # OPTIONS method response
704
+ resource "aws_api_gateway_method_response" "options_response" {
705
+ rest_api_id = module.rest_api.api_id
706
+ resource_id = aws_api_gateway_resource.proxy_resource.id
707
+ http_method = aws_api_gateway_method.options_method.http_method
708
+ status_code = "204"
709
+
710
+ response_parameters = {
711
+ "method.response.header.Access-Control-Allow-Headers" = true
712
+ "method.response.header.Access-Control-Allow-Methods" = true
713
+ "method.response.header.Access-Control-Allow-Origin" = true
714
+ }
715
+ }
716
+
717
+ # OPTIONS integration response
718
+ resource "aws_api_gateway_integration_response" "options_integration_response" {
719
+ rest_api_id = module.rest_api.api_id
720
+ resource_id = aws_api_gateway_resource.proxy_resource.id
721
+ http_method = aws_api_gateway_method.options_method.http_method
722
+ status_code = aws_api_gateway_method_response.options_response.status_code
723
+
724
+ response_parameters = {
725
+ "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
726
+ "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
727
+ "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
728
+ }
729
+ }
730
+
731
+ # API Gateway deployment
732
+ resource "aws_api_gateway_deployment" "api_deployment" {
733
+ rest_api_id = module.rest_api.api_id
734
+
735
+ triggers = {
736
+ redeployment = sha1(jsonencode([
737
+ aws_api_gateway_resource.proxy_resource.id,
738
+ aws_api_gateway_method.proxy_method.id,
739
+ aws_api_gateway_integration.lambda_integration.id,
740
+ aws_api_gateway_method.options_method.id,
741
+ aws_api_gateway_integration.options_integration.id,
742
+ ]))
743
+ }
744
+
745
+ lifecycle {
746
+ create_before_destroy = true
747
+ }
748
+
749
+ depends_on = [
750
+ aws_api_gateway_method.proxy_method,
751
+ aws_api_gateway_integration.lambda_integration,
752
+ aws_api_gateway_method.options_method,
753
+ aws_api_gateway_integration.options_integration,
754
+ aws_api_gateway_method_response.options_response,
755
+ aws_api_gateway_integration_response.options_integration_response,
756
+ ]
757
+ }
758
+
759
+ # API Gateway stage
760
+ resource "aws_api_gateway_stage" "api_stage" {
761
+ #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
762
+ #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
763
+ #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
764
+ #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
765
+ deployment_id = aws_api_gateway_deployment.api_deployment.id
766
+ rest_api_id = module.rest_api.api_id
767
+ stage_name = "prod"
768
+ xray_tracing_enabled = true
769
+
770
+ tags = var.tags
771
+
772
+ depends_on = [aws_api_gateway_deployment.api_deployment]
773
+ }
774
+
775
+ # API Gateway Resource Policy
776
+ resource "aws_api_gateway_rest_api_policy" "api_policy" {
777
+ rest_api_id = module.rest_api.api_id
778
+
779
+ policy = jsonencode({
780
+ Version = "2012-10-17"
781
+ Statement = [
782
+ {
783
+ # Allow all callers to invoke the API in the resource policy
784
+ Effect = "Allow"
785
+ Principal = "*"
786
+ Action = "execute-api:Invoke"
787
+ Resource = "execute-api:/*"
788
+ }
789
+ ]
790
+ })
791
+ }
792
+
793
+ # Lambda permission for API Gateway to invoke the function
794
+ resource "aws_lambda_permission" "api_gateway_invoke" {
795
+ statement_id = "AllowExecutionFromAPIGateway"
796
+ action = "lambda:InvokeFunction"
797
+ function_name = aws_lambda_function.api_lambda.function_name
798
+ principal = "apigateway.amazonaws.com"
799
+ source_arn = "\${module.rest_api.api_execution_arn}/*/*"
800
+
801
+ depends_on = [module.rest_api, aws_lambda_function.api_lambda]
802
+ }
803
+
804
+ # Add API url to runtime config
805
+ module "add_url_to_runtime_config" {
806
+ source = "../../../core/runtime-config/entry"
807
+
808
+ key_path = "apis.TestApi"
809
+ value = aws_api_gateway_stage.api_stage.invoke_url
810
+
811
+ depends_on = [aws_api_gateway_stage.api_stage]
812
+ }
813
+
814
+ # Outputs
815
+
816
+ # API Gateway Outputs (from core module)
817
+ output "api_id" {
818
+ description = "ID of the REST API Gateway"
819
+ value = module.rest_api.api_id
820
+ }
821
+
822
+ output "api_arn" {
823
+ description = "ARN of the REST API Gateway"
824
+ value = module.rest_api.api_arn
825
+ }
826
+
827
+ output "api_endpoint" {
828
+ description = "Base URL of the REST API Gateway"
829
+ value = module.rest_api.api_endpoint
830
+ }
831
+
832
+ output "api_execution_arn" {
833
+ description = "Execution ARN of the REST API Gateway"
834
+ value = module.rest_api.api_execution_arn
835
+ }
836
+
837
+ output "stage_invoke_url" {
838
+ description = "Invoke URL of the API Gateway stage"
839
+ value = aws_api_gateway_stage.api_stage.invoke_url
840
+ }
841
+
842
+ output "stage_arn" {
843
+ description = "ARN of the API Gateway stage"
844
+ value = aws_api_gateway_stage.api_stage.arn
845
+ }
846
+
847
+ output "stage_execution_arn" {
848
+ description = "Execution ARN of the API Gateway stage"
849
+ value = aws_api_gateway_stage.api_stage.execution_arn
850
+ }
851
+
852
+ output "deployment_id" {
853
+ description = "ID of the API Gateway deployment"
854
+ value = aws_api_gateway_deployment.api_deployment.id
855
+ }
856
+
857
+ output "stage_id" {
858
+ description = "ID of the API Gateway stage"
859
+ value = aws_api_gateway_stage.api_stage.id
860
+ }
861
+
862
+ # Lambda Function Outputs
863
+ output "lambda_function_name" {
864
+ description = "Name of the Lambda function"
865
+ value = aws_lambda_function.api_lambda.function_name
866
+ }
867
+
868
+ output "lambda_function_arn" {
869
+ description = "ARN of the Lambda function"
870
+ value = aws_lambda_function.api_lambda.arn
871
+ }
872
+
873
+ output "lambda_invoke_arn" {
874
+ description = "Invoke ARN of the Lambda function"
875
+ value = aws_lambda_function.api_lambda.invoke_arn
876
+ }
877
+
878
+ output "lambda_qualified_arn" {
879
+ description = "Qualified ARN of the Lambda function"
880
+ value = aws_lambda_function.api_lambda.qualified_arn
881
+ }
882
+
883
+ output "lambda_version" {
884
+ description = "Version of the Lambda function"
885
+ value = aws_lambda_function.api_lambda.version
886
+ }
887
+
888
+ output "lambda_source_code_hash" {
889
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
890
+ value = aws_lambda_function.api_lambda.source_code_hash
891
+ }
892
+
893
+ output "lambda_source_code_size" {
894
+ description = "Size of the Lambda deployment package in bytes"
895
+ value = aws_lambda_function.api_lambda.source_code_size
896
+ }
897
+
898
+ # IAM Role Outputs
899
+ output "lambda_execution_role_arn" {
900
+ description = "ARN of the Lambda execution role"
901
+ value = aws_iam_role.lambda_execution_role.arn
902
+ }
903
+
904
+ output "lambda_execution_role_name" {
905
+ description = "Name of the Lambda execution role"
906
+ value = aws_iam_role.lambda_execution_role.name
907
+ }
908
+
909
+ # Integration Outputs
910
+ output "integration_id" {
911
+ description = "ID of the Lambda integration"
912
+ value = aws_api_gateway_integration.lambda_integration.id
913
+ }
914
+
915
+ output "proxy_resource_id" {
916
+ description = "ID of the proxy resource"
917
+ value = aws_api_gateway_resource.proxy_resource.id
918
+ }
919
+
920
+ output "proxy_method_id" {
921
+ description = "ID of the proxy method"
922
+ value = aws_api_gateway_method.proxy_method.id
923
+ }
924
+
925
+ # CloudWatch Log Groups
926
+ output "lambda_log_group_name" {
927
+ description = "Name of the Lambda CloudWatch log group"
928
+ value = aws_cloudwatch_log_group.lambda_logs.name
929
+ }
930
+
931
+ output "lambda_log_group_arn" {
932
+ description = "ARN of the Lambda CloudWatch log group"
933
+ value = aws_cloudwatch_log_group.lambda_logs.arn
934
+ }
935
+ "
936
+ `;
937
+
938
+ exports[`tsSmithyApiGenerator > should generate smithy ts api with custom namespace > custom-namespace-main.smithy 1`] = `
939
+ "$version: "2.0"
940
+
941
+ namespace com.example.custom
942
+
943
+ use aws.protocols#restJson1
944
+ use smithy.framework#ValidationException
945
+
946
+ /// TODO: describe your service here
947
+ @title("TestApi")
948
+ @restJson1
949
+ service TestApi {
950
+ version: "1.0.0"
951
+ operations: [
952
+ Echo
953
+ ]
954
+ errors: [
955
+ ValidationException
956
+ ]
957
+ }
958
+ "
959
+ `;
960
+
961
+ exports[`tsSmithyApiGenerator > should generate smithy ts api with default options > backend-project.json 1`] = `
962
+ "{
963
+ "name": "@proj/test-api",
964
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
965
+ "sourceRoot": "test-api/backend/src",
966
+ "projectType": "library",
967
+ "tags": [],
968
+ "metadata": {
969
+ "generator": "ts#smithy-api",
970
+ "apiName": "test-api",
971
+ "auth": "None",
972
+ "modelProject": "@proj/test-api-model",
973
+ "ports": [3001]
974
+ },
975
+ "targets": {
976
+ "build": {
977
+ "dependsOn": ["lint", "compile", "test", "bundle"]
978
+ },
979
+ "compile": {
980
+ "executor": "nx:run-commands",
981
+ "outputs": ["{workspaceRoot}/dist/test-api/backend/tsc"],
982
+ "options": {
983
+ "command": "tsc --build tsconfig.lib.json",
984
+ "cwd": "{projectRoot}"
985
+ },
986
+ "dependsOn": ["copy-ssdk"]
987
+ },
988
+ "test": {
989
+ "executor": "@nx/vite:test",
990
+ "outputs": ["{options.reportsDirectory}"],
991
+ "options": {
992
+ "reportsDirectory": "../../coverage/test-api/backend"
993
+ }
994
+ },
995
+ "bundle": {
996
+ "cache": true,
997
+ "outputs": ["{workspaceRoot}/dist/{projectRoot}/bundle"],
998
+ "executor": "nx:run-commands",
999
+ "options": {
1000
+ "command": "rolldown -c rolldown.config.ts",
1001
+ "cwd": "{projectRoot}"
1002
+ },
1003
+ "dependsOn": ["compile"]
1004
+ },
1005
+ "copy-ssdk": {
1006
+ "cache": true,
1007
+ "inputs": [
1008
+ {
1009
+ "dependentTasksOutputFiles": "**/*"
1010
+ }
1011
+ ],
1012
+ "executor": "nx:run-commands",
1013
+ "options": {
1014
+ "commands": [
1015
+ "rimraf test-api/backend/src/generated",
1016
+ "make-dir test-api/backend/src/generated",
1017
+ "ncp dist/test-api/model/build/ssdk test-api/backend/src/generated/ssdk"
1018
+ ],
1019
+ "parallel": false
1020
+ },
1021
+ "outputs": ["{projectRoot}/src/generated"],
1022
+ "dependsOn": ["@proj/test-api-model:build"]
1023
+ },
1024
+ "watch-copy-ssdk": {
1025
+ "executor": "nx:run-commands",
1026
+ "options": {
1027
+ "command": "nx watch --projects=@proj/test-api-model --includeDependentProjects -- nx run @proj/test-api:copy-ssdk"
1028
+ },
1029
+ "continuous": true
1030
+ },
1031
+ "serve": {
1032
+ "executor": "nx:run-commands",
1033
+ "options": {
1034
+ "command": "tsx --watch src/local-server.ts",
1035
+ "cwd": "{projectRoot}"
1036
+ },
1037
+ "continuous": true,
1038
+ "dependsOn": ["copy-ssdk", "watch-copy-ssdk"]
1039
+ }
1040
+ }
1041
+ }
1042
+ "
1043
+ `;
1044
+
1045
+ exports[`tsSmithyApiGenerator > should generate smithy ts api with default options > context.ts 1`] = `
1046
+ "import { Logger } from '@aws-lambda-powertools/logger';
1047
+ import { Metrics } from '@aws-lambda-powertools/metrics';
1048
+ import { Tracer } from '@aws-lambda-powertools/tracer';
1049
+
1050
+ /**
1051
+ * Context provided to all operations.
1052
+ */
1053
+ export interface ServiceContext {
1054
+ tracer: Tracer;
1055
+ logger: Logger;
1056
+ metrics: Metrics;
1057
+ }
1058
+ "
1059
+ `;
1060
+
1061
+ exports[`tsSmithyApiGenerator > should generate smithy ts api with default options > echo.ts 1`] = `
1062
+ "import { ServiceContext } from '../context.js';
1063
+ import { Echo as EchoOperation } from '../generated/ssdk/index.js';
1064
+
1065
+ export const Echo: EchoOperation<ServiceContext> = async (input, ctx) => {
1066
+ ctx.logger.info('Starting Echo operation');
1067
+ return input;
1068
+ };
1069
+ "
1070
+ `;
1071
+
1072
+ exports[`tsSmithyApiGenerator > should generate smithy ts api with default options > handler.ts 1`] = `
1073
+ "import {
1074
+ convertEvent,
1075
+ convertVersion1Response,
1076
+ } from '@aws-smithy/server-apigateway';
1077
+ import type { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
1078
+ import middy from '@middy/core';
1079
+ import { Tracer } from '@aws-lambda-powertools/tracer';
1080
+ import { captureLambdaHandler } from '@aws-lambda-powertools/tracer/middleware';
1081
+ import { injectLambdaContext } from '@aws-lambda-powertools/logger/middleware';
1082
+ import { Logger } from '@aws-lambda-powertools/logger';
1083
+ import { Metrics } from '@aws-lambda-powertools/metrics';
1084
+ import { logMetrics } from '@aws-lambda-powertools/metrics/middleware';
1085
+ import { Service } from './service.js';
1086
+ import { getTestApiServiceHandler } from './generated/ssdk/index.js';
1087
+
1088
+ process.env.POWERTOOLS_METRICS_NAMESPACE = 'TestApi';
1089
+ process.env.POWERTOOLS_SERVICE_NAME = 'TestApi';
1090
+
1091
+ const tracer = new Tracer();
1092
+ const logger = new Logger();
1093
+ const metrics = new Metrics();
1094
+
1095
+ const serviceHandler = getTestApiServiceHandler(Service);
1096
+
1097
+ export const lambdaHandler = async (
1098
+ event: APIGatewayProxyEvent,
1099
+ ): Promise<APIGatewayProxyResult> => {
1100
+ const httpRequest = convertEvent(event);
1101
+ const httpResponse = await serviceHandler.handle(httpRequest, {
1102
+ tracer,
1103
+ logger,
1104
+ metrics,
1105
+ });
1106
+ const apiGatewayResponse = convertVersion1Response(httpResponse);
1107
+
1108
+ return {
1109
+ ...apiGatewayResponse,
1110
+ headers: {
1111
+ 'Access-Control-Allow-Origin': '*',
1112
+ 'Access-Control-Allow-Methods': '*',
1113
+ ...apiGatewayResponse.headers,
1114
+ },
1115
+ };
1116
+ };
1117
+
1118
+ export const handler = middy<APIGatewayProxyEvent, APIGatewayProxyResult>()
1119
+ .use(captureLambdaHandler(tracer))
1120
+ .use(injectLambdaContext(logger))
1121
+ .use(logMetrics(metrics))
1122
+ .handler(lambdaHandler);
1123
+ "
1124
+ `;
1125
+
1126
+ exports[`tsSmithyApiGenerator > should generate smithy ts api with default options > local-server.ts 1`] = `
1127
+ "import { IncomingMessage, ServerResponse, createServer } from 'http';
1128
+ import { convertRequest, writeResponse } from '@aws-smithy/server-node';
1129
+ import { Logger } from '@aws-lambda-powertools/logger';
1130
+ import { Metrics } from '@aws-lambda-powertools/metrics';
1131
+ import { Tracer } from '@aws-lambda-powertools/tracer';
1132
+ import { Service } from './service.js';
1133
+ import { getTestApiServiceHandler } from './generated/ssdk/index.js';
1134
+
1135
+ const PORT = 3001;
1136
+
1137
+ const tracer = new Tracer();
1138
+ const logger = new Logger();
1139
+ const metrics = new Metrics();
1140
+
1141
+ const serviceHandler = getTestApiServiceHandler(Service);
1142
+
1143
+ const server = createServer(async function (
1144
+ req: IncomingMessage,
1145
+ res: ServerResponse<IncomingMessage> & { req: IncomingMessage },
1146
+ ) {
1147
+ res.setHeader('Access-Control-Allow-Origin', '*');
1148
+ res.setHeader('Access-Control-Allow-Methods', '*');
1149
+ res.setHeader('Access-Control-Allow-Headers', '*');
1150
+
1151
+ if (req.method === 'OPTIONS') {
1152
+ res.writeHead(204);
1153
+ res.end();
1154
+ return;
1155
+ }
1156
+
1157
+ const httpRequest = convertRequest(req);
1158
+ const httpResponse = await serviceHandler.handle(httpRequest, {
1159
+ tracer,
1160
+ logger,
1161
+ metrics,
1162
+ });
1163
+ return writeResponse(httpResponse, res);
1164
+ });
1165
+
1166
+ server.listen(PORT);
1167
+ console.log(\`Started server on port \${PORT}...\`);
1168
+ "
1169
+ `;
1170
+
1171
+ exports[`tsSmithyApiGenerator > should generate smithy ts api with default options > model-project.json 1`] = `
1172
+ "{
1173
+ "name": "@proj/test-api-model",
1174
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
1175
+ "sourceRoot": "test-api/model/src",
1176
+ "projectType": "library",
1177
+ "metadata": {
1178
+ "generator": "smithy#project",
1179
+ "apiName": "test-api-model",
1180
+ "backendProject": "@proj/test-api"
1181
+ },
1182
+ "targets": {
1183
+ "build": {
1184
+ "dependsOn": ["compile"]
1185
+ },
1186
+ "compile": {
1187
+ "cache": true,
1188
+ "outputs": ["{workspaceRoot}/dist/{projectRoot}/build"],
1189
+ "executor": "nx:run-commands",
1190
+ "options": {
1191
+ "commands": [
1192
+ "rimraf dist/test-api/model/build",
1193
+ "make-dir dist/test-api/model/build",
1194
+ "docker build -f test-api/model/build.Dockerfile --target export --output type=local,dest=dist/test-api/model/build test-api/model"
1195
+ ],
1196
+ "parallel": false,
1197
+ "cwd": "{workspaceRoot}"
1198
+ }
1199
+ }
1200
+ }
1201
+ }
1202
+ "
1203
+ `;
1204
+
1205
+ exports[`tsSmithyApiGenerator > should generate smithy ts api with default options > service.ts 1`] = `
1206
+ "import { ServiceContext } from './context.js';
1207
+ import { TestApiService } from './generated/ssdk/index.js';
1208
+ import { Echo } from './operations/echo.js';
1209
+
1210
+ // Register operations to the service here
1211
+ export const Service: TestApiService<ServiceContext> = {
1212
+ Echo,
1213
+ };
1214
+ "
1215
+ `;
1216
+
1217
+ exports[`tsSmithyApiGenerator > should handle kebab-case API names correctly > kebab-case-handler.ts 1`] = `
1218
+ "import {
1219
+ convertEvent,
1220
+ convertVersion1Response,
1221
+ } from '@aws-smithy/server-apigateway';
1222
+ import type { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
1223
+ import middy from '@middy/core';
1224
+ import { Tracer } from '@aws-lambda-powertools/tracer';
1225
+ import { captureLambdaHandler } from '@aws-lambda-powertools/tracer/middleware';
1226
+ import { injectLambdaContext } from '@aws-lambda-powertools/logger/middleware';
1227
+ import { Logger } from '@aws-lambda-powertools/logger';
1228
+ import { Metrics } from '@aws-lambda-powertools/metrics';
1229
+ import { logMetrics } from '@aws-lambda-powertools/metrics/middleware';
1230
+ import { Service } from './service.js';
1231
+ import { getMyTestApiServiceHandler } from './generated/ssdk/index.js';
1232
+
1233
+ process.env.POWERTOOLS_METRICS_NAMESPACE = 'MyTestApi';
1234
+ process.env.POWERTOOLS_SERVICE_NAME = 'MyTestApi';
1235
+
1236
+ const tracer = new Tracer();
1237
+ const logger = new Logger();
1238
+ const metrics = new Metrics();
1239
+
1240
+ const serviceHandler = getMyTestApiServiceHandler(Service);
1241
+
1242
+ export const lambdaHandler = async (
1243
+ event: APIGatewayProxyEvent,
1244
+ ): Promise<APIGatewayProxyResult> => {
1245
+ const httpRequest = convertEvent(event);
1246
+ const httpResponse = await serviceHandler.handle(httpRequest, {
1247
+ tracer,
1248
+ logger,
1249
+ metrics,
1250
+ });
1251
+ const apiGatewayResponse = convertVersion1Response(httpResponse);
1252
+
1253
+ return {
1254
+ ...apiGatewayResponse,
1255
+ headers: {
1256
+ 'Access-Control-Allow-Origin': '*',
1257
+ 'Access-Control-Allow-Methods': '*',
1258
+ ...apiGatewayResponse.headers,
1259
+ },
1260
+ };
1261
+ };
1262
+
1263
+ export const handler = middy<APIGatewayProxyEvent, APIGatewayProxyResult>()
1264
+ .use(captureLambdaHandler(tracer))
1265
+ .use(injectLambdaContext(logger))
1266
+ .use(logMetrics(metrics))
1267
+ .handler(lambdaHandler);
1268
+ "
1269
+ `;
1270
+
1271
+ exports[`tsSmithyApiGenerator > should handle kebab-case API names correctly > kebab-case-main.smithy 1`] = `
1272
+ "$version: "2.0"
1273
+
1274
+ namespace proj
1275
+
1276
+ use aws.protocols#restJson1
1277
+ use smithy.framework#ValidationException
1278
+
1279
+ /// TODO: describe your service here
1280
+ @title("MyTestApi")
1281
+ @restJson1
1282
+ service MyTestApi {
1283
+ version: "1.0.0"
1284
+ operations: [
1285
+ Echo
1286
+ ]
1287
+ errors: [
1288
+ ValidationException
1289
+ ]
1290
+ }
1291
+ "
1292
+ `;
1293
+
1294
+ exports[`tsSmithyApiGenerator > terraform iacProvider > should generate terraform files for REST API with Cognito auth and snapshot them > terraform-rest-cognito-files 1`] = `
1295
+ {
1296
+ "rest-api.tf": "# Core REST API Gateway module
1297
+ # This module creates the API Gateway REST API, deployment, stage, and logging resources
1298
+
1299
+ terraform {
1300
+ required_version = ">= 1.0"
1301
+
1302
+ required_providers {
1303
+ aws = {
1304
+ source = "hashicorp/aws"
1305
+ version = "~> 6.0"
1306
+ }
1307
+ }
1308
+ }
1309
+
1310
+ # Core REST API Gateway Variables
1311
+
1312
+ variable "api_name" {
1313
+ description = "Name of the REST API Gateway"
1314
+ type = string
1315
+ }
1316
+
1317
+ variable "api_description" {
1318
+ description = "Description of the REST API Gateway"
1319
+ type = string
1320
+ default = "REST API Gateway"
1321
+ }
1322
+
1323
+ variable "stage_name" {
1324
+ description = "Name of the API Gateway stage"
1325
+ type = string
1326
+ default = "prod"
1327
+ }
1328
+
1329
+ variable "stage_auto_deploy" {
1330
+ description = "Whether to automatically deploy the API stage"
1331
+ type = bool
1332
+ default = true
1333
+ }
1334
+
1335
+ # CORS Configuration
1336
+
1337
+ variable "cors_allow_headers" {
1338
+ description = "List of allowed headers for CORS"
1339
+ type = list(string)
1340
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
1341
+ }
1342
+
1343
+ variable "cors_allow_methods" {
1344
+ description = "List of allowed HTTP methods for CORS"
1345
+ type = list(string)
1346
+ default = ["*"]
1347
+ }
1348
+
1349
+ variable "cors_allow_origins" {
1350
+ description = "List of allowed origins for CORS"
1351
+ type = list(string)
1352
+ default = ["*"]
1353
+ }
1354
+
1355
+ # Tags
1356
+
1357
+ variable "tags" {
1358
+ description = "Tags to apply to all resources"
1359
+ type = map(string)
1360
+ default = {}
1361
+ }
1362
+
1363
+ # Data sources
1364
+ data "aws_region" "current" {}
1365
+ data "aws_caller_identity" "current" {}
1366
+
1367
+ # Resources
1368
+
1369
+ # Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
1370
+
1371
+ # REST API Gateway
1372
+ resource "aws_api_gateway_rest_api" "rest_api" {
1373
+ name = var.api_name
1374
+ description = var.api_description
1375
+
1376
+ endpoint_configuration {
1377
+ types = ["REGIONAL"]
1378
+ }
1379
+
1380
+ lifecycle {
1381
+ create_before_destroy = true
1382
+ }
1383
+
1384
+ tags = var.tags
1385
+ }
1386
+
1387
+ # Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
1388
+ # after all methods and integrations are defined
1389
+
1390
+ # Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
1391
+
1392
+ # Gateway Response for CORS (4XX errors)
1393
+ resource "aws_api_gateway_gateway_response" "cors_4xx" {
1394
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
1395
+ response_type = "DEFAULT_4XX"
1396
+
1397
+ response_parameters = {
1398
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
1399
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
1400
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
1401
+ }
1402
+ }
1403
+
1404
+ # Gateway Response for CORS (5XX errors)
1405
+ resource "aws_api_gateway_gateway_response" "cors_5xx" {
1406
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
1407
+ response_type = "DEFAULT_5XX"
1408
+
1409
+ response_parameters = {
1410
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
1411
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
1412
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
1413
+ }
1414
+ }
1415
+
1416
+ # Outputs
1417
+
1418
+ output "api_id" {
1419
+ description = "ID of the REST API Gateway"
1420
+ value = aws_api_gateway_rest_api.rest_api.id
1421
+ }
1422
+
1423
+ output "api_arn" {
1424
+ description = "ARN of the REST API Gateway"
1425
+ value = aws_api_gateway_rest_api.rest_api.arn
1426
+ }
1427
+
1428
+ output "api_endpoint" {
1429
+ description = "Base URL of the REST API Gateway"
1430
+ value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
1431
+ }
1432
+
1433
+ output "api_execution_arn" {
1434
+ description = "Execution ARN of the REST API Gateway"
1435
+ value = aws_api_gateway_rest_api.rest_api.execution_arn
1436
+ }
1437
+
1438
+ output "api_root_resource_id" {
1439
+ description = "Root resource ID of the REST API Gateway"
1440
+ value = aws_api_gateway_rest_api.rest_api.root_resource_id
1441
+ }
1442
+
1443
+ # Note: Stage and deployment outputs are provided by the consuming module
1444
+
1445
+ # Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
1446
+ "test-api.tf": "terraform {
1447
+ required_version = ">= 1.0"
1448
+
1449
+ required_providers {
1450
+ aws = {
1451
+ source = "hashicorp/aws"
1452
+ version = "~> 6.0"
1453
+ }
1454
+ }
1455
+ }
1456
+
1457
+ # Authentication Configuration
1458
+ variable "user_pool_id" {
1459
+ description = "Cognito User Pool ID for authentication"
1460
+ type = string
1461
+ }
1462
+
1463
+ variable "user_pool_client_ids" {
1464
+ description = "List of Cognito User Pool Client IDs"
1465
+ type = list(string)
1466
+ }
1467
+
1468
+ variable "env" {
1469
+ description = "Environment variables for the Lambda function"
1470
+ type = map(string)
1471
+ default = {}
1472
+ }
1473
+
1474
+ variable "additional_iam_policy_statements" {
1475
+ description = "Additional IAM policy statements for the Lambda function"
1476
+ type = list(object({
1477
+ Effect = string
1478
+ Action = list(string)
1479
+ Resource = list(string)
1480
+ }))
1481
+ default = []
1482
+ }
1483
+
1484
+ # CORS Configuration (passed to core module)
1485
+
1486
+ variable "cors_allow_headers" {
1487
+ description = "List of allowed headers for CORS"
1488
+ type = list(string)
1489
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
1490
+ }
1491
+
1492
+ variable "cors_allow_methods" {
1493
+ description = "List of allowed HTTP methods for CORS"
1494
+ type = list(string)
1495
+ default = ["*"]
1496
+ }
1497
+
1498
+ variable "cors_allow_origins" {
1499
+ description = "List of allowed origins for CORS"
1500
+ type = list(string)
1501
+ default = ["*"]
1502
+ }
1503
+
1504
+ # Tags
1505
+ variable "tags" {
1506
+ description = "Tags to apply to all resources"
1507
+ type = map(string)
1508
+ default = {}
1509
+ }
1510
+
1511
+ # Get current AWS region and account ID
1512
+ data "aws_region" "current" {}
1513
+ data "aws_caller_identity" "current" {}
1514
+
1515
+ # Resources
1516
+
1517
+ # Create Lambda ZIP file from the FastAPI bundle directory
1518
+ data "archive_file" "lambda_zip" {
1519
+ type = "zip"
1520
+ source_dir = "\${path.module}/../../../../../../../dist/test-api/backend/bundle"
1521
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
1522
+ }
1523
+
1524
+ # Use the core REST API module
1525
+ module "rest_api" {
1526
+ source = "../../../core/api/rest-api"
1527
+
1528
+ api_name = "TestApi"
1529
+ api_description = "TestApi REST API"
1530
+ stage_name = "prod"
1531
+ stage_auto_deploy = true
1532
+
1533
+ # CORS Configuration
1534
+ cors_allow_headers = var.cors_allow_headers
1535
+ cors_allow_methods = var.cors_allow_methods
1536
+ cors_allow_origins = var.cors_allow_origins
1537
+
1538
+ # Tags
1539
+ tags = var.tags
1540
+ }
1541
+
1542
+ # Lambda function
1543
+ resource "aws_lambda_function" "api_lambda" {
1544
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
1545
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
1546
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
1547
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
1548
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
1549
+ filename = data.archive_file.lambda_zip.output_path
1550
+ function_name = "TestApiHandler"
1551
+ role = aws_iam_role.lambda_execution_role.arn
1552
+ handler = "index.handler"
1553
+ runtime = "nodejs22.x"
1554
+ timeout = 30
1555
+ memory_size = 128
1556
+
1557
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
1558
+
1559
+ # Enable X-Ray tracing
1560
+ tracing_config {
1561
+ mode = "Active"
1562
+ }
1563
+
1564
+ environment {
1565
+ variables = merge({
1566
+ AWS_CONNECTION_REUSE_ENABLED = "1"
1567
+ }, var.env)
1568
+ }
1569
+
1570
+ tags = var.tags
1571
+ }
1572
+
1573
+ # IAM role for Lambda execution
1574
+ resource "aws_iam_role" "lambda_execution_role" {
1575
+ name = "TestApiHandler-execution-role"
1576
+
1577
+ assume_role_policy = jsonencode({
1578
+ Version = "2012-10-17"
1579
+ Statement = [
1580
+ {
1581
+ Action = "sts:AssumeRole"
1582
+ Effect = "Allow"
1583
+ Principal = {
1584
+ Service = "lambda.amazonaws.com"
1585
+ }
1586
+ }
1587
+ ]
1588
+ })
1589
+
1590
+ tags = var.tags
1591
+ }
1592
+
1593
+ # Attach basic execution policy to Lambda role
1594
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
1595
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
1596
+ role = aws_iam_role.lambda_execution_role.name
1597
+ }
1598
+
1599
+ # Attach X-Ray tracing policy to Lambda role
1600
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
1601
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
1602
+ role = aws_iam_role.lambda_execution_role.name
1603
+ }
1604
+
1605
+ # Additional IAM policies for Lambda (if provided)
1606
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
1607
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
1608
+ name = "TestApiHandler-additional-policies"
1609
+ role = aws_iam_role.lambda_execution_role.id
1610
+
1611
+ policy = jsonencode({
1612
+ Version = "2012-10-17"
1613
+ Statement = var.additional_iam_policy_statements
1614
+ })
1615
+ }
1616
+
1617
+ # CloudWatch Log Group for Lambda
1618
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
1619
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
1620
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
1621
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
1622
+ name = "/aws/lambda/TestApiHandler"
1623
+ tags = var.tags
1624
+ }
1625
+
1626
+ # Cognito User Pool Authorizer
1627
+ resource "aws_api_gateway_authorizer" "cognito_authorizer" {
1628
+ name = "TestApiAuthorizer"
1629
+ rest_api_id = module.rest_api.api_id
1630
+ type = "COGNITO_USER_POOLS"
1631
+ provider_arns = ["arn:aws:cognito-idp:\${data.aws_region.current.name}:\${data.aws_caller_identity.current.account_id}:userpool/\${var.user_pool_id}"]
1632
+ identity_source = "method.request.header.Authorization"
1633
+ }
1634
+
1635
+ # Create proxy resource (captures all paths)
1636
+ resource "aws_api_gateway_resource" "proxy_resource" {
1637
+ rest_api_id = module.rest_api.api_id
1638
+ parent_id = module.rest_api.api_root_resource_id
1639
+ path_part = "{proxy+}"
1640
+ }
1641
+
1642
+ # Lambda integration for REST API
1643
+ resource "aws_api_gateway_integration" "lambda_integration" {
1644
+ rest_api_id = module.rest_api.api_id
1645
+ resource_id = aws_api_gateway_resource.proxy_resource.id
1646
+ http_method = aws_api_gateway_method.proxy_method.http_method
1647
+
1648
+ integration_http_method = "POST"
1649
+ type = "AWS_PROXY"
1650
+ uri = aws_lambda_function.api_lambda.invoke_arn
1651
+
1652
+ depends_on = [aws_lambda_function.api_lambda]
1653
+ }
1654
+
1655
+ # Method for proxy integration
1656
+ resource "aws_api_gateway_method" "proxy_method" {
1657
+ #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
1658
+ rest_api_id = module.rest_api.api_id
1659
+ resource_id = aws_api_gateway_resource.proxy_resource.id
1660
+ http_method = "ANY"
1661
+
1662
+ authorization = "COGNITO_USER_POOLS"
1663
+ authorizer_id = aws_api_gateway_authorizer.cognito_authorizer.id
1664
+
1665
+ request_parameters = {
1666
+ "method.request.path.proxy" = true
1667
+ }
1668
+
1669
+ depends_on = [aws_api_gateway_authorizer.cognito_authorizer]
1670
+ }
1671
+
1672
+ # OPTIONS method for CORS preflight
1673
+ resource "aws_api_gateway_method" "options_method" {
1674
+ #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
1675
+ #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
1676
+ rest_api_id = module.rest_api.api_id
1677
+ resource_id = aws_api_gateway_resource.proxy_resource.id
1678
+ http_method = "OPTIONS"
1679
+ authorization = "NONE"
1680
+ }
1681
+
1682
+ # CORS integration for OPTIONS method
1683
+ resource "aws_api_gateway_integration" "options_integration" {
1684
+ rest_api_id = module.rest_api.api_id
1685
+ resource_id = aws_api_gateway_resource.proxy_resource.id
1686
+ http_method = aws_api_gateway_method.options_method.http_method
1687
+
1688
+ type = "MOCK"
1689
+ request_templates = {
1690
+ "application/json" = "{\\"statusCode\\": 204}"
1691
+ }
1692
+ }
1693
+
1694
+ # OPTIONS method response
1695
+ resource "aws_api_gateway_method_response" "options_response" {
1696
+ rest_api_id = module.rest_api.api_id
1697
+ resource_id = aws_api_gateway_resource.proxy_resource.id
1698
+ http_method = aws_api_gateway_method.options_method.http_method
1699
+ status_code = "204"
1700
+
1701
+ response_parameters = {
1702
+ "method.response.header.Access-Control-Allow-Headers" = true
1703
+ "method.response.header.Access-Control-Allow-Methods" = true
1704
+ "method.response.header.Access-Control-Allow-Origin" = true
1705
+ }
1706
+ }
1707
+
1708
+ # OPTIONS integration response
1709
+ resource "aws_api_gateway_integration_response" "options_integration_response" {
1710
+ rest_api_id = module.rest_api.api_id
1711
+ resource_id = aws_api_gateway_resource.proxy_resource.id
1712
+ http_method = aws_api_gateway_method.options_method.http_method
1713
+ status_code = aws_api_gateway_method_response.options_response.status_code
1714
+
1715
+ response_parameters = {
1716
+ "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
1717
+ "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
1718
+ "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
1719
+ }
1720
+ }
1721
+
1722
+ # API Gateway deployment
1723
+ resource "aws_api_gateway_deployment" "api_deployment" {
1724
+ rest_api_id = module.rest_api.api_id
1725
+
1726
+ triggers = {
1727
+ redeployment = sha1(jsonencode([
1728
+ aws_api_gateway_resource.proxy_resource.id,
1729
+ aws_api_gateway_method.proxy_method.id,
1730
+ aws_api_gateway_integration.lambda_integration.id,
1731
+ aws_api_gateway_method.options_method.id,
1732
+ aws_api_gateway_integration.options_integration.id,
1733
+ ]))
1734
+ }
1735
+
1736
+ lifecycle {
1737
+ create_before_destroy = true
1738
+ }
1739
+
1740
+ depends_on = [
1741
+ aws_api_gateway_method.proxy_method,
1742
+ aws_api_gateway_integration.lambda_integration,
1743
+ aws_api_gateway_method.options_method,
1744
+ aws_api_gateway_integration.options_integration,
1745
+ aws_api_gateway_method_response.options_response,
1746
+ aws_api_gateway_integration_response.options_integration_response,
1747
+ ]
1748
+ }
1749
+
1750
+ # API Gateway stage
1751
+ resource "aws_api_gateway_stage" "api_stage" {
1752
+ #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
1753
+ #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
1754
+ #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
1755
+ #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
1756
+ deployment_id = aws_api_gateway_deployment.api_deployment.id
1757
+ rest_api_id = module.rest_api.api_id
1758
+ stage_name = "prod"
1759
+ xray_tracing_enabled = true
1760
+
1761
+ tags = var.tags
1762
+
1763
+ depends_on = [aws_api_gateway_deployment.api_deployment]
1764
+ }
1765
+
1766
+ # API Gateway Resource Policy
1767
+ resource "aws_api_gateway_rest_api_policy" "api_policy" {
1768
+ rest_api_id = module.rest_api.api_id
1769
+
1770
+ policy = jsonencode({
1771
+ Version = "2012-10-17"
1772
+ Statement = [
1773
+ {
1774
+ # Allow all callers to invoke the API in the resource policy, since auth is handled by Cognito
1775
+ Effect = "Allow"
1776
+ Principal = "*"
1777
+ Action = "execute-api:Invoke"
1778
+ Resource = "execute-api:/*"
1779
+ }
1780
+ ]
1781
+ })
1782
+ }
1783
+
1784
+ # Lambda permission for API Gateway to invoke the function
1785
+ resource "aws_lambda_permission" "api_gateway_invoke" {
1786
+ statement_id = "AllowExecutionFromAPIGateway"
1787
+ action = "lambda:InvokeFunction"
1788
+ function_name = aws_lambda_function.api_lambda.function_name
1789
+ principal = "apigateway.amazonaws.com"
1790
+ source_arn = "\${module.rest_api.api_execution_arn}/*/*"
1791
+
1792
+ depends_on = [module.rest_api, aws_lambda_function.api_lambda]
1793
+ }
1794
+
1795
+ # Add API url to runtime config
1796
+ module "add_url_to_runtime_config" {
1797
+ source = "../../../core/runtime-config/entry"
1798
+
1799
+ key_path = "apis.TestApi"
1800
+ value = aws_api_gateway_stage.api_stage.invoke_url
1801
+
1802
+ depends_on = [aws_api_gateway_stage.api_stage]
1803
+ }
1804
+
1805
+ # Outputs
1806
+
1807
+ # API Gateway Outputs (from core module)
1808
+ output "api_id" {
1809
+ description = "ID of the REST API Gateway"
1810
+ value = module.rest_api.api_id
1811
+ }
1812
+
1813
+ output "api_arn" {
1814
+ description = "ARN of the REST API Gateway"
1815
+ value = module.rest_api.api_arn
1816
+ }
1817
+
1818
+ output "api_endpoint" {
1819
+ description = "Base URL of the REST API Gateway"
1820
+ value = module.rest_api.api_endpoint
1821
+ }
1822
+
1823
+ output "api_execution_arn" {
1824
+ description = "Execution ARN of the REST API Gateway"
1825
+ value = module.rest_api.api_execution_arn
1826
+ }
1827
+
1828
+ output "stage_invoke_url" {
1829
+ description = "Invoke URL of the API Gateway stage"
1830
+ value = aws_api_gateway_stage.api_stage.invoke_url
1831
+ }
1832
+
1833
+ output "stage_arn" {
1834
+ description = "ARN of the API Gateway stage"
1835
+ value = aws_api_gateway_stage.api_stage.arn
1836
+ }
1837
+
1838
+ output "stage_execution_arn" {
1839
+ description = "Execution ARN of the API Gateway stage"
1840
+ value = aws_api_gateway_stage.api_stage.execution_arn
1841
+ }
1842
+
1843
+ output "deployment_id" {
1844
+ description = "ID of the API Gateway deployment"
1845
+ value = aws_api_gateway_deployment.api_deployment.id
1846
+ }
1847
+
1848
+ output "stage_id" {
1849
+ description = "ID of the API Gateway stage"
1850
+ value = aws_api_gateway_stage.api_stage.id
1851
+ }
1852
+
1853
+ # Lambda Function Outputs
1854
+ output "lambda_function_name" {
1855
+ description = "Name of the Lambda function"
1856
+ value = aws_lambda_function.api_lambda.function_name
1857
+ }
1858
+
1859
+ output "lambda_function_arn" {
1860
+ description = "ARN of the Lambda function"
1861
+ value = aws_lambda_function.api_lambda.arn
1862
+ }
1863
+
1864
+ output "lambda_invoke_arn" {
1865
+ description = "Invoke ARN of the Lambda function"
1866
+ value = aws_lambda_function.api_lambda.invoke_arn
1867
+ }
1868
+
1869
+ output "lambda_qualified_arn" {
1870
+ description = "Qualified ARN of the Lambda function"
1871
+ value = aws_lambda_function.api_lambda.qualified_arn
1872
+ }
1873
+
1874
+ output "lambda_version" {
1875
+ description = "Version of the Lambda function"
1876
+ value = aws_lambda_function.api_lambda.version
1877
+ }
1878
+
1879
+ output "lambda_source_code_hash" {
1880
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
1881
+ value = aws_lambda_function.api_lambda.source_code_hash
1882
+ }
1883
+
1884
+ output "lambda_source_code_size" {
1885
+ description = "Size of the Lambda deployment package in bytes"
1886
+ value = aws_lambda_function.api_lambda.source_code_size
1887
+ }
1888
+
1889
+ # IAM Role Outputs
1890
+ output "lambda_execution_role_arn" {
1891
+ description = "ARN of the Lambda execution role"
1892
+ value = aws_iam_role.lambda_execution_role.arn
1893
+ }
1894
+
1895
+ output "lambda_execution_role_name" {
1896
+ description = "Name of the Lambda execution role"
1897
+ value = aws_iam_role.lambda_execution_role.name
1898
+ }
1899
+
1900
+ # Integration Outputs
1901
+ output "integration_id" {
1902
+ description = "ID of the Lambda integration"
1903
+ value = aws_api_gateway_integration.lambda_integration.id
1904
+ }
1905
+
1906
+ output "proxy_resource_id" {
1907
+ description = "ID of the proxy resource"
1908
+ value = aws_api_gateway_resource.proxy_resource.id
1909
+ }
1910
+
1911
+ output "proxy_method_id" {
1912
+ description = "ID of the proxy method"
1913
+ value = aws_api_gateway_method.proxy_method.id
1914
+ }
1915
+
1916
+ # CloudWatch Log Groups
1917
+ output "lambda_log_group_name" {
1918
+ description = "Name of the Lambda CloudWatch log group"
1919
+ value = aws_cloudwatch_log_group.lambda_logs.name
1920
+ }
1921
+
1922
+ output "lambda_log_group_arn" {
1923
+ description = "ARN of the Lambda CloudWatch log group"
1924
+ value = aws_cloudwatch_log_group.lambda_logs.arn
1925
+ }
1926
+ ",
1927
+ }
1928
+ `;
1929
+
1930
+ exports[`tsSmithyApiGenerator > terraform iacProvider > should generate terraform files for REST API with IAM auth and snapshot them > terraform-rest-iam-files 1`] = `
1931
+ {
1932
+ "rest-api.tf": "# Core REST API Gateway module
1933
+ # This module creates the API Gateway REST API, deployment, stage, and logging resources
1934
+
1935
+ terraform {
1936
+ required_version = ">= 1.0"
1937
+
1938
+ required_providers {
1939
+ aws = {
1940
+ source = "hashicorp/aws"
1941
+ version = "~> 6.0"
1942
+ }
1943
+ }
1944
+ }
1945
+
1946
+ # Core REST API Gateway Variables
1947
+
1948
+ variable "api_name" {
1949
+ description = "Name of the REST API Gateway"
1950
+ type = string
1951
+ }
1952
+
1953
+ variable "api_description" {
1954
+ description = "Description of the REST API Gateway"
1955
+ type = string
1956
+ default = "REST API Gateway"
1957
+ }
1958
+
1959
+ variable "stage_name" {
1960
+ description = "Name of the API Gateway stage"
1961
+ type = string
1962
+ default = "prod"
1963
+ }
1964
+
1965
+ variable "stage_auto_deploy" {
1966
+ description = "Whether to automatically deploy the API stage"
1967
+ type = bool
1968
+ default = true
1969
+ }
1970
+
1971
+ # CORS Configuration
1972
+
1973
+ variable "cors_allow_headers" {
1974
+ description = "List of allowed headers for CORS"
1975
+ type = list(string)
1976
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
1977
+ }
1978
+
1979
+ variable "cors_allow_methods" {
1980
+ description = "List of allowed HTTP methods for CORS"
1981
+ type = list(string)
1982
+ default = ["*"]
1983
+ }
1984
+
1985
+ variable "cors_allow_origins" {
1986
+ description = "List of allowed origins for CORS"
1987
+ type = list(string)
1988
+ default = ["*"]
1989
+ }
1990
+
1991
+ # Tags
1992
+
1993
+ variable "tags" {
1994
+ description = "Tags to apply to all resources"
1995
+ type = map(string)
1996
+ default = {}
1997
+ }
1998
+
1999
+ # Data sources
2000
+ data "aws_region" "current" {}
2001
+ data "aws_caller_identity" "current" {}
2002
+
2003
+ # Resources
2004
+
2005
+ # Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
2006
+
2007
+ # REST API Gateway
2008
+ resource "aws_api_gateway_rest_api" "rest_api" {
2009
+ name = var.api_name
2010
+ description = var.api_description
2011
+
2012
+ endpoint_configuration {
2013
+ types = ["REGIONAL"]
2014
+ }
2015
+
2016
+ lifecycle {
2017
+ create_before_destroy = true
2018
+ }
2019
+
2020
+ tags = var.tags
2021
+ }
2022
+
2023
+ # Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
2024
+ # after all methods and integrations are defined
2025
+
2026
+ # Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
2027
+
2028
+ # Gateway Response for CORS (4XX errors)
2029
+ resource "aws_api_gateway_gateway_response" "cors_4xx" {
2030
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
2031
+ response_type = "DEFAULT_4XX"
2032
+
2033
+ response_parameters = {
2034
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
2035
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
2036
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
2037
+ }
2038
+ }
2039
+
2040
+ # Gateway Response for CORS (5XX errors)
2041
+ resource "aws_api_gateway_gateway_response" "cors_5xx" {
2042
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
2043
+ response_type = "DEFAULT_5XX"
2044
+
2045
+ response_parameters = {
2046
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
2047
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
2048
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
2049
+ }
2050
+ }
2051
+
2052
+ # Outputs
2053
+
2054
+ output "api_id" {
2055
+ description = "ID of the REST API Gateway"
2056
+ value = aws_api_gateway_rest_api.rest_api.id
2057
+ }
2058
+
2059
+ output "api_arn" {
2060
+ description = "ARN of the REST API Gateway"
2061
+ value = aws_api_gateway_rest_api.rest_api.arn
2062
+ }
2063
+
2064
+ output "api_endpoint" {
2065
+ description = "Base URL of the REST API Gateway"
2066
+ value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
2067
+ }
2068
+
2069
+ output "api_execution_arn" {
2070
+ description = "Execution ARN of the REST API Gateway"
2071
+ value = aws_api_gateway_rest_api.rest_api.execution_arn
2072
+ }
2073
+
2074
+ output "api_root_resource_id" {
2075
+ description = "Root resource ID of the REST API Gateway"
2076
+ value = aws_api_gateway_rest_api.rest_api.root_resource_id
2077
+ }
2078
+
2079
+ # Note: Stage and deployment outputs are provided by the consuming module
2080
+
2081
+ # Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
2082
+ "test-api.tf": "terraform {
2083
+ required_version = ">= 1.0"
2084
+
2085
+ required_providers {
2086
+ aws = {
2087
+ source = "hashicorp/aws"
2088
+ version = "~> 6.0"
2089
+ }
2090
+ }
2091
+ }
2092
+
2093
+
2094
+ variable "env" {
2095
+ description = "Environment variables for the Lambda function"
2096
+ type = map(string)
2097
+ default = {}
2098
+ }
2099
+
2100
+ variable "additional_iam_policy_statements" {
2101
+ description = "Additional IAM policy statements for the Lambda function"
2102
+ type = list(object({
2103
+ Effect = string
2104
+ Action = list(string)
2105
+ Resource = list(string)
2106
+ }))
2107
+ default = []
2108
+ }
2109
+
2110
+ # CORS Configuration (passed to core module)
2111
+
2112
+ variable "cors_allow_headers" {
2113
+ description = "List of allowed headers for CORS"
2114
+ type = list(string)
2115
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
2116
+ }
2117
+
2118
+ variable "cors_allow_methods" {
2119
+ description = "List of allowed HTTP methods for CORS"
2120
+ type = list(string)
2121
+ default = ["*"]
2122
+ }
2123
+
2124
+ variable "cors_allow_origins" {
2125
+ description = "List of allowed origins for CORS"
2126
+ type = list(string)
2127
+ default = ["*"]
2128
+ }
2129
+
2130
+ # Tags
2131
+ variable "tags" {
2132
+ description = "Tags to apply to all resources"
2133
+ type = map(string)
2134
+ default = {}
2135
+ }
2136
+
2137
+ # Get current AWS region and account ID
2138
+ data "aws_region" "current" {}
2139
+ data "aws_caller_identity" "current" {}
2140
+
2141
+ # Resources
2142
+
2143
+ # Create Lambda ZIP file from the FastAPI bundle directory
2144
+ data "archive_file" "lambda_zip" {
2145
+ type = "zip"
2146
+ source_dir = "\${path.module}/../../../../../../../dist/test-api/backend/bundle"
2147
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
2148
+ }
2149
+
2150
+ # Use the core REST API module
2151
+ module "rest_api" {
2152
+ source = "../../../core/api/rest-api"
2153
+
2154
+ api_name = "TestApi"
2155
+ api_description = "TestApi REST API"
2156
+ stage_name = "prod"
2157
+ stage_auto_deploy = true
2158
+
2159
+ # CORS Configuration
2160
+ cors_allow_headers = var.cors_allow_headers
2161
+ cors_allow_methods = var.cors_allow_methods
2162
+ cors_allow_origins = var.cors_allow_origins
2163
+
2164
+ # Tags
2165
+ tags = var.tags
2166
+ }
2167
+
2168
+ # Lambda function
2169
+ resource "aws_lambda_function" "api_lambda" {
2170
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
2171
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
2172
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
2173
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
2174
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
2175
+ filename = data.archive_file.lambda_zip.output_path
2176
+ function_name = "TestApiHandler"
2177
+ role = aws_iam_role.lambda_execution_role.arn
2178
+ handler = "index.handler"
2179
+ runtime = "nodejs22.x"
2180
+ timeout = 30
2181
+ memory_size = 128
2182
+
2183
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
2184
+
2185
+ # Enable X-Ray tracing
2186
+ tracing_config {
2187
+ mode = "Active"
2188
+ }
2189
+
2190
+ environment {
2191
+ variables = merge({
2192
+ AWS_CONNECTION_REUSE_ENABLED = "1"
2193
+ }, var.env)
2194
+ }
2195
+
2196
+ tags = var.tags
2197
+ }
2198
+
2199
+ # IAM role for Lambda execution
2200
+ resource "aws_iam_role" "lambda_execution_role" {
2201
+ name = "TestApiHandler-execution-role"
2202
+
2203
+ assume_role_policy = jsonencode({
2204
+ Version = "2012-10-17"
2205
+ Statement = [
2206
+ {
2207
+ Action = "sts:AssumeRole"
2208
+ Effect = "Allow"
2209
+ Principal = {
2210
+ Service = "lambda.amazonaws.com"
2211
+ }
2212
+ }
2213
+ ]
2214
+ })
2215
+
2216
+ tags = var.tags
2217
+ }
2218
+
2219
+ # Attach basic execution policy to Lambda role
2220
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
2221
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
2222
+ role = aws_iam_role.lambda_execution_role.name
2223
+ }
2224
+
2225
+ # Attach X-Ray tracing policy to Lambda role
2226
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
2227
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
2228
+ role = aws_iam_role.lambda_execution_role.name
2229
+ }
2230
+
2231
+ # Additional IAM policies for Lambda (if provided)
2232
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
2233
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
2234
+ name = "TestApiHandler-additional-policies"
2235
+ role = aws_iam_role.lambda_execution_role.id
2236
+
2237
+ policy = jsonencode({
2238
+ Version = "2012-10-17"
2239
+ Statement = var.additional_iam_policy_statements
2240
+ })
2241
+ }
2242
+
2243
+ # CloudWatch Log Group for Lambda
2244
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
2245
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
2246
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
2247
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
2248
+ name = "/aws/lambda/TestApiHandler"
2249
+ tags = var.tags
2250
+ }
2251
+
2252
+
2253
+ # Create proxy resource (captures all paths)
2254
+ resource "aws_api_gateway_resource" "proxy_resource" {
2255
+ rest_api_id = module.rest_api.api_id
2256
+ parent_id = module.rest_api.api_root_resource_id
2257
+ path_part = "{proxy+}"
2258
+ }
2259
+
2260
+ # Lambda integration for REST API
2261
+ resource "aws_api_gateway_integration" "lambda_integration" {
2262
+ rest_api_id = module.rest_api.api_id
2263
+ resource_id = aws_api_gateway_resource.proxy_resource.id
2264
+ http_method = aws_api_gateway_method.proxy_method.http_method
2265
+
2266
+ integration_http_method = "POST"
2267
+ type = "AWS_PROXY"
2268
+ uri = aws_lambda_function.api_lambda.invoke_arn
2269
+
2270
+ depends_on = [aws_lambda_function.api_lambda]
2271
+ }
2272
+
2273
+ # Method for proxy integration
2274
+ resource "aws_api_gateway_method" "proxy_method" {
2275
+ #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
2276
+ rest_api_id = module.rest_api.api_id
2277
+ resource_id = aws_api_gateway_resource.proxy_resource.id
2278
+ http_method = "ANY"
2279
+
2280
+ authorization = "AWS_IAM"
2281
+
2282
+ request_parameters = {
2283
+ "method.request.path.proxy" = true
2284
+ }
2285
+
2286
+ depends_on = []
2287
+ }
2288
+
2289
+ # OPTIONS method for CORS preflight
2290
+ resource "aws_api_gateway_method" "options_method" {
2291
+ #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
2292
+ #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
2293
+ rest_api_id = module.rest_api.api_id
2294
+ resource_id = aws_api_gateway_resource.proxy_resource.id
2295
+ http_method = "OPTIONS"
2296
+ authorization = "NONE"
2297
+ }
2298
+
2299
+ # CORS integration for OPTIONS method
2300
+ resource "aws_api_gateway_integration" "options_integration" {
2301
+ rest_api_id = module.rest_api.api_id
2302
+ resource_id = aws_api_gateway_resource.proxy_resource.id
2303
+ http_method = aws_api_gateway_method.options_method.http_method
2304
+
2305
+ type = "MOCK"
2306
+ request_templates = {
2307
+ "application/json" = "{\\"statusCode\\": 204}"
2308
+ }
2309
+ }
2310
+
2311
+ # OPTIONS method response
2312
+ resource "aws_api_gateway_method_response" "options_response" {
2313
+ rest_api_id = module.rest_api.api_id
2314
+ resource_id = aws_api_gateway_resource.proxy_resource.id
2315
+ http_method = aws_api_gateway_method.options_method.http_method
2316
+ status_code = "204"
2317
+
2318
+ response_parameters = {
2319
+ "method.response.header.Access-Control-Allow-Headers" = true
2320
+ "method.response.header.Access-Control-Allow-Methods" = true
2321
+ "method.response.header.Access-Control-Allow-Origin" = true
2322
+ }
2323
+ }
2324
+
2325
+ # OPTIONS integration response
2326
+ resource "aws_api_gateway_integration_response" "options_integration_response" {
2327
+ rest_api_id = module.rest_api.api_id
2328
+ resource_id = aws_api_gateway_resource.proxy_resource.id
2329
+ http_method = aws_api_gateway_method.options_method.http_method
2330
+ status_code = aws_api_gateway_method_response.options_response.status_code
2331
+
2332
+ response_parameters = {
2333
+ "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
2334
+ "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
2335
+ "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
2336
+ }
2337
+ }
2338
+
2339
+ # API Gateway deployment
2340
+ resource "aws_api_gateway_deployment" "api_deployment" {
2341
+ rest_api_id = module.rest_api.api_id
2342
+
2343
+ triggers = {
2344
+ redeployment = sha1(jsonencode([
2345
+ aws_api_gateway_resource.proxy_resource.id,
2346
+ aws_api_gateway_method.proxy_method.id,
2347
+ aws_api_gateway_integration.lambda_integration.id,
2348
+ aws_api_gateway_method.options_method.id,
2349
+ aws_api_gateway_integration.options_integration.id,
2350
+ ]))
2351
+ }
2352
+
2353
+ lifecycle {
2354
+ create_before_destroy = true
2355
+ }
2356
+
2357
+ depends_on = [
2358
+ aws_api_gateway_method.proxy_method,
2359
+ aws_api_gateway_integration.lambda_integration,
2360
+ aws_api_gateway_method.options_method,
2361
+ aws_api_gateway_integration.options_integration,
2362
+ aws_api_gateway_method_response.options_response,
2363
+ aws_api_gateway_integration_response.options_integration_response,
2364
+ ]
2365
+ }
2366
+
2367
+ # API Gateway stage
2368
+ resource "aws_api_gateway_stage" "api_stage" {
2369
+ #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
2370
+ #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
2371
+ #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
2372
+ #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
2373
+ deployment_id = aws_api_gateway_deployment.api_deployment.id
2374
+ rest_api_id = module.rest_api.api_id
2375
+ stage_name = "prod"
2376
+ xray_tracing_enabled = true
2377
+
2378
+ tags = var.tags
2379
+
2380
+ depends_on = [aws_api_gateway_deployment.api_deployment]
2381
+ }
2382
+
2383
+ # API Gateway Resource Policy
2384
+ resource "aws_api_gateway_rest_api_policy" "api_policy" {
2385
+ rest_api_id = module.rest_api.api_id
2386
+
2387
+ policy = jsonencode({
2388
+ Version = "2012-10-17"
2389
+ Statement = [
2390
+ {
2391
+ # Grant any AWS credentials from the account to call the API
2392
+ # Machine to machine fine-grained access can be defined here using more specific principals
2393
+ # (eg roles or users) and resources (eg which api paths may be invoked by which principal) if required
2394
+ Effect = "Allow"
2395
+ Principal = {
2396
+ AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
2397
+ }
2398
+ Action = "execute-api:Invoke"
2399
+ Resource = "execute-api:/*"
2400
+ },
2401
+ {
2402
+ # Open up OPTIONS to allow browsers to make unauthenticated preflight requests
2403
+ Effect = "Allow"
2404
+ Principal = "*"
2405
+ Action = "execute-api:Invoke"
2406
+ Resource = "execute-api:/*/OPTIONS/*"
2407
+ }
2408
+ ]
2409
+ })
2410
+ }
2411
+
2412
+ # Lambda permission for API Gateway to invoke the function
2413
+ resource "aws_lambda_permission" "api_gateway_invoke" {
2414
+ statement_id = "AllowExecutionFromAPIGateway"
2415
+ action = "lambda:InvokeFunction"
2416
+ function_name = aws_lambda_function.api_lambda.function_name
2417
+ principal = "apigateway.amazonaws.com"
2418
+ source_arn = "\${module.rest_api.api_execution_arn}/*/*"
2419
+
2420
+ depends_on = [module.rest_api, aws_lambda_function.api_lambda]
2421
+ }
2422
+
2423
+ # Add API url to runtime config
2424
+ module "add_url_to_runtime_config" {
2425
+ source = "../../../core/runtime-config/entry"
2426
+
2427
+ key_path = "apis.TestApi"
2428
+ value = aws_api_gateway_stage.api_stage.invoke_url
2429
+
2430
+ depends_on = [aws_api_gateway_stage.api_stage]
2431
+ }
2432
+
2433
+ # Outputs
2434
+
2435
+ # API Gateway Outputs (from core module)
2436
+ output "api_id" {
2437
+ description = "ID of the REST API Gateway"
2438
+ value = module.rest_api.api_id
2439
+ }
2440
+
2441
+ output "api_arn" {
2442
+ description = "ARN of the REST API Gateway"
2443
+ value = module.rest_api.api_arn
2444
+ }
2445
+
2446
+ output "api_endpoint" {
2447
+ description = "Base URL of the REST API Gateway"
2448
+ value = module.rest_api.api_endpoint
2449
+ }
2450
+
2451
+ output "api_execution_arn" {
2452
+ description = "Execution ARN of the REST API Gateway"
2453
+ value = module.rest_api.api_execution_arn
2454
+ }
2455
+
2456
+ output "stage_invoke_url" {
2457
+ description = "Invoke URL of the API Gateway stage"
2458
+ value = aws_api_gateway_stage.api_stage.invoke_url
2459
+ }
2460
+
2461
+ output "stage_arn" {
2462
+ description = "ARN of the API Gateway stage"
2463
+ value = aws_api_gateway_stage.api_stage.arn
2464
+ }
2465
+
2466
+ output "stage_execution_arn" {
2467
+ description = "Execution ARN of the API Gateway stage"
2468
+ value = aws_api_gateway_stage.api_stage.execution_arn
2469
+ }
2470
+
2471
+ output "deployment_id" {
2472
+ description = "ID of the API Gateway deployment"
2473
+ value = aws_api_gateway_deployment.api_deployment.id
2474
+ }
2475
+
2476
+ output "stage_id" {
2477
+ description = "ID of the API Gateway stage"
2478
+ value = aws_api_gateway_stage.api_stage.id
2479
+ }
2480
+
2481
+ # Lambda Function Outputs
2482
+ output "lambda_function_name" {
2483
+ description = "Name of the Lambda function"
2484
+ value = aws_lambda_function.api_lambda.function_name
2485
+ }
2486
+
2487
+ output "lambda_function_arn" {
2488
+ description = "ARN of the Lambda function"
2489
+ value = aws_lambda_function.api_lambda.arn
2490
+ }
2491
+
2492
+ output "lambda_invoke_arn" {
2493
+ description = "Invoke ARN of the Lambda function"
2494
+ value = aws_lambda_function.api_lambda.invoke_arn
2495
+ }
2496
+
2497
+ output "lambda_qualified_arn" {
2498
+ description = "Qualified ARN of the Lambda function"
2499
+ value = aws_lambda_function.api_lambda.qualified_arn
2500
+ }
2501
+
2502
+ output "lambda_version" {
2503
+ description = "Version of the Lambda function"
2504
+ value = aws_lambda_function.api_lambda.version
2505
+ }
2506
+
2507
+ output "lambda_source_code_hash" {
2508
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
2509
+ value = aws_lambda_function.api_lambda.source_code_hash
2510
+ }
2511
+
2512
+ output "lambda_source_code_size" {
2513
+ description = "Size of the Lambda deployment package in bytes"
2514
+ value = aws_lambda_function.api_lambda.source_code_size
2515
+ }
2516
+
2517
+ # IAM Role Outputs
2518
+ output "lambda_execution_role_arn" {
2519
+ description = "ARN of the Lambda execution role"
2520
+ value = aws_iam_role.lambda_execution_role.arn
2521
+ }
2522
+
2523
+ output "lambda_execution_role_name" {
2524
+ description = "Name of the Lambda execution role"
2525
+ value = aws_iam_role.lambda_execution_role.name
2526
+ }
2527
+
2528
+ # Integration Outputs
2529
+ output "integration_id" {
2530
+ description = "ID of the Lambda integration"
2531
+ value = aws_api_gateway_integration.lambda_integration.id
2532
+ }
2533
+
2534
+ output "proxy_resource_id" {
2535
+ description = "ID of the proxy resource"
2536
+ value = aws_api_gateway_resource.proxy_resource.id
2537
+ }
2538
+
2539
+ output "proxy_method_id" {
2540
+ description = "ID of the proxy method"
2541
+ value = aws_api_gateway_method.proxy_method.id
2542
+ }
2543
+
2544
+ # CloudWatch Log Groups
2545
+ output "lambda_log_group_name" {
2546
+ description = "Name of the Lambda CloudWatch log group"
2547
+ value = aws_cloudwatch_log_group.lambda_logs.name
2548
+ }
2549
+
2550
+ output "lambda_log_group_arn" {
2551
+ description = "ARN of the Lambda CloudWatch log group"
2552
+ value = aws_cloudwatch_log_group.lambda_logs.arn
2553
+ }
2554
+ ",
2555
+ }
2556
+ `;
2557
+
2558
+ exports[`tsSmithyApiGenerator > terraform iacProvider > should generate terraform with custom namespace > terraform-custom-namespace.tf 1`] = `
2559
+ "terraform {
2560
+ required_version = ">= 1.0"
2561
+
2562
+ required_providers {
2563
+ aws = {
2564
+ source = "hashicorp/aws"
2565
+ version = "~> 6.0"
2566
+ }
2567
+ }
2568
+ }
2569
+
2570
+
2571
+ variable "env" {
2572
+ description = "Environment variables for the Lambda function"
2573
+ type = map(string)
2574
+ default = {}
2575
+ }
2576
+
2577
+ variable "additional_iam_policy_statements" {
2578
+ description = "Additional IAM policy statements for the Lambda function"
2579
+ type = list(object({
2580
+ Effect = string
2581
+ Action = list(string)
2582
+ Resource = list(string)
2583
+ }))
2584
+ default = []
2585
+ }
2586
+
2587
+ # CORS Configuration (passed to core module)
2588
+
2589
+ variable "cors_allow_headers" {
2590
+ description = "List of allowed headers for CORS"
2591
+ type = list(string)
2592
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
2593
+ }
2594
+
2595
+ variable "cors_allow_methods" {
2596
+ description = "List of allowed HTTP methods for CORS"
2597
+ type = list(string)
2598
+ default = ["*"]
2599
+ }
2600
+
2601
+ variable "cors_allow_origins" {
2602
+ description = "List of allowed origins for CORS"
2603
+ type = list(string)
2604
+ default = ["*"]
2605
+ }
2606
+
2607
+ # Tags
2608
+ variable "tags" {
2609
+ description = "Tags to apply to all resources"
2610
+ type = map(string)
2611
+ default = {}
2612
+ }
2613
+
2614
+ # Get current AWS region and account ID
2615
+ data "aws_region" "current" {}
2616
+ data "aws_caller_identity" "current" {}
2617
+
2618
+ # Resources
2619
+
2620
+ # Create Lambda ZIP file from the FastAPI bundle directory
2621
+ data "archive_file" "lambda_zip" {
2622
+ type = "zip"
2623
+ source_dir = "\${path.module}/../../../../../../../dist/custom-api/backend/bundle"
2624
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/custom-api/lambda.zip"
2625
+ }
2626
+
2627
+ # Use the core REST API module
2628
+ module "rest_api" {
2629
+ source = "../../../core/api/rest-api"
2630
+
2631
+ api_name = "CustomApi"
2632
+ api_description = "CustomApi REST API"
2633
+ stage_name = "prod"
2634
+ stage_auto_deploy = true
2635
+
2636
+ # CORS Configuration
2637
+ cors_allow_headers = var.cors_allow_headers
2638
+ cors_allow_methods = var.cors_allow_methods
2639
+ cors_allow_origins = var.cors_allow_origins
2640
+
2641
+ # Tags
2642
+ tags = var.tags
2643
+ }
2644
+
2645
+ # Lambda function
2646
+ resource "aws_lambda_function" "api_lambda" {
2647
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
2648
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
2649
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
2650
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
2651
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
2652
+ filename = data.archive_file.lambda_zip.output_path
2653
+ function_name = "CustomApiHandler"
2654
+ role = aws_iam_role.lambda_execution_role.arn
2655
+ handler = "index.handler"
2656
+ runtime = "nodejs22.x"
2657
+ timeout = 30
2658
+ memory_size = 128
2659
+
2660
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
2661
+
2662
+ # Enable X-Ray tracing
2663
+ tracing_config {
2664
+ mode = "Active"
2665
+ }
2666
+
2667
+ environment {
2668
+ variables = merge({
2669
+ AWS_CONNECTION_REUSE_ENABLED = "1"
2670
+ }, var.env)
2671
+ }
2672
+
2673
+ tags = var.tags
2674
+ }
2675
+
2676
+ # IAM role for Lambda execution
2677
+ resource "aws_iam_role" "lambda_execution_role" {
2678
+ name = "CustomApiHandler-execution-role"
2679
+
2680
+ assume_role_policy = jsonencode({
2681
+ Version = "2012-10-17"
2682
+ Statement = [
2683
+ {
2684
+ Action = "sts:AssumeRole"
2685
+ Effect = "Allow"
2686
+ Principal = {
2687
+ Service = "lambda.amazonaws.com"
2688
+ }
2689
+ }
2690
+ ]
2691
+ })
2692
+
2693
+ tags = var.tags
2694
+ }
2695
+
2696
+ # Attach basic execution policy to Lambda role
2697
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
2698
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
2699
+ role = aws_iam_role.lambda_execution_role.name
2700
+ }
2701
+
2702
+ # Attach X-Ray tracing policy to Lambda role
2703
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
2704
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
2705
+ role = aws_iam_role.lambda_execution_role.name
2706
+ }
2707
+
2708
+ # Additional IAM policies for Lambda (if provided)
2709
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
2710
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
2711
+ name = "CustomApiHandler-additional-policies"
2712
+ role = aws_iam_role.lambda_execution_role.id
2713
+
2714
+ policy = jsonencode({
2715
+ Version = "2012-10-17"
2716
+ Statement = var.additional_iam_policy_statements
2717
+ })
2718
+ }
2719
+
2720
+ # CloudWatch Log Group for Lambda
2721
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
2722
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
2723
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
2724
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
2725
+ name = "/aws/lambda/CustomApiHandler"
2726
+ tags = var.tags
2727
+ }
2728
+
2729
+
2730
+ # Create proxy resource (captures all paths)
2731
+ resource "aws_api_gateway_resource" "proxy_resource" {
2732
+ rest_api_id = module.rest_api.api_id
2733
+ parent_id = module.rest_api.api_root_resource_id
2734
+ path_part = "{proxy+}"
2735
+ }
2736
+
2737
+ # Lambda integration for REST API
2738
+ resource "aws_api_gateway_integration" "lambda_integration" {
2739
+ rest_api_id = module.rest_api.api_id
2740
+ resource_id = aws_api_gateway_resource.proxy_resource.id
2741
+ http_method = aws_api_gateway_method.proxy_method.http_method
2742
+
2743
+ integration_http_method = "POST"
2744
+ type = "AWS_PROXY"
2745
+ uri = aws_lambda_function.api_lambda.invoke_arn
2746
+
2747
+ depends_on = [aws_lambda_function.api_lambda]
2748
+ }
2749
+
2750
+ # Method for proxy integration
2751
+ resource "aws_api_gateway_method" "proxy_method" {
2752
+ #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
2753
+ rest_api_id = module.rest_api.api_id
2754
+ resource_id = aws_api_gateway_resource.proxy_resource.id
2755
+ http_method = "ANY"
2756
+
2757
+ # Note: you may wish to suppress the checkov rule CKV_AWS_59 if you are absolutely sure you
2758
+ # need a public API without authentication
2759
+ authorization = "NONE"
2760
+
2761
+ request_parameters = {
2762
+ "method.request.path.proxy" = true
2763
+ }
2764
+
2765
+ depends_on = []
2766
+ }
2767
+
2768
+ # OPTIONS method for CORS preflight
2769
+ resource "aws_api_gateway_method" "options_method" {
2770
+ #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
2771
+ #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
2772
+ rest_api_id = module.rest_api.api_id
2773
+ resource_id = aws_api_gateway_resource.proxy_resource.id
2774
+ http_method = "OPTIONS"
2775
+ authorization = "NONE"
2776
+ }
2777
+
2778
+ # CORS integration for OPTIONS method
2779
+ resource "aws_api_gateway_integration" "options_integration" {
2780
+ rest_api_id = module.rest_api.api_id
2781
+ resource_id = aws_api_gateway_resource.proxy_resource.id
2782
+ http_method = aws_api_gateway_method.options_method.http_method
2783
+
2784
+ type = "MOCK"
2785
+ request_templates = {
2786
+ "application/json" = "{\\"statusCode\\": 204}"
2787
+ }
2788
+ }
2789
+
2790
+ # OPTIONS method response
2791
+ resource "aws_api_gateway_method_response" "options_response" {
2792
+ rest_api_id = module.rest_api.api_id
2793
+ resource_id = aws_api_gateway_resource.proxy_resource.id
2794
+ http_method = aws_api_gateway_method.options_method.http_method
2795
+ status_code = "204"
2796
+
2797
+ response_parameters = {
2798
+ "method.response.header.Access-Control-Allow-Headers" = true
2799
+ "method.response.header.Access-Control-Allow-Methods" = true
2800
+ "method.response.header.Access-Control-Allow-Origin" = true
2801
+ }
2802
+ }
2803
+
2804
+ # OPTIONS integration response
2805
+ resource "aws_api_gateway_integration_response" "options_integration_response" {
2806
+ rest_api_id = module.rest_api.api_id
2807
+ resource_id = aws_api_gateway_resource.proxy_resource.id
2808
+ http_method = aws_api_gateway_method.options_method.http_method
2809
+ status_code = aws_api_gateway_method_response.options_response.status_code
2810
+
2811
+ response_parameters = {
2812
+ "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
2813
+ "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
2814
+ "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
2815
+ }
2816
+ }
2817
+
2818
+ # API Gateway deployment
2819
+ resource "aws_api_gateway_deployment" "api_deployment" {
2820
+ rest_api_id = module.rest_api.api_id
2821
+
2822
+ triggers = {
2823
+ redeployment = sha1(jsonencode([
2824
+ aws_api_gateway_resource.proxy_resource.id,
2825
+ aws_api_gateway_method.proxy_method.id,
2826
+ aws_api_gateway_integration.lambda_integration.id,
2827
+ aws_api_gateway_method.options_method.id,
2828
+ aws_api_gateway_integration.options_integration.id,
2829
+ ]))
2830
+ }
2831
+
2832
+ lifecycle {
2833
+ create_before_destroy = true
2834
+ }
2835
+
2836
+ depends_on = [
2837
+ aws_api_gateway_method.proxy_method,
2838
+ aws_api_gateway_integration.lambda_integration,
2839
+ aws_api_gateway_method.options_method,
2840
+ aws_api_gateway_integration.options_integration,
2841
+ aws_api_gateway_method_response.options_response,
2842
+ aws_api_gateway_integration_response.options_integration_response,
2843
+ ]
2844
+ }
2845
+
2846
+ # API Gateway stage
2847
+ resource "aws_api_gateway_stage" "api_stage" {
2848
+ #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
2849
+ #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
2850
+ #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
2851
+ #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
2852
+ deployment_id = aws_api_gateway_deployment.api_deployment.id
2853
+ rest_api_id = module.rest_api.api_id
2854
+ stage_name = "prod"
2855
+ xray_tracing_enabled = true
2856
+
2857
+ tags = var.tags
2858
+
2859
+ depends_on = [aws_api_gateway_deployment.api_deployment]
2860
+ }
2861
+
2862
+ # API Gateway Resource Policy
2863
+ resource "aws_api_gateway_rest_api_policy" "api_policy" {
2864
+ rest_api_id = module.rest_api.api_id
2865
+
2866
+ policy = jsonencode({
2867
+ Version = "2012-10-17"
2868
+ Statement = [
2869
+ {
2870
+ # Allow all callers to invoke the API in the resource policy
2871
+ Effect = "Allow"
2872
+ Principal = "*"
2873
+ Action = "execute-api:Invoke"
2874
+ Resource = "execute-api:/*"
2875
+ }
2876
+ ]
2877
+ })
2878
+ }
2879
+
2880
+ # Lambda permission for API Gateway to invoke the function
2881
+ resource "aws_lambda_permission" "api_gateway_invoke" {
2882
+ statement_id = "AllowExecutionFromAPIGateway"
2883
+ action = "lambda:InvokeFunction"
2884
+ function_name = aws_lambda_function.api_lambda.function_name
2885
+ principal = "apigateway.amazonaws.com"
2886
+ source_arn = "\${module.rest_api.api_execution_arn}/*/*"
2887
+
2888
+ depends_on = [module.rest_api, aws_lambda_function.api_lambda]
2889
+ }
2890
+
2891
+ # Add API url to runtime config
2892
+ module "add_url_to_runtime_config" {
2893
+ source = "../../../core/runtime-config/entry"
2894
+
2895
+ key_path = "apis.CustomApi"
2896
+ value = aws_api_gateway_stage.api_stage.invoke_url
2897
+
2898
+ depends_on = [aws_api_gateway_stage.api_stage]
2899
+ }
2900
+
2901
+ # Outputs
2902
+
2903
+ # API Gateway Outputs (from core module)
2904
+ output "api_id" {
2905
+ description = "ID of the REST API Gateway"
2906
+ value = module.rest_api.api_id
2907
+ }
2908
+
2909
+ output "api_arn" {
2910
+ description = "ARN of the REST API Gateway"
2911
+ value = module.rest_api.api_arn
2912
+ }
2913
+
2914
+ output "api_endpoint" {
2915
+ description = "Base URL of the REST API Gateway"
2916
+ value = module.rest_api.api_endpoint
2917
+ }
2918
+
2919
+ output "api_execution_arn" {
2920
+ description = "Execution ARN of the REST API Gateway"
2921
+ value = module.rest_api.api_execution_arn
2922
+ }
2923
+
2924
+ output "stage_invoke_url" {
2925
+ description = "Invoke URL of the API Gateway stage"
2926
+ value = aws_api_gateway_stage.api_stage.invoke_url
2927
+ }
2928
+
2929
+ output "stage_arn" {
2930
+ description = "ARN of the API Gateway stage"
2931
+ value = aws_api_gateway_stage.api_stage.arn
2932
+ }
2933
+
2934
+ output "stage_execution_arn" {
2935
+ description = "Execution ARN of the API Gateway stage"
2936
+ value = aws_api_gateway_stage.api_stage.execution_arn
2937
+ }
2938
+
2939
+ output "deployment_id" {
2940
+ description = "ID of the API Gateway deployment"
2941
+ value = aws_api_gateway_deployment.api_deployment.id
2942
+ }
2943
+
2944
+ output "stage_id" {
2945
+ description = "ID of the API Gateway stage"
2946
+ value = aws_api_gateway_stage.api_stage.id
2947
+ }
2948
+
2949
+ # Lambda Function Outputs
2950
+ output "lambda_function_name" {
2951
+ description = "Name of the Lambda function"
2952
+ value = aws_lambda_function.api_lambda.function_name
2953
+ }
2954
+
2955
+ output "lambda_function_arn" {
2956
+ description = "ARN of the Lambda function"
2957
+ value = aws_lambda_function.api_lambda.arn
2958
+ }
2959
+
2960
+ output "lambda_invoke_arn" {
2961
+ description = "Invoke ARN of the Lambda function"
2962
+ value = aws_lambda_function.api_lambda.invoke_arn
2963
+ }
2964
+
2965
+ output "lambda_qualified_arn" {
2966
+ description = "Qualified ARN of the Lambda function"
2967
+ value = aws_lambda_function.api_lambda.qualified_arn
2968
+ }
2969
+
2970
+ output "lambda_version" {
2971
+ description = "Version of the Lambda function"
2972
+ value = aws_lambda_function.api_lambda.version
2973
+ }
2974
+
2975
+ output "lambda_source_code_hash" {
2976
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
2977
+ value = aws_lambda_function.api_lambda.source_code_hash
2978
+ }
2979
+
2980
+ output "lambda_source_code_size" {
2981
+ description = "Size of the Lambda deployment package in bytes"
2982
+ value = aws_lambda_function.api_lambda.source_code_size
2983
+ }
2984
+
2985
+ # IAM Role Outputs
2986
+ output "lambda_execution_role_arn" {
2987
+ description = "ARN of the Lambda execution role"
2988
+ value = aws_iam_role.lambda_execution_role.arn
2989
+ }
2990
+
2991
+ output "lambda_execution_role_name" {
2992
+ description = "Name of the Lambda execution role"
2993
+ value = aws_iam_role.lambda_execution_role.name
2994
+ }
2995
+
2996
+ # Integration Outputs
2997
+ output "integration_id" {
2998
+ description = "ID of the Lambda integration"
2999
+ value = aws_api_gateway_integration.lambda_integration.id
3000
+ }
3001
+
3002
+ output "proxy_resource_id" {
3003
+ description = "ID of the proxy resource"
3004
+ value = aws_api_gateway_resource.proxy_resource.id
3005
+ }
3006
+
3007
+ output "proxy_method_id" {
3008
+ description = "ID of the proxy method"
3009
+ value = aws_api_gateway_method.proxy_method.id
3010
+ }
3011
+
3012
+ # CloudWatch Log Groups
3013
+ output "lambda_log_group_name" {
3014
+ description = "Name of the Lambda CloudWatch log group"
3015
+ value = aws_cloudwatch_log_group.lambda_logs.name
3016
+ }
3017
+
3018
+ output "lambda_log_group_arn" {
3019
+ description = "ARN of the Lambda CloudWatch log group"
3020
+ value = aws_cloudwatch_log_group.lambda_logs.arn
3021
+ }
3022
+ "
3023
+ `;