@aws/nx-plugin 0.45.1 → 0.47.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 (114) hide show
  1. package/package.json +1 -1
  2. package/src/infra/app/generator.js +4 -1
  3. package/src/infra/app/generator.js.map +1 -1
  4. package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +3729 -0
  5. package/src/py/fast-api/generator.js +57 -50
  6. package/src/py/fast-api/generator.js.map +1 -1
  7. package/src/py/fast-api/react/__snapshots__/generator.spec.ts.snap +1 -0
  8. package/src/py/fast-api/schema.d.ts +1 -0
  9. package/src/py/fast-api/schema.json +8 -0
  10. package/src/py/lambda-function/__snapshots__/generator.spec.ts.snap +310 -0
  11. package/src/py/lambda-function/generator.js +15 -25
  12. package/src/py/lambda-function/generator.js.map +1 -1
  13. package/src/py/lambda-function/schema.d.ts +1 -0
  14. package/src/py/lambda-function/schema.json +8 -0
  15. package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +590 -0
  16. package/src/py/mcp-server/generator.js +4 -18
  17. package/src/py/mcp-server/generator.js.map +1 -1
  18. package/src/py/mcp-server/schema.d.ts +1 -0
  19. package/src/py/mcp-server/schema.json +8 -0
  20. package/src/py/strands-agent/__snapshots__/generator.spec.ts.snap +590 -0
  21. package/src/py/strands-agent/generator.js +4 -18
  22. package/src/py/strands-agent/generator.js.map +1 -1
  23. package/src/py/strands-agent/schema.d.ts +1 -0
  24. package/src/py/strands-agent/schema.json +8 -0
  25. package/src/terraform/project/generator.js +23 -7
  26. package/src/terraform/project/generator.js.map +1 -1
  27. package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +3729 -0
  28. package/src/trpc/backend/generator.js +6 -17
  29. package/src/trpc/backend/generator.js.map +1 -1
  30. package/src/trpc/backend/schema.d.ts +1 -0
  31. package/src/trpc/backend/schema.json +8 -0
  32. package/src/trpc/react/__snapshots__/generator.spec.ts.snap +1 -0
  33. package/src/ts/lambda-function/__snapshots__/generator.spec.ts.snap +310 -0
  34. package/src/ts/lambda-function/generator.js +15 -27
  35. package/src/ts/lambda-function/generator.js.map +1 -1
  36. package/src/ts/lambda-function/schema.d.ts +1 -0
  37. package/src/ts/lambda-function/schema.json +8 -0
  38. package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +590 -0
  39. package/src/ts/mcp-server/generator.js +4 -18
  40. package/src/ts/mcp-server/generator.js.map +1 -1
  41. package/src/ts/mcp-server/schema.d.ts +1 -0
  42. package/src/ts/mcp-server/schema.json +8 -0
  43. package/src/ts/nx-plugin/generator.js +1 -0
  44. package/src/ts/nx-plugin/generator.js.map +1 -1
  45. package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +756 -0
  46. package/src/ts/react-website/app/generator.js +42 -43
  47. package/src/ts/react-website/app/generator.js.map +1 -1
  48. package/src/ts/react-website/app/schema.d.ts +1 -0
  49. package/src/ts/react-website/app/schema.json +8 -0
  50. package/src/ts/react-website/cognito-auth/__snapshots__/generator.spec.ts.snap +259 -0
  51. package/src/ts/react-website/cognito-auth/generator.js +10 -13
  52. package/src/ts/react-website/cognito-auth/generator.js.map +1 -1
  53. package/src/ts/react-website/cognito-auth/schema.d.ts +1 -0
  54. package/src/ts/react-website/cognito-auth/schema.json +8 -0
  55. package/src/ts/react-website/runtime-config/__snapshots__/generator.spec.ts.snap +0 -40
  56. package/src/ts/react-website/runtime-config/generator.js +0 -2
  57. package/src/ts/react-website/runtime-config/generator.js.map +1 -1
  58. package/src/utils/agent-core-constructs/agent-core-constructs.d.ts +11 -5
  59. package/src/utils/agent-core-constructs/agent-core-constructs.js +51 -10
  60. package/src/utils/agent-core-constructs/agent-core-constructs.js.map +1 -1
  61. package/src/utils/agent-core-constructs/files/terraform/app/agent-core/__nameKebabCase__/__nameKebabCase__.tf.template +46 -0
  62. package/src/utils/agent-core-constructs/files/terraform/core/agent-core/runtime.tf.template +536 -0
  63. package/src/utils/api-constructs/api-constructs.d.ts +4 -4
  64. package/src/utils/api-constructs/api-constructs.js +45 -5
  65. package/src/utils/api-constructs/api-constructs.js.map +1 -1
  66. package/src/utils/api-constructs/files/terraform/app/apis/http/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +392 -0
  67. package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +518 -0
  68. package/src/utils/api-constructs/files/terraform/core/api/http/http-api/http-api.tf.template +250 -0
  69. package/src/utils/api-constructs/files/terraform/core/api/rest/rest-api/rest-api.tf.template +150 -0
  70. package/src/utils/files/terraform/src/core/runtime-config/entry/entry.tf.template +119 -0
  71. package/src/utils/files/terraform/src/core/runtime-config/read/read.tf.template +28 -0
  72. package/src/utils/files/terraform/src/metrics/metrics.tf.template +3 -2
  73. package/src/{py/lambda-function/files/common/constructs/src/app/lambda-functions/__constructFunctionKebabCase__.ts.template → utils/function-constructs/files/cdk/app/lambda-functions/__functionNameKebabCase__.ts.template} +5 -5
  74. package/src/utils/function-constructs/files/terraform/app/lambda-functions/__functionNameKebabCase__/__functionNameKebabCase__.tf.template +150 -0
  75. package/src/utils/function-constructs/function-constructs.d.ts +19 -0
  76. package/src/utils/function-constructs/function-constructs.js +57 -0
  77. package/src/utils/function-constructs/function-constructs.js.map +1 -0
  78. package/src/utils/identity-constructs/files/terraform/core/user-identity/add-callback-url/add-callback-url.tf.template +123 -0
  79. package/src/utils/identity-constructs/files/terraform/core/user-identity/identity/identity.tf.template +421 -0
  80. package/src/utils/identity-constructs/files/terraform/core/user-identity/main.tf.template +47 -0
  81. package/src/utils/identity-constructs/identity-constructs.d.ts +15 -0
  82. package/src/utils/identity-constructs/identity-constructs.js +84 -0
  83. package/src/utils/identity-constructs/identity-constructs.js.map +1 -0
  84. package/src/utils/metrics.js +1 -1
  85. package/src/utils/metrics.js.map +1 -1
  86. package/src/utils/py.d.ts +5 -0
  87. package/src/utils/py.js +9 -1
  88. package/src/utils/py.js.map +1 -1
  89. package/src/utils/shared-constructs-constants.d.ts +2 -0
  90. package/src/utils/shared-constructs-constants.js +3 -1
  91. package/src/utils/shared-constructs-constants.js.map +1 -1
  92. package/src/utils/shared-constructs.js +26 -2
  93. package/src/utils/shared-constructs.js.map +1 -1
  94. package/src/utils/versions.d.ts +3 -1
  95. package/src/utils/versions.js +2 -0
  96. package/src/utils/versions.js.map +1 -1
  97. package/src/utils/website-constructs/files/terraform/app/static-websites/__websiteNameKebabCase__/__websiteNameKebabCase__.tf.template +42 -0
  98. package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template +709 -0
  99. package/src/utils/website-constructs/website-constructs.d.ts +18 -0
  100. package/src/utils/website-constructs/website-constructs.js +61 -0
  101. package/src/utils/website-constructs/website-constructs.js.map +1 -0
  102. package/src/ts/lambda-function/files/common/constructs/src/app/lambda-functions/__constructFunctionNameKebabCase__.ts.template +0 -24
  103. /package/src/utils/agent-core-constructs/files/{app → cdk/app}/agent-core/__nameKebabCase__/Dockerfile.template +0 -0
  104. /package/src/utils/agent-core-constructs/files/{app → cdk/app}/agent-core/__nameKebabCase__/__nameKebabCase__.ts.template +0 -0
  105. /package/src/utils/agent-core-constructs/files/{core → cdk/core}/agent-core/runtime.ts.template +0 -0
  106. /package/src/utils/api-constructs/files/{app → cdk/app}/apis/http/__apiNameKebabCase__.ts.template +0 -0
  107. /package/src/utils/api-constructs/files/{app → cdk/app}/apis/rest/__apiNameKebabCase__.ts.template +0 -0
  108. /package/src/utils/api-constructs/files/{core → cdk/core}/api/http/http-api.ts.template +0 -0
  109. /package/src/utils/api-constructs/files/{core → cdk/core}/api/rest/rest-api.ts.template +0 -0
  110. /package/src/utils/api-constructs/files/{core → cdk/core}/api/trpc/trpc-utils.ts.template +0 -0
  111. /package/src/utils/api-constructs/files/{core → cdk/core}/api/utils/utils.ts.template +0 -0
  112. /package/src/{ts/react-website/cognito-auth/files/common/constructs/src → utils/identity-constructs/files/cdk}/core/user-identity.ts.template +0 -0
  113. /package/src/{ts/react-website/app/files/common/constructs/src → utils/website-constructs/files/cdk}/app/static-websites/__websiteNameKebabCase__.ts.template +0 -0
  114. /package/src/{ts/react-website/app/files/common/constructs/src → utils/website-constructs/files/cdk}/core/static-website.ts.template +0 -0
@@ -2015,3 +2015,3732 @@ export class IntegrationBuilder<
2015
2015
  }
2016
2016
  "
