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