@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
@@ -1123,3 +1123,3732 @@ export class IntegrationBuilder<
1123
1123
  }
1124
1124
  "
1125
1125
  `;
1126
+
1127
+ exports[`fastapi project generator > terraform iacProvider > should generate terraform files for HTTP API with Cognito auth and snapshot them > terraform-http-cognito-files 1`] = `
1128
+ {
1129
+ "http-api.tf": "# Core HTTP API Gateway module
1130
+ # This module creates the API Gateway HTTP API, stage, and logging resources
1131
+
1132
+ terraform {
1133
+ required_version = ">= 1.0"
1134
+
1135
+ required_providers {
1136
+ aws = {
1137
+ source = "hashicorp/aws"
1138
+ version = "~> 6.0"
1139
+ }
1140
+ }
1141
+ }
1142
+
1143
+ # Core HTTP API Gateway Variables
1144
+
1145
+ variable "api_name" {
1146
+ description = "Name of the HTTP API Gateway"
1147
+ type = string
1148
+ }
1149
+
1150
+ variable "api_description" {
1151
+ description = "Description of the HTTP API Gateway"
1152
+ type = string
1153
+ default = "HTTP API Gateway"
1154
+ }
1155
+
1156
+ variable "stage_name" {
1157
+ description = "Name of the API Gateway stage"
1158
+ type = string
1159
+ default = "prod"
1160
+ }
1161
+
1162
+ variable "stage_auto_deploy" {
1163
+ description = "Whether to automatically deploy the API stage"
1164
+ type = bool
1165
+ default = true
1166
+ }
1167
+
1168
+ # CORS Configuration
1169
+
1170
+ variable "cors_allow_credentials" {
1171
+ description = "Whether to allow credentials in CORS requests"
1172
+ type = bool
1173
+ default = false
1174
+ }
1175
+
1176
+ variable "cors_allow_headers" {
1177
+ description = "List of allowed headers for CORS"
1178
+ type = list(string)
1179
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
1180
+ }
1181
+
1182
+ variable "cors_allow_methods" {
1183
+ description = "List of allowed HTTP methods for CORS"
1184
+ type = list(string)
1185
+ default = ["*"]
1186
+ }
1187
+
1188
+ variable "cors_allow_origins" {
1189
+ description = "List of allowed origins for CORS"
1190
+ type = list(string)
1191
+ default = ["*"]
1192
+ }
1193
+
1194
+ variable "cors_expose_headers" {
1195
+ description = "List of headers to expose in CORS responses"
1196
+ type = list(string)
1197
+ default = []
1198
+ }
1199
+
1200
+ variable "cors_max_age" {
1201
+ description = "Maximum age for CORS preflight requests in seconds"
1202
+ type = number
1203
+ default = 86400
1204
+ }
1205
+
1206
+ # Tags
1207
+
1208
+ variable "tags" {
1209
+ description = "Tags to apply to all resources"
1210
+ type = map(string)
1211
+ default = {}
1212
+ }
1213
+
1214
+ # Data sources
1215
+ data "aws_region" "current" {}
1216
+ data "aws_caller_identity" "current" {}
1217
+
1218
+ # Resources
1219
+
1220
+ # KMS key for CloudWatch log group encryption
1221
+ resource "aws_kms_key" "logs_key" {
1222
+ description = "KMS key for CloudWatch log group encryption"
1223
+ deletion_window_in_days = 7
1224
+ enable_key_rotation = true
1225
+
1226
+ policy = jsonencode({
1227
+ Version = "2012-10-17"
1228
+ Statement = [
1229
+ {
1230
+ Sid = "Enable IAM User Permissions"
1231
+ Effect = "Allow"
1232
+ Principal = {
1233
+ AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
1234
+ }
1235
+ Action = "kms:*"
1236
+ Resource = "*"
1237
+ },
1238
+ {
1239
+ Sid = "Allow CloudWatch Logs"
1240
+ Effect = "Allow"
1241
+ Principal = {
1242
+ Service = "logs.\${data.aws_region.current.name}.amazonaws.com"
1243
+ }
1244
+ Action = [
1245
+ "kms:Encrypt",
1246
+ "kms:Decrypt",
1247
+ "kms:ReEncrypt*",
1248
+ "kms:GenerateDataKey*",
1249
+ "kms:DescribeKey"
1250
+ ]
1251
+ Resource = "*"
1252
+ Condition = {
1253
+ ArnEquals = {
1254
+ "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}"
1255
+ }
1256
+ }
1257
+ }
1258
+ ]
1259
+ })
1260
+
1261
+ tags = var.tags
1262
+ }
1263
+
1264
+ resource "aws_kms_alias" "logs_key_alias" {
1265
+ name = "alias/\${var.api_name}-api-logs-encryption"
1266
+ target_key_id = aws_kms_key.logs_key.key_id
1267
+ }
1268
+
1269
+ # HTTP API Gateway
1270
+ resource "aws_apigatewayv2_api" "http_api" {
1271
+ name = var.api_name
1272
+ protocol_type = "HTTP"
1273
+ description = var.api_description
1274
+
1275
+ cors_configuration {
1276
+ allow_credentials = var.cors_allow_credentials
1277
+ allow_headers = var.cors_allow_headers
1278
+ allow_methods = var.cors_allow_methods
1279
+ allow_origins = var.cors_allow_origins
1280
+ expose_headers = var.cors_expose_headers
1281
+ max_age = var.cors_max_age
1282
+ }
1283
+
1284
+ tags = var.tags
1285
+ }
1286
+
1287
+ # API Gateway stage
1288
+ resource "aws_apigatewayv2_stage" "api_stage" {
1289
+ api_id = aws_apigatewayv2_api.http_api.id
1290
+ name = var.stage_name
1291
+ auto_deploy = var.stage_auto_deploy
1292
+
1293
+ access_log_settings {
1294
+ destination_arn = aws_cloudwatch_log_group.api_logs.arn
1295
+ format = jsonencode({
1296
+ requestId = "$context.requestId"
1297
+ ip = "$context.identity.sourceIp"
1298
+ requestTime = "$context.requestTime"
1299
+ httpMethod = "$context.httpMethod"
1300
+ routeKey = "$context.routeKey"
1301
+ status = "$context.status"
1302
+ protocol = "$context.protocol"
1303
+ responseLength = "$context.responseLength"
1304
+ error = "$context.error.message"
1305
+ integrationError = "$context.integrationErrorMessage"
1306
+ })
1307
+ }
1308
+
1309
+ default_route_settings {
1310
+ throttling_burst_limit = 5000
1311
+ throttling_rate_limit = 10000
1312
+ }
1313
+
1314
+ tags = var.tags
1315
+
1316
+ depends_on = [aws_cloudwatch_log_group.api_logs]
1317
+ }
1318
+
1319
+ # CloudWatch Log Group for API Gateway
1320
+ resource "aws_cloudwatch_log_group" "api_logs" {
1321
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
1322
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
1323
+ name = "/aws/apigateway/\${var.api_name}"
1324
+ kms_key_id = aws_kms_key.logs_key.arn
1325
+ tags = var.tags
1326
+ }
1327
+
1328
+ # Outputs
1329
+
1330
+ output "api_id" {
1331
+ description = "ID of the HTTP API Gateway"
1332
+ value = aws_apigatewayv2_api.http_api.id
1333
+ }
1334
+
1335
+ output "api_arn" {
1336
+ description = "ARN of the HTTP API Gateway"
1337
+ value = aws_apigatewayv2_api.http_api.arn
1338
+ }
1339
+
1340
+ output "api_endpoint" {
1341
+ description = "Base URL of the HTTP API Gateway"
1342
+ value = aws_apigatewayv2_api.http_api.api_endpoint
1343
+ }
1344
+
1345
+ output "api_execution_arn" {
1346
+ description = "Execution ARN of the HTTP API Gateway"
1347
+ value = aws_apigatewayv2_api.http_api.execution_arn
1348
+ }
1349
+
1350
+ output "stage_id" {
1351
+ description = "ID of the API Gateway stage"
1352
+ value = aws_apigatewayv2_stage.api_stage.id
1353
+ }
1354
+
1355
+ output "stage_arn" {
1356
+ description = "ARN of the API Gateway stage"
1357
+ value = aws_apigatewayv2_stage.api_stage.arn
1358
+ }
1359
+
1360
+ output "stage_execution_arn" {
1361
+ description = "Execution ARN of the API Gateway stage"
1362
+ value = aws_apigatewayv2_stage.api_stage.execution_arn
1363
+ }
1364
+
1365
+ output "stage_invoke_url" {
1366
+ description = "Invoke URL of the API Gateway stage"
1367
+ value = aws_apigatewayv2_stage.api_stage.invoke_url
1368
+ }
1369
+
1370
+ output "api_log_group_name" {
1371
+ description = "Name of the API Gateway CloudWatch log group"
1372
+ value = aws_cloudwatch_log_group.api_logs.name
1373
+ }
1374
+
1375
+ output "api_log_group_arn" {
1376
+ description = "ARN of the API Gateway CloudWatch log group"
1377
+ value = aws_cloudwatch_log_group.api_logs.arn
1378
+ }",
1379
+ "test-api.tf": "terraform {
1380
+ required_version = ">= 1.0"
1381
+
1382
+ required_providers {
1383
+ aws = {
1384
+ source = "hashicorp/aws"
1385
+ version = "~> 6.0"
1386
+ }
1387
+ }
1388
+ }
1389
+
1390
+ # Authentication Configuration
1391
+ variable "user_pool_id" {
1392
+ description = "Cognito User Pool ID for authentication"
1393
+ type = string
1394
+ }
1395
+
1396
+ variable "user_pool_client_ids" {
1397
+ description = "List of Cognito User Pool Client IDs"
1398
+ type = list(string)
1399
+ }
1400
+
1401
+ variable "env" {
1402
+ description = "Environment variables for the Lambda function"
1403
+ type = map(string)
1404
+ default = {}
1405
+ }
1406
+
1407
+ variable "additional_iam_policy_statements" {
1408
+ description = "Additional IAM policy statements for the Lambda function"
1409
+ type = list(object({
1410
+ Effect = string
1411
+ Action = list(string)
1412
+ Resource = list(string)
1413
+ }))
1414
+ default = []
1415
+ }
1416
+
1417
+ # CORS Configuration (passed to core module)
1418
+ variable "cors_allow_credentials" {
1419
+ description = "Whether to allow credentials in CORS requests"
1420
+ type = bool
1421
+ default = false
1422
+ }
1423
+
1424
+ variable "cors_allow_headers" {
1425
+ description = "List of allowed headers for CORS"
1426
+ type = list(string)
1427
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
1428
+ }
1429
+
1430
+ variable "cors_allow_methods" {
1431
+ description = "List of allowed HTTP methods for CORS"
1432
+ type = list(string)
1433
+ default = ["*"]
1434
+ }
1435
+
1436
+ variable "cors_allow_origins" {
1437
+ description = "List of allowed origins for CORS"
1438
+ type = list(string)
1439
+ default = ["*"]
1440
+ }
1441
+
1442
+ variable "cors_expose_headers" {
1443
+ description = "List of headers to expose in CORS responses"
1444
+ type = list(string)
1445
+ default = []
1446
+ }
1447
+
1448
+ variable "cors_max_age" {
1449
+ description = "Maximum age for CORS preflight requests in seconds"
1450
+ type = number
1451
+ default = 0
1452
+ }
1453
+
1454
+ # Tags
1455
+ variable "tags" {
1456
+ description = "Tags to apply to all resources"
1457
+ type = map(string)
1458
+ default = {}
1459
+ }
1460
+
1461
+ # Get current AWS region and account ID
1462
+ data "aws_region" "current" {}
1463
+ data "aws_caller_identity" "current" {}
1464
+
1465
+ # Resources
1466
+
1467
+ # Create Lambda ZIP file from the bundle directory
1468
+ data "archive_file" "lambda_zip" {
1469
+ type = "zip"
1470
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle"
1471
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
1472
+ }
1473
+
1474
+
1475
+ # Use the core HTTP API module
1476
+ module "http_api" {
1477
+ source = "../../../core/api/http-api"
1478
+
1479
+ api_name = "TestApi"
1480
+ api_description = "TestApi HTTP API"
1481
+ stage_name = "$default"
1482
+ stage_auto_deploy = true
1483
+
1484
+ # CORS Configuration
1485
+ cors_allow_credentials = var.cors_allow_credentials
1486
+ cors_allow_headers = var.cors_allow_headers
1487
+ cors_allow_methods = var.cors_allow_methods
1488
+ cors_allow_origins = var.cors_allow_origins
1489
+ cors_expose_headers = var.cors_expose_headers
1490
+ cors_max_age = var.cors_max_age
1491
+
1492
+ # Tags
1493
+ tags = var.tags
1494
+ }
1495
+
1496
+ # Lambda function
1497
+ # This configures a single "router" lambda to serve all requests
1498
+ resource "aws_lambda_function" "api_lambda" {
1499
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
1500
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
1501
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
1502
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
1503
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
1504
+ filename = data.archive_file.lambda_zip.output_path
1505
+ function_name = "TestApiHandler"
1506
+ role = aws_iam_role.lambda_execution_role.arn
1507
+ handler = "proj_test_api.main.handler"
1508
+ runtime = "python3.12"
1509
+ timeout = 30
1510
+ memory_size = 128
1511
+
1512
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
1513
+
1514
+ # Enable X-Ray tracing
1515
+ tracing_config {
1516
+ mode = "Active"
1517
+ }
1518
+
1519
+ environment {
1520
+ variables = merge({
1521
+ AWS_CONNECTION_REUSE_ENABLED = "1"
1522
+ }, var.env)
1523
+ }
1524
+
1525
+ tags = var.tags
1526
+ }
1527
+
1528
+ # IAM role for Lambda execution
1529
+ resource "aws_iam_role" "lambda_execution_role" {
1530
+ name = "TestApiHandler-execution-role"
1531
+
1532
+ assume_role_policy = jsonencode({
1533
+ Version = "2012-10-17"
1534
+ Statement = [
1535
+ {
1536
+ Action = "sts:AssumeRole"
1537
+ Effect = "Allow"
1538
+ Principal = {
1539
+ Service = "lambda.amazonaws.com"
1540
+ }
1541
+ }
1542
+ ]
1543
+ })
1544
+
1545
+ tags = var.tags
1546
+ }
1547
+
1548
+ # Attach basic execution policy to Lambda role
1549
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
1550
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
1551
+ role = aws_iam_role.lambda_execution_role.name
1552
+ }
1553
+
1554
+ # Attach X-Ray tracing policy to Lambda role
1555
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
1556
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
1557
+ role = aws_iam_role.lambda_execution_role.name
1558
+ }
1559
+
1560
+ # Additional IAM policies for Lambda (if provided)
1561
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
1562
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
1563
+ name = "TestApiHandler-additional-policies"
1564
+ role = aws_iam_role.lambda_execution_role.id
1565
+
1566
+ policy = jsonencode({
1567
+ Version = "2012-10-17"
1568
+ Statement = var.additional_iam_policy_statements
1569
+ })
1570
+ }
1571
+
1572
+ # CloudWatch Log Group for Lambda
1573
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
1574
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
1575
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
1576
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
1577
+ name = "/aws/lambda/TestApiHandler"
1578
+ tags = var.tags
1579
+ }
1580
+
1581
+ # Cognito User Pool Authorizer
1582
+ resource "aws_apigatewayv2_authorizer" "cognito_authorizer" {
1583
+ api_id = module.http_api.api_id
1584
+ authorizer_type = "JWT"
1585
+ identity_sources = ["$request.header.Authorization"]
1586
+ name = "TestApiAuthorizer"
1587
+
1588
+ jwt_configuration {
1589
+ audience = var.user_pool_client_ids
1590
+ issuer = "https://cognito-idp.\${data.aws_region.current.name}.amazonaws.com/\${var.user_pool_id}"
1591
+ }
1592
+ }
1593
+
1594
+ # Lambda integration for HTTP API
1595
+ resource "aws_apigatewayv2_integration" "lambda_integration" {
1596
+ api_id = module.http_api.api_id
1597
+ integration_type = "AWS_PROXY"
1598
+ integration_uri = aws_lambda_function.api_lambda.invoke_arn
1599
+
1600
+ payload_format_version = "2.0"
1601
+ timeout_milliseconds = 30000
1602
+
1603
+ depends_on = [aws_lambda_function.api_lambda]
1604
+ }
1605
+
1606
+ # Route for proxy integration (catches all requests)
1607
+ resource "aws_apigatewayv2_route" "proxy_routes" {
1608
+ # NB: OPTIONS is omitted here since API Gateway manages responding to preflight requests
1609
+ # when cors settings are configured
1610
+ for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"])
1611
+
1612
+ api_id = module.http_api.api_id
1613
+ route_key = "\${each.key} /{proxy+}"
1614
+ target = "integrations/\${aws_apigatewayv2_integration.lambda_integration.id}"
1615
+
1616
+ authorization_type = "JWT"
1617
+ authorizer_id = aws_apigatewayv2_authorizer.cognito_authorizer.id
1618
+
1619
+ depends_on = [aws_apigatewayv2_integration.lambda_integration, aws_apigatewayv2_authorizer.cognito_authorizer]
1620
+ }
1621
+
1622
+ # Add API url to runtime config
1623
+ module "add_url_to_runtime_config" {
1624
+ source = "../../../core/runtime-config/entry"
1625
+
1626
+ key_path = "apis.TestApi"
1627
+ value = module.http_api.stage_invoke_url
1628
+
1629
+ depends_on = [module.http_api]
1630
+ }
1631
+
1632
+ # Lambda permission for API Gateway to invoke the function
1633
+ resource "aws_lambda_permission" "api_gateway_invoke" {
1634
+ statement_id = "AllowExecutionFromAPIGateway"
1635
+ action = "lambda:InvokeFunction"
1636
+ function_name = aws_lambda_function.api_lambda.function_name
1637
+ principal = "apigateway.amazonaws.com"
1638
+ source_arn = "\${module.http_api.api_execution_arn}/*/*"
1639
+
1640
+ depends_on = [module.http_api, aws_lambda_function.api_lambda]
1641
+ }
1642
+
1643
+ # Outputs
1644
+
1645
+ # API Gateway Outputs (from core module)
1646
+ output "api_id" {
1647
+ description = "ID of the HTTP API Gateway"
1648
+ value = module.http_api.api_id
1649
+ }
1650
+
1651
+ output "api_arn" {
1652
+ description = "ARN of the HTTP API Gateway"
1653
+ value = module.http_api.api_arn
1654
+ }
1655
+
1656
+ output "api_endpoint" {
1657
+ description = "Base URL of the HTTP API Gateway"
1658
+ value = module.http_api.api_endpoint
1659
+ }
1660
+
1661
+ output "api_execution_arn" {
1662
+ description = "Execution ARN of the HTTP API Gateway"
1663
+ value = module.http_api.api_execution_arn
1664
+ }
1665
+
1666
+ output "stage_invoke_url" {
1667
+ description = "Invoke URL of the API Gateway stage"
1668
+ value = module.http_api.stage_invoke_url
1669
+ }
1670
+
1671
+ output "stage_arn" {
1672
+ description = "ARN of the API Gateway stage"
1673
+ value = module.http_api.stage_arn
1674
+ }
1675
+
1676
+ output "stage_execution_arn" {
1677
+ description = "Execution ARN of the API Gateway stage"
1678
+ value = module.http_api.stage_execution_arn
1679
+ }
1680
+
1681
+ # Lambda Function Outputs
1682
+ output "lambda_function_name" {
1683
+ description = "Name of the Lambda function"
1684
+ value = aws_lambda_function.api_lambda.function_name
1685
+ }
1686
+
1687
+ output "lambda_function_arn" {
1688
+ description = "ARN of the Lambda function"
1689
+ value = aws_lambda_function.api_lambda.arn
1690
+ }
1691
+
1692
+ output "lambda_invoke_arn" {
1693
+ description = "Invoke ARN of the Lambda function"
1694
+ value = aws_lambda_function.api_lambda.invoke_arn
1695
+ }
1696
+
1697
+ output "lambda_qualified_arn" {
1698
+ description = "Qualified ARN of the Lambda function"
1699
+ value = aws_lambda_function.api_lambda.qualified_arn
1700
+ }
1701
+
1702
+ output "lambda_version" {
1703
+ description = "Version of the Lambda function"
1704
+ value = aws_lambda_function.api_lambda.version
1705
+ }
1706
+
1707
+ output "lambda_source_code_hash" {
1708
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
1709
+ value = aws_lambda_function.api_lambda.source_code_hash
1710
+ }
1711
+
1712
+ output "lambda_source_code_size" {
1713
+ description = "Size of the Lambda deployment package in bytes"
1714
+ value = aws_lambda_function.api_lambda.source_code_size
1715
+ }
1716
+
1717
+ # IAM Role Outputs
1718
+ output "lambda_execution_role_arn" {
1719
+ description = "ARN of the Lambda execution role"
1720
+ value = aws_iam_role.lambda_execution_role.arn
1721
+ }
1722
+
1723
+ output "lambda_execution_role_name" {
1724
+ description = "Name of the Lambda execution role"
1725
+ value = aws_iam_role.lambda_execution_role.name
1726
+ }
1727
+
1728
+ # Integration Outputs
1729
+ output "integration_id" {
1730
+ description = "ID of the Lambda integration"
1731
+ value = aws_apigatewayv2_integration.lambda_integration.id
1732
+ }
1733
+
1734
+ # CloudWatch Log Groups
1735
+ output "lambda_log_group_name" {
1736
+ description = "Name of the Lambda CloudWatch log group"
1737
+ value = aws_cloudwatch_log_group.lambda_logs.name
1738
+ }
1739
+
1740
+ output "lambda_log_group_arn" {
1741
+ description = "ARN of the Lambda CloudWatch log group"
1742
+ value = aws_cloudwatch_log_group.lambda_logs.arn
1743
+ }
1744
+
1745
+ output "api_log_group_name" {
1746
+ description = "Name of the API Gateway CloudWatch log group"
1747
+ value = module.http_api.api_log_group_name
1748
+ }
1749
+
1750
+ output "api_log_group_arn" {
1751
+ description = "ARN of the API Gateway CloudWatch log group"
1752
+ value = module.http_api.api_log_group_arn
1753
+ }",
1754
+ }
1755
+ `;
1756
+
1757
+ exports[`fastapi project generator > terraform iacProvider > should generate terraform files for HTTP API with IAM auth and snapshot them > terraform-http-iam-files 1`] = `
1758
+ {
1759
+ "http-api.tf": "# Core HTTP API Gateway module
1760
+ # This module creates the API Gateway HTTP API, stage, and logging resources
1761
+
1762
+ terraform {
1763
+ required_version = ">= 1.0"
1764
+
1765
+ required_providers {
1766
+ aws = {
1767
+ source = "hashicorp/aws"
1768
+ version = "~> 6.0"
1769
+ }
1770
+ }
1771
+ }
1772
+
1773
+ # Core HTTP API Gateway Variables
1774
+
1775
+ variable "api_name" {
1776
+ description = "Name of the HTTP API Gateway"
1777
+ type = string
1778
+ }
1779
+
1780
+ variable "api_description" {
1781
+ description = "Description of the HTTP API Gateway"
1782
+ type = string
1783
+ default = "HTTP API Gateway"
1784
+ }
1785
+
1786
+ variable "stage_name" {
1787
+ description = "Name of the API Gateway stage"
1788
+ type = string
1789
+ default = "prod"
1790
+ }
1791
+
1792
+ variable "stage_auto_deploy" {
1793
+ description = "Whether to automatically deploy the API stage"
1794
+ type = bool
1795
+ default = true
1796
+ }
1797
+
1798
+ # CORS Configuration
1799
+
1800
+ variable "cors_allow_credentials" {
1801
+ description = "Whether to allow credentials in CORS requests"
1802
+ type = bool
1803
+ default = false
1804
+ }
1805
+
1806
+ variable "cors_allow_headers" {
1807
+ description = "List of allowed headers for CORS"
1808
+ type = list(string)
1809
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
1810
+ }
1811
+
1812
+ variable "cors_allow_methods" {
1813
+ description = "List of allowed HTTP methods for CORS"
1814
+ type = list(string)
1815
+ default = ["*"]
1816
+ }
1817
+
1818
+ variable "cors_allow_origins" {
1819
+ description = "List of allowed origins for CORS"
1820
+ type = list(string)
1821
+ default = ["*"]
1822
+ }
1823
+
1824
+ variable "cors_expose_headers" {
1825
+ description = "List of headers to expose in CORS responses"
1826
+ type = list(string)
1827
+ default = []
1828
+ }
1829
+
1830
+ variable "cors_max_age" {
1831
+ description = "Maximum age for CORS preflight requests in seconds"
1832
+ type = number
1833
+ default = 86400
1834
+ }
1835
+
1836
+ # Tags
1837
+
1838
+ variable "tags" {
1839
+ description = "Tags to apply to all resources"
1840
+ type = map(string)
1841
+ default = {}
1842
+ }
1843
+
1844
+ # Data sources
1845
+ data "aws_region" "current" {}
1846
+ data "aws_caller_identity" "current" {}
1847
+
1848
+ # Resources
1849
+
1850
+ # KMS key for CloudWatch log group encryption
1851
+ resource "aws_kms_key" "logs_key" {
1852
+ description = "KMS key for CloudWatch log group encryption"
1853
+ deletion_window_in_days = 7
1854
+ enable_key_rotation = true
1855
+
1856
+ policy = jsonencode({
1857
+ Version = "2012-10-17"
1858
+ Statement = [
1859
+ {
1860
+ Sid = "Enable IAM User Permissions"
1861
+ Effect = "Allow"
1862
+ Principal = {
1863
+ AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
1864
+ }
1865
+ Action = "kms:*"
1866
+ Resource = "*"
1867
+ },
1868
+ {
1869
+ Sid = "Allow CloudWatch Logs"
1870
+ Effect = "Allow"
1871
+ Principal = {
1872
+ Service = "logs.\${data.aws_region.current.name}.amazonaws.com"
1873
+ }
1874
+ Action = [
1875
+ "kms:Encrypt",
1876
+ "kms:Decrypt",
1877
+ "kms:ReEncrypt*",
1878
+ "kms:GenerateDataKey*",
1879
+ "kms:DescribeKey"
1880
+ ]
1881
+ Resource = "*"
1882
+ Condition = {
1883
+ ArnEquals = {
1884
+ "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}"
1885
+ }
1886
+ }
1887
+ }
1888
+ ]
1889
+ })
1890
+
1891
+ tags = var.tags
1892
+ }
1893
+
1894
+ resource "aws_kms_alias" "logs_key_alias" {
1895
+ name = "alias/\${var.api_name}-api-logs-encryption"
1896
+ target_key_id = aws_kms_key.logs_key.key_id
1897
+ }
1898
+
1899
+ # HTTP API Gateway
1900
+ resource "aws_apigatewayv2_api" "http_api" {
1901
+ name = var.api_name
1902
+ protocol_type = "HTTP"
1903
+ description = var.api_description
1904
+
1905
+ cors_configuration {
1906
+ allow_credentials = var.cors_allow_credentials
1907
+ allow_headers = var.cors_allow_headers
1908
+ allow_methods = var.cors_allow_methods
1909
+ allow_origins = var.cors_allow_origins
1910
+ expose_headers = var.cors_expose_headers
1911
+ max_age = var.cors_max_age
1912
+ }
1913
+
1914
+ tags = var.tags
1915
+ }
1916
+
1917
+ # API Gateway stage
1918
+ resource "aws_apigatewayv2_stage" "api_stage" {
1919
+ api_id = aws_apigatewayv2_api.http_api.id
1920
+ name = var.stage_name
1921
+ auto_deploy = var.stage_auto_deploy
1922
+
1923
+ access_log_settings {
1924
+ destination_arn = aws_cloudwatch_log_group.api_logs.arn
1925
+ format = jsonencode({
1926
+ requestId = "$context.requestId"
1927
+ ip = "$context.identity.sourceIp"
1928
+ requestTime = "$context.requestTime"
1929
+ httpMethod = "$context.httpMethod"
1930
+ routeKey = "$context.routeKey"
1931
+ status = "$context.status"
1932
+ protocol = "$context.protocol"
1933
+ responseLength = "$context.responseLength"
1934
+ error = "$context.error.message"
1935
+ integrationError = "$context.integrationErrorMessage"
1936
+ })
1937
+ }
1938
+
1939
+ default_route_settings {
1940
+ throttling_burst_limit = 5000
1941
+ throttling_rate_limit = 10000
1942
+ }
1943
+
1944
+ tags = var.tags
1945
+
1946
+ depends_on = [aws_cloudwatch_log_group.api_logs]
1947
+ }
1948
+
1949
+ # CloudWatch Log Group for API Gateway
1950
+ resource "aws_cloudwatch_log_group" "api_logs" {
1951
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
1952
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
1953
+ name = "/aws/apigateway/\${var.api_name}"
1954
+ kms_key_id = aws_kms_key.logs_key.arn
1955
+ tags = var.tags
1956
+ }
1957
+
1958
+ # Outputs
1959
+
1960
+ output "api_id" {
1961
+ description = "ID of the HTTP API Gateway"
1962
+ value = aws_apigatewayv2_api.http_api.id
1963
+ }
1964
+
1965
+ output "api_arn" {
1966
+ description = "ARN of the HTTP API Gateway"
1967
+ value = aws_apigatewayv2_api.http_api.arn
1968
+ }
1969
+
1970
+ output "api_endpoint" {
1971
+ description = "Base URL of the HTTP API Gateway"
1972
+ value = aws_apigatewayv2_api.http_api.api_endpoint
1973
+ }
1974
+
1975
+ output "api_execution_arn" {
1976
+ description = "Execution ARN of the HTTP API Gateway"
1977
+ value = aws_apigatewayv2_api.http_api.execution_arn
1978
+ }
1979
+
1980
+ output "stage_id" {
1981
+ description = "ID of the API Gateway stage"
1982
+ value = aws_apigatewayv2_stage.api_stage.id
1983
+ }
1984
+
1985
+ output "stage_arn" {
1986
+ description = "ARN of the API Gateway stage"
1987
+ value = aws_apigatewayv2_stage.api_stage.arn
1988
+ }
1989
+
1990
+ output "stage_execution_arn" {
1991
+ description = "Execution ARN of the API Gateway stage"
1992
+ value = aws_apigatewayv2_stage.api_stage.execution_arn
1993
+ }
1994
+
1995
+ output "stage_invoke_url" {
1996
+ description = "Invoke URL of the API Gateway stage"
1997
+ value = aws_apigatewayv2_stage.api_stage.invoke_url
1998
+ }
1999
+
2000
+ output "api_log_group_name" {
2001
+ description = "Name of the API Gateway CloudWatch log group"
2002
+ value = aws_cloudwatch_log_group.api_logs.name
2003
+ }
2004
+
2005
+ output "api_log_group_arn" {
2006
+ description = "ARN of the API Gateway CloudWatch log group"
2007
+ value = aws_cloudwatch_log_group.api_logs.arn
2008
+ }",
2009
+ "test-api.tf": "terraform {
2010
+ required_version = ">= 1.0"
2011
+
2012
+ required_providers {
2013
+ aws = {
2014
+ source = "hashicorp/aws"
2015
+ version = "~> 6.0"
2016
+ }
2017
+ }
2018
+ }
2019
+
2020
+
2021
+ variable "env" {
2022
+ description = "Environment variables for the Lambda function"
2023
+ type = map(string)
2024
+ default = {}
2025
+ }
2026
+
2027
+ variable "additional_iam_policy_statements" {
2028
+ description = "Additional IAM policy statements for the Lambda function"
2029
+ type = list(object({
2030
+ Effect = string
2031
+ Action = list(string)
2032
+ Resource = list(string)
2033
+ }))
2034
+ default = []
2035
+ }
2036
+
2037
+ # CORS Configuration (passed to core module)
2038
+ variable "cors_allow_credentials" {
2039
+ description = "Whether to allow credentials in CORS requests"
2040
+ type = bool
2041
+ default = false
2042
+ }
2043
+
2044
+ variable "cors_allow_headers" {
2045
+ description = "List of allowed headers for CORS"
2046
+ type = list(string)
2047
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
2048
+ }
2049
+
2050
+ variable "cors_allow_methods" {
2051
+ description = "List of allowed HTTP methods for CORS"
2052
+ type = list(string)
2053
+ default = ["*"]
2054
+ }
2055
+
2056
+ variable "cors_allow_origins" {
2057
+ description = "List of allowed origins for CORS"
2058
+ type = list(string)
2059
+ default = ["*"]
2060
+ }
2061
+
2062
+ variable "cors_expose_headers" {
2063
+ description = "List of headers to expose in CORS responses"
2064
+ type = list(string)
2065
+ default = []
2066
+ }
2067
+
2068
+ variable "cors_max_age" {
2069
+ description = "Maximum age for CORS preflight requests in seconds"
2070
+ type = number
2071
+ default = 0
2072
+ }
2073
+
2074
+ # Tags
2075
+ variable "tags" {
2076
+ description = "Tags to apply to all resources"
2077
+ type = map(string)
2078
+ default = {}
2079
+ }
2080
+
2081
+ # Get current AWS region and account ID
2082
+ data "aws_region" "current" {}
2083
+ data "aws_caller_identity" "current" {}
2084
+
2085
+ # Resources
2086
+
2087
+ # Create Lambda ZIP file from the bundle directory
2088
+ data "archive_file" "lambda_zip" {
2089
+ type = "zip"
2090
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle"
2091
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
2092
+ }
2093
+
2094
+
2095
+ # Use the core HTTP API module
2096
+ module "http_api" {
2097
+ source = "../../../core/api/http-api"
2098
+
2099
+ api_name = "TestApi"
2100
+ api_description = "TestApi HTTP API"
2101
+ stage_name = "$default"
2102
+ stage_auto_deploy = true
2103
+
2104
+ # CORS Configuration
2105
+ cors_allow_credentials = var.cors_allow_credentials
2106
+ cors_allow_headers = var.cors_allow_headers
2107
+ cors_allow_methods = var.cors_allow_methods
2108
+ cors_allow_origins = var.cors_allow_origins
2109
+ cors_expose_headers = var.cors_expose_headers
2110
+ cors_max_age = var.cors_max_age
2111
+
2112
+ # Tags
2113
+ tags = var.tags
2114
+ }
2115
+
2116
+ # Lambda function
2117
+ # This configures a single "router" lambda to serve all requests
2118
+ resource "aws_lambda_function" "api_lambda" {
2119
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
2120
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
2121
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
2122
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
2123
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
2124
+ filename = data.archive_file.lambda_zip.output_path
2125
+ function_name = "TestApiHandler"
2126
+ role = aws_iam_role.lambda_execution_role.arn
2127
+ handler = "proj_test_api.main.handler"
2128
+ runtime = "python3.12"
2129
+ timeout = 30
2130
+ memory_size = 128
2131
+
2132
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
2133
+
2134
+ # Enable X-Ray tracing
2135
+ tracing_config {
2136
+ mode = "Active"
2137
+ }
2138
+
2139
+ environment {
2140
+ variables = merge({
2141
+ AWS_CONNECTION_REUSE_ENABLED = "1"
2142
+ }, var.env)
2143
+ }
2144
+
2145
+ tags = var.tags
2146
+ }
2147
+
2148
+ # IAM role for Lambda execution
2149
+ resource "aws_iam_role" "lambda_execution_role" {
2150
+ name = "TestApiHandler-execution-role"
2151
+
2152
+ assume_role_policy = jsonencode({
2153
+ Version = "2012-10-17"
2154
+ Statement = [
2155
+ {
2156
+ Action = "sts:AssumeRole"
2157
+ Effect = "Allow"
2158
+ Principal = {
2159
+ Service = "lambda.amazonaws.com"
2160
+ }
2161
+ }
2162
+ ]
2163
+ })
2164
+
2165
+ tags = var.tags
2166
+ }
2167
+
2168
+ # Attach basic execution policy to Lambda role
2169
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
2170
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
2171
+ role = aws_iam_role.lambda_execution_role.name
2172
+ }
2173
+
2174
+ # Attach X-Ray tracing policy to Lambda role
2175
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
2176
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
2177
+ role = aws_iam_role.lambda_execution_role.name
2178
+ }
2179
+
2180
+ # Additional IAM policies for Lambda (if provided)
2181
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
2182
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
2183
+ name = "TestApiHandler-additional-policies"
2184
+ role = aws_iam_role.lambda_execution_role.id
2185
+
2186
+ policy = jsonencode({
2187
+ Version = "2012-10-17"
2188
+ Statement = var.additional_iam_policy_statements
2189
+ })
2190
+ }
2191
+
2192
+ # CloudWatch Log Group for Lambda
2193
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
2194
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
2195
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
2196
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
2197
+ name = "/aws/lambda/TestApiHandler"
2198
+ tags = var.tags
2199
+ }
2200
+
2201
+
2202
+ # Lambda integration for HTTP API
2203
+ resource "aws_apigatewayv2_integration" "lambda_integration" {
2204
+ api_id = module.http_api.api_id
2205
+ integration_type = "AWS_PROXY"
2206
+ integration_uri = aws_lambda_function.api_lambda.invoke_arn
2207
+
2208
+ payload_format_version = "2.0"
2209
+ timeout_milliseconds = 30000
2210
+
2211
+ depends_on = [aws_lambda_function.api_lambda]
2212
+ }
2213
+
2214
+ # Route for proxy integration (catches all requests)
2215
+ resource "aws_apigatewayv2_route" "proxy_routes" {
2216
+ # NB: OPTIONS is omitted here since API Gateway manages responding to preflight requests
2217
+ # when cors settings are configured
2218
+ for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"])
2219
+
2220
+ api_id = module.http_api.api_id
2221
+ route_key = "\${each.key} /{proxy+}"
2222
+ target = "integrations/\${aws_apigatewayv2_integration.lambda_integration.id}"
2223
+
2224
+ authorization_type = "AWS_IAM"
2225
+
2226
+ depends_on = [aws_apigatewayv2_integration.lambda_integration]
2227
+ }
2228
+
2229
+ # Add API url to runtime config
2230
+ module "add_url_to_runtime_config" {
2231
+ source = "../../../core/runtime-config/entry"
2232
+
2233
+ key_path = "apis.TestApi"
2234
+ value = module.http_api.stage_invoke_url
2235
+
2236
+ depends_on = [module.http_api]
2237
+ }
2238
+
2239
+ # Lambda permission for API Gateway to invoke the function
2240
+ resource "aws_lambda_permission" "api_gateway_invoke" {
2241
+ statement_id = "AllowExecutionFromAPIGateway"
2242
+ action = "lambda:InvokeFunction"
2243
+ function_name = aws_lambda_function.api_lambda.function_name
2244
+ principal = "apigateway.amazonaws.com"
2245
+ source_arn = "\${module.http_api.api_execution_arn}/*/*"
2246
+
2247
+ depends_on = [module.http_api, aws_lambda_function.api_lambda]
2248
+ }
2249
+
2250
+ # Outputs
2251
+
2252
+ # API Gateway Outputs (from core module)
2253
+ output "api_id" {
2254
+ description = "ID of the HTTP API Gateway"
2255
+ value = module.http_api.api_id
2256
+ }
2257
+
2258
+ output "api_arn" {
2259
+ description = "ARN of the HTTP API Gateway"
2260
+ value = module.http_api.api_arn
2261
+ }
2262
+
2263
+ output "api_endpoint" {
2264
+ description = "Base URL of the HTTP API Gateway"
2265
+ value = module.http_api.api_endpoint
2266
+ }
2267
+
2268
+ output "api_execution_arn" {
2269
+ description = "Execution ARN of the HTTP API Gateway"
2270
+ value = module.http_api.api_execution_arn
2271
+ }
2272
+
2273
+ output "stage_invoke_url" {
2274
+ description = "Invoke URL of the API Gateway stage"
2275
+ value = module.http_api.stage_invoke_url
2276
+ }
2277
+
2278
+ output "stage_arn" {
2279
+ description = "ARN of the API Gateway stage"
2280
+ value = module.http_api.stage_arn
2281
+ }
2282
+
2283
+ output "stage_execution_arn" {
2284
+ description = "Execution ARN of the API Gateway stage"
2285
+ value = module.http_api.stage_execution_arn
2286
+ }
2287
+
2288
+ # Lambda Function Outputs
2289
+ output "lambda_function_name" {
2290
+ description = "Name of the Lambda function"
2291
+ value = aws_lambda_function.api_lambda.function_name
2292
+ }
2293
+
2294
+ output "lambda_function_arn" {
2295
+ description = "ARN of the Lambda function"
2296
+ value = aws_lambda_function.api_lambda.arn
2297
+ }
2298
+
2299
+ output "lambda_invoke_arn" {
2300
+ description = "Invoke ARN of the Lambda function"
2301
+ value = aws_lambda_function.api_lambda.invoke_arn
2302
+ }
2303
+
2304
+ output "lambda_qualified_arn" {
2305
+ description = "Qualified ARN of the Lambda function"
2306
+ value = aws_lambda_function.api_lambda.qualified_arn
2307
+ }
2308
+
2309
+ output "lambda_version" {
2310
+ description = "Version of the Lambda function"
2311
+ value = aws_lambda_function.api_lambda.version
2312
+ }
2313
+
2314
+ output "lambda_source_code_hash" {
2315
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
2316
+ value = aws_lambda_function.api_lambda.source_code_hash
2317
+ }
2318
+
2319
+ output "lambda_source_code_size" {
2320
+ description = "Size of the Lambda deployment package in bytes"
2321
+ value = aws_lambda_function.api_lambda.source_code_size
2322
+ }
2323
+
2324
+ # IAM Role Outputs
2325
+ output "lambda_execution_role_arn" {
2326
+ description = "ARN of the Lambda execution role"
2327
+ value = aws_iam_role.lambda_execution_role.arn
2328
+ }
2329
+
2330
+ output "lambda_execution_role_name" {
2331
+ description = "Name of the Lambda execution role"
2332
+ value = aws_iam_role.lambda_execution_role.name
2333
+ }
2334
+
2335
+ # Integration Outputs
2336
+ output "integration_id" {
2337
+ description = "ID of the Lambda integration"
2338
+ value = aws_apigatewayv2_integration.lambda_integration.id
2339
+ }
2340
+
2341
+ # CloudWatch Log Groups
2342
+ output "lambda_log_group_name" {
2343
+ description = "Name of the Lambda CloudWatch log group"
2344
+ value = aws_cloudwatch_log_group.lambda_logs.name
2345
+ }
2346
+
2347
+ output "lambda_log_group_arn" {
2348
+ description = "ARN of the Lambda CloudWatch log group"
2349
+ value = aws_cloudwatch_log_group.lambda_logs.arn
2350
+ }
2351
+
2352
+ output "api_log_group_name" {
2353
+ description = "Name of the API Gateway CloudWatch log group"
2354
+ value = module.http_api.api_log_group_name
2355
+ }
2356
+
2357
+ output "api_log_group_arn" {
2358
+ description = "ARN of the API Gateway CloudWatch log group"
2359
+ value = module.http_api.api_log_group_arn
2360
+ }",
2361
+ }
2362
+ `;
2363
+
2364
+ exports[`fastapi project generator > terraform iacProvider > should generate terraform files for HTTP API with None auth and snapshot them > terraform-http-none-files 1`] = `
2365
+ {
2366
+ "http-api.tf": "# Core HTTP API Gateway module
2367
+ # This module creates the API Gateway HTTP API, stage, and logging resources
2368
+
2369
+ terraform {
2370
+ required_version = ">= 1.0"
2371
+
2372
+ required_providers {
2373
+ aws = {
2374
+ source = "hashicorp/aws"
2375
+ version = "~> 6.0"
2376
+ }
2377
+ }
2378
+ }
2379
+
2380
+ # Core HTTP API Gateway Variables
2381
+
2382
+ variable "api_name" {
2383
+ description = "Name of the HTTP API Gateway"
2384
+ type = string
2385
+ }
2386
+
2387
+ variable "api_description" {
2388
+ description = "Description of the HTTP API Gateway"
2389
+ type = string
2390
+ default = "HTTP API Gateway"
2391
+ }
2392
+
2393
+ variable "stage_name" {
2394
+ description = "Name of the API Gateway stage"
2395
+ type = string
2396
+ default = "prod"
2397
+ }
2398
+
2399
+ variable "stage_auto_deploy" {
2400
+ description = "Whether to automatically deploy the API stage"
2401
+ type = bool
2402
+ default = true
2403
+ }
2404
+
2405
+ # CORS Configuration
2406
+
2407
+ variable "cors_allow_credentials" {
2408
+ description = "Whether to allow credentials in CORS requests"
2409
+ type = bool
2410
+ default = false
2411
+ }
2412
+
2413
+ variable "cors_allow_headers" {
2414
+ description = "List of allowed headers for CORS"
2415
+ type = list(string)
2416
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
2417
+ }
2418
+
2419
+ variable "cors_allow_methods" {
2420
+ description = "List of allowed HTTP methods for CORS"
2421
+ type = list(string)
2422
+ default = ["*"]
2423
+ }
2424
+
2425
+ variable "cors_allow_origins" {
2426
+ description = "List of allowed origins for CORS"
2427
+ type = list(string)
2428
+ default = ["*"]
2429
+ }
2430
+
2431
+ variable "cors_expose_headers" {
2432
+ description = "List of headers to expose in CORS responses"
2433
+ type = list(string)
2434
+ default = []
2435
+ }
2436
+
2437
+ variable "cors_max_age" {
2438
+ description = "Maximum age for CORS preflight requests in seconds"
2439
+ type = number
2440
+ default = 86400
2441
+ }
2442
+
2443
+ # Tags
2444
+
2445
+ variable "tags" {
2446
+ description = "Tags to apply to all resources"
2447
+ type = map(string)
2448
+ default = {}
2449
+ }
2450
+
2451
+ # Data sources
2452
+ data "aws_region" "current" {}
2453
+ data "aws_caller_identity" "current" {}
2454
+
2455
+ # Resources
2456
+
2457
+ # KMS key for CloudWatch log group encryption
2458
+ resource "aws_kms_key" "logs_key" {
2459
+ description = "KMS key for CloudWatch log group encryption"
2460
+ deletion_window_in_days = 7
2461
+ enable_key_rotation = true
2462
+
2463
+ policy = jsonencode({
2464
+ Version = "2012-10-17"
2465
+ Statement = [
2466
+ {
2467
+ Sid = "Enable IAM User Permissions"
2468
+ Effect = "Allow"
2469
+ Principal = {
2470
+ AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
2471
+ }
2472
+ Action = "kms:*"
2473
+ Resource = "*"
2474
+ },
2475
+ {
2476
+ Sid = "Allow CloudWatch Logs"
2477
+ Effect = "Allow"
2478
+ Principal = {
2479
+ Service = "logs.\${data.aws_region.current.name}.amazonaws.com"
2480
+ }
2481
+ Action = [
2482
+ "kms:Encrypt",
2483
+ "kms:Decrypt",
2484
+ "kms:ReEncrypt*",
2485
+ "kms:GenerateDataKey*",
2486
+ "kms:DescribeKey"
2487
+ ]
2488
+ Resource = "*"
2489
+ Condition = {
2490
+ ArnEquals = {
2491
+ "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}"
2492
+ }
2493
+ }
2494
+ }
2495
+ ]
2496
+ })
2497
+
2498
+ tags = var.tags
2499
+ }
2500
+
2501
+ resource "aws_kms_alias" "logs_key_alias" {
2502
+ name = "alias/\${var.api_name}-api-logs-encryption"
2503
+ target_key_id = aws_kms_key.logs_key.key_id
2504
+ }
2505
+
2506
+ # HTTP API Gateway
2507
+ resource "aws_apigatewayv2_api" "http_api" {
2508
+ name = var.api_name
2509
+ protocol_type = "HTTP"
2510
+ description = var.api_description
2511
+
2512
+ cors_configuration {
2513
+ allow_credentials = var.cors_allow_credentials
2514
+ allow_headers = var.cors_allow_headers
2515
+ allow_methods = var.cors_allow_methods
2516
+ allow_origins = var.cors_allow_origins
2517
+ expose_headers = var.cors_expose_headers
2518
+ max_age = var.cors_max_age
2519
+ }
2520
+
2521
+ tags = var.tags
2522
+ }
2523
+
2524
+ # API Gateway stage
2525
+ resource "aws_apigatewayv2_stage" "api_stage" {
2526
+ api_id = aws_apigatewayv2_api.http_api.id
2527
+ name = var.stage_name
2528
+ auto_deploy = var.stage_auto_deploy
2529
+
2530
+ access_log_settings {
2531
+ destination_arn = aws_cloudwatch_log_group.api_logs.arn
2532
+ format = jsonencode({
2533
+ requestId = "$context.requestId"
2534
+ ip = "$context.identity.sourceIp"
2535
+ requestTime = "$context.requestTime"
2536
+ httpMethod = "$context.httpMethod"
2537
+ routeKey = "$context.routeKey"
2538
+ status = "$context.status"
2539
+ protocol = "$context.protocol"
2540
+ responseLength = "$context.responseLength"
2541
+ error = "$context.error.message"
2542
+ integrationError = "$context.integrationErrorMessage"
2543
+ })
2544
+ }
2545
+
2546
+ default_route_settings {
2547
+ throttling_burst_limit = 5000
2548
+ throttling_rate_limit = 10000
2549
+ }
2550
+
2551
+ tags = var.tags
2552
+
2553
+ depends_on = [aws_cloudwatch_log_group.api_logs]
2554
+ }
2555
+
2556
+ # CloudWatch Log Group for API Gateway
2557
+ resource "aws_cloudwatch_log_group" "api_logs" {
2558
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
2559
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
2560
+ name = "/aws/apigateway/\${var.api_name}"
2561
+ kms_key_id = aws_kms_key.logs_key.arn
2562
+ tags = var.tags
2563
+ }
2564
+
2565
+ # Outputs
2566
+
2567
+ output "api_id" {
2568
+ description = "ID of the HTTP API Gateway"
2569
+ value = aws_apigatewayv2_api.http_api.id
2570
+ }
2571
+
2572
+ output "api_arn" {
2573
+ description = "ARN of the HTTP API Gateway"
2574
+ value = aws_apigatewayv2_api.http_api.arn
2575
+ }
2576
+
2577
+ output "api_endpoint" {
2578
+ description = "Base URL of the HTTP API Gateway"
2579
+ value = aws_apigatewayv2_api.http_api.api_endpoint
2580
+ }
2581
+
2582
+ output "api_execution_arn" {
2583
+ description = "Execution ARN of the HTTP API Gateway"
2584
+ value = aws_apigatewayv2_api.http_api.execution_arn
2585
+ }
2586
+
2587
+ output "stage_id" {
2588
+ description = "ID of the API Gateway stage"
2589
+ value = aws_apigatewayv2_stage.api_stage.id
2590
+ }
2591
+
2592
+ output "stage_arn" {
2593
+ description = "ARN of the API Gateway stage"
2594
+ value = aws_apigatewayv2_stage.api_stage.arn
2595
+ }
2596
+
2597
+ output "stage_execution_arn" {
2598
+ description = "Execution ARN of the API Gateway stage"
2599
+ value = aws_apigatewayv2_stage.api_stage.execution_arn
2600
+ }
2601
+
2602
+ output "stage_invoke_url" {
2603
+ description = "Invoke URL of the API Gateway stage"
2604
+ value = aws_apigatewayv2_stage.api_stage.invoke_url
2605
+ }
2606
+
2607
+ output "api_log_group_name" {
2608
+ description = "Name of the API Gateway CloudWatch log group"
2609
+ value = aws_cloudwatch_log_group.api_logs.name
2610
+ }
2611
+
2612
+ output "api_log_group_arn" {
2613
+ description = "ARN of the API Gateway CloudWatch log group"
2614
+ value = aws_cloudwatch_log_group.api_logs.arn
2615
+ }",
2616
+ "test-api.tf": "terraform {
2617
+ required_version = ">= 1.0"
2618
+
2619
+ required_providers {
2620
+ aws = {
2621
+ source = "hashicorp/aws"
2622
+ version = "~> 6.0"
2623
+ }
2624
+ }
2625
+ }
2626
+
2627
+
2628
+ variable "env" {
2629
+ description = "Environment variables for the Lambda function"
2630
+ type = map(string)
2631
+ default = {}
2632
+ }
2633
+
2634
+ variable "additional_iam_policy_statements" {
2635
+ description = "Additional IAM policy statements for the Lambda function"
2636
+ type = list(object({
2637
+ Effect = string
2638
+ Action = list(string)
2639
+ Resource = list(string)
2640
+ }))
2641
+ default = []
2642
+ }
2643
+
2644
+ # CORS Configuration (passed to core module)
2645
+ variable "cors_allow_credentials" {
2646
+ description = "Whether to allow credentials in CORS requests"
2647
+ type = bool
2648
+ default = false
2649
+ }
2650
+
2651
+ variable "cors_allow_headers" {
2652
+ description = "List of allowed headers for CORS"
2653
+ type = list(string)
2654
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
2655
+ }
2656
+
2657
+ variable "cors_allow_methods" {
2658
+ description = "List of allowed HTTP methods for CORS"
2659
+ type = list(string)
2660
+ default = ["*"]
2661
+ }
2662
+
2663
+ variable "cors_allow_origins" {
2664
+ description = "List of allowed origins for CORS"
2665
+ type = list(string)
2666
+ default = ["*"]
2667
+ }
2668
+
2669
+ variable "cors_expose_headers" {
2670
+ description = "List of headers to expose in CORS responses"
2671
+ type = list(string)
2672
+ default = []
2673
+ }
2674
+
2675
+ variable "cors_max_age" {
2676
+ description = "Maximum age for CORS preflight requests in seconds"
2677
+ type = number
2678
+ default = 0
2679
+ }
2680
+
2681
+ # Tags
2682
+ variable "tags" {
2683
+ description = "Tags to apply to all resources"
2684
+ type = map(string)
2685
+ default = {}
2686
+ }
2687
+
2688
+ # Get current AWS region and account ID
2689
+ data "aws_region" "current" {}
2690
+ data "aws_caller_identity" "current" {}
2691
+
2692
+ # Resources
2693
+
2694
+ # Create Lambda ZIP file from the bundle directory
2695
+ data "archive_file" "lambda_zip" {
2696
+ type = "zip"
2697
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle"
2698
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
2699
+ }
2700
+
2701
+
2702
+ # Use the core HTTP API module
2703
+ module "http_api" {
2704
+ source = "../../../core/api/http-api"
2705
+
2706
+ api_name = "TestApi"
2707
+ api_description = "TestApi HTTP API"
2708
+ stage_name = "$default"
2709
+ stage_auto_deploy = true
2710
+
2711
+ # CORS Configuration
2712
+ cors_allow_credentials = var.cors_allow_credentials
2713
+ cors_allow_headers = var.cors_allow_headers
2714
+ cors_allow_methods = var.cors_allow_methods
2715
+ cors_allow_origins = var.cors_allow_origins
2716
+ cors_expose_headers = var.cors_expose_headers
2717
+ cors_max_age = var.cors_max_age
2718
+
2719
+ # Tags
2720
+ tags = var.tags
2721
+ }
2722
+
2723
+ # Lambda function
2724
+ # This configures a single "router" lambda to serve all requests
2725
+ resource "aws_lambda_function" "api_lambda" {
2726
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
2727
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
2728
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
2729
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
2730
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
2731
+ filename = data.archive_file.lambda_zip.output_path
2732
+ function_name = "TestApiHandler"
2733
+ role = aws_iam_role.lambda_execution_role.arn
2734
+ handler = "proj_test_api.main.handler"
2735
+ runtime = "python3.12"
2736
+ timeout = 30
2737
+ memory_size = 128
2738
+
2739
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
2740
+
2741
+ # Enable X-Ray tracing
2742
+ tracing_config {
2743
+ mode = "Active"
2744
+ }
2745
+
2746
+ environment {
2747
+ variables = merge({
2748
+ AWS_CONNECTION_REUSE_ENABLED = "1"
2749
+ }, var.env)
2750
+ }
2751
+
2752
+ tags = var.tags
2753
+ }
2754
+
2755
+ # IAM role for Lambda execution
2756
+ resource "aws_iam_role" "lambda_execution_role" {
2757
+ name = "TestApiHandler-execution-role"
2758
+
2759
+ assume_role_policy = jsonencode({
2760
+ Version = "2012-10-17"
2761
+ Statement = [
2762
+ {
2763
+ Action = "sts:AssumeRole"
2764
+ Effect = "Allow"
2765
+ Principal = {
2766
+ Service = "lambda.amazonaws.com"
2767
+ }
2768
+ }
2769
+ ]
2770
+ })
2771
+
2772
+ tags = var.tags
2773
+ }
2774
+
2775
+ # Attach basic execution policy to Lambda role
2776
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
2777
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
2778
+ role = aws_iam_role.lambda_execution_role.name
2779
+ }
2780
+
2781
+ # Attach X-Ray tracing policy to Lambda role
2782
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
2783
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
2784
+ role = aws_iam_role.lambda_execution_role.name
2785
+ }
2786
+
2787
+ # Additional IAM policies for Lambda (if provided)
2788
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
2789
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
2790
+ name = "TestApiHandler-additional-policies"
2791
+ role = aws_iam_role.lambda_execution_role.id
2792
+
2793
+ policy = jsonencode({
2794
+ Version = "2012-10-17"
2795
+ Statement = var.additional_iam_policy_statements
2796
+ })
2797
+ }
2798
+
2799
+ # CloudWatch Log Group for Lambda
2800
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
2801
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
2802
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
2803
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
2804
+ name = "/aws/lambda/TestApiHandler"
2805
+ tags = var.tags
2806
+ }
2807
+
2808
+
2809
+ # Lambda integration for HTTP API
2810
+ resource "aws_apigatewayv2_integration" "lambda_integration" {
2811
+ api_id = module.http_api.api_id
2812
+ integration_type = "AWS_PROXY"
2813
+ integration_uri = aws_lambda_function.api_lambda.invoke_arn
2814
+
2815
+ payload_format_version = "2.0"
2816
+ timeout_milliseconds = 30000
2817
+
2818
+ depends_on = [aws_lambda_function.api_lambda]
2819
+ }
2820
+
2821
+ # Route for proxy integration (catches all requests)
2822
+ resource "aws_apigatewayv2_route" "proxy_routes" {
2823
+ # NB: OPTIONS is omitted here since API Gateway manages responding to preflight requests
2824
+ # when cors settings are configured
2825
+ for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"])
2826
+
2827
+ api_id = module.http_api.api_id
2828
+ route_key = "\${each.key} /{proxy+}"
2829
+ target = "integrations/\${aws_apigatewayv2_integration.lambda_integration.id}"
2830
+
2831
+ # Note: you may wish to suppress the checkov rule CKV_AWS_309 if you are absolutely sure you
2832
+ # need a public API without authentication
2833
+ authorization_type = "NONE"
2834
+
2835
+ depends_on = [aws_apigatewayv2_integration.lambda_integration]
2836
+ }
2837
+
2838
+ # Add API url to runtime config
2839
+ module "add_url_to_runtime_config" {
2840
+ source = "../../../core/runtime-config/entry"
2841
+
2842
+ key_path = "apis.TestApi"
2843
+ value = module.http_api.stage_invoke_url
2844
+
2845
+ depends_on = [module.http_api]
2846
+ }
2847
+
2848
+ # Lambda permission for API Gateway to invoke the function
2849
+ resource "aws_lambda_permission" "api_gateway_invoke" {
2850
+ statement_id = "AllowExecutionFromAPIGateway"
2851
+ action = "lambda:InvokeFunction"
2852
+ function_name = aws_lambda_function.api_lambda.function_name
2853
+ principal = "apigateway.amazonaws.com"
2854
+ source_arn = "\${module.http_api.api_execution_arn}/*/*"
2855
+
2856
+ depends_on = [module.http_api, aws_lambda_function.api_lambda]
2857
+ }
2858
+
2859
+ # Outputs
2860
+
2861
+ # API Gateway Outputs (from core module)
2862
+ output "api_id" {
2863
+ description = "ID of the HTTP API Gateway"
2864
+ value = module.http_api.api_id
2865
+ }
2866
+
2867
+ output "api_arn" {
2868
+ description = "ARN of the HTTP API Gateway"
2869
+ value = module.http_api.api_arn
2870
+ }
2871
+
2872
+ output "api_endpoint" {
2873
+ description = "Base URL of the HTTP API Gateway"
2874
+ value = module.http_api.api_endpoint
2875
+ }
2876
+
2877
+ output "api_execution_arn" {
2878
+ description = "Execution ARN of the HTTP API Gateway"
2879
+ value = module.http_api.api_execution_arn
2880
+ }
2881
+
2882
+ output "stage_invoke_url" {
2883
+ description = "Invoke URL of the API Gateway stage"
2884
+ value = module.http_api.stage_invoke_url
2885
+ }
2886
+
2887
+ output "stage_arn" {
2888
+ description = "ARN of the API Gateway stage"
2889
+ value = module.http_api.stage_arn
2890
+ }
2891
+
2892
+ output "stage_execution_arn" {
2893
+ description = "Execution ARN of the API Gateway stage"
2894
+ value = module.http_api.stage_execution_arn
2895
+ }
2896
+
2897
+ # Lambda Function Outputs
2898
+ output "lambda_function_name" {
2899
+ description = "Name of the Lambda function"
2900
+ value = aws_lambda_function.api_lambda.function_name
2901
+ }
2902
+
2903
+ output "lambda_function_arn" {
2904
+ description = "ARN of the Lambda function"
2905
+ value = aws_lambda_function.api_lambda.arn
2906
+ }
2907
+
2908
+ output "lambda_invoke_arn" {
2909
+ description = "Invoke ARN of the Lambda function"
2910
+ value = aws_lambda_function.api_lambda.invoke_arn
2911
+ }
2912
+
2913
+ output "lambda_qualified_arn" {
2914
+ description = "Qualified ARN of the Lambda function"
2915
+ value = aws_lambda_function.api_lambda.qualified_arn
2916
+ }
2917
+
2918
+ output "lambda_version" {
2919
+ description = "Version of the Lambda function"
2920
+ value = aws_lambda_function.api_lambda.version
2921
+ }
2922
+
2923
+ output "lambda_source_code_hash" {
2924
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
2925
+ value = aws_lambda_function.api_lambda.source_code_hash
2926
+ }
2927
+
2928
+ output "lambda_source_code_size" {
2929
+ description = "Size of the Lambda deployment package in bytes"
2930
+ value = aws_lambda_function.api_lambda.source_code_size
2931
+ }
2932
+
2933
+ # IAM Role Outputs
2934
+ output "lambda_execution_role_arn" {
2935
+ description = "ARN of the Lambda execution role"
2936
+ value = aws_iam_role.lambda_execution_role.arn
2937
+ }
2938
+
2939
+ output "lambda_execution_role_name" {
2940
+ description = "Name of the Lambda execution role"
2941
+ value = aws_iam_role.lambda_execution_role.name
2942
+ }
2943
+
2944
+ # Integration Outputs
2945
+ output "integration_id" {
2946
+ description = "ID of the Lambda integration"
2947
+ value = aws_apigatewayv2_integration.lambda_integration.id
2948
+ }
2949
+
2950
+ # CloudWatch Log Groups
2951
+ output "lambda_log_group_name" {
2952
+ description = "Name of the Lambda CloudWatch log group"
2953
+ value = aws_cloudwatch_log_group.lambda_logs.name
2954
+ }
2955
+
2956
+ output "lambda_log_group_arn" {
2957
+ description = "ARN of the Lambda CloudWatch log group"
2958
+ value = aws_cloudwatch_log_group.lambda_logs.arn
2959
+ }
2960
+
2961
+ output "api_log_group_name" {
2962
+ description = "Name of the API Gateway CloudWatch log group"
2963
+ value = module.http_api.api_log_group_name
2964
+ }
2965
+
2966
+ output "api_log_group_arn" {
2967
+ description = "ARN of the API Gateway CloudWatch log group"
2968
+ value = module.http_api.api_log_group_arn
2969
+ }",
2970
+ }
2971
+ `;
2972
+
2973
+ exports[`fastapi project generator > terraform iacProvider > should generate terraform files for REST API with Cognito auth and snapshot them > terraform-rest-cognito-files 1`] = `
2974
+ {
2975
+ "rest-api.tf": "# Core REST API Gateway module
2976
+ # This module creates the API Gateway REST API, deployment, stage, and logging resources
2977
+
2978
+ terraform {
2979
+ required_version = ">= 1.0"
2980
+
2981
+ required_providers {
2982
+ aws = {
2983
+ source = "hashicorp/aws"
2984
+ version = "~> 6.0"
2985
+ }
2986
+ }
2987
+ }
2988
+
2989
+ # Core REST API Gateway Variables
2990
+
2991
+ variable "api_name" {
2992
+ description = "Name of the REST API Gateway"
2993
+ type = string
2994
+ }
2995
+
2996
+ variable "api_description" {
2997
+ description = "Description of the REST API Gateway"
2998
+ type = string
2999
+ default = "REST API Gateway"
3000
+ }
3001
+
3002
+ variable "stage_name" {
3003
+ description = "Name of the API Gateway stage"
3004
+ type = string
3005
+ default = "prod"
3006
+ }
3007
+
3008
+ variable "stage_auto_deploy" {
3009
+ description = "Whether to automatically deploy the API stage"
3010
+ type = bool
3011
+ default = true
3012
+ }
3013
+
3014
+ # CORS Configuration
3015
+
3016
+ variable "cors_allow_headers" {
3017
+ description = "List of allowed headers for CORS"
3018
+ type = list(string)
3019
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
3020
+ }
3021
+
3022
+ variable "cors_allow_methods" {
3023
+ description = "List of allowed HTTP methods for CORS"
3024
+ type = list(string)
3025
+ default = ["*"]
3026
+ }
3027
+
3028
+ variable "cors_allow_origins" {
3029
+ description = "List of allowed origins for CORS"
3030
+ type = list(string)
3031
+ default = ["*"]
3032
+ }
3033
+
3034
+ # Tags
3035
+
3036
+ variable "tags" {
3037
+ description = "Tags to apply to all resources"
3038
+ type = map(string)
3039
+ default = {}
3040
+ }
3041
+
3042
+ # Data sources
3043
+ data "aws_region" "current" {}
3044
+ data "aws_caller_identity" "current" {}
3045
+
3046
+ # Resources
3047
+
3048
+ # Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
3049
+
3050
+ # REST API Gateway
3051
+ resource "aws_api_gateway_rest_api" "rest_api" {
3052
+ name = var.api_name
3053
+ description = var.api_description
3054
+
3055
+ endpoint_configuration {
3056
+ types = ["REGIONAL"]
3057
+ }
3058
+
3059
+ lifecycle {
3060
+ create_before_destroy = true
3061
+ }
3062
+
3063
+ tags = var.tags
3064
+ }
3065
+
3066
+ # Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
3067
+ # after all methods and integrations are defined
3068
+
3069
+ # Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
3070
+
3071
+ # Gateway Response for CORS (4XX errors)
3072
+ resource "aws_api_gateway_gateway_response" "cors_4xx" {
3073
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
3074
+ response_type = "DEFAULT_4XX"
3075
+
3076
+ response_parameters = {
3077
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
3078
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
3079
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
3080
+ }
3081
+ }
3082
+
3083
+ # Gateway Response for CORS (5XX errors)
3084
+ resource "aws_api_gateway_gateway_response" "cors_5xx" {
3085
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
3086
+ response_type = "DEFAULT_5XX"
3087
+
3088
+ response_parameters = {
3089
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
3090
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
3091
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
3092
+ }
3093
+ }
3094
+
3095
+ # Outputs
3096
+
3097
+ output "api_id" {
3098
+ description = "ID of the REST API Gateway"
3099
+ value = aws_api_gateway_rest_api.rest_api.id
3100
+ }
3101
+
3102
+ output "api_arn" {
3103
+ description = "ARN of the REST API Gateway"
3104
+ value = aws_api_gateway_rest_api.rest_api.arn
3105
+ }
3106
+
3107
+ output "api_endpoint" {
3108
+ description = "Base URL of the REST API Gateway"
3109
+ value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
3110
+ }
3111
+
3112
+ output "api_execution_arn" {
3113
+ description = "Execution ARN of the REST API Gateway"
3114
+ value = aws_api_gateway_rest_api.rest_api.execution_arn
3115
+ }
3116
+
3117
+ output "api_root_resource_id" {
3118
+ description = "Root resource ID of the REST API Gateway"
3119
+ value = aws_api_gateway_rest_api.rest_api.root_resource_id
3120
+ }
3121
+
3122
+ # Note: Stage and deployment outputs are provided by the consuming module
3123
+
3124
+ # Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
3125
+ "test-api.tf": "terraform {
3126
+ required_version = ">= 1.0"
3127
+
3128
+ required_providers {
3129
+ aws = {
3130
+ source = "hashicorp/aws"
3131
+ version = "~> 6.0"
3132
+ }
3133
+ }
3134
+ }
3135
+
3136
+ # Authentication Configuration
3137
+ variable "user_pool_id" {
3138
+ description = "Cognito User Pool ID for authentication"
3139
+ type = string
3140
+ }
3141
+
3142
+ variable "user_pool_client_ids" {
3143
+ description = "List of Cognito User Pool Client IDs"
3144
+ type = list(string)
3145
+ }
3146
+
3147
+ variable "env" {
3148
+ description = "Environment variables for the Lambda function"
3149
+ type = map(string)
3150
+ default = {}
3151
+ }
3152
+
3153
+ variable "additional_iam_policy_statements" {
3154
+ description = "Additional IAM policy statements for the Lambda function"
3155
+ type = list(object({
3156
+ Effect = string
3157
+ Action = list(string)
3158
+ Resource = list(string)
3159
+ }))
3160
+ default = []
3161
+ }
3162
+
3163
+ # CORS Configuration (passed to core module)
3164
+
3165
+ variable "cors_allow_headers" {
3166
+ description = "List of allowed headers for CORS"
3167
+ type = list(string)
3168
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
3169
+ }
3170
+
3171
+ variable "cors_allow_methods" {
3172
+ description = "List of allowed HTTP methods for CORS"
3173
+ type = list(string)
3174
+ default = ["*"]
3175
+ }
3176
+
3177
+ variable "cors_allow_origins" {
3178
+ description = "List of allowed origins for CORS"
3179
+ type = list(string)
3180
+ default = ["*"]
3181
+ }
3182
+
3183
+ # Tags
3184
+ variable "tags" {
3185
+ description = "Tags to apply to all resources"
3186
+ type = map(string)
3187
+ default = {}
3188
+ }
3189
+
3190
+ # Get current AWS region and account ID
3191
+ data "aws_region" "current" {}
3192
+ data "aws_caller_identity" "current" {}
3193
+
3194
+ # Resources
3195
+
3196
+ # Create Lambda ZIP file from the FastAPI bundle directory
3197
+ data "archive_file" "lambda_zip" {
3198
+ type = "zip"
3199
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle"
3200
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
3201
+ }
3202
+
3203
+ # Use the core REST API module
3204
+ module "rest_api" {
3205
+ source = "../../../core/api/rest-api"
3206
+
3207
+ api_name = "TestApi"
3208
+ api_description = "TestApi REST API"
3209
+ stage_name = "prod"
3210
+ stage_auto_deploy = true
3211
+
3212
+ # CORS Configuration
3213
+ cors_allow_headers = var.cors_allow_headers
3214
+ cors_allow_methods = var.cors_allow_methods
3215
+ cors_allow_origins = var.cors_allow_origins
3216
+
3217
+ # Tags
3218
+ tags = var.tags
3219
+ }
3220
+
3221
+ # Lambda function
3222
+ resource "aws_lambda_function" "api_lambda" {
3223
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
3224
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
3225
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
3226
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
3227
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
3228
+ filename = data.archive_file.lambda_zip.output_path
3229
+ function_name = "TestApiHandler"
3230
+ role = aws_iam_role.lambda_execution_role.arn
3231
+ handler = "proj_test_api.main.handler"
3232
+ runtime = "python3.12"
3233
+ timeout = 30
3234
+ memory_size = 128
3235
+
3236
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
3237
+
3238
+ # Enable X-Ray tracing
3239
+ tracing_config {
3240
+ mode = "Active"
3241
+ }
3242
+
3243
+ environment {
3244
+ variables = merge({
3245
+ AWS_CONNECTION_REUSE_ENABLED = "1"
3246
+ }, var.env)
3247
+ }
3248
+
3249
+ tags = var.tags
3250
+ }
3251
+
3252
+ # IAM role for Lambda execution
3253
+ resource "aws_iam_role" "lambda_execution_role" {
3254
+ name = "TestApiHandler-execution-role"
3255
+
3256
+ assume_role_policy = jsonencode({
3257
+ Version = "2012-10-17"
3258
+ Statement = [
3259
+ {
3260
+ Action = "sts:AssumeRole"
3261
+ Effect = "Allow"
3262
+ Principal = {
3263
+ Service = "lambda.amazonaws.com"
3264
+ }
3265
+ }
3266
+ ]
3267
+ })
3268
+
3269
+ tags = var.tags
3270
+ }
3271
+
3272
+ # Attach basic execution policy to Lambda role
3273
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
3274
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
3275
+ role = aws_iam_role.lambda_execution_role.name
3276
+ }
3277
+
3278
+ # Attach X-Ray tracing policy to Lambda role
3279
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
3280
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
3281
+ role = aws_iam_role.lambda_execution_role.name
3282
+ }
3283
+
3284
+ # Additional IAM policies for Lambda (if provided)
3285
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
3286
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
3287
+ name = "TestApiHandler-additional-policies"
3288
+ role = aws_iam_role.lambda_execution_role.id
3289
+
3290
+ policy = jsonencode({
3291
+ Version = "2012-10-17"
3292
+ Statement = var.additional_iam_policy_statements
3293
+ })
3294
+ }
3295
+
3296
+ # CloudWatch Log Group for Lambda
3297
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
3298
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
3299
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
3300
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
3301
+ name = "/aws/lambda/TestApiHandler"
3302
+ tags = var.tags
3303
+ }
3304
+
3305
+ # Cognito User Pool Authorizer
3306
+ resource "aws_api_gateway_authorizer" "cognito_authorizer" {
3307
+ name = "TestApiAuthorizer"
3308
+ rest_api_id = module.rest_api.api_id
3309
+ type = "COGNITO_USER_POOLS"
3310
+ provider_arns = ["arn:aws:cognito-idp:\${data.aws_region.current.name}:\${data.aws_caller_identity.current.account_id}:userpool/\${var.user_pool_id}"]
3311
+ identity_source = "method.request.header.Authorization"
3312
+ }
3313
+
3314
+ # Create proxy resource (captures all paths)
3315
+ resource "aws_api_gateway_resource" "proxy_resource" {
3316
+ rest_api_id = module.rest_api.api_id
3317
+ parent_id = module.rest_api.api_root_resource_id
3318
+ path_part = "{proxy+}"
3319
+ }
3320
+
3321
+ # Lambda integration for REST API
3322
+ resource "aws_api_gateway_integration" "lambda_integration" {
3323
+ rest_api_id = module.rest_api.api_id
3324
+ resource_id = aws_api_gateway_resource.proxy_resource.id
3325
+ http_method = aws_api_gateway_method.proxy_method.http_method
3326
+
3327
+ integration_http_method = "POST"
3328
+ type = "AWS_PROXY"
3329
+ uri = aws_lambda_function.api_lambda.invoke_arn
3330
+
3331
+ depends_on = [aws_lambda_function.api_lambda]
3332
+ }
3333
+
3334
+ # Method for proxy integration
3335
+ resource "aws_api_gateway_method" "proxy_method" {
3336
+ #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
3337
+ rest_api_id = module.rest_api.api_id
3338
+ resource_id = aws_api_gateway_resource.proxy_resource.id
3339
+ http_method = "ANY"
3340
+
3341
+ authorization = "COGNITO_USER_POOLS"
3342
+ authorizer_id = aws_api_gateway_authorizer.cognito_authorizer.id
3343
+
3344
+ request_parameters = {
3345
+ "method.request.path.proxy" = true
3346
+ }
3347
+
3348
+ depends_on = [aws_api_gateway_authorizer.cognito_authorizer]
3349
+ }
3350
+
3351
+ # OPTIONS method for CORS preflight
3352
+ resource "aws_api_gateway_method" "options_method" {
3353
+ #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
3354
+ #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
3355
+ rest_api_id = module.rest_api.api_id
3356
+ resource_id = aws_api_gateway_resource.proxy_resource.id
3357
+ http_method = "OPTIONS"
3358
+ authorization = "NONE"
3359
+ }
3360
+
3361
+ # CORS integration for OPTIONS method
3362
+ resource "aws_api_gateway_integration" "options_integration" {
3363
+ rest_api_id = module.rest_api.api_id
3364
+ resource_id = aws_api_gateway_resource.proxy_resource.id
3365
+ http_method = aws_api_gateway_method.options_method.http_method
3366
+
3367
+ type = "MOCK"
3368
+ request_templates = {
3369
+ "application/json" = "{\\"statusCode\\": 204}"
3370
+ }
3371
+ }
3372
+
3373
+ # OPTIONS method response
3374
+ resource "aws_api_gateway_method_response" "options_response" {
3375
+ rest_api_id = module.rest_api.api_id
3376
+ resource_id = aws_api_gateway_resource.proxy_resource.id
3377
+ http_method = aws_api_gateway_method.options_method.http_method
3378
+ status_code = "204"
3379
+
3380
+ response_parameters = {
3381
+ "method.response.header.Access-Control-Allow-Headers" = true
3382
+ "method.response.header.Access-Control-Allow-Methods" = true
3383
+ "method.response.header.Access-Control-Allow-Origin" = true
3384
+ }
3385
+ }
3386
+
3387
+ # OPTIONS integration response
3388
+ resource "aws_api_gateway_integration_response" "options_integration_response" {
3389
+ rest_api_id = module.rest_api.api_id
3390
+ resource_id = aws_api_gateway_resource.proxy_resource.id
3391
+ http_method = aws_api_gateway_method.options_method.http_method
3392
+ status_code = aws_api_gateway_method_response.options_response.status_code
3393
+
3394
+ response_parameters = {
3395
+ "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
3396
+ "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
3397
+ "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
3398
+ }
3399
+ }
3400
+
3401
+ # API Gateway deployment
3402
+ resource "aws_api_gateway_deployment" "api_deployment" {
3403
+ rest_api_id = module.rest_api.api_id
3404
+
3405
+ triggers = {
3406
+ redeployment = sha1(jsonencode([
3407
+ aws_api_gateway_resource.proxy_resource.id,
3408
+ aws_api_gateway_method.proxy_method.id,
3409
+ aws_api_gateway_integration.lambda_integration.id,
3410
+ aws_api_gateway_method.options_method.id,
3411
+ aws_api_gateway_integration.options_integration.id,
3412
+ ]))
3413
+ }
3414
+
3415
+ lifecycle {
3416
+ create_before_destroy = true
3417
+ }
3418
+
3419
+ depends_on = [
3420
+ aws_api_gateway_method.proxy_method,
3421
+ aws_api_gateway_integration.lambda_integration,
3422
+ aws_api_gateway_method.options_method,
3423
+ aws_api_gateway_integration.options_integration,
3424
+ aws_api_gateway_method_response.options_response,
3425
+ aws_api_gateway_integration_response.options_integration_response,
3426
+ ]
3427
+ }
3428
+
3429
+ # API Gateway stage
3430
+ resource "aws_api_gateway_stage" "api_stage" {
3431
+ #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
3432
+ #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
3433
+ #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
3434
+ #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
3435
+ deployment_id = aws_api_gateway_deployment.api_deployment.id
3436
+ rest_api_id = module.rest_api.api_id
3437
+ stage_name = "prod"
3438
+ xray_tracing_enabled = true
3439
+
3440
+ tags = var.tags
3441
+
3442
+ depends_on = [aws_api_gateway_deployment.api_deployment]
3443
+ }
3444
+
3445
+ # API Gateway Resource Policy
3446
+ resource "aws_api_gateway_rest_api_policy" "api_policy" {
3447
+ rest_api_id = module.rest_api.api_id
3448
+
3449
+ policy = jsonencode({
3450
+ Version = "2012-10-17"
3451
+ Statement = [
3452
+ {
3453
+ # Allow all callers to invoke the API in the resource policy, since auth is handled by Cognito
3454
+ Effect = "Allow"
3455
+ Principal = "*"
3456
+ Action = "execute-api:Invoke"
3457
+ Resource = "execute-api:/*"
3458
+ }
3459
+ ]
3460
+ })
3461
+ }
3462
+
3463
+ # Lambda permission for API Gateway to invoke the function
3464
+ resource "aws_lambda_permission" "api_gateway_invoke" {
3465
+ statement_id = "AllowExecutionFromAPIGateway"
3466
+ action = "lambda:InvokeFunction"
3467
+ function_name = aws_lambda_function.api_lambda.function_name
3468
+ principal = "apigateway.amazonaws.com"
3469
+ source_arn = "\${module.rest_api.api_execution_arn}/*/*"
3470
+
3471
+ depends_on = [module.rest_api, aws_lambda_function.api_lambda]
3472
+ }
3473
+
3474
+ # Add API url to runtime config
3475
+ module "add_url_to_runtime_config" {
3476
+ source = "../../../core/runtime-config/entry"
3477
+
3478
+ key_path = "apis.TestApi"
3479
+ value = aws_api_gateway_stage.api_stage.invoke_url
3480
+
3481
+ depends_on = [aws_api_gateway_stage.api_stage]
3482
+ }
3483
+
3484
+ # Outputs
3485
+
3486
+ # API Gateway Outputs (from core module)
3487
+ output "api_id" {
3488
+ description = "ID of the REST API Gateway"
3489
+ value = module.rest_api.api_id
3490
+ }
3491
+
3492
+ output "api_arn" {
3493
+ description = "ARN of the REST API Gateway"
3494
+ value = module.rest_api.api_arn
3495
+ }
3496
+
3497
+ output "api_endpoint" {
3498
+ description = "Base URL of the REST API Gateway"
3499
+ value = module.rest_api.api_endpoint
3500
+ }
3501
+
3502
+ output "api_execution_arn" {
3503
+ description = "Execution ARN of the REST API Gateway"
3504
+ value = module.rest_api.api_execution_arn
3505
+ }
3506
+
3507
+ output "stage_invoke_url" {
3508
+ description = "Invoke URL of the API Gateway stage"
3509
+ value = aws_api_gateway_stage.api_stage.invoke_url
3510
+ }
3511
+
3512
+ output "stage_arn" {
3513
+ description = "ARN of the API Gateway stage"
3514
+ value = aws_api_gateway_stage.api_stage.arn
3515
+ }
3516
+
3517
+ output "stage_execution_arn" {
3518
+ description = "Execution ARN of the API Gateway stage"
3519
+ value = aws_api_gateway_stage.api_stage.execution_arn
3520
+ }
3521
+
3522
+ output "deployment_id" {
3523
+ description = "ID of the API Gateway deployment"
3524
+ value = aws_api_gateway_deployment.api_deployment.id
3525
+ }
3526
+
3527
+ output "stage_id" {
3528
+ description = "ID of the API Gateway stage"
3529
+ value = aws_api_gateway_stage.api_stage.id
3530
+ }
3531
+
3532
+ # Lambda Function Outputs
3533
+ output "lambda_function_name" {
3534
+ description = "Name of the Lambda function"
3535
+ value = aws_lambda_function.api_lambda.function_name
3536
+ }
3537
+
3538
+ output "lambda_function_arn" {
3539
+ description = "ARN of the Lambda function"
3540
+ value = aws_lambda_function.api_lambda.arn
3541
+ }
3542
+
3543
+ output "lambda_invoke_arn" {
3544
+ description = "Invoke ARN of the Lambda function"
3545
+ value = aws_lambda_function.api_lambda.invoke_arn
3546
+ }
3547
+
3548
+ output "lambda_qualified_arn" {
3549
+ description = "Qualified ARN of the Lambda function"
3550
+ value = aws_lambda_function.api_lambda.qualified_arn
3551
+ }
3552
+
3553
+ output "lambda_version" {
3554
+ description = "Version of the Lambda function"
3555
+ value = aws_lambda_function.api_lambda.version
3556
+ }
3557
+
3558
+ output "lambda_source_code_hash" {
3559
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
3560
+ value = aws_lambda_function.api_lambda.source_code_hash
3561
+ }
3562
+
3563
+ output "lambda_source_code_size" {
3564
+ description = "Size of the Lambda deployment package in bytes"
3565
+ value = aws_lambda_function.api_lambda.source_code_size
3566
+ }
3567
+
3568
+ # IAM Role Outputs
3569
+ output "lambda_execution_role_arn" {
3570
+ description = "ARN of the Lambda execution role"
3571
+ value = aws_iam_role.lambda_execution_role.arn
3572
+ }
3573
+
3574
+ output "lambda_execution_role_name" {
3575
+ description = "Name of the Lambda execution role"
3576
+ value = aws_iam_role.lambda_execution_role.name
3577
+ }
3578
+
3579
+ # Integration Outputs
3580
+ output "integration_id" {
3581
+ description = "ID of the Lambda integration"
3582
+ value = aws_api_gateway_integration.lambda_integration.id
3583
+ }
3584
+
3585
+ output "proxy_resource_id" {
3586
+ description = "ID of the proxy resource"
3587
+ value = aws_api_gateway_resource.proxy_resource.id
3588
+ }
3589
+
3590
+ output "proxy_method_id" {
3591
+ description = "ID of the proxy method"
3592
+ value = aws_api_gateway_method.proxy_method.id
3593
+ }
3594
+
3595
+ # CloudWatch Log Groups
3596
+ output "lambda_log_group_name" {
3597
+ description = "Name of the Lambda CloudWatch log group"
3598
+ value = aws_cloudwatch_log_group.lambda_logs.name
3599
+ }
3600
+
3601
+ output "lambda_log_group_arn" {
3602
+ description = "ARN of the Lambda CloudWatch log group"
3603
+ value = aws_cloudwatch_log_group.lambda_logs.arn
3604
+ }
3605
+ ",
3606
+ }
3607
+ `;
3608
+
3609
+ exports[`fastapi project generator > terraform iacProvider > should generate terraform files for REST API with IAM auth and snapshot them > terraform-rest-iam-files 1`] = `
3610
+ {
3611
+ "rest-api.tf": "# Core REST API Gateway module
3612
+ # This module creates the API Gateway REST API, deployment, stage, and logging resources
3613
+
3614
+ terraform {
3615
+ required_version = ">= 1.0"
3616
+
3617
+ required_providers {
3618
+ aws = {
3619
+ source = "hashicorp/aws"
3620
+ version = "~> 6.0"
3621
+ }
3622
+ }
3623
+ }
3624
+
3625
+ # Core REST API Gateway Variables
3626
+
3627
+ variable "api_name" {
3628
+ description = "Name of the REST API Gateway"
3629
+ type = string
3630
+ }
3631
+
3632
+ variable "api_description" {
3633
+ description = "Description of the REST API Gateway"
3634
+ type = string
3635
+ default = "REST API Gateway"
3636
+ }
3637
+
3638
+ variable "stage_name" {
3639
+ description = "Name of the API Gateway stage"
3640
+ type = string
3641
+ default = "prod"
3642
+ }
3643
+
3644
+ variable "stage_auto_deploy" {
3645
+ description = "Whether to automatically deploy the API stage"
3646
+ type = bool
3647
+ default = true
3648
+ }
3649
+
3650
+ # CORS Configuration
3651
+
3652
+ variable "cors_allow_headers" {
3653
+ description = "List of allowed headers for CORS"
3654
+ type = list(string)
3655
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
3656
+ }
3657
+
3658
+ variable "cors_allow_methods" {
3659
+ description = "List of allowed HTTP methods for CORS"
3660
+ type = list(string)
3661
+ default = ["*"]
3662
+ }
3663
+
3664
+ variable "cors_allow_origins" {
3665
+ description = "List of allowed origins for CORS"
3666
+ type = list(string)
3667
+ default = ["*"]
3668
+ }
3669
+
3670
+ # Tags
3671
+
3672
+ variable "tags" {
3673
+ description = "Tags to apply to all resources"
3674
+ type = map(string)
3675
+ default = {}
3676
+ }
3677
+
3678
+ # Data sources
3679
+ data "aws_region" "current" {}
3680
+ data "aws_caller_identity" "current" {}
3681
+
3682
+ # Resources
3683
+
3684
+ # Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
3685
+
3686
+ # REST API Gateway
3687
+ resource "aws_api_gateway_rest_api" "rest_api" {
3688
+ name = var.api_name
3689
+ description = var.api_description
3690
+
3691
+ endpoint_configuration {
3692
+ types = ["REGIONAL"]
3693
+ }
3694
+
3695
+ lifecycle {
3696
+ create_before_destroy = true
3697
+ }
3698
+
3699
+ tags = var.tags
3700
+ }
3701
+
3702
+ # Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
3703
+ # after all methods and integrations are defined
3704
+
3705
+ # Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
3706
+
3707
+ # Gateway Response for CORS (4XX errors)
3708
+ resource "aws_api_gateway_gateway_response" "cors_4xx" {
3709
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
3710
+ response_type = "DEFAULT_4XX"
3711
+
3712
+ response_parameters = {
3713
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
3714
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
3715
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
3716
+ }
3717
+ }
3718
+
3719
+ # Gateway Response for CORS (5XX errors)
3720
+ resource "aws_api_gateway_gateway_response" "cors_5xx" {
3721
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
3722
+ response_type = "DEFAULT_5XX"
3723
+
3724
+ response_parameters = {
3725
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
3726
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
3727
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
3728
+ }
3729
+ }
3730
+
3731
+ # Outputs
3732
+
3733
+ output "api_id" {
3734
+ description = "ID of the REST API Gateway"
3735
+ value = aws_api_gateway_rest_api.rest_api.id
3736
+ }
3737
+
3738
+ output "api_arn" {
3739
+ description = "ARN of the REST API Gateway"
3740
+ value = aws_api_gateway_rest_api.rest_api.arn
3741
+ }
3742
+
3743
+ output "api_endpoint" {
3744
+ description = "Base URL of the REST API Gateway"
3745
+ value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
3746
+ }
3747
+
3748
+ output "api_execution_arn" {
3749
+ description = "Execution ARN of the REST API Gateway"
3750
+ value = aws_api_gateway_rest_api.rest_api.execution_arn
3751
+ }
3752
+
3753
+ output "api_root_resource_id" {
3754
+ description = "Root resource ID of the REST API Gateway"
3755
+ value = aws_api_gateway_rest_api.rest_api.root_resource_id
3756
+ }
3757
+
3758
+ # Note: Stage and deployment outputs are provided by the consuming module
3759
+
3760
+ # Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
3761
+ "test-api.tf": "terraform {
3762
+ required_version = ">= 1.0"
3763
+
3764
+ required_providers {
3765
+ aws = {
3766
+ source = "hashicorp/aws"
3767
+ version = "~> 6.0"
3768
+ }
3769
+ }
3770
+ }
3771
+
3772
+
3773
+ variable "env" {
3774
+ description = "Environment variables for the Lambda function"
3775
+ type = map(string)
3776
+ default = {}
3777
+ }
3778
+
3779
+ variable "additional_iam_policy_statements" {
3780
+ description = "Additional IAM policy statements for the Lambda function"
3781
+ type = list(object({
3782
+ Effect = string
3783
+ Action = list(string)
3784
+ Resource = list(string)
3785
+ }))
3786
+ default = []
3787
+ }
3788
+
3789
+ # CORS Configuration (passed to core module)
3790
+
3791
+ variable "cors_allow_headers" {
3792
+ description = "List of allowed headers for CORS"
3793
+ type = list(string)
3794
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
3795
+ }
3796
+
3797
+ variable "cors_allow_methods" {
3798
+ description = "List of allowed HTTP methods for CORS"
3799
+ type = list(string)
3800
+ default = ["*"]
3801
+ }
3802
+
3803
+ variable "cors_allow_origins" {
3804
+ description = "List of allowed origins for CORS"
3805
+ type = list(string)
3806
+ default = ["*"]
3807
+ }
3808
+
3809
+ # Tags
3810
+ variable "tags" {
3811
+ description = "Tags to apply to all resources"
3812
+ type = map(string)
3813
+ default = {}
3814
+ }
3815
+
3816
+ # Get current AWS region and account ID
3817
+ data "aws_region" "current" {}
3818
+ data "aws_caller_identity" "current" {}
3819
+
3820
+ # Resources
3821
+
3822
+ # Create Lambda ZIP file from the FastAPI bundle directory
3823
+ data "archive_file" "lambda_zip" {
3824
+ type = "zip"
3825
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle"
3826
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
3827
+ }
3828
+
3829
+ # Use the core REST API module
3830
+ module "rest_api" {
3831
+ source = "../../../core/api/rest-api"
3832
+
3833
+ api_name = "TestApi"
3834
+ api_description = "TestApi REST API"
3835
+ stage_name = "prod"
3836
+ stage_auto_deploy = true
3837
+
3838
+ # CORS Configuration
3839
+ cors_allow_headers = var.cors_allow_headers
3840
+ cors_allow_methods = var.cors_allow_methods
3841
+ cors_allow_origins = var.cors_allow_origins
3842
+
3843
+ # Tags
3844
+ tags = var.tags
3845
+ }
3846
+
3847
+ # Lambda function
3848
+ resource "aws_lambda_function" "api_lambda" {
3849
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
3850
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
3851
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
3852
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
3853
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
3854
+ filename = data.archive_file.lambda_zip.output_path
3855
+ function_name = "TestApiHandler"
3856
+ role = aws_iam_role.lambda_execution_role.arn
3857
+ handler = "proj_test_api.main.handler"
3858
+ runtime = "python3.12"
3859
+ timeout = 30
3860
+ memory_size = 128
3861
+
3862
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
3863
+
3864
+ # Enable X-Ray tracing
3865
+ tracing_config {
3866
+ mode = "Active"
3867
+ }
3868
+
3869
+ environment {
3870
+ variables = merge({
3871
+ AWS_CONNECTION_REUSE_ENABLED = "1"
3872
+ }, var.env)
3873
+ }
3874
+
3875
+ tags = var.tags
3876
+ }
3877
+
3878
+ # IAM role for Lambda execution
3879
+ resource "aws_iam_role" "lambda_execution_role" {
3880
+ name = "TestApiHandler-execution-role"
3881
+
3882
+ assume_role_policy = jsonencode({
3883
+ Version = "2012-10-17"
3884
+ Statement = [
3885
+ {
3886
+ Action = "sts:AssumeRole"
3887
+ Effect = "Allow"
3888
+ Principal = {
3889
+ Service = "lambda.amazonaws.com"
3890
+ }
3891
+ }
3892
+ ]
3893
+ })
3894
+
3895
+ tags = var.tags
3896
+ }
3897
+
3898
+ # Attach basic execution policy to Lambda role
3899
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
3900
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
3901
+ role = aws_iam_role.lambda_execution_role.name
3902
+ }
3903
+
3904
+ # Attach X-Ray tracing policy to Lambda role
3905
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
3906
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
3907
+ role = aws_iam_role.lambda_execution_role.name
3908
+ }
3909
+
3910
+ # Additional IAM policies for Lambda (if provided)
3911
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
3912
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
3913
+ name = "TestApiHandler-additional-policies"
3914
+ role = aws_iam_role.lambda_execution_role.id
3915
+
3916
+ policy = jsonencode({
3917
+ Version = "2012-10-17"
3918
+ Statement = var.additional_iam_policy_statements
3919
+ })
3920
+ }
3921
+
3922
+ # CloudWatch Log Group for Lambda
3923
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
3924
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
3925
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
3926
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
3927
+ name = "/aws/lambda/TestApiHandler"
3928
+ tags = var.tags
3929
+ }
3930
+
3931
+
3932
+ # Create proxy resource (captures all paths)
3933
+ resource "aws_api_gateway_resource" "proxy_resource" {
3934
+ rest_api_id = module.rest_api.api_id
3935
+ parent_id = module.rest_api.api_root_resource_id
3936
+ path_part = "{proxy+}"
3937
+ }
3938
+
3939
+ # Lambda integration for REST API
3940
+ resource "aws_api_gateway_integration" "lambda_integration" {
3941
+ rest_api_id = module.rest_api.api_id
3942
+ resource_id = aws_api_gateway_resource.proxy_resource.id
3943
+ http_method = aws_api_gateway_method.proxy_method.http_method
3944
+
3945
+ integration_http_method = "POST"
3946
+ type = "AWS_PROXY"
3947
+ uri = aws_lambda_function.api_lambda.invoke_arn
3948
+
3949
+ depends_on = [aws_lambda_function.api_lambda]
3950
+ }
3951
+
3952
+ # Method for proxy integration
3953
+ resource "aws_api_gateway_method" "proxy_method" {
3954
+ #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
3955
+ rest_api_id = module.rest_api.api_id
3956
+ resource_id = aws_api_gateway_resource.proxy_resource.id
3957
+ http_method = "ANY"
3958
+
3959
+ authorization = "AWS_IAM"
3960
+
3961
+ request_parameters = {
3962
+ "method.request.path.proxy" = true
3963
+ }
3964
+
3965
+ depends_on = []
3966
+ }
3967
+
3968
+ # OPTIONS method for CORS preflight
3969
+ resource "aws_api_gateway_method" "options_method" {
3970
+ #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
3971
+ #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
3972
+ rest_api_id = module.rest_api.api_id
3973
+ resource_id = aws_api_gateway_resource.proxy_resource.id
3974
+ http_method = "OPTIONS"
3975
+ authorization = "NONE"
3976
+ }
3977
+
3978
+ # CORS integration for OPTIONS method
3979
+ resource "aws_api_gateway_integration" "options_integration" {
3980
+ rest_api_id = module.rest_api.api_id
3981
+ resource_id = aws_api_gateway_resource.proxy_resource.id
3982
+ http_method = aws_api_gateway_method.options_method.http_method
3983
+
3984
+ type = "MOCK"
3985
+ request_templates = {
3986
+ "application/json" = "{\\"statusCode\\": 204}"
3987
+ }
3988
+ }
3989
+
3990
+ # OPTIONS method response
3991
+ resource "aws_api_gateway_method_response" "options_response" {
3992
+ rest_api_id = module.rest_api.api_id
3993
+ resource_id = aws_api_gateway_resource.proxy_resource.id
3994
+ http_method = aws_api_gateway_method.options_method.http_method
3995
+ status_code = "204"
3996
+
3997
+ response_parameters = {
3998
+ "method.response.header.Access-Control-Allow-Headers" = true
3999
+ "method.response.header.Access-Control-Allow-Methods" = true
4000
+ "method.response.header.Access-Control-Allow-Origin" = true
4001
+ }
4002
+ }
4003
+
4004
+ # OPTIONS integration response
4005
+ resource "aws_api_gateway_integration_response" "options_integration_response" {
4006
+ rest_api_id = module.rest_api.api_id
4007
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4008
+ http_method = aws_api_gateway_method.options_method.http_method
4009
+ status_code = aws_api_gateway_method_response.options_response.status_code
4010
+
4011
+ response_parameters = {
4012
+ "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
4013
+ "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
4014
+ "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
4015
+ }
4016
+ }
4017
+
4018
+ # API Gateway deployment
4019
+ resource "aws_api_gateway_deployment" "api_deployment" {
4020
+ rest_api_id = module.rest_api.api_id
4021
+
4022
+ triggers = {
4023
+ redeployment = sha1(jsonencode([
4024
+ aws_api_gateway_resource.proxy_resource.id,
4025
+ aws_api_gateway_method.proxy_method.id,
4026
+ aws_api_gateway_integration.lambda_integration.id,
4027
+ aws_api_gateway_method.options_method.id,
4028
+ aws_api_gateway_integration.options_integration.id,
4029
+ ]))
4030
+ }
4031
+
4032
+ lifecycle {
4033
+ create_before_destroy = true
4034
+ }
4035
+
4036
+ depends_on = [
4037
+ aws_api_gateway_method.proxy_method,
4038
+ aws_api_gateway_integration.lambda_integration,
4039
+ aws_api_gateway_method.options_method,
4040
+ aws_api_gateway_integration.options_integration,
4041
+ aws_api_gateway_method_response.options_response,
4042
+ aws_api_gateway_integration_response.options_integration_response,
4043
+ ]
4044
+ }
4045
+
4046
+ # API Gateway stage
4047
+ resource "aws_api_gateway_stage" "api_stage" {
4048
+ #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
4049
+ #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
4050
+ #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
4051
+ #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
4052
+ deployment_id = aws_api_gateway_deployment.api_deployment.id
4053
+ rest_api_id = module.rest_api.api_id
4054
+ stage_name = "prod"
4055
+ xray_tracing_enabled = true
4056
+
4057
+ tags = var.tags
4058
+
4059
+ depends_on = [aws_api_gateway_deployment.api_deployment]
4060
+ }
4061
+
4062
+ # API Gateway Resource Policy
4063
+ resource "aws_api_gateway_rest_api_policy" "api_policy" {
4064
+ rest_api_id = module.rest_api.api_id
4065
+
4066
+ policy = jsonencode({
4067
+ Version = "2012-10-17"
4068
+ Statement = [
4069
+ {
4070
+ # Grant any AWS credentials from the account to call the API
4071
+ # Machine to machine fine-grained access can be defined here using more specific principals
4072
+ # (eg roles or users) and resources (eg which api paths may be invoked by which principal) if required
4073
+ Effect = "Allow"
4074
+ Principal = {
4075
+ AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
4076
+ }
4077
+ Action = "execute-api:Invoke"
4078
+ Resource = "execute-api:/*"
4079
+ },
4080
+ {
4081
+ # Open up OPTIONS to allow browsers to make unauthenticated preflight requests
4082
+ Effect = "Allow"
4083
+ Principal = "*"
4084
+ Action = "execute-api:Invoke"
4085
+ Resource = "execute-api:/*/OPTIONS/*"
4086
+ }
4087
+ ]
4088
+ })
4089
+ }
4090
+
4091
+ # Lambda permission for API Gateway to invoke the function
4092
+ resource "aws_lambda_permission" "api_gateway_invoke" {
4093
+ statement_id = "AllowExecutionFromAPIGateway"
4094
+ action = "lambda:InvokeFunction"
4095
+ function_name = aws_lambda_function.api_lambda.function_name
4096
+ principal = "apigateway.amazonaws.com"
4097
+ source_arn = "\${module.rest_api.api_execution_arn}/*/*"
4098
+
4099
+ depends_on = [module.rest_api, aws_lambda_function.api_lambda]
4100
+ }
4101
+
4102
+ # Add API url to runtime config
4103
+ module "add_url_to_runtime_config" {
4104
+ source = "../../../core/runtime-config/entry"
4105
+
4106
+ key_path = "apis.TestApi"
4107
+ value = aws_api_gateway_stage.api_stage.invoke_url
4108
+
4109
+ depends_on = [aws_api_gateway_stage.api_stage]
4110
+ }
4111
+
4112
+ # Outputs
4113
+
4114
+ # API Gateway Outputs (from core module)
4115
+ output "api_id" {
4116
+ description = "ID of the REST API Gateway"
4117
+ value = module.rest_api.api_id
4118
+ }
4119
+
4120
+ output "api_arn" {
4121
+ description = "ARN of the REST API Gateway"
4122
+ value = module.rest_api.api_arn
4123
+ }
4124
+
4125
+ output "api_endpoint" {
4126
+ description = "Base URL of the REST API Gateway"
4127
+ value = module.rest_api.api_endpoint
4128
+ }
4129
+
4130
+ output "api_execution_arn" {
4131
+ description = "Execution ARN of the REST API Gateway"
4132
+ value = module.rest_api.api_execution_arn
4133
+ }
4134
+
4135
+ output "stage_invoke_url" {
4136
+ description = "Invoke URL of the API Gateway stage"
4137
+ value = aws_api_gateway_stage.api_stage.invoke_url
4138
+ }
4139
+
4140
+ output "stage_arn" {
4141
+ description = "ARN of the API Gateway stage"
4142
+ value = aws_api_gateway_stage.api_stage.arn
4143
+ }
4144
+
4145
+ output "stage_execution_arn" {
4146
+ description = "Execution ARN of the API Gateway stage"
4147
+ value = aws_api_gateway_stage.api_stage.execution_arn
4148
+ }
4149
+
4150
+ output "deployment_id" {
4151
+ description = "ID of the API Gateway deployment"
4152
+ value = aws_api_gateway_deployment.api_deployment.id
4153
+ }
4154
+
4155
+ output "stage_id" {
4156
+ description = "ID of the API Gateway stage"
4157
+ value = aws_api_gateway_stage.api_stage.id
4158
+ }
4159
+
4160
+ # Lambda Function Outputs
4161
+ output "lambda_function_name" {
4162
+ description = "Name of the Lambda function"
4163
+ value = aws_lambda_function.api_lambda.function_name
4164
+ }
4165
+
4166
+ output "lambda_function_arn" {
4167
+ description = "ARN of the Lambda function"
4168
+ value = aws_lambda_function.api_lambda.arn
4169
+ }
4170
+
4171
+ output "lambda_invoke_arn" {
4172
+ description = "Invoke ARN of the Lambda function"
4173
+ value = aws_lambda_function.api_lambda.invoke_arn
4174
+ }
4175
+
4176
+ output "lambda_qualified_arn" {
4177
+ description = "Qualified ARN of the Lambda function"
4178
+ value = aws_lambda_function.api_lambda.qualified_arn
4179
+ }
4180
+
4181
+ output "lambda_version" {
4182
+ description = "Version of the Lambda function"
4183
+ value = aws_lambda_function.api_lambda.version
4184
+ }
4185
+
4186
+ output "lambda_source_code_hash" {
4187
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
4188
+ value = aws_lambda_function.api_lambda.source_code_hash
4189
+ }
4190
+
4191
+ output "lambda_source_code_size" {
4192
+ description = "Size of the Lambda deployment package in bytes"
4193
+ value = aws_lambda_function.api_lambda.source_code_size
4194
+ }
4195
+
4196
+ # IAM Role Outputs
4197
+ output "lambda_execution_role_arn" {
4198
+ description = "ARN of the Lambda execution role"
4199
+ value = aws_iam_role.lambda_execution_role.arn
4200
+ }
4201
+
4202
+ output "lambda_execution_role_name" {
4203
+ description = "Name of the Lambda execution role"
4204
+ value = aws_iam_role.lambda_execution_role.name
4205
+ }
4206
+
4207
+ # Integration Outputs
4208
+ output "integration_id" {
4209
+ description = "ID of the Lambda integration"
4210
+ value = aws_api_gateway_integration.lambda_integration.id
4211
+ }
4212
+
4213
+ output "proxy_resource_id" {
4214
+ description = "ID of the proxy resource"
4215
+ value = aws_api_gateway_resource.proxy_resource.id
4216
+ }
4217
+
4218
+ output "proxy_method_id" {
4219
+ description = "ID of the proxy method"
4220
+ value = aws_api_gateway_method.proxy_method.id
4221
+ }
4222
+
4223
+ # CloudWatch Log Groups
4224
+ output "lambda_log_group_name" {
4225
+ description = "Name of the Lambda CloudWatch log group"
4226
+ value = aws_cloudwatch_log_group.lambda_logs.name
4227
+ }
4228
+
4229
+ output "lambda_log_group_arn" {
4230
+ description = "ARN of the Lambda CloudWatch log group"
4231
+ value = aws_cloudwatch_log_group.lambda_logs.arn
4232
+ }
4233
+ ",
4234
+ }
4235
+ `;
4236
+
4237
+ exports[`fastapi project generator > terraform iacProvider > should generate terraform files for REST API with None auth and snapshot them > terraform-rest-none-files 1`] = `
4238
+ {
4239
+ "rest-api.tf": "# Core REST API Gateway module
4240
+ # This module creates the API Gateway REST API, deployment, stage, and logging resources
4241
+
4242
+ terraform {
4243
+ required_version = ">= 1.0"
4244
+
4245
+ required_providers {
4246
+ aws = {
4247
+ source = "hashicorp/aws"
4248
+ version = "~> 6.0"
4249
+ }
4250
+ }
4251
+ }
4252
+
4253
+ # Core REST API Gateway Variables
4254
+
4255
+ variable "api_name" {
4256
+ description = "Name of the REST API Gateway"
4257
+ type = string
4258
+ }
4259
+
4260
+ variable "api_description" {
4261
+ description = "Description of the REST API Gateway"
4262
+ type = string
4263
+ default = "REST API Gateway"
4264
+ }
4265
+
4266
+ variable "stage_name" {
4267
+ description = "Name of the API Gateway stage"
4268
+ type = string
4269
+ default = "prod"
4270
+ }
4271
+
4272
+ variable "stage_auto_deploy" {
4273
+ description = "Whether to automatically deploy the API stage"
4274
+ type = bool
4275
+ default = true
4276
+ }
4277
+
4278
+ # CORS Configuration
4279
+
4280
+ variable "cors_allow_headers" {
4281
+ description = "List of allowed headers for CORS"
4282
+ type = list(string)
4283
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
4284
+ }
4285
+
4286
+ variable "cors_allow_methods" {
4287
+ description = "List of allowed HTTP methods for CORS"
4288
+ type = list(string)
4289
+ default = ["*"]
4290
+ }
4291
+
4292
+ variable "cors_allow_origins" {
4293
+ description = "List of allowed origins for CORS"
4294
+ type = list(string)
4295
+ default = ["*"]
4296
+ }
4297
+
4298
+ # Tags
4299
+
4300
+ variable "tags" {
4301
+ description = "Tags to apply to all resources"
4302
+ type = map(string)
4303
+ default = {}
4304
+ }
4305
+
4306
+ # Data sources
4307
+ data "aws_region" "current" {}
4308
+ data "aws_caller_identity" "current" {}
4309
+
4310
+ # Resources
4311
+
4312
+ # Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
4313
+
4314
+ # REST API Gateway
4315
+ resource "aws_api_gateway_rest_api" "rest_api" {
4316
+ name = var.api_name
4317
+ description = var.api_description
4318
+
4319
+ endpoint_configuration {
4320
+ types = ["REGIONAL"]
4321
+ }
4322
+
4323
+ lifecycle {
4324
+ create_before_destroy = true
4325
+ }
4326
+
4327
+ tags = var.tags
4328
+ }
4329
+
4330
+ # Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
4331
+ # after all methods and integrations are defined
4332
+
4333
+ # Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
4334
+
4335
+ # Gateway Response for CORS (4XX errors)
4336
+ resource "aws_api_gateway_gateway_response" "cors_4xx" {
4337
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
4338
+ response_type = "DEFAULT_4XX"
4339
+
4340
+ response_parameters = {
4341
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
4342
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
4343
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
4344
+ }
4345
+ }
4346
+
4347
+ # Gateway Response for CORS (5XX errors)
4348
+ resource "aws_api_gateway_gateway_response" "cors_5xx" {
4349
+ rest_api_id = aws_api_gateway_rest_api.rest_api.id
4350
+ response_type = "DEFAULT_5XX"
4351
+
4352
+ response_parameters = {
4353
+ "gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
4354
+ "gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
4355
+ "gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
4356
+ }
4357
+ }
4358
+
4359
+ # Outputs
4360
+
4361
+ output "api_id" {
4362
+ description = "ID of the REST API Gateway"
4363
+ value = aws_api_gateway_rest_api.rest_api.id
4364
+ }
4365
+
4366
+ output "api_arn" {
4367
+ description = "ARN of the REST API Gateway"
4368
+ value = aws_api_gateway_rest_api.rest_api.arn
4369
+ }
4370
+
4371
+ output "api_endpoint" {
4372
+ description = "Base URL of the REST API Gateway"
4373
+ value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
4374
+ }
4375
+
4376
+ output "api_execution_arn" {
4377
+ description = "Execution ARN of the REST API Gateway"
4378
+ value = aws_api_gateway_rest_api.rest_api.execution_arn
4379
+ }
4380
+
4381
+ output "api_root_resource_id" {
4382
+ description = "Root resource ID of the REST API Gateway"
4383
+ value = aws_api_gateway_rest_api.rest_api.root_resource_id
4384
+ }
4385
+
4386
+ # Note: Stage and deployment outputs are provided by the consuming module
4387
+
4388
+ # Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
4389
+ "test-api.tf": "terraform {
4390
+ required_version = ">= 1.0"
4391
+
4392
+ required_providers {
4393
+ aws = {
4394
+ source = "hashicorp/aws"
4395
+ version = "~> 6.0"
4396
+ }
4397
+ }
4398
+ }
4399
+
4400
+
4401
+ variable "env" {
4402
+ description = "Environment variables for the Lambda function"
4403
+ type = map(string)
4404
+ default = {}
4405
+ }
4406
+
4407
+ variable "additional_iam_policy_statements" {
4408
+ description = "Additional IAM policy statements for the Lambda function"
4409
+ type = list(object({
4410
+ Effect = string
4411
+ Action = list(string)
4412
+ Resource = list(string)
4413
+ }))
4414
+ default = []
4415
+ }
4416
+
4417
+ # CORS Configuration (passed to core module)
4418
+
4419
+ variable "cors_allow_headers" {
4420
+ description = "List of allowed headers for CORS"
4421
+ type = list(string)
4422
+ default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
4423
+ }
4424
+
4425
+ variable "cors_allow_methods" {
4426
+ description = "List of allowed HTTP methods for CORS"
4427
+ type = list(string)
4428
+ default = ["*"]
4429
+ }
4430
+
4431
+ variable "cors_allow_origins" {
4432
+ description = "List of allowed origins for CORS"
4433
+ type = list(string)
4434
+ default = ["*"]
4435
+ }
4436
+
4437
+ # Tags
4438
+ variable "tags" {
4439
+ description = "Tags to apply to all resources"
4440
+ type = map(string)
4441
+ default = {}
4442
+ }
4443
+
4444
+ # Get current AWS region and account ID
4445
+ data "aws_region" "current" {}
4446
+ data "aws_caller_identity" "current" {}
4447
+
4448
+ # Resources
4449
+
4450
+ # Create Lambda ZIP file from the FastAPI bundle directory
4451
+ data "archive_file" "lambda_zip" {
4452
+ type = "zip"
4453
+ source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle"
4454
+ output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
4455
+ }
4456
+
4457
+ # Use the core REST API module
4458
+ module "rest_api" {
4459
+ source = "../../../core/api/rest-api"
4460
+
4461
+ api_name = "TestApi"
4462
+ api_description = "TestApi REST API"
4463
+ stage_name = "prod"
4464
+ stage_auto_deploy = true
4465
+
4466
+ # CORS Configuration
4467
+ cors_allow_headers = var.cors_allow_headers
4468
+ cors_allow_methods = var.cors_allow_methods
4469
+ cors_allow_origins = var.cors_allow_origins
4470
+
4471
+ # Tags
4472
+ tags = var.tags
4473
+ }
4474
+
4475
+ # Lambda function
4476
+ resource "aws_lambda_function" "api_lambda" {
4477
+ #checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
4478
+ #checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
4479
+ #checkov:skip=CKV_AWS_272:Code signing not required for this use case
4480
+ #checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
4481
+ #checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
4482
+ filename = data.archive_file.lambda_zip.output_path
4483
+ function_name = "TestApiHandler"
4484
+ role = aws_iam_role.lambda_execution_role.arn
4485
+ handler = "proj_test_api.main.handler"
4486
+ runtime = "python3.12"
4487
+ timeout = 30
4488
+ memory_size = 128
4489
+
4490
+ source_code_hash = data.archive_file.lambda_zip.output_base64sha256
4491
+
4492
+ # Enable X-Ray tracing
4493
+ tracing_config {
4494
+ mode = "Active"
4495
+ }
4496
+
4497
+ environment {
4498
+ variables = merge({
4499
+ AWS_CONNECTION_REUSE_ENABLED = "1"
4500
+ }, var.env)
4501
+ }
4502
+
4503
+ tags = var.tags
4504
+ }
4505
+
4506
+ # IAM role for Lambda execution
4507
+ resource "aws_iam_role" "lambda_execution_role" {
4508
+ name = "TestApiHandler-execution-role"
4509
+
4510
+ assume_role_policy = jsonencode({
4511
+ Version = "2012-10-17"
4512
+ Statement = [
4513
+ {
4514
+ Action = "sts:AssumeRole"
4515
+ Effect = "Allow"
4516
+ Principal = {
4517
+ Service = "lambda.amazonaws.com"
4518
+ }
4519
+ }
4520
+ ]
4521
+ })
4522
+
4523
+ tags = var.tags
4524
+ }
4525
+
4526
+ # Attach basic execution policy to Lambda role
4527
+ resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
4528
+ policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
4529
+ role = aws_iam_role.lambda_execution_role.name
4530
+ }
4531
+
4532
+ # Attach X-Ray tracing policy to Lambda role
4533
+ resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
4534
+ policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
4535
+ role = aws_iam_role.lambda_execution_role.name
4536
+ }
4537
+
4538
+ # Additional IAM policies for Lambda (if provided)
4539
+ resource "aws_iam_role_policy" "lambda_additional_policies" {
4540
+ count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
4541
+ name = "TestApiHandler-additional-policies"
4542
+ role = aws_iam_role.lambda_execution_role.id
4543
+
4544
+ policy = jsonencode({
4545
+ Version = "2012-10-17"
4546
+ Statement = var.additional_iam_policy_statements
4547
+ })
4548
+ }
4549
+
4550
+ # CloudWatch Log Group for Lambda
4551
+ resource "aws_cloudwatch_log_group" "lambda_logs" {
4552
+ #checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
4553
+ #checkov:skip=CKV_AWS_338:Log retention set to forever
4554
+ #checkov:skip=CKV_AWS_66:Log retention set to forever
4555
+ name = "/aws/lambda/TestApiHandler"
4556
+ tags = var.tags
4557
+ }
4558
+
4559
+
4560
+ # Create proxy resource (captures all paths)
4561
+ resource "aws_api_gateway_resource" "proxy_resource" {
4562
+ rest_api_id = module.rest_api.api_id
4563
+ parent_id = module.rest_api.api_root_resource_id
4564
+ path_part = "{proxy+}"
4565
+ }
4566
+
4567
+ # Lambda integration for REST API
4568
+ resource "aws_api_gateway_integration" "lambda_integration" {
4569
+ rest_api_id = module.rest_api.api_id
4570
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4571
+ http_method = aws_api_gateway_method.proxy_method.http_method
4572
+
4573
+ integration_http_method = "POST"
4574
+ type = "AWS_PROXY"
4575
+ uri = aws_lambda_function.api_lambda.invoke_arn
4576
+
4577
+ depends_on = [aws_lambda_function.api_lambda]
4578
+ }
4579
+
4580
+ # Method for proxy integration
4581
+ resource "aws_api_gateway_method" "proxy_method" {
4582
+ #checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
4583
+ rest_api_id = module.rest_api.api_id
4584
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4585
+ http_method = "ANY"
4586
+
4587
+ # Note: you may wish to suppress the checkov rule CKV_AWS_59 if you are absolutely sure you
4588
+ # need a public API without authentication
4589
+ authorization = "NONE"
4590
+
4591
+ request_parameters = {
4592
+ "method.request.path.proxy" = true
4593
+ }
4594
+
4595
+ depends_on = []
4596
+ }
4597
+
4598
+ # OPTIONS method for CORS preflight
4599
+ resource "aws_api_gateway_method" "options_method" {
4600
+ #checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
4601
+ #checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
4602
+ rest_api_id = module.rest_api.api_id
4603
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4604
+ http_method = "OPTIONS"
4605
+ authorization = "NONE"
4606
+ }
4607
+
4608
+ # CORS integration for OPTIONS method
4609
+ resource "aws_api_gateway_integration" "options_integration" {
4610
+ rest_api_id = module.rest_api.api_id
4611
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4612
+ http_method = aws_api_gateway_method.options_method.http_method
4613
+
4614
+ type = "MOCK"
4615
+ request_templates = {
4616
+ "application/json" = "{\\"statusCode\\": 204}"
4617
+ }
4618
+ }
4619
+
4620
+ # OPTIONS method response
4621
+ resource "aws_api_gateway_method_response" "options_response" {
4622
+ rest_api_id = module.rest_api.api_id
4623
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4624
+ http_method = aws_api_gateway_method.options_method.http_method
4625
+ status_code = "204"
4626
+
4627
+ response_parameters = {
4628
+ "method.response.header.Access-Control-Allow-Headers" = true
4629
+ "method.response.header.Access-Control-Allow-Methods" = true
4630
+ "method.response.header.Access-Control-Allow-Origin" = true
4631
+ }
4632
+ }
4633
+
4634
+ # OPTIONS integration response
4635
+ resource "aws_api_gateway_integration_response" "options_integration_response" {
4636
+ rest_api_id = module.rest_api.api_id
4637
+ resource_id = aws_api_gateway_resource.proxy_resource.id
4638
+ http_method = aws_api_gateway_method.options_method.http_method
4639
+ status_code = aws_api_gateway_method_response.options_response.status_code
4640
+
4641
+ response_parameters = {
4642
+ "method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
4643
+ "method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
4644
+ "method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
4645
+ }
4646
+ }
4647
+
4648
+ # API Gateway deployment
4649
+ resource "aws_api_gateway_deployment" "api_deployment" {
4650
+ rest_api_id = module.rest_api.api_id
4651
+
4652
+ triggers = {
4653
+ redeployment = sha1(jsonencode([
4654
+ aws_api_gateway_resource.proxy_resource.id,
4655
+ aws_api_gateway_method.proxy_method.id,
4656
+ aws_api_gateway_integration.lambda_integration.id,
4657
+ aws_api_gateway_method.options_method.id,
4658
+ aws_api_gateway_integration.options_integration.id,
4659
+ ]))
4660
+ }
4661
+
4662
+ lifecycle {
4663
+ create_before_destroy = true
4664
+ }
4665
+
4666
+ depends_on = [
4667
+ aws_api_gateway_method.proxy_method,
4668
+ aws_api_gateway_integration.lambda_integration,
4669
+ aws_api_gateway_method.options_method,
4670
+ aws_api_gateway_integration.options_integration,
4671
+ aws_api_gateway_method_response.options_response,
4672
+ aws_api_gateway_integration_response.options_integration_response,
4673
+ ]
4674
+ }
4675
+
4676
+ # API Gateway stage
4677
+ resource "aws_api_gateway_stage" "api_stage" {
4678
+ #checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
4679
+ #checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
4680
+ #checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
4681
+ #checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
4682
+ deployment_id = aws_api_gateway_deployment.api_deployment.id
4683
+ rest_api_id = module.rest_api.api_id
4684
+ stage_name = "prod"
4685
+ xray_tracing_enabled = true
4686
+
4687
+ tags = var.tags
4688
+
4689
+ depends_on = [aws_api_gateway_deployment.api_deployment]
4690
+ }
4691
+
4692
+ # API Gateway Resource Policy
4693
+ resource "aws_api_gateway_rest_api_policy" "api_policy" {
4694
+ rest_api_id = module.rest_api.api_id
4695
+
4696
+ policy = jsonencode({
4697
+ Version = "2012-10-17"
4698
+ Statement = [
4699
+ {
4700
+ # Allow all callers to invoke the API in the resource policy
4701
+ Effect = "Allow"
4702
+ Principal = "*"
4703
+ Action = "execute-api:Invoke"
4704
+ Resource = "execute-api:/*"
4705
+ }
4706
+ ]
4707
+ })
4708
+ }
4709
+
4710
+ # Lambda permission for API Gateway to invoke the function
4711
+ resource "aws_lambda_permission" "api_gateway_invoke" {
4712
+ statement_id = "AllowExecutionFromAPIGateway"
4713
+ action = "lambda:InvokeFunction"
4714
+ function_name = aws_lambda_function.api_lambda.function_name
4715
+ principal = "apigateway.amazonaws.com"
4716
+ source_arn = "\${module.rest_api.api_execution_arn}/*/*"
4717
+
4718
+ depends_on = [module.rest_api, aws_lambda_function.api_lambda]
4719
+ }
4720
+
4721
+ # Add API url to runtime config
4722
+ module "add_url_to_runtime_config" {
4723
+ source = "../../../core/runtime-config/entry"
4724
+
4725
+ key_path = "apis.TestApi"
4726
+ value = aws_api_gateway_stage.api_stage.invoke_url
4727
+
4728
+ depends_on = [aws_api_gateway_stage.api_stage]
4729
+ }
4730
+
4731
+ # Outputs
4732
+
4733
+ # API Gateway Outputs (from core module)
4734
+ output "api_id" {
4735
+ description = "ID of the REST API Gateway"
4736
+ value = module.rest_api.api_id
4737
+ }
4738
+
4739
+ output "api_arn" {
4740
+ description = "ARN of the REST API Gateway"
4741
+ value = module.rest_api.api_arn
4742
+ }
4743
+
4744
+ output "api_endpoint" {
4745
+ description = "Base URL of the REST API Gateway"
4746
+ value = module.rest_api.api_endpoint
4747
+ }
4748
+
4749
+ output "api_execution_arn" {
4750
+ description = "Execution ARN of the REST API Gateway"
4751
+ value = module.rest_api.api_execution_arn
4752
+ }
4753
+
4754
+ output "stage_invoke_url" {
4755
+ description = "Invoke URL of the API Gateway stage"
4756
+ value = aws_api_gateway_stage.api_stage.invoke_url
4757
+ }
4758
+
4759
+ output "stage_arn" {
4760
+ description = "ARN of the API Gateway stage"
4761
+ value = aws_api_gateway_stage.api_stage.arn
4762
+ }
4763
+
4764
+ output "stage_execution_arn" {
4765
+ description = "Execution ARN of the API Gateway stage"
4766
+ value = aws_api_gateway_stage.api_stage.execution_arn
4767
+ }
4768
+
4769
+ output "deployment_id" {
4770
+ description = "ID of the API Gateway deployment"
4771
+ value = aws_api_gateway_deployment.api_deployment.id
4772
+ }
4773
+
4774
+ output "stage_id" {
4775
+ description = "ID of the API Gateway stage"
4776
+ value = aws_api_gateway_stage.api_stage.id
4777
+ }
4778
+
4779
+ # Lambda Function Outputs
4780
+ output "lambda_function_name" {
4781
+ description = "Name of the Lambda function"
4782
+ value = aws_lambda_function.api_lambda.function_name
4783
+ }
4784
+
4785
+ output "lambda_function_arn" {
4786
+ description = "ARN of the Lambda function"
4787
+ value = aws_lambda_function.api_lambda.arn
4788
+ }
4789
+
4790
+ output "lambda_invoke_arn" {
4791
+ description = "Invoke ARN of the Lambda function"
4792
+ value = aws_lambda_function.api_lambda.invoke_arn
4793
+ }
4794
+
4795
+ output "lambda_qualified_arn" {
4796
+ description = "Qualified ARN of the Lambda function"
4797
+ value = aws_lambda_function.api_lambda.qualified_arn
4798
+ }
4799
+
4800
+ output "lambda_version" {
4801
+ description = "Version of the Lambda function"
4802
+ value = aws_lambda_function.api_lambda.version
4803
+ }
4804
+
4805
+ output "lambda_source_code_hash" {
4806
+ description = "Base64-encoded SHA256 hash of the Lambda deployment package"
4807
+ value = aws_lambda_function.api_lambda.source_code_hash
4808
+ }
4809
+
4810
+ output "lambda_source_code_size" {
4811
+ description = "Size of the Lambda deployment package in bytes"
4812
+ value = aws_lambda_function.api_lambda.source_code_size
4813
+ }
4814
+
4815
+ # IAM Role Outputs
4816
+ output "lambda_execution_role_arn" {
4817
+ description = "ARN of the Lambda execution role"
4818
+ value = aws_iam_role.lambda_execution_role.arn
4819
+ }
4820
+
4821
+ output "lambda_execution_role_name" {
4822
+ description = "Name of the Lambda execution role"
4823
+ value = aws_iam_role.lambda_execution_role.name
4824
+ }
4825
+
4826
+ # Integration Outputs
4827
+ output "integration_id" {
4828
+ description = "ID of the Lambda integration"
4829
+ value = aws_api_gateway_integration.lambda_integration.id
4830
+ }
4831
+
4832
+ output "proxy_resource_id" {
4833
+ description = "ID of the proxy resource"
4834
+ value = aws_api_gateway_resource.proxy_resource.id
4835
+ }
4836
+
4837
+ output "proxy_method_id" {
4838
+ description = "ID of the proxy method"
4839
+ value = aws_api_gateway_method.proxy_method.id
4840
+ }
4841
+
4842
+ # CloudWatch Log Groups
4843
+ output "lambda_log_group_name" {
4844
+ description = "Name of the Lambda CloudWatch log group"
4845
+ value = aws_cloudwatch_log_group.lambda_logs.name
4846
+ }
4847
+
4848
+ output "lambda_log_group_arn" {
4849
+ description = "ARN of the Lambda CloudWatch log group"
4850
+ value = aws_cloudwatch_log_group.lambda_logs.arn
4851
+ }
4852
+ ",
4853
+ }
4854
+ `;