2017
2017
  `;
2018
+
2019
+ exports[`trpc backend generator > terraform iacProvider > should generate terraform files for HTTP API with Cognito auth and snapshot them > terraform-http-cognito-files 1`] = `
2020
+ {
2021
+ "http-api.tf": "# Core HTTP API Gateway module
2022
+ # This module creates the API Gateway HTTP API, stage, and logging resources
2023
+
2024
+ terraform {
2025
+ required_version = ">= 1.0"
2026
+
2027
+ required_providers {
2028
+ aws = {
2029
+ source = "hashicorp/aws"
2030
+ version = "~> 6.0"
2031
+ }
2032
+ }
2033
+ }
2034
+
2035
+ # Core HTTP API Gateway Variables
2036
+
2037
+ variable "api_name" {
2038
+ description = "Name of the HTTP API Gateway"
2039
+ type = string
2040
+ }
2041
+
2042
+ variable "api_description" {
2043
+ description = "Description of the HTTP API Gateway"
2044
+ type = string
2045
+ default = "HTTP API Gateway"
2046
+ }
2047
+
2048
+ variable "stage_name" {
2049
+ description = "Name of the API Gateway stage"
2050
+ type = string
2051
+ default = "prod"
2052
+ }
2053
+
2054
+ variable "stage_auto_deploy" {
2055
+ description = "Whether to automatically deploy the API stage"
2056
+ type = bool
2057
+ default = true
2058
+ }
2059
+
2060
+ # CORS Configuration
2061
+
2062
+ variable "cors_allow_credentials" {
2063
+ description = "Whether to allow credentials in CORS requests"
2064
+ type = bool
2065
+ default = false
2066
+ }
2067
+
2068
+ variable "cors_allow_headers" {
2069
+ description = "List of allowed headers for CORS"
2070
+ type = list(string)
2071
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
2072
+ }
2073
+
2074
+ variable "cors_allow_methods" {
2075
+ description = "List of allowed HTTP methods for CORS"
2076
+ type = list(string)
2077
+ default = ["*"]
2078
+ }
2079
+
2080
+ variable "cors_allow_origins" {
2081
+ description = "List of allowed origins for CORS"
2082
+ type = list(string)
2083
+ default = ["*"]
2084
+ }
2085
+
2086
+ variable "cors_expose_headers" {
2087
+ description = "List of headers to expose in CORS responses"
2088
+ type = list(string)
2089
+ default = []
2090
+ }
2091
+
2092
+ variable "cors_max_age" {
2093
+ description = "Maximum age for CORS preflight requests in seconds"
2094
+ type = number
2095
+ default = 86400
2096
+ }
2097
+
2098
+ # Tags
2099
+
2100
+ variable "tags" {
2101
+ description = "Tags to apply to all resources"
2102
+ type = map(string)
2103
+ default = {}
2104
+ }
2105
+
2106
+ # Data sources
2107
+ data "aws_region" "current" {}
2108
+ data "aws_caller_identity" "current" {}
2109
+
2110
+ # Resources
2111
+
2112
+ # KMS key for CloudWatch log group encryption
2113
+ resource "aws_kms_key" "logs_key" {
2114
+ description = "KMS key for CloudWatch log group encryption"
2115
+ deletion_window_in_days = 7
2116
+ enable_key_rotation = true
2117
+
2118
+ policy = jsonencode({
2119
+ Version = "2012-10-17"
2120
+ Statement = [
2121
+ {
2122
+ Sid = "Enable IAM User Permissions"
2123
+ Effect = "Allow"
2124
+ Principal = {
2125
+ AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
2126
+ }
2127
+ Action = "kms:*"
2128
+ Resource = "*"
2129
+ },
2130
+ {
2131
+ Sid = "Allow CloudWatch Logs"
2132
+ Effect = "Allow"
2133
+ Principal = {
2134
+ Service = "logs.\${data.aws_region.current.name}.amazonaws.com"
2135
+ }
2136
+ Action = [
2137
+ "kms:Encrypt",
2138
+ "kms:Decrypt",
2139
+ "kms:ReEncrypt*",
2140
+ "kms:GenerateDataKey*",
2141
+ "kms:DescribeKey"
2142
+ ]
2143
+ Resource = "*"
2144
+ Condition = {
2145
+ ArnEquals = {
2146
+ "kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:\${data.aws_region.current.name}:\${data.aws_caller_identity.current.account_id}:log-group:/aws/apigateway/\${var.api_name}"
2147
+ }
2148
+ }
2149
+ }
2150
+ ]
2151
+ })
2152
+
2153
+ tags = var.tags
2154
+ }
2155
+
2156
+ resource "aws_kms_alias" "logs_key_alias" {
2157
+ name = "alias/\${var.api_name}-api-logs-encryption"
2158
+ target_key_id = aws_kms_key.logs_key.key_id
2159
+ }
2160
+
2161
+ # HTTP API Gateway
2162
+ resource "aws_apigatewayv2_api" "http_api" {
2163
+ name = var.api_name
2164
+ protocol_type = "HTTP"
2165
+ description = var.api_description
2166
+
2167
+ cors_configuration {
2168
+ allow_credentials = var.cors_allow_credentials
2169
+ allow_headers = var.cors_allow_headers
2170
+ allow_methods = var.cors_allow_methods
2171
+ allow_origins = var.cors_allow_origins
2172
+ expose_headers = var.cors_expose_headers
2173
+ max_age = var.cors_max_age
2174
+ }
2175
+
2176
+ tags = var.tags
2177
+ }
2178
+
2179
+ # API Gateway stage
2180
+ resource "aws_apigatewayv2_stage" "api_stage" {
2181
+ api_id = aws_apigatewayv2_api.http_api.id
2182
+ name = var.stage_name
2183
+ auto_deploy = var.stage_auto_deploy
2184
+
2185
+ access_log_settings {
2186
+ destination_arn = aws_cloudwatch_log_group.api_logs.arn
2187
+ format = jsonencode({
2188
+ requestId = "$context.requestId"
2189
+ ip = "$context.identity.sourceIp"
2190
+ requestTime = "$context.requestTime"
2191
+ httpMethod = "$context.httpMethod"
2192
+ routeKey = "$context.routeKey"
2193
+ status = "$context.status"
2194
+ protocol = "$context.protocol"
2195
+ responseLength = "$context.responseLength"
2196
+ error = "$context.error.message"
2197
+ integrationError = "$context.integrationErrorMessage"
2198
+ })
2199
+ }
2200
+
2201
+ default_route_settings {
2202
+ throttling_burst_limit = 5000
2203
+ throttling_rate_limit = 10000
2204
+ }
2205
+
2206
+ tags = var.tags
2207
+
2208
+ depends_on = [aws_cloudwatch_log_group.api_logs]
2209
+ }
2210
+
2211
+ # CloudWatch Log Group for API Gateway
2212
+ resource "aws_cloudwatch_log_group" "api_logs" {
2213
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
2214
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
2215
+ name = "/aws/apigateway/\${var.api_name}"
2216
+ kms_key_id = aws_kms_key.logs_key.arn
2217
+ tags = var.tags
2218
+ }
2219
+
2220
+ # Outputs
2221
+
2222
+ output "api_id" {
2223
+ description = "ID of the HTTP API Gateway"
2224
+ value = aws_apigatewayv2_api.http_api.id
2225
+ }
2226
+
2227
+ output "api_arn" {
2228
+ description = "ARN of the HTTP API Gateway"
2229
+ value = aws_apigatewayv2_api.http_api.arn
2230
+ }
2231
+
2232
+ output "api_endpoint" {
2233
+ description = "Base URL of the HTTP API Gateway"
2234
+ value = aws_apigatewayv2_api.http_api.api_endpoint
2235
+ }
2236
+
2237
+ output "api_execution_arn" {
2238
+ description = "Execution ARN of the HTTP API Gateway"
2239
+ value = aws_apigatewayv2_api.http_api.execution_arn
2240
+ }
2241
+
2242
+ output "stage_id" {
2243
+ description = "ID of the API Gateway stage"
2244
+ value = aws_apigatewayv2_stage.api_stage.id
2245
+ }
2246
+
2247
+ output "stage_arn" {
2248
+ description = "ARN of the API Gateway stage"
2249
+ value = aws_apigatewayv2_stage.api_stage.arn
2250
+ }
2251
+
2252
+ output "stage_execution_arn" {
2253
+ description = "Execution ARN of the API Gateway stage"
2254
+ value = aws_apigatewayv2_stage.api_stage.execution_arn
2255
+ }
2256
+
2257
+ output "stage_invoke_url" {
2258
+ description = "Invoke URL of the API Gateway stage"
2259
+ value = aws_apigatewayv2_stage.api_stage.invoke_url
2260
+ }
2261
+
2262
+ output "api_log_group_name" {
2263
+ description = "Name of the API Gateway CloudWatch log group"
2264
+ value = aws_cloudwatch_log_group.api_logs.name
2265
+ }
2266
+
2267
+ output "api_log_group_arn" {
2268
+ description = "ARN of the API Gateway CloudWatch log group"
2269
+ value = aws_cloudwatch_log_group.api_logs.arn
2270
+ }",
2271
+ "test-api.tf": "terraform {
2272
+ required_version = ">= 1.0"
2273
+
2274
+ required_providers {
2275
+ aws = {
2276
+ source = "hashicorp/aws"
2277
+ version = "~> 6.0"
2278
+ }
2279
+ }
2280
+ }
2281
+
2282
+ # Authentication Configuration
2283
+ variable "user_pool_id" {
2284
+ description = "Cognito User Pool ID for authentication"
2285
+ type = string
2286
+ }
2287
+
2288
+ variable "user_pool_client_ids" {
2289
+ description = "List of Cognito User Pool Client IDs"
2290
+ type = list(string)
2291
+ }
2292
+
2293
+ variable "env" {
2294
+ description = "Environment variables for the Lambda function"
2295
+ type = map(string)
2296
+ default = {}
2297
+ }
2298
+
2299
+ variable "additional_iam_policy_statements" {
2300
+ description = "Additional IAM policy statements for the Lambda function"
2301
+ type = list(object({
2302
+ Effect = string
2303
+ Action = list(string)
2304
+ Resource = list(string)
2305
+ }))
2306
+ default = []
2307
+ }
2308
+
2309
+ # CORS Configuration (passed to core module)
2310
+ variable "cors_allow_credentials" {
2311
+ description = "Whether to allow credentials in CORS requests"
2312
+ type = bool
2313
+ default = false
2314
+ }
2315
+
2316
+ variable "cors_allow_headers" {
2317
+ description = "List of allowed headers for CORS"
2318
+ type = list(string)
2319
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
2320
+ }
2321
+
2322
+ variable "cors_allow_methods" {
2323
+ description = "List of allowed HTTP methods for CORS"
2324
+ type = list(string)
2325
+ default = ["*"]
2326
+ }
2327
+
2328
+ variable "cors_allow_origins" {
2329
+ description = "List of allowed origins for CORS"
2330
+ type = list(string)
2331
+ default = ["*"]
2332
+ }
2333
+
2334
+ variable "cors_expose_headers" {
2335
+ description = "List of headers to expose in CORS responses"
2336
+ type = list(string)
2337
+ default = []
2338
+ }
2339
+
2340
+ variable "cors_max_age" {
2341
+ description = "Maximum age for CORS preflight requests in seconds"
2342
+ type = number
2343
+ default = 0
2344
+ }
2345
+
2346
+ # Tags
2347
+ variable "tags" {
2348
+ description = "Tags to apply to all resources"
2349
+ type = map(string)
2350
+ default = {}
2351
+ }
2352
+
2353
+ # Get current AWS region and account ID
2354
+ data "aws_region" "current" {}
2355
+ data "aws_caller_identity" "current" {}
2356
+
2357
+ # Resources
2358
+
2359
+ # Create Lambda ZIP file from the bundle directory
2360
+ data "archive_file" "lambda_zip" {
2361
+ type = "zip"
2362
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test-api/bundle"
2363
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
2364
+ }
2365
+
2366
+
2367
+ # Use the core HTTP API module
2368
+ module "http_api" {
2369
+ source = "../../../core/api/http-api"
2370
+
2371
+ api_name = "TestApi"
2372
+ api_description = "TestApi HTTP API"
2373
+ stage_name = "$default"
2374
+ stage_auto_deploy = true
2375
+
2376
+ # CORS Configuration
2377
+ cors_allow_credentials = var.cors_allow_credentials
2378
+ cors_allow_headers = var.cors_allow_headers
2379
+ cors_allow_methods = var.cors_allow_methods
2380
+ cors_allow_origins = var.cors_allow_origins
2381
+ cors_expose_headers = var.cors_expose_headers
2382
+ cors_max_age = var.cors_max_age
2383
+
2384
+ # Tags
2385
+ tags = var.tags
2386
+ }
2387
+
2388
+ # Lambda function
2389
+ # This configures a single "router" lambda to serve all requests
2390
+ resource "aws_lambda_function" "api_lambda" {
2391
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
2392
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
2393
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
2394
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
2395
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
2396
+ filename = data.archive_file.lambda_zip.output_path
2397
+ function_name = "TestApiHandler"
2398
+ role = aws_iam_role.lambda_execution_role.arn
2399
+ handler = "index.handler"
2400
+ runtime = "nodejs22.x"
2401
+ timeout = 30
2402
+ memory_size = 128
2403
+
2404
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
2405
+
2406
+ # Enable X-Ray tracing
2407
+ tracing_config {
2408
+ mode = "Active"
2409
+ }
2410
+
2411
+ environment {
2412
+ variables = merge({
2413
+ AWS_CONNECTION_REUSE_ENABLED = "1"
2414
+ }, var.env)
2415
+ }
2416
+
2417
+ tags = var.tags
2418
+ }
2419
+
2420
+ # IAM role for Lambda execution
2421
+ resource "aws_iam_role" "lambda_execution_role" {
2422
+ name = "TestApiHandler-execution-role"
2423
+
2424
+ assume_role_policy = jsonencode({
2425
+ Version = "2012-10-17"
2426
+ Statement = [
2427
+ {
2428
+ Action = "sts:AssumeRole"
2429
+ Effect = "Allow"
2430
+ Principal = {
2431
+ Service = "lambda.amazonaws.com"
2432
+ }
2433
+ }
2434
+ ]
2435
+ })
2436
+
2437
+ tags = var.tags
2438
+ }
2439
+
2440
+ # Attach basic execution policy to Lambda role
2441
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
2442
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
2443
+ role = aws_iam_role.lambda_execution_role.name
2444
+ }
2445
+
2446
+ # Attach X-Ray tracing policy to Lambda role
2447
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
2448
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
2449
+ role = aws_iam_role.lambda_execution_role.name
2450
+ }
2451
+
2452
+ # Additional IAM policies for Lambda (if provided)
2453
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
2454
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
2455
+ name = "TestApiHandler-additional-policies"
2456
+ role = aws_iam_role.lambda_execution_role.id
2457
+
2458
+ policy = jsonencode({
2459
+ Version = "2012-10-17"
2460
+ Statement = var.additional_iam_policy_statements
2461
+ })
2462
+ }
2463
+
2464
+ # CloudWatch Log Group for Lambda
2465
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
2466
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
2467
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
2468
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
2469
+ name = "/aws/lambda/TestApiHandler"
2470
+ tags = var.tags
2471
+ }
2472
+
2473
+ # Cognito User Pool Authorizer
2474
+ resource "aws_apigatewayv2_authorizer" "cognito_authorizer" {
2475
+ api_id = module.http_api.api_id
2476
+ authorizer_type = "JWT"
2477
+ identity_sources = ["$request.header.Authorization"]
2478
+ name = "TestApiAuthorizer"
2479
+
2480
+ jwt_configuration {
2481
+ audience = var.user_pool_client_ids
2482
+ issuer = "https://cognito-idp.\${data.aws_region.current.name}.amazonaws.com/\${var.user_pool_id}"
2483
+ }
2484
+ }
2485
+
2486
+ # Lambda integration for HTTP API
2487
+ resource "aws_apigatewayv2_integration" "lambda_integration" {
2488
+ api_id = module.http_api.api_id
2489
+ integration_type = "AWS_PROXY"
2490
+ integration_uri = aws_lambda_function.api_lambda.invoke_arn
2491
+
2492
+ payload_format_version = "2.0"
2493
+ timeout_milliseconds = 30000
2494
+
2495
+ depends_on = [aws_lambda_function.api_lambda]
2496
+ }
2497
+
2498
+ # Route for proxy integration (catches all requests)
2499
+ resource "aws_apigatewayv2_route" "proxy_routes" {
2500
+ # NB: OPTIONS is omitted here since API Gateway manages responding to preflight requests
2501
+ # when cors settings are configured
2502
+ for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"])
2503
+
2504
+ api_id = module.http_api.api_id
2505
+ route_key = "\${each.key} /{proxy+}"
2506
+ target = "integrations/\${aws_apigatewayv2_integration.lambda_integration.id}"
2507
+
2508
+ authorization_type = "JWT"
2509
+ authorizer_id = aws_apigatewayv2_authorizer.cognito_authorizer.id
2510
+
2511
+ depends_on = [aws_apigatewayv2_integration.lambda_integration, aws_apigatewayv2_authorizer.cognito_authorizer]
2512
+ }
2513
+
2514
+ # Add API url to runtime config
2515
+ module "add_url_to_runtime_config" {
2516
+ source = "../../../core/runtime-config/entry"
2517
+
2518
+ key_path = "apis.TestApi"
2519
+ value = module.http_api.stage_invoke_url
2520
+
2521
+ depends_on = [module.http_api]
2522
+ }
2523
+
2524
+ # Lambda permission for API Gateway to invoke the function
2525
+ resource "aws_lambda_permission" "api_gateway_invoke" {
2526
+ statement_id = "AllowExecutionFromAPIGateway"
2527
+ action = "lambda:InvokeFunction"
2528
+ function_name = aws_lambda_function.api_lambda.function_name
2529
+ principal = "apigateway.amazonaws.com"
2530
+ source_arn = "\${module.http_api.api_execution_arn}/*/*"
2531
+
2532
+ depends_on = [module.http_api, aws_lambda_function.api_lambda]
2533
+ }
2534
+
2535
+ # Outputs
2536
+
2537
+ # API Gateway Outputs (from core module)
2538
+ output "api_id" {
2539
+ description = "ID of the HTTP API Gateway"
2540
+ value = module.http_api.api_id
2541
+ }
2542
+
2543
+ output "api_arn" {
2544
+ description = "ARN of the HTTP API Gateway"
2545
+ value = module.http_api.api_arn
2546
+ }
2547
+
2548
+ output "api_endpoint" {
2549
+ description = "Base URL of the HTTP API Gateway"
2550
+ value = module.http_api.api_endpoint
2551
+ }
2552
+
2553
+ output "api_execution_arn" {
2554
+ description = "Execution ARN of the HTTP API Gateway"
2555
+ value = module.http_api.api_execution_arn
2556
+ }
2557
+
2558
+ output "stage_invoke_url" {
2559
+ description = "Invoke URL of the API Gateway stage"
2560
+ value = module.http_api.stage_invoke_url
2561
+ }
2562
+
2563
+ output "stage_arn" {
2564
+ description = "ARN of the API Gateway stage"
2565
+ value = module.http_api.stage_arn
2566
+ }
2567
+
2568
+ output "stage_execution_arn" {
2569
+ description = "Execution ARN of the API Gateway stage"
2570
+ value = module.http_api.stage_execution_arn
2571
+ }
2572
+
2573
+ # Lambda Function Outputs
2574
+ output "lambda_function_name" {
2575
+ description = "Name of the Lambda function"
2576
+ value = aws_lambda_function.api_lambda.function_name
2577
+ }
2578
+
2579
+ output "lambda_function_arn" {
2580
+ description = "ARN of the Lambda function"
2581
+ value = aws_lambda_function.api_lambda.arn
2582
+ }
2583
+
2584
+ output "lambda_invoke_arn" {
2585
+ description = "Invoke ARN of the Lambda function"
2586
+ value = aws_lambda_function.api_lambda.invoke_arn
2587
+ }
2588
+
2589
+ output "lambda_qualified_arn" {
2590
+ description = "Qualified ARN of the Lambda function"
2591
+ value = aws_lambda_function.api_lambda.qualified_arn
2592
+ }
2593
+
2594
+ output "lambda_version" {
2595
+ description = "Version of the Lambda function"
2596
+ value = aws_lambda_function.api_lambda.version
2597
+ }
2598
+
2599
+ output "lambda_source_code_hash" {
2600
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
2601
+ value = aws_lambda_function.api_lambda.source_code_hash
2602
+ }
2603
+
2604
+ output "lambda_source_code_size" {
2605
+ description = "Size of the Lambda deployment package in bytes"
2606
+ value = aws_lambda_function.api_lambda.source_code_size
2607
+ }
2608
+
2609
+ # IAM Role Outputs
2610
+ output "lambda_execution_role_arn" {
2611
+ description = "ARN of the Lambda execution role"
2612
+ value = aws_iam_role.lambda_execution_role.arn
2613
+ }
2614
+
2615
+ output "lambda_execution_role_name" {
2616
+ description = "Name of the Lambda execution role"
2617
+ value = aws_iam_role.lambda_execution_role.name
2618
+ }
2619
+
2620
+ # Integration Outputs
2621
+ output "integration_id" {
2622
+ description = "ID of the Lambda integration"
2623
+ value = aws_apigatewayv2_integration.lambda_integration.id
2624
+ }
2625
+
2626
+ # CloudWatch Log Groups
2627
+ output "lambda_log_group_name" {
2628
+ description = "Name of the Lambda CloudWatch log group"
2629
+ value = aws_cloudwatch_log_group.lambda_logs.name
2630
+ }
2631
+
2632
+ output "lambda_log_group_arn" {
2633
+ description = "ARN of the Lambda CloudWatch log group"
2634
+ value = aws_cloudwatch_log_group.lambda_logs.arn
2635
+ }
2636
+
2637
+ output "api_log_group_name" {
2638
+ description = "Name of the API Gateway CloudWatch log group"
2639
+ value = module.http_api.api_log_group_name
2640
+ }
2641
+
2642
+ output "api_log_group_arn" {
2643
+ description = "ARN of the API Gateway CloudWatch log group"
2644
+ value = module.http_api.api_log_group_arn
2645
+ }",
2646
+ }
2647
+ `;
2648
+
2649
+ exports[`trpc backend generator > terraform iacProvider > should generate terraform files for HTTP API with IAM auth and snapshot them > terraform-http-iam-files 1`] = `
2650
+ {
2651
+ "http-api.tf": "# Core HTTP API Gateway module
2652
+ # This module creates the API Gateway HTTP API, stage, and logging resources
2653
+
2654
+ terraform {
2655
+ required_version = ">= 1.0"
2656
+
2657
+ required_providers {
2658
+ aws = {
2659
+ source = "hashicorp/aws"
2660
+ version = "~> 6.0"
2661
+ }
2662
+ }
2663
+ }
2664
+
2665
+ # Core HTTP API Gateway Variables
2666
+
2667
+ variable "api_name" {
2668
+ description = "Name of the HTTP API Gateway"
2669
+ type = string
2670
+ }
2671
+
2672
+ variable "api_description" {
2673
+ description = "Description of the HTTP API Gateway"
2674
+ type = string
2675
+ default = "HTTP API Gateway"
2676
+ }
2677
+
2678
+ variable "stage_name" {
2679
+ description = "Name of the API Gateway stage"
2680
+ type = string
2681
+ default = "prod"
2682
+ }
2683
+
2684
+ variable "stage_auto_deploy" {
2685
+ description = "Whether to automatically deploy the API stage"
2686
+ type = bool
2687
+ default = true
2688
+ }
2689
+
2690
+ # CORS Configuration
2691
+
2692
+ variable "cors_allow_credentials" {
2693
+ description = "Whether to allow credentials in CORS requests"
2694
+ type = bool
2695
+ default = false
2696
+ }
2697
+
2698
+ variable "cors_allow_headers" {
2699
+ description = "List of allowed headers for CORS"
2700
+ type = list(string)
2701
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
2702
+ }
2703
+
2704
+ variable "cors_allow_methods" {
2705
+ description = "List of allowed HTTP methods for CORS"
2706
+ type = list(string)
2707
+ default = ["*"]
2708
+ }
2709
+
2710
+ variable "cors_allow_origins" {
2711
+ description = "List of allowed origins for CORS"
2712
+ type = list(string)
2713
+ default = ["*"]
2714
+ }
2715
+
2716
+ variable "cors_expose_headers" {
2717
+ description = "List of headers to expose in CORS responses"
2718
+ type = list(string)
2719
+ default = []
2720
+ }
2721
+
2722
+ variable "cors_max_age" {
2723
+ description = "Maximum age for CORS preflight requests in seconds"
2724
+ type = number
2725
+ default = 86400
2726
+ }
2727
+
2728
+ # Tags
2729
+
2730
+ variable "tags" {
2731
+ description = "Tags to apply to all resources"
2732
+ type = map(string)
2733
+ default = {}
2734
+ }
2735
+
2736
+ # Data sources
2737
+ data "aws_region" "current" {}
2738
+ data "aws_caller_identity" "current" {}
2739
+
2740
+ # Resources
2741
+
2742
+ # KMS key for CloudWatch log group encryption
2743
+ resource "aws_kms_key" "logs_key" {
2744
+ description = "KMS key for CloudWatch log group encryption"
2745
+ deletion_window_in_days = 7
2746
+ enable_key_rotation = true
2747
+
2748
+ policy = jsonencode({
2749
+ Version = "2012-10-17"
2750
+ Statement = [
2751
+ {
2752
+ Sid = "Enable IAM User Permissions"
2753
+ Effect = "Allow"
2754
+ Principal = {
2755
+ AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
2756
+ }
2757
+ Action = "kms:*"
2758
+ Resource = "*"
2759
+ },
2760
+ {
2761
+ Sid = "Allow CloudWatch Logs"
2762
+ Effect = "Allow"
2763
+ Principal = {
2764
+ Service = "logs.\${data.aws_region.current.name}.amazonaws.com"
2765
+ }
2766
+ Action = [
2767
+ "kms:Encrypt",
2768
+ "kms:Decrypt",
2769
+ "kms:ReEncrypt*",
2770
+ "kms:GenerateDataKey*",
2771
+ "kms:DescribeKey"
2772
+ ]
2773
+ Resource = "*"
2774
+ Condition = {
2775
+ ArnEquals = {
2776
+ "kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:\${data.aws_region.current.name}:\${data.aws_caller_identity.current.account_id}:log-group:/aws/apigateway/\${var.api_name}"
2777
+ }
2778
+ }
2779
+ }
2780
+ ]
2781
+ })
2782
+
2783
+ tags = var.tags
2784
+ }
2785
+
2786
+ resource "aws_kms_alias" "logs_key_alias" {
2787
+ name = "alias/\${var.api_name}-api-logs-encryption"
2788
+ target_key_id = aws_kms_key.logs_key.key_id
2789
+ }
2790
+
2791
+ # HTTP API Gateway
2792
+ resource "aws_apigatewayv2_api" "http_api" {
2793
+ name = var.api_name
2794
+ protocol_type = "HTTP"
2795
+ description = var.api_description
2796
+
2797
+ cors_configuration {
2798
+ allow_credentials = var.cors_allow_credentials
2799
+ allow_headers = var.cors_allow_headers
2800
+ allow_methods = var.cors_allow_methods
2801
+ allow_origins = var.cors_allow_origins
2802
+ expose_headers = var.cors_expose_headers
2803
+ max_age = var.cors_max_age
2804
+ }
2805
+
2806
+ tags = var.tags
2807
+ }
2808
+
2809
+ # API Gateway stage
2810
+ resource "aws_apigatewayv2_stage" "api_stage" {
2811
+ api_id = aws_apigatewayv2_api.http_api.id
2812
+ name = var.stage_name
2813
+ auto_deploy = var.stage_auto_deploy
2814
+
2815
+ access_log_settings {
2816
+ destination_arn = aws_cloudwatch_log_group.api_logs.arn
2817
+ format = jsonencode({
2818
+ requestId = "$context.requestId"
2819
+ ip = "$context.identity.sourceIp"
2820
+ requestTime = "$context.requestTime"
2821
+ httpMethod = "$context.httpMethod"
2822
+ routeKey = "$context.routeKey"
2823
+ status = "$context.status"
2824
+ protocol = "$context.protocol"
2825
+ responseLength = "$context.responseLength"
2826
+ error = "$context.error.message"
2827
+ integrationError = "$context.integrationErrorMessage"
2828
+ })
2829
+ }
2830
+
2831
+ default_route_settings {
2832
+ throttling_burst_limit = 5000
2833
+ throttling_rate_limit = 10000
2834
+ }
2835
+
2836
+ tags = var.tags
2837
+
2838
+ depends_on = [aws_cloudwatch_log_group.api_logs]
2839
+ }
2840
+
2841
+ # CloudWatch Log Group for API Gateway
2842
+ resource "aws_cloudwatch_log_group" "api_logs" {
2843
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
2844
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
2845
+ name = "/aws/apigateway/\${var.api_name}"
2846
+ kms_key_id = aws_kms_key.logs_key.arn
2847
+ tags = var.tags
2848
+ }
2849
+
2850
+ # Outputs
2851
+
2852
+ output "api_id" {
2853
+ description = "ID of the HTTP API Gateway"
2854
+ value = aws_apigatewayv2_api.http_api.id
2855
+ }
2856
+
2857
+ output "api_arn" {
2858
+ description = "ARN of the HTTP API Gateway"
2859
+ value = aws_apigatewayv2_api.http_api.arn
2860
+ }
2861
+
2862
+ output "api_endpoint" {
2863
+ description = "Base URL of the HTTP API Gateway"
2864
+ value = aws_apigatewayv2_api.http_api.api_endpoint
2865
+ }
2866
+
2867
+ output "api_execution_arn" {
2868
+ description = "Execution ARN of the HTTP API Gateway"
2869
+ value = aws_apigatewayv2_api.http_api.execution_arn
2870
+ }
2871
+
2872
+ output "stage_id" {
2873
+ description = "ID of the API Gateway stage"
2874
+ value = aws_apigatewayv2_stage.api_stage.id
2875
+ }
2876
+
2877
+ output "stage_arn" {
2878
+ description = "ARN of the API Gateway stage"
2879
+ value = aws_apigatewayv2_stage.api_stage.arn
2880
+ }
2881
+
2882
+ output "stage_execution_arn" {
2883
+ description = "Execution ARN of the API Gateway stage"
2884
+ value = aws_apigatewayv2_stage.api_stage.execution_arn
2885
+ }
2886
+
2887
+ output "stage_invoke_url" {
2888
+ description = "Invoke URL of the API Gateway stage"
2889
+ value = aws_apigatewayv2_stage.api_stage.invoke_url
2890
+ }
2891
+
2892
+ output "api_log_group_name" {
2893
+ description = "Name of the API Gateway CloudWatch log group"
2894
+ value = aws_cloudwatch_log_group.api_logs.name
2895
+ }
2896
+
2897
+ output "api_log_group_arn" {
2898
+ description = "ARN of the API Gateway CloudWatch log group"
2899
+ value = aws_cloudwatch_log_group.api_logs.arn
2900
+ }",
2901
+ "test-api.tf": "terraform {
2902
+ required_version = ">= 1.0"
2903
+
2904
+ required_providers {
2905
+ aws = {
2906
+ source = "hashicorp/aws"
2907
+ version = "~> 6.0"
2908
+ }
2909
+ }
2910
+ }
2911
+
2912
+
2913
+ variable "env" {
2914
+ description = "Environment variables for the Lambda function"
2915
+ type = map(string)
2916
+ default = {}
2917
+ }
2918
+
2919
+ variable "additional_iam_policy_statements" {
2920
+ description = "Additional IAM policy statements for the Lambda function"
2921
+ type = list(object({
2922
+ Effect = string
2923
+ Action = list(string)
2924
+ Resource = list(string)
2925
+ }))
2926
+ default = []
2927
+ }
2928
+
2929
+ # CORS Configuration (passed to core module)
2930
+ variable "cors_allow_credentials" {
2931
+ description = "Whether to allow credentials in CORS requests"
2932
+ type = bool
2933
+ default = false
2934
+ }
2935
+
2936
+ variable "cors_allow_headers" {
2937
+ description = "List of allowed headers for CORS"
2938
+ type = list(string)
2939
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
2940
+ }
2941
+
2942
+ variable "cors_allow_methods" {
2943
+ description = "List of allowed HTTP methods for CORS"
2944
+ type = list(string)
2945
+ default = ["*"]
2946
+ }
2947
+
2948
+ variable "cors_allow_origins" {
2949
+ description = "List of allowed origins for CORS"
2950
+ type = list(string)
2951
+ default = ["*"]
2952
+ }
2953
+
2954
+ variable "cors_expose_headers" {
2955
+ description = "List of headers to expose in CORS responses"
2956
+ type = list(string)
2957
+ default = []
2958
+ }
2959
+
2960
+ variable "cors_max_age" {
2961
+ description = "Maximum age for CORS preflight requests in seconds"
2962
+ type = number
2963
+ default = 0
2964
+ }
2965
+
2966
+ # Tags
2967
+ variable "tags" {
2968
+ description = "Tags to apply to all resources"
2969
+ type = map(string)
2970
+ default = {}
2971
+ }
2972
+
2973
+ # Get current AWS region and account ID
2974
+ data "aws_region" "current" {}
2975
+ data "aws_caller_identity" "current" {}
2976
+
2977
+ # Resources
2978
+
2979
+ # Create Lambda ZIP file from the bundle directory
2980
+ data "archive_file" "lambda_zip" {
2981
+ type = "zip"
2982
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test-api/bundle"
2983
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
2984
+ }
2985
+
2986
+
2987
+ # Use the core HTTP API module
2988
+ module "http_api" {
2989
+ source = "../../../core/api/http-api"
2990
+
2991
+ api_name = "TestApi"
2992
+ api_description = "TestApi HTTP API"
2993
+ stage_name = "$default"
2994
+ stage_auto_deploy = true
2995
+
2996
+ # CORS Configuration
2997
+ cors_allow_credentials = var.cors_allow_credentials
2998
+ cors_allow_headers = var.cors_allow_headers
2999
+ cors_allow_methods = var.cors_allow_methods
3000
+ cors_allow_origins = var.cors_allow_origins
3001
+ cors_expose_headers = var.cors_expose_headers
3002
+ cors_max_age = var.cors_max_age
3003
+
3004
+ # Tags
3005
+ tags = var.tags
3006
+ }
3007
+
3008
+ # Lambda function
3009
+ # This configures a single "router" lambda to serve all requests
3010
+ resource "aws_lambda_function" "api_lambda" {
3011
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
3012
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
3013
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
3014
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
3015
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
3016
+ filename = data.archive_file.lambda_zip.output_path
3017
+ function_name = "TestApiHandler"
3018
+ role = aws_iam_role.lambda_execution_role.arn
3019
+ handler = "index.handler"
3020
+ runtime = "nodejs22.x"
3021
+ timeout = 30
3022
+ memory_size = 128
3023
+
3024
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
3025
+
3026
+ # Enable X-Ray tracing
3027
+ tracing_config {
3028
+ mode = "Active"
3029
+ }
3030
+
3031
+ environment {
3032
+ variables = merge({
3033
+ AWS_CONNECTION_REUSE_ENABLED = "1"
3034
+ }, var.env)
3035
+ }
3036
+
3037
+ tags = var.tags
3038
+ }
3039
+
3040
+ # IAM role for Lambda execution
3041
+ resource "aws_iam_role" "lambda_execution_role" {
3042
+ name = "TestApiHandler-execution-role"
3043
+
3044
+ assume_role_policy = jsonencode({
3045
+ Version = "2012-10-17"
3046
+ Statement = [
3047
+ {
3048
+ Action = "sts:AssumeRole"
3049
+ Effect = "Allow"
3050
+ Principal = {
3051
+ Service = "lambda.amazonaws.com"
3052
+ }
3053
+ }
3054
+ ]
3055
+ })
3056
+
3057
+ tags = var.tags
3058
+ }
3059
+
3060
+ # Attach basic execution policy to Lambda role
3061
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
3062
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
3063
+ role = aws_iam_role.lambda_execution_role.name
3064
+ }
3065
+
3066
+ # Attach X-Ray tracing policy to Lambda role
3067
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
3068
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
3069
+ role = aws_iam_role.lambda_execution_role.name
3070
+ }
3071
+
3072
+ # Additional IAM policies for Lambda (if provided)
3073
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
3074
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
3075
+ name = "TestApiHandler-additional-policies"
3076
+ role = aws_iam_role.lambda_execution_role.id
3077
+
3078
+ policy = jsonencode({
3079
+ Version = "2012-10-17"
3080
+ Statement = var.additional_iam_policy_statements
3081
+ })
3082
+ }
3083
+
3084
+ # CloudWatch Log Group for Lambda
3085
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
3086
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
3087
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
3088
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
3089
+ name = "/aws/lambda/TestApiHandler"
3090
+ tags = var.tags
3091
+ }
3092
+
3093
+
3094
+ # Lambda integration for HTTP API
3095
+ resource "aws_apigatewayv2_integration" "lambda_integration" {
3096
+ api_id = module.http_api.api_id
3097
+ integration_type = "AWS_PROXY"
3098
+ integration_uri = aws_lambda_function.api_lambda.invoke_arn
3099
+
3100
+ payload_format_version = "2.0"
3101
+ timeout_milliseconds = 30000
3102
+
3103
+ depends_on = [aws_lambda_function.api_lambda]
3104
+ }
3105
+
3106
+ # Route for proxy integration (catches all requests)
3107
+ resource "aws_apigatewayv2_route" "proxy_routes" {
3108
+ # NB: OPTIONS is omitted here since API Gateway manages responding to preflight requests
3109
+ # when cors settings are configured
3110
+ for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"])
3111
+
3112
+ api_id = module.http_api.api_id
3113
+ route_key = "\${each.key} /{proxy+}"
3114
+ target = "integrations/\${aws_apigatewayv2_integration.lambda_integration.id}"
3115
+
3116
+ authorization_type = "AWS_IAM"
3117
+
3118
+ depends_on = [aws_apigatewayv2_integration.lambda_integration]
3119
+ }
3120
+
3121
+ # Add API url to runtime config
3122
+ module "add_url_to_runtime_config" {
3123
+ source = "../../../core/runtime-config/entry"
3124
+
3125
+ key_path = "apis.TestApi"
3126
+ value = module.http_api.stage_invoke_url
3127
+
3128
+ depends_on = [module.http_api]
3129
+ }
3130
+
3131
+ # Lambda permission for API Gateway to invoke the function
3132
+ resource "aws_lambda_permission" "api_gateway_invoke" {
3133
+ statement_id = "AllowExecutionFromAPIGateway"
3134
+ action = "lambda:InvokeFunction"
3135
+ function_name = aws_lambda_function.api_lambda.function_name
3136
+ principal = "apigateway.amazonaws.com"
3137
+ source_arn = "\${module.http_api.api_execution_arn}/*/*"
3138
+
3139
+ depends_on = [module.http_api, aws_lambda_function.api_lambda]
3140
+ }
3141
+
3142
+ # Outputs
3143
+
3144
+ # API Gateway Outputs (from core module)
3145
+ output "api_id" {
3146
+ description = "ID of the HTTP API Gateway"
3147
+ value = module.http_api.api_id
3148
+ }
3149
+
3150
+ output "api_arn" {
3151
+ description = "ARN of the HTTP API Gateway"
3152
+ value = module.http_api.api_arn
3153
+ }
3154
+
3155
+ output "api_endpoint" {
3156
+ description = "Base URL of the HTTP API Gateway"
3157
+ value = module.http_api.api_endpoint
3158
+ }
3159
+
3160
+ output "api_execution_arn" {
3161
+ description = "Execution ARN of the HTTP API Gateway"
3162
+ value = module.http_api.api_execution_arn
3163
+ }
3164
+
3165
+ output "stage_invoke_url" {
3166
+ description = "Invoke URL of the API Gateway stage"
3167
+ value = module.http_api.stage_invoke_url
3168
+ }
3169
+
3170
+ output "stage_arn" {
3171
+ description = "ARN of the API Gateway stage"
3172
+ value = module.http_api.stage_arn
3173
+ }
3174
+
3175
+ output "stage_execution_arn" {
3176
+ description = "Execution ARN of the API Gateway stage"
3177
+ value = module.http_api.stage_execution_arn
3178
+ }
3179
+
3180
+ # Lambda Function Outputs
3181
+ output "lambda_function_name" {
3182
+ description = "Name of the Lambda function"
3183
+ value = aws_lambda_function.api_lambda.function_name
3184
+ }
3185
+
3186
+ output "lambda_function_arn" {
3187
+ description = "ARN of the Lambda function"
3188
+ value = aws_lambda_function.api_lambda.arn
3189
+ }
3190
+
3191
+ output "lambda_invoke_arn" {
3192
+ description = "Invoke ARN of the Lambda function"
3193
+ value = aws_lambda_function.api_lambda.invoke_arn
3194
+ }
3195
+
3196
+ output "lambda_qualified_arn" {
3197
+ description = "Qualified ARN of the Lambda function"
3198
+ value = aws_lambda_function.api_lambda.qualified_arn
3199
+ }
3200
+
3201
+ output "lambda_version" {
3202
+ description = "Version of the Lambda function"
3203
+ value = aws_lambda_function.api_lambda.version
3204
+ }
3205
+
3206
+ output "lambda_source_code_hash" {
3207
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
3208
+ value = aws_lambda_function.api_lambda.source_code_hash
3209
+ }
3210
+
3211
+ output "lambda_source_code_size" {
3212
+ description = "Size of the Lambda deployment package in bytes"
3213
+ value = aws_lambda_function.api_lambda.source_code_size
3214
+ }
3215
+
3216
+ # IAM Role Outputs
3217
+ output "lambda_execution_role_arn" {
3218
+ description = "ARN of the Lambda execution role"
3219
+ value = aws_iam_role.lambda_execution_role.arn
3220
+ }
3221
+
3222
+ output "lambda_execution_role_name" {
3223
+ description = "Name of the Lambda execution role"
3224
+ value = aws_iam_role.lambda_execution_role.name
3225
+ }
3226
+
3227
+ # Integration Outputs
3228
+ output "integration_id" {
3229
+ description = "ID of the Lambda integration"
3230
+ value = aws_apigatewayv2_integration.lambda_integration.id
3231
+ }
3232
+
3233
+ # CloudWatch Log Groups
3234
+ output "lambda_log_group_name" {
3235
+ description = "Name of the Lambda CloudWatch log group"
3236
+ value = aws_cloudwatch_log_group.lambda_logs.name
3237
+ }
3238
+
3239
+ output "lambda_log_group_arn" {
3240
+ description = "ARN of the Lambda CloudWatch log group"
3241
+ value = aws_cloudwatch_log_group.lambda_logs.arn
3242
+ }
3243
+
3244
+ output "api_log_group_name" {
3245
+ description = "Name of the API Gateway CloudWatch log group"
3246
+ value = module.http_api.api_log_group_name
3247
+ }
3248
+
3249
+ output "api_log_group_arn" {
3250
+ description = "ARN of the API Gateway CloudWatch log group"
3251
+ value = module.http_api.api_log_group_arn
3252
+ }",
3253
+ }
3254
+ `;
3255
+
3256
+ exports[`trpc backend generator > terraform iacProvider > should generate terraform files for HTTP API with None auth and snapshot them > terraform-http-none-files 1`] = `
3257
+ {
3258
+ "http-api.tf": "# Core HTTP API Gateway module
3259
+ # This module creates the API Gateway HTTP API, stage, and logging resources
3260
+
3261
+ terraform {
3262
+ required_version = ">= 1.0"
3263
+
3264
+ required_providers {
3265
+ aws = {
3266
+ source = "hashicorp/aws"
3267
+ version = "~> 6.0"
3268
+ }
3269
+ }
3270
+ }
3271
+
3272
+ # Core HTTP API Gateway Variables
3273
+
3274
+ variable "api_name" {
3275
+ description = "Name of the HTTP API Gateway"
3276
+ type = string
3277
+ }
3278
+
3279
+ variable "api_description" {
3280
+ description = "Description of the HTTP API Gateway"
3281
+ type = string
3282
+ default = "HTTP API Gateway"
3283
+ }
3284
+
3285
+ variable "stage_name" {
3286
+ description = "Name of the API Gateway stage"
3287
+ type = string
3288
+ default = "prod"
3289
+ }
3290
+
3291
+ variable "stage_auto_deploy" {
3292
+ description = "Whether to automatically deploy the API stage"
3293
+ type = bool
3294
+ default = true
3295
+ }
3296
+
3297
+ # CORS Configuration
3298
+
3299
+ variable "cors_allow_credentials" {
3300
+ description = "Whether to allow credentials in CORS requests"
3301
+ type = bool
3302
+ default = false
3303
+ }
3304
+
3305
+ variable "cors_allow_headers" {
3306
+ description = "List of allowed headers for CORS"
3307
+ type = list(string)
3308
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
3309
+ }
3310
+
3311
+ variable "cors_allow_methods" {
3312
+ description = "List of allowed HTTP methods for CORS"
3313
+ type = list(string)
3314
+ default = ["*"]
3315
+ }
3316
+
3317
+ variable "cors_allow_origins" {
3318
+ description = "List of allowed origins for CORS"
3319
+ type = list(string)
3320
+ default = ["*"]
3321
+ }
3322
+
3323
+ variable "cors_expose_headers" {
3324
+ description = "List of headers to expose in CORS responses"
3325
+ type = list(string)
3326
+ default = []
3327
+ }
3328
+
3329
+ variable "cors_max_age" {
3330
+ description = "Maximum age for CORS preflight requests in seconds"
3331
+ type = number
3332
+ default = 86400
3333
+ }
3334
+
3335
+ # Tags
3336
+
3337
+ variable "tags" {
3338
+ description = "Tags to apply to all resources"
3339
+ type = map(string)
3340
+ default = {}
3341
+ }
3342
+
3343
+ # Data sources
3344
+ data "aws_region" "current" {}
3345
+ data "aws_caller_identity" "current" {}
3346
+
3347
+ # Resources
3348
+
3349
+ # KMS key for CloudWatch log group encryption
3350
+ resource "aws_kms_key" "logs_key" {
3351
+ description = "KMS key for CloudWatch log group encryption"
3352
+ deletion_window_in_days = 7
3353
+ enable_key_rotation = true
3354
+
3355
+ policy = jsonencode({
3356
+ Version = "2012-10-17"
3357
+ Statement = [
3358
+ {
3359
+ Sid = "Enable IAM User Permissions"
3360
+ Effect = "Allow"
3361
+ Principal = {
3362
+ AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
3363
+ }
3364
+ Action = "kms:*"
3365
+ Resource = "*"
3366
+ },
3367
+ {
3368
+ Sid = "Allow CloudWatch Logs"
3369
+ Effect = "Allow"
3370
+ Principal = {
3371
+ Service = "logs.\${data.aws_region.current.name}.amazonaws.com"
3372
+ }
3373
+ Action = [
3374
+ "kms:Encrypt",
3375
+ "kms:Decrypt",
3376
+ "kms:ReEncrypt*",
3377
+ "kms:GenerateDataKey*",
3378
+ "kms:DescribeKey"
3379
+ ]
3380
+ Resource = "*"
3381
+ Condition = {
3382
+ ArnEquals = {
3383
+ "kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:\${data.aws_region.current.name}:\${data.aws_caller_identity.current.account_id}:log-group:/aws/apigateway/\${var.api_name}"
3384
+ }
3385
+ }
3386
+ }
3387
+ ]
3388
+ })
3389
+
3390
+ tags = var.tags
3391
+ }
3392
+
3393
+ resource "aws_kms_alias" "logs_key_alias" {
3394
+ name = "alias/\${var.api_name}-api-logs-encryption"
3395
+ target_key_id = aws_kms_key.logs_key.key_id
3396
+ }
3397
+
3398
+ # HTTP API Gateway
3399
+ resource "aws_apigatewayv2_api" "http_api" {
3400
+ name = var.api_name
3401
+ protocol_type = "HTTP"
3402
+ description = var.api_description
3403
+
3404
+ cors_configuration {
3405
+ allow_credentials = var.cors_allow_credentials
3406
+ allow_headers = var.cors_allow_headers
3407
+ allow_methods = var.cors_allow_methods
3408
+ allow_origins = var.cors_allow_origins
3409
+ expose_headers = var.cors_expose_headers
3410
+ max_age = var.cors_max_age
3411
+ }
3412
+
3413
+ tags = var.tags
3414
+ }
3415
+
3416
+ # API Gateway stage
3417
+ resource "aws_apigatewayv2_stage" "api_stage" {
3418
+ api_id = aws_apigatewayv2_api.http_api.id
3419
+ name = var.stage_name
3420
+ auto_deploy = var.stage_auto_deploy
3421
+
3422
+ access_log_settings {
3423
+ destination_arn = aws_cloudwatch_log_group.api_logs.arn
3424
+ format = jsonencode({
3425
+ requestId = "$context.requestId"
3426
+ ip = "$context.identity.sourceIp"
3427
+ requestTime = "$context.requestTime"
3428
+ httpMethod = "$context.httpMethod"
3429
+ routeKey = "$context.routeKey"
3430
+ status = "$context.status"
3431
+ protocol = "$context.protocol"
3432
+ responseLength = "$context.responseLength"
3433
+ error = "$context.error.message"
3434
+ integrationError = "$context.integrationErrorMessage"
3435
+ })
3436
+ }
3437
+
3438
+ default_route_settings {
3439
+ throttling_burst_limit = 5000
3440
+ throttling_rate_limit = 10000
3441
+ }
3442
+
3443
+ tags = var.tags
3444
+
3445
+ depends_on = [aws_cloudwatch_log_group.api_logs]
3446
+ }
3447
+
3448
+ # CloudWatch Log Group for API Gateway
3449
+ resource "aws_cloudwatch_log_group" "api_logs" {
3450
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
3451
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
3452
+ name = "/aws/apigateway/\${var.api_name}"
3453
+ kms_key_id = aws_kms_key.logs_key.arn
3454
+ tags = var.tags
3455
+ }
3456
+
3457
+ # Outputs
3458
+
3459
+ output "api_id" {
3460
+ description = "ID of the HTTP API Gateway"
3461
+ value = aws_apigatewayv2_api.http_api.id
3462
+ }
3463
+
3464
+ output "api_arn" {
3465
+ description = "ARN of the HTTP API Gateway"
3466
+ value = aws_apigatewayv2_api.http_api.arn
3467
+ }
3468
+
3469
+ output "api_endpoint" {
3470
+ description = "Base URL of the HTTP API Gateway"
3471
+ value = aws_apigatewayv2_api.http_api.api_endpoint
3472
+ }
3473
+
3474
+ output "api_execution_arn" {
3475
+ description = "Execution ARN of the HTTP API Gateway"
3476
+ value = aws_apigatewayv2_api.http_api.execution_arn
3477
+ }
3478
+
3479
+ output "stage_id" {
3480
+ description = "ID of the API Gateway stage"
3481
+ value = aws_apigatewayv2_stage.api_stage.id
3482
+ }
3483
+
3484
+ output "stage_arn" {
3485
+ description = "ARN of the API Gateway stage"
3486
+ value = aws_apigatewayv2_stage.api_stage.arn
3487
+ }
3488
+
3489
+ output "stage_execution_arn" {
3490
+ description = "Execution ARN of the API Gateway stage"
3491
+ value = aws_apigatewayv2_stage.api_stage.execution_arn
3492
+ }
3493
+
3494
+ output "stage_invoke_url" {
3495
+ description = "Invoke URL of the API Gateway stage"
3496
+ value = aws_apigatewayv2_stage.api_stage.invoke_url
3497
+ }
3498
+
3499
+ output "api_log_group_name" {
3500
+ description = "Name of the API Gateway CloudWatch log group"
3501
+ value = aws_cloudwatch_log_group.api_logs.name
3502
+ }
3503
+
3504
+ output "api_log_group_arn" {
3505
+ description = "ARN of the API Gateway CloudWatch log group"
3506
+ value = aws_cloudwatch_log_group.api_logs.arn
3507
+ }",
3508
+ "test-api.tf": "terraform {
3509
+ required_version = ">= 1.0"
3510
+
3511
+ required_providers {
3512
+ aws = {
3513
+ source = "hashicorp/aws"
3514
+ version = "~> 6.0"
3515
+ }
3516
+ }
3517
+ }
3518
+
3519
+
3520
+ variable "env" {
3521
+ description = "Environment variables for the Lambda function"
3522
+ type = map(string)
3523
+ default = {}
3524
+ }
3525
+
3526
+ variable "additional_iam_policy_statements" {
3527
+ description = "Additional IAM policy statements for the Lambda function"
3528
+ type = list(object({
3529
+ Effect = string
3530
+ Action = list(string)
3531
+ Resource = list(string)
3532
+ }))
3533
+ default = []
3534
+ }
3535
+
3536
+ # CORS Configuration (passed to core module)
3537
+ variable "cors_allow_credentials" {
3538
+ description = "Whether to allow credentials in CORS requests"
3539
+ type = bool
3540
+ default = false
3541
+ }
3542
+
3543
+ variable "cors_allow_headers" {
3544
+ description = "List of allowed headers for CORS"
3545
+ type = list(string)
3546
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
3547
+ }
3548
+
3549
+ variable "cors_allow_methods" {
3550
+ description = "List of allowed HTTP methods for CORS"
3551
+ type = list(string)
3552
+ default = ["*"]
3553
+ }
3554
+
3555
+ variable "cors_allow_origins" {
3556
+ description = "List of allowed origins for CORS"
3557
+ type = list(string)
3558
+ default = ["*"]
3559
+ }
3560
+
3561
+ variable "cors_expose_headers" {
3562
+ description = "List of headers to expose in CORS responses"
3563
+ type = list(string)
3564
+ default = []
3565
+ }
3566
+
3567
+ variable "cors_max_age" {
3568
+ description = "Maximum age for CORS preflight requests in seconds"
3569
+ type = number
3570
+ default = 0
3571
+ }
3572
+
3573
+ # Tags
3574
+ variable "tags" {
3575
+ description = "Tags to apply to all resources"
3576
+ type = map(string)
3577
+ default = {}
3578
+ }
3579
+
3580
+ # Get current AWS region and account ID
3581
+ data "aws_region" "current" {}
3582
+ data "aws_caller_identity" "current" {}
3583
+
3584
+ # Resources
3585
+
3586
+ # Create Lambda ZIP file from the bundle directory
3587
+ data "archive_file" "lambda_zip" {
3588
+ type = "zip"
3589
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test-api/bundle"
3590
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
3591
+ }
3592
+
3593
+
3594
+ # Use the core HTTP API module
3595
+ module "http_api" {
3596
+ source = "../../../core/api/http-api"
3597
+
3598
+ api_name = "TestApi"
3599
+ api_description = "TestApi HTTP API"
3600
+ stage_name = "$default"
3601
+ stage_auto_deploy = true
3602
+
3603
+ # CORS Configuration
3604
+ cors_allow_credentials = var.cors_allow_credentials
3605
+ cors_allow_headers = var.cors_allow_headers
3606
+ cors_allow_methods = var.cors_allow_methods
3607
+ cors_allow_origins = var.cors_allow_origins
3608
+ cors_expose_headers = var.cors_expose_headers
3609
+ cors_max_age = var.cors_max_age
3610
+
3611
+ # Tags
3612
+ tags = var.tags
3613
+ }
3614
+
3615
+ # Lambda function
3616
+ # This configures a single "router" lambda to serve all requests
3617
+ resource "aws_lambda_function" "api_lambda" {
3618
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
3619
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
3620
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
3621
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
3622
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
3623
+ filename = data.archive_file.lambda_zip.output_path
3624
+ function_name = "TestApiHandler"
3625
+ role = aws_iam_role.lambda_execution_role.arn
3626
+ handler = "index.handler"
3627
+ runtime = "nodejs22.x"
3628
+ timeout = 30
3629
+ memory_size = 128
3630
+
3631
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
3632
+
3633
+ # Enable X-Ray tracing
3634
+ tracing_config {
3635
+ mode = "Active"
3636
+ }
3637
+
3638
+ environment {
3639
+ variables = merge({
3640
+ AWS_CONNECTION_REUSE_ENABLED = "1"
3641
+ }, var.env)
3642
+ }
3643
+
3644
+ tags = var.tags
3645
+ }
3646
+
3647
+ # IAM role for Lambda execution
3648
+ resource "aws_iam_role" "lambda_execution_role" {
3649
+ name = "TestApiHandler-execution-role"
3650
+
3651
+ assume_role_policy = jsonencode({
3652
+ Version = "2012-10-17"
3653
+ Statement = [
3654
+ {
3655
+ Action = "sts:AssumeRole"
3656
+ Effect = "Allow"
3657
+ Principal = {
3658
+ Service = "lambda.amazonaws.com"
3659
+ }
3660
+ }
3661
+ ]
3662
+ })
3663
+
3664
+ tags = var.tags
3665
+ }
3666
+
3667
+ # Attach basic execution policy to Lambda role
3668
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
3669
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
3670
+ role = aws_iam_role.lambda_execution_role.name
3671
+ }
3672
+
3673
+ # Attach X-Ray tracing policy to Lambda role
3674
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
3675
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
3676
+ role = aws_iam_role.lambda_execution_role.name
3677
+ }
3678
+
3679
+ # Additional IAM policies for Lambda (if provided)
3680
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
3681
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
3682
+ name = "TestApiHandler-additional-policies"
3683
+ role = aws_iam_role.lambda_execution_role.id
3684
+
3685
+ policy = jsonencode({
3686
+ Version = "2012-10-17"
3687
+ Statement = var.additional_iam_policy_statements
3688
+ })
3689
+ }
3690
+
3691
+ # CloudWatch Log Group for Lambda
3692
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
3693
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
3694
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
3695
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
3696
+ name = "/aws/lambda/TestApiHandler"
3697
+ tags = var.tags
3698
+ }
3699
+
3700
+
3701
+ # Lambda integration for HTTP API
3702
+ resource "aws_apigatewayv2_integration" "lambda_integration" {
3703
+ api_id = module.http_api.api_id
3704
+ integration_type = "AWS_PROXY"
3705
+ integration_uri = aws_lambda_function.api_lambda.invoke_arn
3706
+
3707
+ payload_format_version = "2.0"
3708
+ timeout_milliseconds = 30000
3709
+
3710
+ depends_on = [aws_lambda_function.api_lambda]
3711
+ }
3712
+
3713
+ # Route for proxy integration (catches all requests)
3714
+ resource "aws_apigatewayv2_route" "proxy_routes" {
3715
+ # NB: OPTIONS is omitted here since API Gateway manages responding to preflight requests
3716
+ # when cors settings are configured
3717
+ for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"])
3718
+
3719
+ api_id = module.http_api.api_id
3720
+ route_key = "\${each.key} /{proxy+}"
3721
+ target = "integrations/\${aws_apigatewayv2_integration.lambda_integration.id}"
3722
+
3723
+ # Note: you may wish to suppress the checkov rule CKV_AWS_309 if you are absolutely sure you
3724
+ # need a public API without authentication
3725
+ authorization_type = "NONE"
3726
+
3727
+ depends_on = [aws_apigatewayv2_integration.lambda_integration]
3728
+ }
3729
+
3730
+ # Add API url to runtime config
3731
+ module "add_url_to_runtime_config" {
3732
+ source = "../../../core/runtime-config/entry"
3733
+
3734
+ key_path = "apis.TestApi"
3735
+ value = module.http_api.stage_invoke_url
3736
+
3737
+ depends_on = [module.http_api]
3738
+ }
3739
+
3740
+ # Lambda permission for API Gateway to invoke the function
3741
+ resource "aws_lambda_permission" "api_gateway_invoke" {
3742
+ statement_id = "AllowExecutionFromAPIGateway"
3743
+ action = "lambda:InvokeFunction"
3744
+ function_name = aws_lambda_function.api_lambda.function_name
3745
+ principal = "apigateway.amazonaws.com"
3746
+ source_arn = "\${module.http_api.api_execution_arn}/*/*"
3747
+
3748
+ depends_on = [module.http_api, aws_lambda_function.api_lambda]
3749
+ }
3750
+
3751
+ # Outputs
3752
+
3753
+ # API Gateway Outputs (from core module)
3754
+ output "api_id" {
3755
+ description = "ID of the HTTP API Gateway"
3756
+ value = module.http_api.api_id
3757
+ }
3758
+
3759
+ output "api_arn" {
3760
+ description = "ARN of the HTTP API Gateway"
3761
+ value = module.http_api.api_arn
3762
+ }
3763
+
3764
+ output "api_endpoint" {
3765
+ description = "Base URL of the HTTP API Gateway"
3766
+ value = module.http_api.api_endpoint
3767
+ }
3768
+
3769
+ output "api_execution_arn" {
3770
+ description = "Execution ARN of the HTTP API Gateway"
3771
+ value = module.http_api.api_execution_arn
3772
+ }
3773
+
3774
+ output "stage_invoke_url" {
3775
+ description = "Invoke URL of the API Gateway stage"
3776
+ value = module.http_api.stage_invoke_url
3777
+ }
3778
+
3779
+ output "stage_arn" {
3780
+ description = "ARN of the API Gateway stage"
3781
+ value = module.http_api.stage_arn
3782
+ }
3783
+
3784
+ output "stage_execution_arn" {
3785
+ description = "Execution ARN of the API Gateway stage"
3786
+ value = module.http_api.stage_execution_arn
3787
+ }
3788
+
3789
+ # Lambda Function Outputs
3790
+ output "lambda_function_name" {
3791
+ description = "Name of the Lambda function"
3792
+ value = aws_lambda_function.api_lambda.function_name
3793
+ }
3794
+
3795
+ output "lambda_function_arn" {
3796
+ description = "ARN of the Lambda function"
3797
+ value = aws_lambda_function.api_lambda.arn
3798
+ }
3799
+
3800
+ output "lambda_invoke_arn" {
3801
+ description = "Invoke ARN of the Lambda function"
3802
+ value = aws_lambda_function.api_lambda.invoke_arn
3803
+ }
3804
+
3805
+ output "lambda_qualified_arn" {
3806
+ description = "Qualified ARN of the Lambda function"
3807
+ value = aws_lambda_function.api_lambda.qualified_arn
3808
+ }
3809
+
3810
+ output "lambda_version" {
3811
+ description = "Version of the Lambda function"
3812
+ value = aws_lambda_function.api_lambda.version
3813
+ }
3814
+
3815
+ output "lambda_source_code_hash" {
3816
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
3817
+ value = aws_lambda_function.api_lambda.source_code_hash
3818
+ }
3819
+
3820
+ output "lambda_source_code_size" {
3821
+ description = "Size of the Lambda deployment package in bytes"
3822
+ value = aws_lambda_function.api_lambda.source_code_size
3823
+ }
3824
+
3825
+ # IAM Role Outputs
3826
+ output "lambda_execution_role_arn" {
3827
+ description = "ARN of the Lambda execution role"
3828
+ value = aws_iam_role.lambda_execution_role.arn
3829
+ }
3830
+
3831
+ output "lambda_execution_role_name" {
3832
+ description = "Name of the Lambda execution role"
3833
+ value = aws_iam_role.lambda_execution_role.name
3834
+ }
3835
+
3836
+ # Integration Outputs
3837
+ output "integration_id" {
3838
+ description = "ID of the Lambda integration"
3839
+ value = aws_apigatewayv2_integration.lambda_integration.id
3840
+ }
3841
+
3842
+ # CloudWatch Log Groups
3843
+ output "lambda_log_group_name" {
3844
+ description = "Name of the Lambda CloudWatch log group"
3845
+ value = aws_cloudwatch_log_group.lambda_logs.name
3846
+ }
3847
+
3848
+ output "lambda_log_group_arn" {
3849
+ description = "ARN of the Lambda CloudWatch log group"
3850
+ value = aws_cloudwatch_log_group.lambda_logs.arn
3851
+ }
3852
+
3853
+ output "api_log_group_name" {
3854
+ description = "Name of the API Gateway CloudWatch log group"
3855
+ value = module.http_api.api_log_group_name
3856
+ }
3857
+
3858
+ output "api_log_group_arn" {
3859
+ description = "ARN of the API Gateway CloudWatch log group"
3860
+ value = module.http_api.api_log_group_arn
3861
+ }",
3862
+ }
3863
+ `;
3864
+
3865
+ exports[`trpc backend generator > terraform iacProvider > should generate terraform files for REST API with Cognito auth and snapshot them > terraform-rest-cognito-files 1`] = `
3866
+ {
3867
+ "rest-api.tf": "# Core REST API Gateway module
3868
+ # This module creates the API Gateway REST API, deployment, stage, and logging resources
3869
+
3870
+ terraform {
3871
+ required_version = ">= 1.0"
3872
+
3873
+ required_providers {
3874
+ aws = {
3875
+ source = "hashicorp/aws"
3876
+ version = "~> 6.0"
3877
+ }
3878
+ }
3879
+ }
3880
+
3881
+ # Core REST API Gateway Variables
3882
+
3883
+ variable "api_name" {
3884
+ description = "Name of the REST API Gateway"
3885
+ type = string
3886
+ }
3887
+
3888
+ variable "api_description" {
3889
+ description = "Description of the REST API Gateway"
3890
+ type = string
3891
+ default = "REST API Gateway"
3892
+ }
3893
+
3894
+ variable "stage_name" {
3895
+ description = "Name of the API Gateway stage"
3896
+ type = string
3897
+ default = "prod"
3898
+ }
3899
+
3900
+ variable "stage_auto_deploy" {
3901
+ description = "Whether to automatically deploy the API stage"
3902
+ type = bool
3903
+ default = true
3904
+ }
3905
+
3906
+ # CORS Configuration
3907
+
3908
+ variable "cors_allow_headers" {
3909
+ description = "List of allowed headers for CORS"
3910
+ type = list(string)
3911
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
3912
+ }
3913
+
3914
+ variable "cors_allow_methods" {
3915
+ description = "List of allowed HTTP methods for CORS"
3916
+ type = list(string)
3917
+ default = ["*"]
3918
+ }
3919
+
3920
+ variable "cors_allow_origins" {
3921
+ description = "List of allowed origins for CORS"
3922
+ type = list(string)
3923
+ default = ["*"]
3924
+ }
3925
+
3926
+ # Tags
3927
+
3928
+ variable "tags" {
3929
+ description = "Tags to apply to all resources"
3930
+ type = map(string)
3931
+ default = {}
3932
+ }
3933
+
3934
+ # Data sources
3935
+ data "aws_region" "current" {}
3936
+ data "aws_caller_identity" "current" {}
3937
+
3938
+ # Resources
3939
+
3940
+ # Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
3941
+
3942
+ # REST API Gateway
3943
+ resource "aws_api_gateway_rest_api" "rest_api" {
3944
+ name = var.api_name
3945
+ description = var.api_description
3946
+
3947
+ endpoint_configuration {
3948
+ types = ["REGIONAL"]
3949
+ }
3950
+
3951
+ lifecycle {
3952
+ create_before_destroy = true
3953
+ }
3954
+
3955
+ tags = var.tags
3956
+ }
3957
+
3958
+ # Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
3959
+ # after all methods and integrations are defined
3960
+
3961
+ # Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
3962
+
3963
+ # Gateway Response for CORS (4XX errors)
3964
+ resource "aws_api_gateway_gateway_response" "cors_4xx" {
3965
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
3966
+ response_type = "DEFAULT_4XX"
3967
+
3968
+ response_parameters = {
3969
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
3970
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
3971
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
3972
+ }
3973
+ }
3974
+
3975
+ # Gateway Response for CORS (5XX errors)
3976
+ resource "aws_api_gateway_gateway_response" "cors_5xx" {
3977
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
3978
+ response_type = "DEFAULT_5XX"
3979
+
3980
+ response_parameters = {
3981
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
3982
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
3983
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
3984
+ }
3985
+ }
3986
+
3987
+ # Outputs
3988
+
3989
+ output "api_id" {
3990
+ description = "ID of the REST API Gateway"
3991
+ value = aws_api_gateway_rest_api.rest_api.id
3992
+ }
3993
+
3994
+ output "api_arn" {
3995
+ description = "ARN of the REST API Gateway"
3996
+ value = aws_api_gateway_rest_api.rest_api.arn
3997
+ }
3998
+
3999
+ output "api_endpoint" {
4000
+ description = "Base URL of the REST API Gateway"
4001
+ value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
4002
+ }
4003
+
4004
+ output "api_execution_arn" {
4005
+ description = "Execution ARN of the REST API Gateway"
4006
+ value = aws_api_gateway_rest_api.rest_api.execution_arn
4007
+ }
4008
+
4009
+ output "api_root_resource_id" {
4010
+ description = "Root resource ID of the REST API Gateway"
4011
+ value = aws_api_gateway_rest_api.rest_api.root_resource_id
4012
+ }
4013
+
4014
+ # Note: Stage and deployment outputs are provided by the consuming module
4015
+
4016
+ # Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
4017
+ "test-api.tf": "terraform {
4018
+ required_version = ">= 1.0"
4019
+
4020
+ required_providers {
4021
+ aws = {
4022
+ source = "hashicorp/aws"
4023
+ version = "~> 6.0"
4024
+ }
4025
+ }
4026
+ }
4027
+
4028
+ # Authentication Configuration
4029
+ variable "user_pool_id" {
4030
+ description = "Cognito User Pool ID for authentication"
4031
+ type = string
4032
+ }
4033
+
4034
+ variable "user_pool_client_ids" {
4035
+ description = "List of Cognito User Pool Client IDs"
4036
+ type = list(string)
4037
+ }
4038
+
4039
+ variable "env" {
4040
+ description = "Environment variables for the Lambda function"
4041
+ type = map(string)
4042
+ default = {}
4043
+ }
4044
+
4045
+ variable "additional_iam_policy_statements" {
4046
+ description = "Additional IAM policy statements for the Lambda function"
4047
+ type = list(object({
4048
+ Effect = string
4049
+ Action = list(string)
4050
+ Resource = list(string)
4051
+ }))
4052
+ default = []
4053
+ }
4054
+
4055
+ # CORS Configuration (passed to core module)
4056
+
4057
+ variable "cors_allow_headers" {
4058
+ description = "List of allowed headers for CORS"
4059
+ type = list(string)
4060
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
4061
+ }
4062
+
4063
+ variable "cors_allow_methods" {
4064
+ description = "List of allowed HTTP methods for CORS"
4065
+ type = list(string)
4066
+ default = ["*"]
4067
+ }
4068
+
4069
+ variable "cors_allow_origins" {
4070
+ description = "List of allowed origins for CORS"
4071
+ type = list(string)
4072
+ default = ["*"]
4073
+ }
4074
+
4075
+ # Tags
4076
+ variable "tags" {
4077
+ description = "Tags to apply to all resources"
4078
+ type = map(string)
4079
+ default = {}
4080
+ }
4081
+
4082
+ # Get current AWS region and account ID
4083
+ data "aws_region" "current" {}
4084
+ data "aws_caller_identity" "current" {}
4085
+
4086
+ # Resources
4087
+
4088
+ # Create Lambda ZIP file from the FastAPI bundle directory
4089
+ data "archive_file" "lambda_zip" {
4090
+ type = "zip"
4091
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test-api/bundle"
4092
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
4093
+ }
4094
+
4095
+ # Use the core REST API module
4096
+ module "rest_api" {
4097
+ source = "../../../core/api/rest-api"
4098
+
4099
+ api_name = "TestApi"
4100
+ api_description = "TestApi REST API"
4101
+ stage_name = "prod"
4102
+ stage_auto_deploy = true
4103
+
4104
+ # CORS Configuration
4105
+ cors_allow_headers = var.cors_allow_headers
4106
+ cors_allow_methods = var.cors_allow_methods
4107
+ cors_allow_origins = var.cors_allow_origins
4108
+
4109
+ # Tags
4110
+ tags = var.tags
4111
+ }
4112
+
4113
+ # Lambda function
4114
+ resource "aws_lambda_function" "api_lambda" {
4115
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
4116
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
4117
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
4118
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
4119
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
4120
+ filename = data.archive_file.lambda_zip.output_path
4121
+ function_name = "TestApiHandler"
4122
+ role = aws_iam_role.lambda_execution_role.arn
4123
+ handler = "index.handler"
4124
+ runtime = "nodejs22.x"
4125
+ timeout = 30
4126
+ memory_size = 128
4127
+
4128
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
4129
+
4130
+ # Enable X-Ray tracing
4131
+ tracing_config {
4132
+ mode = "Active"
4133
+ }
4134
+
4135
+ environment {
4136
+ variables = merge({
4137
+ AWS_CONNECTION_REUSE_ENABLED = "1"
4138
+ }, var.env)
4139
+ }
4140
+
4141
+ tags = var.tags
4142
+ }
4143
+
4144
+ # IAM role for Lambda execution
4145
+ resource "aws_iam_role" "lambda_execution_role" {
4146
+ name = "TestApiHandler-execution-role"
4147
+
4148
+ assume_role_policy = jsonencode({
4149
+ Version = "2012-10-17"
4150
+ Statement = [
4151
+ {
4152
+ Action = "sts:AssumeRole"
4153
+ Effect = "Allow"
4154
+ Principal = {
4155
+ Service = "lambda.amazonaws.com"
4156
+ }
4157
+ }
4158
+ ]
4159
+ })
4160
+
4161
+ tags = var.tags
4162
+ }
4163
+
4164
+ # Attach basic execution policy to Lambda role
4165
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
4166
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
4167
+ role = aws_iam_role.lambda_execution_role.name
4168
+ }
4169
+
4170
+ # Attach X-Ray tracing policy to Lambda role
4171
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
4172
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
4173
+ role = aws_iam_role.lambda_execution_role.name
4174
+ }
4175
+
4176
+ # Additional IAM policies for Lambda (if provided)
4177
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
4178
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
4179
+ name = "TestApiHandler-additional-policies"
4180
+ role = aws_iam_role.lambda_execution_role.id
4181
+
4182
+ policy = jsonencode({
4183
+ Version = "2012-10-17"
4184
+ Statement = var.additional_iam_policy_statements
4185
+ })
4186
+ }
4187
+
4188
+ # CloudWatch Log Group for Lambda
4189
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
4190
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
4191
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
4192
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
4193
+ name = "/aws/lambda/TestApiHandler"
4194
+ tags = var.tags
4195
+ }
4196
+
4197
+ # Cognito User Pool Authorizer
4198
+ resource "aws_api_gateway_authorizer" "cognito_authorizer" {
4199
+ name = "TestApiAuthorizer"
4200
+ rest_api_id = module.rest_api.api_id
4201
+ type = "COGNITO_USER_POOLS"
4202
+ provider_arns = ["arn:aws:cognito-idp:\${data.aws_region.current.name}:\${data.aws_caller_identity.current.account_id}:userpool/\${var.user_pool_id}"]
4203
+ identity_source = "method.request.header.Authorization"
4204
+ }
4205
+
4206
+ # Create proxy resource (captures all paths)
4207
+ resource "aws_api_gateway_resource" "proxy_resource" {
4208
+ rest_api_id = module.rest_api.api_id
4209
+ parent_id = module.rest_api.api_root_resource_id
4210
+ path_part = "{proxy+}"
4211
+ }
4212
+
4213
+ # Lambda integration for REST API
4214
+ resource "aws_api_gateway_integration" "lambda_integration" {
4215
+ rest_api_id = module.rest_api.api_id
4216
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4217
+ http_method = aws_api_gateway_method.proxy_method.http_method
4218
+
4219
+ integration_http_method = "POST"
4220
+ type = "AWS_PROXY"
4221
+ uri = aws_lambda_function.api_lambda.invoke_arn
4222
+
4223
+ depends_on = [aws_lambda_function.api_lambda]
4224
+ }
4225
+
4226
+ # Method for proxy integration
4227
+ resource "aws_api_gateway_method" "proxy_method" {
4228
+ #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
4229
+ rest_api_id = module.rest_api.api_id
4230
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4231
+ http_method = "ANY"
4232
+
4233
+ authorization = "COGNITO_USER_POOLS"
4234
+ authorizer_id = aws_api_gateway_authorizer.cognito_authorizer.id
4235
+
4236
+ request_parameters = {
4237
+ "method.request.path.proxy" = true
4238
+ }
4239
+
4240
+ depends_on = [aws_api_gateway_authorizer.cognito_authorizer]
4241
+ }
4242
+
4243
+ # OPTIONS method for CORS preflight
4244
+ resource "aws_api_gateway_method" "options_method" {
4245
+ #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
4246
+ #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
4247
+ rest_api_id = module.rest_api.api_id
4248
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4249
+ http_method = "OPTIONS"
4250
+ authorization = "NONE"
4251
+ }
4252
+
4253
+ # CORS integration for OPTIONS method
4254
+ resource "aws_api_gateway_integration" "options_integration" {
4255
+ rest_api_id = module.rest_api.api_id
4256
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4257
+ http_method = aws_api_gateway_method.options_method.http_method
4258
+
4259
+ type = "MOCK"
4260
+ request_templates = {
4261
+ "application/json" = "{\\"statusCode\\": 204}"
4262
+ }
4263
+ }
4264
+
4265
+ # OPTIONS method response
4266
+ resource "aws_api_gateway_method_response" "options_response" {
4267
+ rest_api_id = module.rest_api.api_id
4268
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4269
+ http_method = aws_api_gateway_method.options_method.http_method
4270
+ status_code = "204"
4271
+
4272
+ response_parameters = {
4273
+ "method.response.header.Access-Control-Allow-Headers" = true
4274
+ "method.response.header.Access-Control-Allow-Methods" = true
4275
+ "method.response.header.Access-Control-Allow-Origin" = true
4276
+ }
4277
+ }
4278
+
4279
+ # OPTIONS integration response
4280
+ resource "aws_api_gateway_integration_response" "options_integration_response" {
4281
+ rest_api_id = module.rest_api.api_id
4282
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4283
+ http_method = aws_api_gateway_method.options_method.http_method
4284
+ status_code = aws_api_gateway_method_response.options_response.status_code
4285
+
4286
+ response_parameters = {
4287
+ "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
4288
+ "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
4289
+ "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
4290
+ }
4291
+ }
4292
+
4293
+ # API Gateway deployment
4294
+ resource "aws_api_gateway_deployment" "api_deployment" {
4295
+ rest_api_id = module.rest_api.api_id
4296
+
4297
+ triggers = {
4298
+ redeployment = sha1(jsonencode([
4299
+ aws_api_gateway_resource.proxy_resource.id,
4300
+ aws_api_gateway_method.proxy_method.id,
4301
+ aws_api_gateway_integration.lambda_integration.id,
4302
+ aws_api_gateway_method.options_method.id,
4303
+ aws_api_gateway_integration.options_integration.id,
4304
+ ]))
4305
+ }
4306
+
4307
+ lifecycle {
4308
+ create_before_destroy = true
4309
+ }
4310
+
4311
+ depends_on = [
4312
+ aws_api_gateway_method.proxy_method,
4313
+ aws_api_gateway_integration.lambda_integration,
4314
+ aws_api_gateway_method.options_method,
4315
+ aws_api_gateway_integration.options_integration,
4316
+ aws_api_gateway_method_response.options_response,
4317
+ aws_api_gateway_integration_response.options_integration_response,
4318
+ ]
4319
+ }
4320
+
4321
+ # API Gateway stage
4322
+ resource "aws_api_gateway_stage" "api_stage" {
4323
+ #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
4324
+ #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
4325
+ #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
4326
+ #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
4327
+ deployment_id = aws_api_gateway_deployment.api_deployment.id
4328
+ rest_api_id = module.rest_api.api_id
4329
+ stage_name = "prod"
4330
+ xray_tracing_enabled = true
4331
+
4332
+ tags = var.tags
4333
+
4334
+ depends_on = [aws_api_gateway_deployment.api_deployment]
4335
+ }
4336
+
4337
+ # API Gateway Resource Policy
4338
+ resource "aws_api_gateway_rest_api_policy" "api_policy" {
4339
+ rest_api_id = module.rest_api.api_id
4340
+
4341
+ policy = jsonencode({
4342
+ Version = "2012-10-17"
4343
+ Statement = [
4344
+ {
4345
+ # Allow all callers to invoke the API in the resource policy, since auth is handled by Cognito
4346
+ Effect = "Allow"
4347
+ Principal = "*"
4348
+ Action = "execute-api:Invoke"
4349
+ Resource = "execute-api:/*"
4350
+ }
4351
+ ]
4352
+ })
4353
+ }
4354
+
4355
+ # Lambda permission for API Gateway to invoke the function
4356
+ resource "aws_lambda_permission" "api_gateway_invoke" {
4357
+ statement_id = "AllowExecutionFromAPIGateway"
4358
+ action = "lambda:InvokeFunction"
4359
+ function_name = aws_lambda_function.api_lambda.function_name
4360
+ principal = "apigateway.amazonaws.com"
4361
+ source_arn = "\${module.rest_api.api_execution_arn}/*/*"
4362
+
4363
+ depends_on = [module.rest_api, aws_lambda_function.api_lambda]
4364
+ }
4365
+
4366
+ # Add API url to runtime config
4367
+ module "add_url_to_runtime_config" {
4368
+ source = "../../../core/runtime-config/entry"
4369
+
4370
+ key_path = "apis.TestApi"
4371
+ value = aws_api_gateway_stage.api_stage.invoke_url
4372
+
4373
+ depends_on = [aws_api_gateway_stage.api_stage]
4374
+ }
4375
+
4376
+ # Outputs
4377
+
4378
+ # API Gateway Outputs (from core module)
4379
+ output "api_id" {
4380
+ description = "ID of the REST API Gateway"
4381
+ value = module.rest_api.api_id
4382
+ }
4383
+
4384
+ output "api_arn" {
4385
+ description = "ARN of the REST API Gateway"
4386
+ value = module.rest_api.api_arn
4387
+ }
4388
+
4389
+ output "api_endpoint" {
4390
+ description = "Base URL of the REST API Gateway"
4391
+ value = module.rest_api.api_endpoint
4392
+ }
4393
+
4394
+ output "api_execution_arn" {
4395
+ description = "Execution ARN of the REST API Gateway"
4396
+ value = module.rest_api.api_execution_arn
4397
+ }
4398
+
4399
+ output "stage_invoke_url" {
4400
+ description = "Invoke URL of the API Gateway stage"
4401
+ value = aws_api_gateway_stage.api_stage.invoke_url
4402
+ }
4403
+
4404
+ output "stage_arn" {
4405
+ description = "ARN of the API Gateway stage"
4406
+ value = aws_api_gateway_stage.api_stage.arn
4407
+ }
4408
+
4409
+ output "stage_execution_arn" {
4410
+ description = "Execution ARN of the API Gateway stage"
4411
+ value = aws_api_gateway_stage.api_stage.execution_arn
4412
+ }
4413
+
4414
+ output "deployment_id" {
4415
+ description = "ID of the API Gateway deployment"
4416
+ value = aws_api_gateway_deployment.api_deployment.id
4417
+ }
4418
+
4419
+ output "stage_id" {
4420
+ description = "ID of the API Gateway stage"
4421
+ value = aws_api_gateway_stage.api_stage.id
4422
+ }
4423
+
4424
+ # Lambda Function Outputs
4425
+ output "lambda_function_name" {
4426
+ description = "Name of the Lambda function"
4427
+ value = aws_lambda_function.api_lambda.function_name
4428
+ }
4429
+
4430
+ output "lambda_function_arn" {
4431
+ description = "ARN of the Lambda function"
4432
+ value = aws_lambda_function.api_lambda.arn
4433
+ }
4434
+
4435
+ output "lambda_invoke_arn" {
4436
+ description = "Invoke ARN of the Lambda function"
4437
+ value = aws_lambda_function.api_lambda.invoke_arn
4438
+ }
4439
+
4440
+ output "lambda_qualified_arn" {
4441
+ description = "Qualified ARN of the Lambda function"
4442
+ value = aws_lambda_function.api_lambda.qualified_arn
4443
+ }
4444
+
4445
+ output "lambda_version" {
4446
+ description = "Version of the Lambda function"
4447
+ value = aws_lambda_function.api_lambda.version
4448
+ }
4449
+
4450
+ output "lambda_source_code_hash" {
4451
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
4452
+ value = aws_lambda_function.api_lambda.source_code_hash
4453
+ }
4454
+
4455
+ output "lambda_source_code_size" {
4456
+ description = "Size of the Lambda deployment package in bytes"
4457
+ value = aws_lambda_function.api_lambda.source_code_size
4458
+ }
4459
+
4460
+ # IAM Role Outputs
4461
+ output "lambda_execution_role_arn" {
4462
+ description = "ARN of the Lambda execution role"
4463
+ value = aws_iam_role.lambda_execution_role.arn
4464
+ }
4465
+
4466
+ output "lambda_execution_role_name" {
4467
+ description = "Name of the Lambda execution role"
4468
+ value = aws_iam_role.lambda_execution_role.name
4469
+ }
4470
+
4471
+ # Integration Outputs
4472
+ output "integration_id" {
4473
+ description = "ID of the Lambda integration"
4474
+ value = aws_api_gateway_integration.lambda_integration.id
4475
+ }
4476
+
4477
+ output "proxy_resource_id" {
4478
+ description = "ID of the proxy resource"
4479
+ value = aws_api_gateway_resource.proxy_resource.id
4480
+ }
4481
+
4482
+ output "proxy_method_id" {
4483
+ description = "ID of the proxy method"
4484
+ value = aws_api_gateway_method.proxy_method.id
4485
+ }
4486
+
4487
+ # CloudWatch Log Groups
4488
+ output "lambda_log_group_name" {
4489
+ description = "Name of the Lambda CloudWatch log group"
4490
+ value = aws_cloudwatch_log_group.lambda_logs.name
4491
+ }
4492
+
4493
+ output "lambda_log_group_arn" {
4494
+ description = "ARN of the Lambda CloudWatch log group"
4495
+ value = aws_cloudwatch_log_group.lambda_logs.arn
4496
+ }
4497
+ ",
4498
+ }
4499
+ `;
4500
+
4501
+ exports[`trpc backend generator > terraform iacProvider > should generate terraform files for REST API with IAM auth and snapshot them > terraform-rest-iam-files 1`] = `
4502
+ {
4503
+ "rest-api.tf": "# Core REST API Gateway module
4504
+ # This module creates the API Gateway REST API, deployment, stage, and logging resources
4505
+
4506
+ terraform {
4507
+ required_version = ">= 1.0"
4508
+
4509
+ required_providers {
4510
+ aws = {
4511
+ source = "hashicorp/aws"
4512
+ version = "~> 6.0"
4513
+ }
4514
+ }
4515
+ }
4516
+
4517
+ # Core REST API Gateway Variables
4518
+
4519
+ variable "api_name" {
4520
+ description = "Name of the REST API Gateway"
4521
+ type = string
4522
+ }
4523
+
4524
+ variable "api_description" {
4525
+ description = "Description of the REST API Gateway"
4526
+ type = string
4527
+ default = "REST API Gateway"
4528
+ }
4529
+
4530
+ variable "stage_name" {
4531
+ description = "Name of the API Gateway stage"
4532
+ type = string
4533
+ default = "prod"
4534
+ }
4535
+
4536
+ variable "stage_auto_deploy" {
4537
+ description = "Whether to automatically deploy the API stage"
4538
+ type = bool
4539
+ default = true
4540
+ }
4541
+
4542
+ # CORS Configuration
4543
+
4544
+ variable "cors_allow_headers" {
4545
+ description = "List of allowed headers for CORS"
4546
+ type = list(string)
4547
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
4548
+ }
4549
+
4550
+ variable "cors_allow_methods" {
4551
+ description = "List of allowed HTTP methods for CORS"
4552
+ type = list(string)
4553
+ default = ["*"]
4554
+ }
4555
+
4556
+ variable "cors_allow_origins" {
4557
+ description = "List of allowed origins for CORS"
4558
+ type = list(string)
4559
+ default = ["*"]
4560
+ }
4561
+
4562
+ # Tags
4563
+
4564
+ variable "tags" {
4565
+ description = "Tags to apply to all resources"
4566
+ type = map(string)
4567
+ default = {}
4568
+ }
4569
+
4570
+ # Data sources
4571
+ data "aws_region" "current" {}
4572
+ data "aws_caller_identity" "current" {}
4573
+
4574
+ # Resources
4575
+
4576
+ # Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
4577
+
4578
+ # REST API Gateway
4579
+ resource "aws_api_gateway_rest_api" "rest_api" {
4580
+ name = var.api_name
4581
+ description = var.api_description
4582
+
4583
+ endpoint_configuration {
4584
+ types = ["REGIONAL"]
4585
+ }
4586
+
4587
+ lifecycle {
4588
+ create_before_destroy = true
4589
+ }
4590
+
4591
+ tags = var.tags
4592
+ }
4593
+
4594
+ # Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
4595
+ # after all methods and integrations are defined
4596
+
4597
+ # Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
4598
+
4599
+ # Gateway Response for CORS (4XX errors)
4600
+ resource "aws_api_gateway_gateway_response" "cors_4xx" {
4601
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
4602
+ response_type = "DEFAULT_4XX"
4603
+
4604
+ response_parameters = {
4605
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
4606
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
4607
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
4608
+ }
4609
+ }
4610
+
4611
+ # Gateway Response for CORS (5XX errors)
4612
+ resource "aws_api_gateway_gateway_response" "cors_5xx" {
4613
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
4614
+ response_type = "DEFAULT_5XX"
4615
+
4616
+ response_parameters = {
4617
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
4618
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
4619
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
4620
+ }
4621
+ }
4622
+
4623
+ # Outputs
4624
+
4625
+ output "api_id" {
4626
+ description = "ID of the REST API Gateway"
4627
+ value = aws_api_gateway_rest_api.rest_api.id
4628
+ }
4629
+
4630
+ output "api_arn" {
4631
+ description = "ARN of the REST API Gateway"
4632
+ value = aws_api_gateway_rest_api.rest_api.arn
4633
+ }
4634
+
4635
+ output "api_endpoint" {
4636
+ description = "Base URL of the REST API Gateway"
4637
+ value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
4638
+ }
4639
+
4640
+ output "api_execution_arn" {
4641
+ description = "Execution ARN of the REST API Gateway"
4642
+ value = aws_api_gateway_rest_api.rest_api.execution_arn
4643
+ }
4644
+
4645
+ output "api_root_resource_id" {
4646
+ description = "Root resource ID of the REST API Gateway"
4647
+ value = aws_api_gateway_rest_api.rest_api.root_resource_id
4648
+ }
4649
+
4650
+ # Note: Stage and deployment outputs are provided by the consuming module
4651
+
4652
+ # Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
4653
+ "test-api.tf": "terraform {
4654
+ required_version = ">= 1.0"
4655
+
4656
+ required_providers {
4657
+ aws = {
4658
+ source = "hashicorp/aws"
4659
+ version = "~> 6.0"
4660
+ }
4661
+ }
4662
+ }
4663
+
4664
+
4665
+ variable "env" {
4666
+ description = "Environment variables for the Lambda function"
4667
+ type = map(string)
4668
+ default = {}
4669
+ }
4670
+
4671
+ variable "additional_iam_policy_statements" {
4672
+ description = "Additional IAM policy statements for the Lambda function"
4673
+ type = list(object({
4674
+ Effect = string
4675
+ Action = list(string)
4676
+ Resource = list(string)
4677
+ }))
4678
+ default = []
4679
+ }
4680
+
4681
+ # CORS Configuration (passed to core module)
4682
+
4683
+ variable "cors_allow_headers" {
4684
+ description = "List of allowed headers for CORS"
4685
+ type = list(string)
4686
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
4687
+ }
4688
+
4689
+ variable "cors_allow_methods" {
4690
+ description = "List of allowed HTTP methods for CORS"
4691
+ type = list(string)
4692
+ default = ["*"]
4693
+ }
4694
+
4695
+ variable "cors_allow_origins" {
4696
+ description = "List of allowed origins for CORS"
4697
+ type = list(string)
4698
+ default = ["*"]
4699
+ }
4700
+
4701
+ # Tags
4702
+ variable "tags" {
4703
+ description = "Tags to apply to all resources"
4704
+ type = map(string)
4705
+ default = {}
4706
+ }
4707
+
4708
+ # Get current AWS region and account ID
4709
+ data "aws_region" "current" {}
4710
+ data "aws_caller_identity" "current" {}
4711
+
4712
+ # Resources
4713
+
4714
+ # Create Lambda ZIP file from the FastAPI bundle directory
4715
+ data "archive_file" "lambda_zip" {
4716
+ type = "zip"
4717
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test-api/bundle"
4718
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
4719
+ }
4720
+
4721
+ # Use the core REST API module
4722
+ module "rest_api" {
4723
+ source = "../../../core/api/rest-api"
4724
+
4725
+ api_name = "TestApi"
4726
+ api_description = "TestApi REST API"
4727
+ stage_name = "prod"
4728
+ stage_auto_deploy = true
4729
+
4730
+ # CORS Configuration
4731
+ cors_allow_headers = var.cors_allow_headers
4732
+ cors_allow_methods = var.cors_allow_methods
4733
+ cors_allow_origins = var.cors_allow_origins
4734
+
4735
+ # Tags
4736
+ tags = var.tags
4737
+ }
4738
+
4739
+ # Lambda function
4740
+ resource "aws_lambda_function" "api_lambda" {
4741
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
4742
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
4743
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
4744
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
4745
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
4746
+ filename = data.archive_file.lambda_zip.output_path
4747
+ function_name = "TestApiHandler"
4748
+ role = aws_iam_role.lambda_execution_role.arn
4749
+ handler = "index.handler"
4750
+ runtime = "nodejs22.x"
4751
+ timeout = 30
4752
+ memory_size = 128
4753
+
4754
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
4755
+
4756
+ # Enable X-Ray tracing
4757
+ tracing_config {
4758
+ mode = "Active"
4759
+ }
4760
+
4761
+ environment {
4762
+ variables = merge({
4763
+ AWS_CONNECTION_REUSE_ENABLED = "1"
4764
+ }, var.env)
4765
+ }
4766
+
4767
+ tags = var.tags
4768
+ }
4769
+
4770
+ # IAM role for Lambda execution
4771
+ resource "aws_iam_role" "lambda_execution_role" {
4772
+ name = "TestApiHandler-execution-role"
4773
+
4774
+ assume_role_policy = jsonencode({
4775
+ Version = "2012-10-17"
4776
+ Statement = [
4777
+ {
4778
+ Action = "sts:AssumeRole"
4779
+ Effect = "Allow"
4780
+ Principal = {
4781
+ Service = "lambda.amazonaws.com"
4782
+ }
4783
+ }
4784
+ ]
4785
+ })
4786
+
4787
+ tags = var.tags
4788
+ }
4789
+
4790
+ # Attach basic execution policy to Lambda role
4791
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
4792
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
4793
+ role = aws_iam_role.lambda_execution_role.name
4794
+ }
4795
+
4796
+ # Attach X-Ray tracing policy to Lambda role
4797
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
4798
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
4799
+ role = aws_iam_role.lambda_execution_role.name
4800
+ }
4801
+
4802
+ # Additional IAM policies for Lambda (if provided)
4803
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
4804
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
4805
+ name = "TestApiHandler-additional-policies"
4806
+ role = aws_iam_role.lambda_execution_role.id
4807
+
4808
+ policy = jsonencode({
4809
+ Version = "2012-10-17"
4810
+ Statement = var.additional_iam_policy_statements
4811
+ })
4812
+ }
4813
+
4814
+ # CloudWatch Log Group for Lambda
4815
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
4816
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
4817
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
4818
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
4819
+ name = "/aws/lambda/TestApiHandler"
4820
+ tags = var.tags
4821
+ }
4822
+
4823
+
4824
+ # Create proxy resource (captures all paths)
4825
+ resource "aws_api_gateway_resource" "proxy_resource" {
4826
+ rest_api_id = module.rest_api.api_id
4827
+ parent_id = module.rest_api.api_root_resource_id
4828
+ path_part = "{proxy+}"
4829
+ }
4830
+
4831
+ # Lambda integration for REST API
4832
+ resource "aws_api_gateway_integration" "lambda_integration" {
4833
+ rest_api_id = module.rest_api.api_id
4834
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4835
+ http_method = aws_api_gateway_method.proxy_method.http_method
4836
+
4837
+ integration_http_method = "POST"
4838
+ type = "AWS_PROXY"
4839
+ uri = aws_lambda_function.api_lambda.invoke_arn
4840
+
4841
+ depends_on = [aws_lambda_function.api_lambda]
4842
+ }
4843
+
4844
+ # Method for proxy integration
4845
+ resource "aws_api_gateway_method" "proxy_method" {
4846
+ #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
4847
+ rest_api_id = module.rest_api.api_id
4848
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4849
+ http_method = "ANY"
4850
+
4851
+ authorization = "AWS_IAM"
4852
+
4853
+ request_parameters = {
4854
+ "method.request.path.proxy" = true
4855
+ }
4856
+
4857
+ depends_on = []
4858
+ }
4859
+
4860
+ # OPTIONS method for CORS preflight
4861
+ resource "aws_api_gateway_method" "options_method" {
4862
+ #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
4863
+ #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
4864
+ rest_api_id = module.rest_api.api_id
4865
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4866
+ http_method = "OPTIONS"
4867
+ authorization = "NONE"
4868
+ }
4869
+
4870
+ # CORS integration for OPTIONS method
4871
+ resource "aws_api_gateway_integration" "options_integration" {
4872
+ rest_api_id = module.rest_api.api_id
4873
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4874
+ http_method = aws_api_gateway_method.options_method.http_method
4875
+
4876
+ type = "MOCK"
4877
+ request_templates = {
4878
+ "application/json" = "{\\"statusCode\\": 204}"
4879
+ }
4880
+ }
4881
+
4882
+ # OPTIONS method response
4883
+ resource "aws_api_gateway_method_response" "options_response" {
4884
+ rest_api_id = module.rest_api.api_id
4885
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4886
+ http_method = aws_api_gateway_method.options_method.http_method
4887
+ status_code = "204"
4888
+
4889
+ response_parameters = {
4890
+ "method.response.header.Access-Control-Allow-Headers" = true
4891
+ "method.response.header.Access-Control-Allow-Methods" = true
4892
+ "method.response.header.Access-Control-Allow-Origin" = true
4893
+ }
4894
+ }
4895
+
4896
+ # OPTIONS integration response
4897
+ resource "aws_api_gateway_integration_response" "options_integration_response" {
4898
+ rest_api_id = module.rest_api.api_id
4899
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4900
+ http_method = aws_api_gateway_method.options_method.http_method
4901
+ status_code = aws_api_gateway_method_response.options_response.status_code
4902
+
4903
+ response_parameters = {
4904
+ "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
4905
+ "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
4906
+ "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
4907
+ }
4908
+ }
4909
+
4910
+ # API Gateway deployment
4911
+ resource "aws_api_gateway_deployment" "api_deployment" {
4912
+ rest_api_id = module.rest_api.api_id
4913
+
4914
+ triggers = {
4915
+ redeployment = sha1(jsonencode([
4916
+ aws_api_gateway_resource.proxy_resource.id,
4917
+ aws_api_gateway_method.proxy_method.id,
4918
+ aws_api_gateway_integration.lambda_integration.id,
4919
+ aws_api_gateway_method.options_method.id,
4920
+ aws_api_gateway_integration.options_integration.id,
4921
+ ]))
4922
+ }
4923
+
4924
+ lifecycle {
4925
+ create_before_destroy = true
4926
+ }
4927
+
4928
+ depends_on = [
4929
+ aws_api_gateway_method.proxy_method,
4930
+ aws_api_gateway_integration.lambda_integration,
4931
+ aws_api_gateway_method.options_method,
4932
+ aws_api_gateway_integration.options_integration,
4933
+ aws_api_gateway_method_response.options_response,
4934
+ aws_api_gateway_integration_response.options_integration_response,
4935
+ ]
4936
+ }
4937
+
4938
+ # API Gateway stage
4939
+ resource "aws_api_gateway_stage" "api_stage" {
4940
+ #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
4941
+ #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
4942
+ #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
4943
+ #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
4944
+ deployment_id = aws_api_gateway_deployment.api_deployment.id
4945
+ rest_api_id = module.rest_api.api_id
4946
+ stage_name = "prod"
4947
+ xray_tracing_enabled = true
4948
+
4949
+ tags = var.tags
4950
+
4951
+ depends_on = [aws_api_gateway_deployment.api_deployment]
4952
+ }
4953
+
4954
+ # API Gateway Resource Policy
4955
+ resource "aws_api_gateway_rest_api_policy" "api_policy" {
4956
+ rest_api_id = module.rest_api.api_id
4957
+
4958
+ policy = jsonencode({
4959
+ Version = "2012-10-17"
4960
+ Statement = [
4961
+ {
4962
+ # Grant any AWS credentials from the account to call the API
4963
+ # Machine to machine fine-grained access can be defined here using more specific principals
4964
+ # (eg roles or users) and resources (eg which api paths may be invoked by which principal) if required
4965
+ Effect = "Allow"
4966
+ Principal = {
4967
+ AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
4968
+ }
4969
+ Action = "execute-api:Invoke"
4970
+ Resource = "execute-api:/*"
4971
+ },
4972
+ {
4973
+ # Open up OPTIONS to allow browsers to make unauthenticated preflight requests
4974
+ Effect = "Allow"
4975
+ Principal = "*"
4976
+ Action = "execute-api:Invoke"
4977
+ Resource = "execute-api:/*/OPTIONS/*"
4978
+ }
4979
+ ]
4980
+ })
4981
+ }
4982
+
4983
+ # Lambda permission for API Gateway to invoke the function
4984
+ resource "aws_lambda_permission" "api_gateway_invoke" {
4985
+ statement_id = "AllowExecutionFromAPIGateway"
4986
+ action = "lambda:InvokeFunction"
4987
+ function_name = aws_lambda_function.api_lambda.function_name
4988
+ principal = "apigateway.amazonaws.com"
4989
+ source_arn = "\${module.rest_api.api_execution_arn}/*/*"
4990
+
4991
+ depends_on = [module.rest_api, aws_lambda_function.api_lambda]
4992
+ }
4993
+
4994
+ # Add API url to runtime config
4995
+ module "add_url_to_runtime_config" {
4996
+ source = "../../../core/runtime-config/entry"
4997
+
4998
+ key_path = "apis.TestApi"
4999
+ value = aws_api_gateway_stage.api_stage.invoke_url
5000
+
5001
+ depends_on = [aws_api_gateway_stage.api_stage]
5002
+ }
5003
+
5004
+ # Outputs
5005
+
5006
+ # API Gateway Outputs (from core module)
5007
+ output "api_id" {
5008
+ description = "ID of the REST API Gateway"
5009
+ value = module.rest_api.api_id
5010
+ }
5011
+
5012
+ output "api_arn" {
5013
+ description = "ARN of the REST API Gateway"
5014
+ value = module.rest_api.api_arn
5015
+ }
5016
+
5017
+ output "api_endpoint" {
5018
+ description = "Base URL of the REST API Gateway"
5019
+ value = module.rest_api.api_endpoint
5020
+ }
5021
+
5022
+ output "api_execution_arn" {
5023
+ description = "Execution ARN of the REST API Gateway"
5024
+ value = module.rest_api.api_execution_arn
5025
+ }
5026
+
5027
+ output "stage_invoke_url" {
5028
+ description = "Invoke URL of the API Gateway stage"
5029
+ value = aws_api_gateway_stage.api_stage.invoke_url
5030
+ }
5031
+
5032
+ output "stage_arn" {
5033
+ description = "ARN of the API Gateway stage"
5034
+ value = aws_api_gateway_stage.api_stage.arn
5035
+ }
5036
+
5037
+ output "stage_execution_arn" {
5038
+ description = "Execution ARN of the API Gateway stage"
5039
+ value = aws_api_gateway_stage.api_stage.execution_arn
5040
+ }
5041
+
5042
+ output "deployment_id" {
5043
+ description = "ID of the API Gateway deployment"
5044
+ value = aws_api_gateway_deployment.api_deployment.id
5045
+ }
5046
+
5047
+ output "stage_id" {
5048
+ description = "ID of the API Gateway stage"
5049
+ value = aws_api_gateway_stage.api_stage.id
5050
+ }
5051
+
5052
+ # Lambda Function Outputs
5053
+ output "lambda_function_name" {
5054
+ description = "Name of the Lambda function"
5055
+ value = aws_lambda_function.api_lambda.function_name
5056
+ }
5057
+
5058
+ output "lambda_function_arn" {
5059
+ description = "ARN of the Lambda function"
5060
+ value = aws_lambda_function.api_lambda.arn
5061
+ }
5062
+
5063
+ output "lambda_invoke_arn" {
5064
+ description = "Invoke ARN of the Lambda function"
5065
+ value = aws_lambda_function.api_lambda.invoke_arn
5066
+ }
5067
+
5068
+ output "lambda_qualified_arn" {
5069
+ description = "Qualified ARN of the Lambda function"
5070
+ value = aws_lambda_function.api_lambda.qualified_arn
5071
+ }
5072
+
5073
+ output "lambda_version" {
5074
+ description = "Version of the Lambda function"
5075
+ value = aws_lambda_function.api_lambda.version
5076
+ }
5077
+
5078
+ output "lambda_source_code_hash" {
5079
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
5080
+ value = aws_lambda_function.api_lambda.source_code_hash
5081
+ }
5082
+
5083
+ output "lambda_source_code_size" {
5084
+ description = "Size of the Lambda deployment package in bytes"
5085
+ value = aws_lambda_function.api_lambda.source_code_size
5086
+ }
5087
+
5088
+ # IAM Role Outputs
5089
+ output "lambda_execution_role_arn" {
5090
+ description = "ARN of the Lambda execution role"
5091
+ value = aws_iam_role.lambda_execution_role.arn
5092
+ }
5093
+
5094
+ output "lambda_execution_role_name" {
5095
+ description = "Name of the Lambda execution role"
5096
+ value = aws_iam_role.lambda_execution_role.name
5097
+ }
5098
+
5099
+ # Integration Outputs
5100
+ output "integration_id" {
5101
+ description = "ID of the Lambda integration"
5102
+ value = aws_api_gateway_integration.lambda_integration.id
5103
+ }
5104
+
5105
+ output "proxy_resource_id" {
5106
+ description = "ID of the proxy resource"
5107
+ value = aws_api_gateway_resource.proxy_resource.id
5108
+ }
5109
+
5110
+ output "proxy_method_id" {
5111
+ description = "ID of the proxy method"
5112
+ value = aws_api_gateway_method.proxy_method.id
5113
+ }
5114
+
5115
+ # CloudWatch Log Groups
5116
+ output "lambda_log_group_name" {
5117
+ description = "Name of the Lambda CloudWatch log group"
5118
+ value = aws_cloudwatch_log_group.lambda_logs.name
5119
+ }
5120
+
5121
+ output "lambda_log_group_arn" {
5122
+ description = "ARN of the Lambda CloudWatch log group"
5123
+ value = aws_cloudwatch_log_group.lambda_logs.arn
5124
+ }
5125
+ ",
5126
+ }
5127
+ `;
5128
+
5129
+ exports[`trpc backend generator > terraform iacProvider > should generate terraform files for REST API with None auth and snapshot them > terraform-rest-none-files 1`] = `
5130
+ {
5131
+ "rest-api.tf": "# Core REST API Gateway module
5132
+ # This module creates the API Gateway REST API, deployment, stage, and logging resources
5133
+
5134
+ terraform {
5135
+ required_version = ">= 1.0"
5136
+
5137
+ required_providers {
5138
+ aws = {
5139
+ source = "hashicorp/aws"
5140
+ version = "~> 6.0"
5141
+ }
5142
+ }
5143
+ }
5144
+
5145
+ # Core REST API Gateway Variables
5146
+
5147
+ variable "api_name" {
5148
+ description = "Name of the REST API Gateway"
5149
+ type = string
5150
+ }
5151
+
5152
+ variable "api_description" {
5153
+ description = "Description of the REST API Gateway"
5154
+ type = string
5155
+ default = "REST API Gateway"
5156
+ }
5157
+
5158
+ variable "stage_name" {
5159
+ description = "Name of the API Gateway stage"
5160
+ type = string
5161
+ default = "prod"
5162
+ }
5163
+
5164
+ variable "stage_auto_deploy" {
5165
+ description = "Whether to automatically deploy the API stage"
5166
+ type = bool
5167
+ default = true
5168
+ }
5169
+
5170
+ # CORS Configuration
5171
+
5172
+ variable "cors_allow_headers" {
5173
+ description = "List of allowed headers for CORS"
5174
+ type = list(string)
5175
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
5176
+ }
5177
+
5178
+ variable "cors_allow_methods" {
5179
+ description = "List of allowed HTTP methods for CORS"
5180
+ type = list(string)
5181
+ default = ["*"]
5182
+ }
5183
+
5184
+ variable "cors_allow_origins" {
5185
+ description = "List of allowed origins for CORS"
5186
+ type = list(string)
5187
+ default = ["*"]
5188
+ }
5189
+
5190
+ # Tags
5191
+
5192
+ variable "tags" {
5193
+ description = "Tags to apply to all resources"
5194
+ type = map(string)
5195
+ default = {}
5196
+ }
5197
+
5198
+ # Data sources
5199
+ data "aws_region" "current" {}
5200
+ data "aws_caller_identity" "current" {}
5201
+
5202
+ # Resources
5203
+
5204
+ # Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
5205
+
5206
+ # REST API Gateway
5207
+ resource "aws_api_gateway_rest_api" "rest_api" {
5208
+ name = var.api_name
5209
+ description = var.api_description
5210
+
5211
+ endpoint_configuration {
5212
+ types = ["REGIONAL"]
5213
+ }
5214
+
5215
+ lifecycle {
5216
+ create_before_destroy = true
5217
+ }
5218
+
5219
+ tags = var.tags
5220
+ }
5221
+
5222
+ # Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
5223
+ # after all methods and integrations are defined
5224
+
5225
+ # Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
5226
+
5227
+ # Gateway Response for CORS (4XX errors)
5228
+ resource "aws_api_gateway_gateway_response" "cors_4xx" {
5229
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
5230
+ response_type = "DEFAULT_4XX"
5231
+
5232
+ response_parameters = {
5233
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
5234
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
5235
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
5236
+ }
5237
+ }
5238
+
5239
+ # Gateway Response for CORS (5XX errors)
5240
+ resource "aws_api_gateway_gateway_response" "cors_5xx" {
5241
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
5242
+ response_type = "DEFAULT_5XX"
5243
+
5244
+ response_parameters = {
5245
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
5246
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
5247
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
5248
+ }
5249
+ }
5250
+
5251
+ # Outputs
5252
+
5253
+ output "api_id" {
5254
+ description = "ID of the REST API Gateway"
5255
+ value = aws_api_gateway_rest_api.rest_api.id
5256
+ }
5257
+
5258
+ output "api_arn" {
5259
+ description = "ARN of the REST API Gateway"
5260
+ value = aws_api_gateway_rest_api.rest_api.arn
5261
+ }
5262
+
5263
+ output "api_endpoint" {
5264
+ description = "Base URL of the REST API Gateway"
5265
+ value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
5266
+ }
5267
+
5268
+ output "api_execution_arn" {
5269
+ description = "Execution ARN of the REST API Gateway"
5270
+ value = aws_api_gateway_rest_api.rest_api.execution_arn
5271
+ }
5272
+
5273
+ output "api_root_resource_id" {
5274
+ description = "Root resource ID of the REST API Gateway"
5275
+ value = aws_api_gateway_rest_api.rest_api.root_resource_id
5276
+ }
5277
+
5278
+ # Note: Stage and deployment outputs are provided by the consuming module
5279
+
5280
+ # Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
5281
+ "test-api.tf": "terraform {
5282
+ required_version = ">= 1.0"
5283
+
5284
+ required_providers {
5285
+ aws = {
5286
+ source = "hashicorp/aws"
5287
+ version = "~> 6.0"
5288
+ }
5289
+ }
5290
+ }
5291
+
5292
+
5293
+ variable "env" {
5294
+ description = "Environment variables for the Lambda function"
5295
+ type = map(string)
5296
+ default = {}
5297
+ }
5298
+
5299
+ variable "additional_iam_policy_statements" {
5300
+ description = "Additional IAM policy statements for the Lambda function"
5301
+ type = list(object({
5302
+ Effect = string
5303
+ Action = list(string)
5304
+ Resource = list(string)
5305
+ }))
5306
+ default = []
5307
+ }
5308
+
5309
+ # CORS Configuration (passed to core module)
5310
+
5311
+ variable "cors_allow_headers" {
5312
+ description = "List of allowed headers for CORS"
5313
+ type = list(string)
5314
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
5315
+ }
5316
+
5317
+ variable "cors_allow_methods" {
5318
+ description = "List of allowed HTTP methods for CORS"
5319
+ type = list(string)
5320
+ default = ["*"]
5321
+ }
5322
+
5323
+ variable "cors_allow_origins" {
5324
+ description = "List of allowed origins for CORS"
5325
+ type = list(string)
5326
+ default = ["*"]
5327
+ }
5328
+
5329
+ # Tags
5330
+ variable "tags" {
5331
+ description = "Tags to apply to all resources"
5332
+ type = map(string)
5333
+ default = {}
5334
+ }
5335
+
5336
+ # Get current AWS region and account ID
5337
+ data "aws_region" "current" {}
5338
+ data "aws_caller_identity" "current" {}
5339
+
5340
+ # Resources
5341
+
5342
+ # Create Lambda ZIP file from the FastAPI bundle directory
5343
+ data "archive_file" "lambda_zip" {
5344
+ type = "zip"
5345
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test-api/bundle"
5346
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
5347
+ }
5348
+
5349
+ # Use the core REST API module
5350
+ module "rest_api" {
5351
+ source = "../../../core/api/rest-api"
5352
+
5353
+ api_name = "TestApi"
5354
+ api_description = "TestApi REST API"
5355
+ stage_name = "prod"
5356
+ stage_auto_deploy = true
5357
+
5358
+ # CORS Configuration
5359
+ cors_allow_headers = var.cors_allow_headers
5360
+ cors_allow_methods = var.cors_allow_methods
5361
+ cors_allow_origins = var.cors_allow_origins
5362
+
5363
+ # Tags
5364
+ tags = var.tags
5365
+ }
5366
+
5367
+ # Lambda function
5368
+ resource "aws_lambda_function" "api_lambda" {
5369
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
5370
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
5371
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
5372
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
5373
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
5374
+ filename = data.archive_file.lambda_zip.output_path
5375
+ function_name = "TestApiHandler"
5376
+ role = aws_iam_role.lambda_execution_role.arn
5377
+ handler = "index.handler"
5378
+ runtime = "nodejs22.x"
5379
+ timeout = 30
5380
+ memory_size = 128
5381
+
5382
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
5383
+
5384
+ # Enable X-Ray tracing
5385
+ tracing_config {
5386
+ mode = "Active"
5387
+ }
5388
+
5389
+ environment {
5390
+ variables = merge({
5391
+ AWS_CONNECTION_REUSE_ENABLED = "1"
5392
+ }, var.env)
5393
+ }
5394
+
5395
+ tags = var.tags
5396
+ }
5397
+
5398
+ # IAM role for Lambda execution
5399
+ resource "aws_iam_role" "lambda_execution_role" {
5400
+ name = "TestApiHandler-execution-role"
5401
+
5402
+ assume_role_policy = jsonencode({
5403
+ Version = "2012-10-17"
5404
+ Statement = [
5405
+ {
5406
+ Action = "sts:AssumeRole"
5407
+ Effect = "Allow"
5408
+ Principal = {
5409
+ Service = "lambda.amazonaws.com"
5410
+ }
5411
+ }
5412
+ ]
5413
+ })
5414
+
5415
+ tags = var.tags
5416
+ }
5417
+
5418
+ # Attach basic execution policy to Lambda role
5419
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
5420
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
5421
+ role = aws_iam_role.lambda_execution_role.name
5422
+ }
5423
+
5424
+ # Attach X-Ray tracing policy to Lambda role
5425
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
5426
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
5427
+ role = aws_iam_role.lambda_execution_role.name
5428
+ }
5429
+
5430
+ # Additional IAM policies for Lambda (if provided)
5431
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
5432
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
5433
+ name = "TestApiHandler-additional-policies"
5434
+ role = aws_iam_role.lambda_execution_role.id
5435
+
5436
+ policy = jsonencode({
5437
+ Version = "2012-10-17"
5438
+ Statement = var.additional_iam_policy_statements
5439
+ })
5440
+ }
5441
+
5442
+ # CloudWatch Log Group for Lambda
5443
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
5444
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
5445
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
5446
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
5447
+ name = "/aws/lambda/TestApiHandler"
5448
+ tags = var.tags
5449
+ }
5450
+
5451
+
5452
+ # Create proxy resource (captures all paths)
5453
+ resource "aws_api_gateway_resource" "proxy_resource" {
5454
+ rest_api_id = module.rest_api.api_id
5455
+ parent_id = module.rest_api.api_root_resource_id
5456
+ path_part = "{proxy+}"
5457
+ }
5458
+
5459
+ # Lambda integration for REST API
5460
+ resource "aws_api_gateway_integration" "lambda_integration" {
5461
+ rest_api_id = module.rest_api.api_id
5462
+ resource_id = aws_api_gateway_resource.proxy_resource.id
5463
+ http_method = aws_api_gateway_method.proxy_method.http_method
5464
+
5465
+ integration_http_method = "POST"
5466
+ type = "AWS_PROXY"
5467
+ uri = aws_lambda_function.api_lambda.invoke_arn
5468
+
5469
+ depends_on = [aws_lambda_function.api_lambda]
5470
+ }
5471
+
5472
+ # Method for proxy integration
5473
+ resource "aws_api_gateway_method" "proxy_method" {
5474
+ #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
5475
+ rest_api_id = module.rest_api.api_id
5476
+ resource_id = aws_api_gateway_resource.proxy_resource.id
5477
+ http_method = "ANY"
5478
+
5479
+ # Note: you may wish to suppress the checkov rule CKV_AWS_59 if you are absolutely sure you
5480
+ # need a public API without authentication
5481
+ authorization = "NONE"
5482
+
5483
+ request_parameters = {
5484
+ "method.request.path.proxy" = true
5485
+ }
5486
+
5487
+ depends_on = []
5488
+ }
5489
+
5490
+ # OPTIONS method for CORS preflight
5491
+ resource "aws_api_gateway_method" "options_method" {
5492
+ #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
5493
+ #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
5494
+ rest_api_id = module.rest_api.api_id
5495
+ resource_id = aws_api_gateway_resource.proxy_resource.id
5496
+ http_method = "OPTIONS"
5497
+ authorization = "NONE"
5498
+ }
5499
+
5500
+ # CORS integration for OPTIONS method
5501
+ resource "aws_api_gateway_integration" "options_integration" {
5502
+ rest_api_id = module.rest_api.api_id
5503
+ resource_id = aws_api_gateway_resource.proxy_resource.id
5504
+ http_method = aws_api_gateway_method.options_method.http_method
5505
+
5506
+ type = "MOCK"
5507
+ request_templates = {
5508
+ "application/json" = "{\\"statusCode\\": 204}"
5509
+ }
5510
+ }
5511
+
5512
+ # OPTIONS method response
5513
+ resource "aws_api_gateway_method_response" "options_response" {
5514
+ rest_api_id = module.rest_api.api_id
5515
+ resource_id = aws_api_gateway_resource.proxy_resource.id
5516
+ http_method = aws_api_gateway_method.options_method.http_method
5517
+ status_code = "204"
5518
+
5519
+ response_parameters = {
5520
+ "method.response.header.Access-Control-Allow-Headers" = true
5521
+ "method.response.header.Access-Control-Allow-Methods" = true
5522
+ "method.response.header.Access-Control-Allow-Origin" = true
5523
+ }
5524
+ }
5525
+
5526
+ # OPTIONS integration response
5527
+ resource "aws_api_gateway_integration_response" "options_integration_response" {
5528
+ rest_api_id = module.rest_api.api_id
5529
+ resource_id = aws_api_gateway_resource.proxy_resource.id
5530
+ http_method = aws_api_gateway_method.options_method.http_method
5531
+ status_code = aws_api_gateway_method_response.options_response.status_code
5532
+
5533
+ response_parameters = {
5534
+ "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
5535
+ "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
5536
+ "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
5537
+ }
5538
+ }
5539
+
5540
+ # API Gateway deployment
5541
+ resource "aws_api_gateway_deployment" "api_deployment" {
5542
+ rest_api_id = module.rest_api.api_id
5543
+
5544
+ triggers = {
5545
+ redeployment = sha1(jsonencode([
5546
+ aws_api_gateway_resource.proxy_resource.id,
5547
+ aws_api_gateway_method.proxy_method.id,
5548
+ aws_api_gateway_integration.lambda_integration.id,
5549
+ aws_api_gateway_method.options_method.id,
5550
+ aws_api_gateway_integration.options_integration.id,
5551
+ ]))
5552
+ }
5553
+
5554
+ lifecycle {
5555
+ create_before_destroy = true
5556
+ }
5557
+
5558
+ depends_on = [
5559
+ aws_api_gateway_method.proxy_method,
5560
+ aws_api_gateway_integration.lambda_integration,
5561
+ aws_api_gateway_method.options_method,
5562
+ aws_api_gateway_integration.options_integration,
5563
+ aws_api_gateway_method_response.options_response,
5564
+ aws_api_gateway_integration_response.options_integration_response,
5565
+ ]
5566
+ }
5567
+
5568
+ # API Gateway stage
5569
+ resource "aws_api_gateway_stage" "api_stage" {
5570
+ #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
5571
+ #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
5572
+ #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
5573
+ #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
5574
+ deployment_id = aws_api_gateway_deployment.api_deployment.id
5575
+ rest_api_id = module.rest_api.api_id
5576
+ stage_name = "prod"
5577
+ xray_tracing_enabled = true
5578
+
5579
+ tags = var.tags
5580
+
5581
+ depends_on = [aws_api_gateway_deployment.api_deployment]
5582
+ }
5583
+
5584
+ # API Gateway Resource Policy
5585
+ resource "aws_api_gateway_rest_api_policy" "api_policy" {
5586
+ rest_api_id = module.rest_api.api_id
5587
+
5588
+ policy = jsonencode({
5589
+ Version = "2012-10-17"
5590
+ Statement = [
5591
+ {
5592
+ # Allow all callers to invoke the API in the resource policy
5593
+ Effect = "Allow"
5594
+ Principal = "*"
5595
+ Action = "execute-api:Invoke"
5596
+ Resource = "execute-api:/*"
5597
+ }
5598
+ ]
5599
+ })
5600
+ }
5601
+
5602
+ # Lambda permission for API Gateway to invoke the function
5603
+ resource "aws_lambda_permission" "api_gateway_invoke" {
5604
+ statement_id = "AllowExecutionFromAPIGateway"
5605
+ action = "lambda:InvokeFunction"
5606
+ function_name = aws_lambda_function.api_lambda.function_name
5607
+ principal = "apigateway.amazonaws.com"
5608
+ source_arn = "\${module.rest_api.api_execution_arn}/*/*"
5609
+
5610
+ depends_on = [module.rest_api, aws_lambda_function.api_lambda]
5611
+ }
5612
+
5613
+ # Add API url to runtime config
5614
+ module "add_url_to_runtime_config" {
5615
+ source = "../../../core/runtime-config/entry"
5616
+
5617
+ key_path = "apis.TestApi"
5618
+ value = aws_api_gateway_stage.api_stage.invoke_url
5619
+
5620
+ depends_on = [aws_api_gateway_stage.api_stage]
5621
+ }
5622
+
5623
+ # Outputs
5624
+
5625
+ # API Gateway Outputs (from core module)
5626
+ output "api_id" {
5627
+ description = "ID of the REST API Gateway"
5628
+ value = module.rest_api.api_id
5629
+ }
5630
+
5631
+ output "api_arn" {
5632
+ description = "ARN of the REST API Gateway"
5633
+ value = module.rest_api.api_arn
5634
+ }
5635
+
5636
+ output "api_endpoint" {
5637
+ description = "Base URL of the REST API Gateway"
5638
+ value = module.rest_api.api_endpoint
5639
+ }
5640
+
5641
+ output "api_execution_arn" {
5642
+ description = "Execution ARN of the REST API Gateway"
5643
+ value = module.rest_api.api_execution_arn
5644
+ }
5645
+
5646
+ output "stage_invoke_url" {
5647
+ description = "Invoke URL of the API Gateway stage"
5648
+ value = aws_api_gateway_stage.api_stage.invoke_url
5649
+ }
5650
+
5651
+ output "stage_arn" {
5652
+ description = "ARN of the API Gateway stage"
5653
+ value = aws_api_gateway_stage.api_stage.arn
5654
+ }
5655
+
5656
+ output "stage_execution_arn" {
5657
+ description = "Execution ARN of the API Gateway stage"
5658
+ value = aws_api_gateway_stage.api_stage.execution_arn
5659
+ }
5660
+
5661
+ output "deployment_id" {
5662
+ description = "ID of the API Gateway deployment"
5663
+ value = aws_api_gateway_deployment.api_deployment.id
5664
+ }
5665
+
5666
+ output "stage_id" {
5667
+ description = "ID of the API Gateway stage"
5668
+ value = aws_api_gateway_stage.api_stage.id
5669
+ }
5670
+
5671
+ # Lambda Function Outputs
5672
+ output "lambda_function_name" {
5673
+ description = "Name of the Lambda function"
5674
+ value = aws_lambda_function.api_lambda.function_name
5675
+ }
5676
+
5677
+ output "lambda_function_arn" {
5678
+ description = "ARN of the Lambda function"
5679
+ value = aws_lambda_function.api_lambda.arn
5680
+ }
5681
+
5682
+ output "lambda_invoke_arn" {
5683
+ description = "Invoke ARN of the Lambda function"
5684
+ value = aws_lambda_function.api_lambda.invoke_arn
5685
+ }
5686
+
5687
+ output "lambda_qualified_arn" {
5688
+ description = "Qualified ARN of the Lambda function"
5689
+ value = aws_lambda_function.api_lambda.qualified_arn
5690
+ }
5691
+
5692
+ output "lambda_version" {
5693
+ description = "Version of the Lambda function"
5694
+ value = aws_lambda_function.api_lambda.version
5695
+ }
5696
+
5697
+ output "lambda_source_code_hash" {
5698
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
5699
+ value = aws_lambda_function.api_lambda.source_code_hash
5700
+ }
5701
+
5702
+ output "lambda_source_code_size" {
5703
+ description = "Size of the Lambda deployment package in bytes"
5704
+ value = aws_lambda_function.api_lambda.source_code_size
5705
+ }
5706
+
5707
+ # IAM Role Outputs
5708
+ output "lambda_execution_role_arn" {
5709
+ description = "ARN of the Lambda execution role"
5710
+ value = aws_iam_role.lambda_execution_role.arn
5711
+ }
5712
+
5713
+ output "lambda_execution_role_name" {
5714
+ description = "Name of the Lambda execution role"
5715
+ value = aws_iam_role.lambda_execution_role.name
5716
+ }
5717
+
5718
+ # Integration Outputs
5719
+ output "integration_id" {
5720
+ description = "ID of the Lambda integration"
5721
+ value = aws_api_gateway_integration.lambda_integration.id
5722
+ }
5723
+
5724
+ output "proxy_resource_id" {
5725
+ description = "ID of the proxy resource"
5726
+ value = aws_api_gateway_resource.proxy_resource.id
5727
+ }
5728
+
5729
+ output "proxy_method_id" {
5730
+ description = "ID of the proxy method"
5731
+ value = aws_api_gateway_method.proxy_method.id
5732
+ }
5733
+
5734
+ # CloudWatch Log Groups
5735
+ output "lambda_log_group_name" {
5736
+ description = "Name of the Lambda CloudWatch log group"
5737
+ value = aws_cloudwatch_log_group.lambda_logs.name
5738
+ }
5739
+
5740
+ output "lambda_log_group_arn" {
5741
+ description = "ARN of the Lambda CloudWatch log group"
5742
+ value = aws_cloudwatch_log_group.lambda_logs.arn
5743
+ }
5744
+ ",
5745
+ }
5746
+ `;