@aws/nx-plugin 1.0.0-rc.83 → 1.0.0-rc.85
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/generators.json +7 -0
- package/migrations.json +31 -1
- package/package.json +1 -1
- package/src/agentcore-gateway/__snapshots__/generator.spec.ts.snap +51 -2
- package/src/agentcore-harness/__snapshots__/generator.spec.ts.snap +4 -5
- package/src/agentcore-harness/files/project/README.md.template +4 -5
- package/src/internal/test-matrix/generator.js +16 -0
- package/src/internal/test-matrix/generator.js.map +1 -1
- package/src/migrations/latest/agentcore-harness-no-tools-by-default/__snapshots__/migration.spec.ts.snap +79 -0
- package/src/migrations/latest/agentcore-harness-no-tools-by-default/metadata.json +3 -0
- package/src/migrations/latest/agentcore-harness-no-tools-by-default/migration.d.ts +6 -0
- package/src/migrations/latest/agentcore-harness-no-tools-by-default/migration.js +87 -0
- package/src/migrations/latest/agentcore-harness-no-tools-by-default/migration.js.map +1 -0
- package/src/migrations/latest/rdb-terraform-aurora-app-module-defaults/__snapshots__/migration.spec.ts.snap +54 -0
- package/src/migrations/latest/rdb-terraform-aurora-app-module-defaults/metadata.json +3 -0
- package/src/migrations/latest/rdb-terraform-aurora-app-module-defaults/migration.d.ts +6 -0
- package/src/migrations/latest/rdb-terraform-aurora-app-module-defaults/migration.js +86 -0
- package/src/migrations/latest/rdb-terraform-aurora-app-module-defaults/migration.js.map +1 -0
- package/src/migrations/latest/terraform-declare-used-providers/metadata.json +3 -0
- package/src/migrations/latest/terraform-declare-used-providers/migration.d.ts +6 -0
- package/src/migrations/latest/terraform-declare-used-providers/migration.js +147 -0
- package/src/migrations/latest/terraform-declare-used-providers/migration.js.map +1 -0
- package/src/migrations/latest/terraform-harness-vpc-role-and-memory-grant/__snapshots__/migration.spec.ts.snap +521 -0
- package/src/migrations/latest/terraform-harness-vpc-role-and-memory-grant/metadata.json +3 -0
- package/src/migrations/latest/terraform-harness-vpc-role-and-memory-grant/migration.d.ts +6 -0
- package/src/migrations/latest/terraform-harness-vpc-role-and-memory-grant/migration.js +504 -0
- package/src/migrations/latest/terraform-harness-vpc-role-and-memory-grant/migration.js.map +1 -0
- package/src/migrations/latest/terraform-project-checkov-target-and-bootstrap-destroy/metadata.json +3 -0
- package/src/migrations/latest/terraform-project-checkov-target-and-bootstrap-destroy/migration.d.ts +6 -0
- package/src/migrations/latest/terraform-project-checkov-target-and-bootstrap-destroy/migration.js +146 -0
- package/src/migrations/latest/terraform-project-checkov-target-and-bootstrap-destroy/migration.js.map +1 -0
- package/src/migrations/latest/waf-log-group-cmk-encryption/__snapshots__/migration.spec.ts.snap +286 -0
- package/src/migrations/latest/waf-log-group-cmk-encryption/metadata.json +3 -0
- package/src/migrations/latest/waf-log-group-cmk-encryption/migration.d.ts +6 -0
- package/src/migrations/latest/waf-log-group-cmk-encryption/migration.js +238 -0
- package/src/migrations/latest/waf-log-group-cmk-encryption/migration.js.map +1 -0
- package/src/open-api/json-metadata/generator.d.ts +24 -0
- package/src/open-api/json-metadata/generator.js +37 -0
- package/src/open-api/json-metadata/generator.js.map +1 -0
- package/src/open-api/json-metadata/schema.d.js +6 -0
- package/src/open-api/json-metadata/schema.d.js.map +1 -0
- package/src/open-api/json-metadata/schema.d.ts +9 -0
- package/src/open-api/json-metadata/schema.json +20 -0
- package/src/py/agent/__snapshots__/generator.protocols.spec.ts.snap +12 -0
- package/src/py/fast-api/__snapshots__/generator.terraform.spec.ts.snap +1875 -623
- package/src/py/fast-api/generator.js +2 -1
- package/src/py/fast-api/generator.js.map +1 -1
- package/src/py/lambda-function/__snapshots__/generator.spec.ts.snap +8 -0
- package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +4 -0
- package/src/py/rdb/__snapshots__/generator.spec.ts.snap +31 -2
- package/src/sdk/open-api.d.ts +2 -0
- package/src/sdk/open-api.js +2 -1
- package/src/sdk/open-api.js.map +1 -1
- package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +1756 -457
- package/src/smithy/ts/api/generator.js +2 -1
- package/src/smithy/ts/api/generator.js.map +1 -1
- package/src/terraform/project/files/application/bootstrap/providers.tf.template +11 -0
- package/src/terraform/project/files/application/scripts/bootstrap-destroy.ts.template +111 -0
- package/src/terraform/project/files/application/src/providers.tf.template +6 -0
- package/src/terraform/project/files/checkov/checkov.yml.template +8 -0
- package/src/terraform/project/files/library/src/main.tf.template +9 -0
- package/src/terraform/project/generator.js +47 -4
- package/src/terraform/project/generator.js.map +1 -1
- package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +1848 -614
- package/src/trpc/backend/files-operations/scripts/generate-operations.ts.template +40 -0
- package/src/trpc/backend/generator.js +10 -0
- package/src/trpc/backend/generator.js.map +1 -1
- package/src/ts/agent/__snapshots__/generator.spec.ts.snap +8 -0
- package/src/ts/dcr-proxy/__snapshots__/generator.spec.ts.snap +4 -0
- package/src/ts/lambda-function/__snapshots__/generator.spec.ts.snap +8 -0
- package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +4 -0
- package/src/ts/rdb/__snapshots__/generator.spec.ts.snap +62 -4
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +4 -0
- package/src/ts/react-website/cognito-auth/__snapshots__/generator.spec.ts.snap +35 -8
- package/src/ts/react-website/cognito-auth/__snapshots__/generator.terraform.spec.ts.snap +4 -0
- package/src/utils/agent-core-constructs/files/cdk/app/agentcore-harness/__nameKebabCase__/__nameKebabCase__.ts.template +39 -1
- package/src/utils/agent-core-constructs/files/terraform/app/agent-core/__nameKebabCase__/__nameKebabCase__.tf.template +8 -1
- package/src/utils/agent-core-constructs/files/terraform/app/agentcore-gateway/__nameKebabCase__/__nameKebabCase__.tf.template +57 -2
- package/src/utils/agent-core-constructs/files/terraform/app/agentcore-harness/__nameKebabCase__/__nameKebabCase__.tf.template +258 -24
- package/src/utils/agent-core-constructs/files/terraform/core/agent-core/runtime.tf.template +4 -0
- package/src/utils/api-constructs/api-constructs.js +11 -0
- 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 +251 -3
- package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +437 -2
- package/src/utils/api-constructs/files/terraform/core/api/rest/rest-api/rest-api.tf.template +60 -7
- package/src/utils/api-constructs/open-api-metadata.d.ts +20 -3
- package/src/utils/api-constructs/open-api-metadata.js +64 -8
- package/src/utils/api-constructs/open-api-metadata.js.map +1 -1
- package/src/utils/api-constructs/trpc-operations-metadata.d.ts +21 -0
- package/src/utils/api-constructs/trpc-operations-metadata.js +55 -0
- package/src/utils/api-constructs/trpc-operations-metadata.js.map +1 -0
- package/src/utils/dcr-proxy-constructs/files/terraform/app/dcr-proxies/__nameKebabCase__/__nameKebabCase__.tf.template +4 -0
- package/src/utils/files/terraform/src/metrics/providers.tf.template +12 -0
- package/src/utils/function-constructs/files/terraform/app/lambda-functions/__nameKebabCase__/__nameKebabCase__.tf.template +4 -0
- package/src/utils/identity-constructs/files/cdk/core/user-identity.ts.template +33 -8
- package/src/utils/identity-constructs/files/terraform/core/user-identity/add-callback-url/add-callback-url.tf.template +4 -0
- package/src/utils/identity-constructs/files/terraform/core/user-identity/identity/identity.tf.template +51 -2
- package/src/utils/rdb-constructs/files/cdk/core/rdb/aurora.ts.template +18 -0
- package/src/utils/rdb-constructs/files/terraform/app/dbs/__nameKebabCase__/__nameKebabCase__.tf.template +15 -4
- package/src/utils/rdb-constructs/files/terraform/core/rdb/aurora/aurora.tf.template +2 -2
- package/src/utils/rdb-constructs/rdb-constructs.d.ts +12 -0
- package/src/utils/rdb-constructs/rdb-constructs.js +24 -3
- package/src/utils/rdb-constructs/rdb-constructs.js.map +1 -1
- package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template +4 -0
|
@@ -2,10 +2,18 @@ terraform {
|
|
|
2
2
|
required_version = ">= 1.0"
|
|
3
3
|
|
|
4
4
|
required_providers {
|
|
5
|
+
archive = {
|
|
6
|
+
source = "hashicorp/archive"
|
|
7
|
+
version = "<%- archiveProviderVersion %>"
|
|
8
|
+
}
|
|
5
9
|
aws = {
|
|
6
10
|
source = "hashicorp/aws"
|
|
7
11
|
version = "<%- awsProviderVersion %>"
|
|
8
12
|
}
|
|
13
|
+
random = {
|
|
14
|
+
source = "hashicorp/random"
|
|
15
|
+
version = "<%- randomProviderVersion %>"
|
|
16
|
+
}
|
|
9
17
|
}
|
|
10
18
|
}
|
|
11
19
|
|
|
@@ -123,6 +131,95 @@ resource "random_string" "suffix" {
|
|
|
123
131
|
upper = false
|
|
124
132
|
}
|
|
125
133
|
|
|
134
|
+
<%_ if (backend.integrationPattern === 'isolated') { _%>
|
|
135
|
+
locals {
|
|
136
|
+
# Generated at build time from the API definition
|
|
137
|
+
operations_file = "${path.module}/../../../generated/<%- apiNameKebabCase %>/operations.json"
|
|
138
|
+
operations = fileexists(local.operations_file) ? jsondecode(file(local.operations_file)) : {}
|
|
139
|
+
|
|
140
|
+
operation_slug = { for op in keys(local.operations) : op => replace(op, "/[^a-zA-Z0-9-_]/", "-") }
|
|
141
|
+
operation_hash = { for op in keys(local.operations) : op => substr(sha256(op), 0, 8) }
|
|
142
|
+
|
|
143
|
+
function_name = { for op in keys(local.operations) : op =>
|
|
144
|
+
"${substr("<%- apiNameClassName %>-${local.operation_slug[op]}", 0, 64 - length(local.operation_hash[op]) - length(random_string.suffix.result) - 2)}-${local.operation_hash[op]}-${random_string.suffix.result}"
|
|
145
|
+
}
|
|
146
|
+
role_name = { for op in keys(local.operations) : op =>
|
|
147
|
+
"${substr("<%- apiNameClassName %>-${local.operation_slug[op]}-role", 0, 64 - length(local.operation_hash[op]) - length(random_string.suffix.result) - 2)}-${local.operation_hash[op]}-${random_string.suffix.result}"
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# A REST API needs a resource per path segment, so each operation's path is
|
|
151
|
+
# split into the distinct path prefixes the resource tree is built from below
|
|
152
|
+
operation_segments = { for op, details in local.operations : op => compact(split("/", details.path)) }
|
|
153
|
+
|
|
154
|
+
path_nodes = merge([
|
|
155
|
+
for op, segments in local.operation_segments : {
|
|
156
|
+
for i, segment in segments : join("/", slice(segments, 0, i + 1)) => {
|
|
157
|
+
depth = i
|
|
158
|
+
parent = i == 0 ? "" : join("/", slice(segments, 0, i))
|
|
159
|
+
part = segment
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
]...)
|
|
163
|
+
|
|
164
|
+
# Instances of a resource cannot reference one another, so each depth is a
|
|
165
|
+
# separate resource. Deeper paths are reported by the precondition below.
|
|
166
|
+
max_path_depth = <%- maxRestPathDepth %>
|
|
167
|
+
path_nodes_by_depth = { for depth in range(local.max_path_depth) : depth => {
|
|
168
|
+
for path, node in local.path_nodes : path => node if node.depth == depth
|
|
169
|
+
} }
|
|
170
|
+
paths_too_deep = [for path, node in local.path_nodes : path if node.depth >= local.max_path_depth]
|
|
171
|
+
|
|
172
|
+
resource_ids = merge(
|
|
173
|
+
<%_ for (let depth = 0; depth < maxRestPathDepth; depth++) { _%>
|
|
174
|
+
{ for path, resource in aws_api_gateway_resource.path_depth_<%- depth %> : path => resource.id },
|
|
175
|
+
<%_ } _%>
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
operation_resource_id = { for op, segments in local.operation_segments : op =>
|
|
179
|
+
local.resource_ids[join("/", segments)]
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
permission_source_suffix = { for op, details in local.operations : op =>
|
|
183
|
+
"${upper(details.method)}/${replace(join("/", local.operation_segments[op]), "/{[^}]*}/", "*")}"
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
resource "terraform_data" "operations_metadata" {
|
|
188
|
+
# Fails the plan when the operations metadata has not been generated
|
|
189
|
+
input = local.operations_file
|
|
190
|
+
|
|
191
|
+
lifecycle {
|
|
192
|
+
precondition {
|
|
193
|
+
condition = fileexists(local.operations_file)
|
|
194
|
+
error_message = "Operations metadata not found at ${local.operations_file}. Build the <%- apiNameClassName %> project to generate it."
|
|
195
|
+
}
|
|
196
|
+
precondition {
|
|
197
|
+
condition = length(local.operations) > 0
|
|
198
|
+
error_message = "No operations found in ${local.operations_file}. The <%- apiNameClassName %> API must define at least one operation."
|
|
199
|
+
}
|
|
200
|
+
precondition {
|
|
201
|
+
condition = length(local.paths_too_deep) == 0
|
|
202
|
+
error_message = "Operation paths exceed the supported depth of ${local.max_path_depth} segments: ${join(", ", local.paths_too_deep)}. Add further aws_api_gateway_resource levels to this module to support them."
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
<%_ for (let depth = 0; depth < maxRestPathDepth; depth++) { _%>
|
|
208
|
+
# API Gateway resources at path depth <%- depth %>
|
|
209
|
+
resource "aws_api_gateway_resource" "path_depth_<%- depth %>" {
|
|
210
|
+
for_each = local.path_nodes_by_depth[<%- depth %>]
|
|
211
|
+
|
|
212
|
+
rest_api_id = module.rest_api.api_id
|
|
213
|
+
<%_ if (depth === 0) { _%>
|
|
214
|
+
parent_id = module.rest_api.api_root_resource_id
|
|
215
|
+
<%_ } else { _%>
|
|
216
|
+
parent_id = aws_api_gateway_resource.path_depth_<%- depth - 1 %>[each.value.parent].id
|
|
217
|
+
<%_ } _%>
|
|
218
|
+
path_part = each.value.part
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
<%_ } _%>
|
|
222
|
+
<%_ } _%>
|
|
126
223
|
# Resources
|
|
127
224
|
|
|
128
225
|
# Create Lambda ZIP file from the bundle directory
|
|
@@ -197,8 +294,14 @@ resource "aws_vpc_security_group_egress_rule" "api_lambda_https" {
|
|
|
197
294
|
description = "Allow outbound HTTPS to AWS service endpoints"
|
|
198
295
|
}
|
|
199
296
|
|
|
200
|
-
# Lambda function
|
|
201
297
|
resource "aws_lambda_function" "api_lambda" {
|
|
298
|
+
<%_ if (backend.integrationPattern === 'isolated') { _%>
|
|
299
|
+
# One lambda function per operation, each serving just that operation
|
|
300
|
+
for_each = local.operations
|
|
301
|
+
|
|
302
|
+
<%_ } else { _%>
|
|
303
|
+
# A single "router" lambda serving all requests
|
|
304
|
+
<%_ } _%>
|
|
202
305
|
#checkov:skip=CKV_AWS_117:Lambda function is optionally deployed into a VPC via vpc_id/subnet_ids; not required for this use case
|
|
203
306
|
#checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
|
|
204
307
|
#checkov:skip=CKV_AWS_272:Code signing not required for this use case
|
|
@@ -207,8 +310,13 @@ resource "aws_lambda_function" "api_lambda" {
|
|
|
207
310
|
s3_bucket = aws_s3_object.lambda_zip.bucket
|
|
208
311
|
s3_key = aws_s3_object.lambda_zip.key
|
|
209
312
|
s3_object_version = aws_s3_object.lambda_zip.version_id
|
|
313
|
+
<%_ if (backend.integrationPattern === 'isolated') { _%>
|
|
314
|
+
function_name = local.function_name[each.key]
|
|
315
|
+
role = aws_iam_role.lambda_execution_role[each.key].arn
|
|
316
|
+
<%_ } else { _%>
|
|
210
317
|
function_name = "<%- apiNameClassName %>Handler-${random_string.suffix.result}"
|
|
211
318
|
role = aws_iam_role.lambda_execution_role.arn
|
|
319
|
+
<%_ } _%>
|
|
212
320
|
<%_ if (['trpc', 'smithy'].includes(backend.type)) { _%>
|
|
213
321
|
handler = "index.handler"
|
|
214
322
|
runtime = "nodejs22.x"
|
|
@@ -264,7 +372,13 @@ resource "aws_lambda_function" "api_lambda" {
|
|
|
264
372
|
|
|
265
373
|
# IAM role for Lambda execution
|
|
266
374
|
resource "aws_iam_role" "lambda_execution_role" {
|
|
375
|
+
<%_ if (backend.integrationPattern === 'isolated') { _%>
|
|
376
|
+
for_each = local.operations
|
|
377
|
+
|
|
378
|
+
name = local.role_name[each.key]
|
|
379
|
+
<%_ } else { _%>
|
|
267
380
|
name = "<%- apiNameClassName %>Handler-execution-role-${random_string.suffix.result}"
|
|
381
|
+
<%_ } _%>
|
|
268
382
|
|
|
269
383
|
assume_role_policy = jsonencode({
|
|
270
384
|
Version = "2012-10-17"
|
|
@@ -284,25 +398,57 @@ resource "aws_iam_role" "lambda_execution_role" {
|
|
|
284
398
|
|
|
285
399
|
# Attach basic execution policy to Lambda role
|
|
286
400
|
resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
|
|
401
|
+
<%_ if (backend.integrationPattern === 'isolated') { _%>
|
|
402
|
+
for_each = local.operations
|
|
403
|
+
|
|
404
|
+
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
405
|
+
role = aws_iam_role.lambda_execution_role[each.key].name
|
|
406
|
+
<%_ } else { _%>
|
|
287
407
|
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
288
408
|
role = aws_iam_role.lambda_execution_role.name
|
|
409
|
+
<%_ } _%>
|
|
289
410
|
}
|
|
290
411
|
|
|
291
412
|
# Attach X-Ray tracing policy to Lambda role
|
|
292
413
|
resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
|
|
414
|
+
<%_ if (backend.integrationPattern === 'isolated') { _%>
|
|
415
|
+
for_each = local.operations
|
|
416
|
+
|
|
417
|
+
policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
|
|
418
|
+
role = aws_iam_role.lambda_execution_role[each.key].name
|
|
419
|
+
<%_ } else { _%>
|
|
293
420
|
policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
|
|
294
421
|
role = aws_iam_role.lambda_execution_role.name
|
|
422
|
+
<%_ } _%>
|
|
295
423
|
}
|
|
296
424
|
|
|
297
425
|
# Attach VPC access policy to Lambda role when deployed into a VPC
|
|
298
426
|
resource "aws_iam_role_policy_attachment" "lambda_vpc_access" {
|
|
427
|
+
<%_ if (backend.integrationPattern === 'isolated') { _%>
|
|
428
|
+
for_each = var.enable_vpc ? local.operations : {}
|
|
429
|
+
|
|
430
|
+
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
|
|
431
|
+
role = aws_iam_role.lambda_execution_role[each.key].name
|
|
432
|
+
<%_ } else { _%>
|
|
299
433
|
count = var.enable_vpc ? 1 : 0
|
|
300
434
|
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
|
|
301
435
|
role = aws_iam_role.lambda_execution_role.name
|
|
436
|
+
<%_ } _%>
|
|
302
437
|
}
|
|
303
438
|
|
|
304
439
|
# Additional IAM policies for Lambda (if provided)
|
|
305
440
|
resource "aws_iam_role_policy" "lambda_additional_policies" {
|
|
441
|
+
<%_ if (backend.integrationPattern === 'isolated') { _%>
|
|
442
|
+
for_each = local.operations
|
|
443
|
+
|
|
444
|
+
name = "${substr(local.function_name[each.key], 0, 55)}-policies"
|
|
445
|
+
role = aws_iam_role.lambda_execution_role[each.key].id
|
|
446
|
+
|
|
447
|
+
policy = jsonencode({
|
|
448
|
+
Version = "2012-10-17"
|
|
449
|
+
Statement = local.lambda_policy_document_statements
|
|
450
|
+
})
|
|
451
|
+
<%_ } else { _%>
|
|
306
452
|
count = length(local.lambda_policy_statements) > 0 ? 1 : 0
|
|
307
453
|
name = "<%- apiNameClassName %>Handler-additional-policies-${random_string.suffix.result}"
|
|
308
454
|
role = aws_iam_role.lambda_execution_role.id
|
|
@@ -311,6 +457,7 @@ resource "aws_iam_role_policy" "lambda_additional_policies" {
|
|
|
311
457
|
Version = "2012-10-17"
|
|
312
458
|
Statement = local.lambda_policy_statements
|
|
313
459
|
})
|
|
460
|
+
<%_ } _%>
|
|
314
461
|
}
|
|
315
462
|
|
|
316
463
|
locals {
|
|
@@ -325,6 +472,17 @@ locals {
|
|
|
325
472
|
Resource = ["${var.appconfig_application_arn}/*"]
|
|
326
473
|
}
|
|
327
474
|
] : [], var.additional_iam_policy_statements)
|
|
475
|
+
<%_ if (backend.integrationPattern === 'isolated') { _%>
|
|
476
|
+
|
|
477
|
+
# IAM rejects an empty statement list, so fall back to a no-op deny
|
|
478
|
+
lambda_policy_document_statements = length(local.lambda_policy_statements) > 0 ? local.lambda_policy_statements : [
|
|
479
|
+
{
|
|
480
|
+
Effect = "Deny"
|
|
481
|
+
Action = ["appconfig:GetLatestConfiguration"]
|
|
482
|
+
Resource = ["arn:aws:appconfig:${data.aws_region.current.region}:${data.aws_caller_identity.current.account_id}:application/none"]
|
|
483
|
+
}
|
|
484
|
+
]
|
|
485
|
+
<%_ } _%>
|
|
328
486
|
}
|
|
329
487
|
|
|
330
488
|
# CloudWatch Log Group for Lambda
|
|
@@ -332,16 +490,31 @@ resource "aws_cloudwatch_log_group" "lambda_logs" {
|
|
|
332
490
|
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
333
491
|
#checkov:skip=CKV_AWS_338:Log retention set to forever
|
|
334
492
|
#checkov:skip=CKV_AWS_66:Log retention set to forever
|
|
493
|
+
<%_ if (backend.integrationPattern === 'isolated') { _%>
|
|
494
|
+
for_each = local.operations
|
|
495
|
+
|
|
496
|
+
name = "/aws/lambda/${local.function_name[each.key]}"
|
|
497
|
+
tags = var.tags
|
|
498
|
+
<%_ } else { _%>
|
|
335
499
|
name = "/aws/lambda/<%- apiNameClassName %>Handler-${random_string.suffix.result}"
|
|
336
500
|
tags = var.tags
|
|
501
|
+
<%_ } _%>
|
|
337
502
|
}
|
|
338
503
|
|
|
339
504
|
<%_ if (backend.type === 'fastapi') { _%>
|
|
340
505
|
# Lambda alias pointing to the latest published version for SnapStart
|
|
341
506
|
resource "aws_lambda_alias" "live" {
|
|
507
|
+
<%_ if (backend.integrationPattern === 'isolated') { _%>
|
|
508
|
+
for_each = local.operations
|
|
509
|
+
|
|
510
|
+
name = "live"
|
|
511
|
+
function_name = aws_lambda_function.api_lambda[each.key].function_name
|
|
512
|
+
function_version = aws_lambda_function.api_lambda[each.key].version
|
|
513
|
+
<%_ } else { _%>
|
|
342
514
|
name = "live"
|
|
343
515
|
function_name = aws_lambda_function.api_lambda.function_name
|
|
344
516
|
function_version = aws_lambda_function.api_lambda.version
|
|
517
|
+
<%_ } _%>
|
|
345
518
|
|
|
346
519
|
depends_on = [aws_lambda_function.api_lambda]
|
|
347
520
|
}
|
|
@@ -464,6 +637,161 @@ resource "aws_lambda_permission" "authorizer_invoke" {
|
|
|
464
637
|
}
|
|
465
638
|
<%_ } _%>
|
|
466
639
|
|
|
640
|
+
<%_ if (backend.integrationPattern === 'isolated') { _%>
|
|
641
|
+
# Method per operation
|
|
642
|
+
resource "aws_api_gateway_method" "operation_methods" {
|
|
643
|
+
#checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
|
|
644
|
+
for_each = local.operations
|
|
645
|
+
|
|
646
|
+
rest_api_id = module.rest_api.api_id
|
|
647
|
+
resource_id = local.operation_resource_id[each.key]
|
|
648
|
+
http_method = upper(each.value.method)
|
|
649
|
+
|
|
650
|
+
<%_ if (auth === 'iam') { _%>
|
|
651
|
+
authorization = "AWS_IAM"
|
|
652
|
+
<%_ } else if (auth === 'cognito') { _%>
|
|
653
|
+
authorization = "COGNITO_USER_POOLS"
|
|
654
|
+
authorizer_id = aws_api_gateway_authorizer.cognito_authorizer.id
|
|
655
|
+
# Accept access tokens from both sign-in flows: 'openid' (Cognito hosted UI)
|
|
656
|
+
# and 'aws.cognito.signin.user.admin' (Cognito admin/SRP auth APIs).
|
|
657
|
+
authorization_scopes = ["openid", "aws.cognito.signin.user.admin"]
|
|
658
|
+
<%_ } else if (auth === 'custom') { _%>
|
|
659
|
+
authorization = "CUSTOM"
|
|
660
|
+
authorizer_id = aws_api_gateway_authorizer.custom_authorizer.id
|
|
661
|
+
<%_ } _%>
|
|
662
|
+
|
|
663
|
+
# Path parameters must be declared on the method
|
|
664
|
+
request_parameters = {
|
|
665
|
+
for segment in local.operation_segments[each.key] :
|
|
666
|
+
"method.request.path.${trimsuffix(trimprefix(segment, "{"), "}")}" => true
|
|
667
|
+
if startswith(segment, "{")
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
depends_on = [<% if (auth === 'cognito') { %>aws_api_gateway_authorizer.cognito_authorizer<% } else if (auth === 'custom') { %>aws_api_gateway_authorizer.custom_authorizer<% } %>]
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
# Lambda integration per operation
|
|
674
|
+
resource "aws_api_gateway_integration" "lambda_integration" {
|
|
675
|
+
for_each = local.operations
|
|
676
|
+
|
|
677
|
+
rest_api_id = module.rest_api.api_id
|
|
678
|
+
resource_id = local.operation_resource_id[each.key]
|
|
679
|
+
http_method = aws_api_gateway_method.operation_methods[each.key].http_method
|
|
680
|
+
|
|
681
|
+
integration_http_method = "POST"
|
|
682
|
+
type = "AWS_PROXY"
|
|
683
|
+
<%_ if (backend.type === 'fastapi') { _%>
|
|
684
|
+
# Use the response streaming invocation path with the alias ARN for SnapStart
|
|
685
|
+
uri = "arn:aws:apigateway:${data.aws_region.current.region}:lambda:path/2021-11-15/functions/${aws_lambda_alias.live[each.key].arn}/response-streaming-invocations"
|
|
686
|
+
response_transfer_mode = "STREAM"
|
|
687
|
+
<%_ } else if (backend.type === 'trpc') { _%>
|
|
688
|
+
uri = aws_lambda_function.api_lambda[each.key].response_streaming_invoke_arn
|
|
689
|
+
response_transfer_mode = "STREAM"
|
|
690
|
+
<%_ } else { _%>
|
|
691
|
+
uri = aws_lambda_function.api_lambda[each.key].invoke_arn
|
|
692
|
+
<%_ } _%>
|
|
693
|
+
|
|
694
|
+
<%_ if (backend.type === 'fastapi') { _%>
|
|
695
|
+
depends_on = [aws_lambda_alias.live]
|
|
696
|
+
<%_ } else { _%>
|
|
697
|
+
depends_on = [aws_lambda_function.api_lambda]
|
|
698
|
+
<%_ } _%>
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
# OPTIONS method for CORS preflight, on every resource an operation is served from
|
|
702
|
+
resource "aws_api_gateway_method" "options_method" {
|
|
703
|
+
#checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
|
|
704
|
+
#checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
|
|
705
|
+
for_each = local.resource_ids
|
|
706
|
+
|
|
707
|
+
rest_api_id = module.rest_api.api_id
|
|
708
|
+
resource_id = each.value
|
|
709
|
+
http_method = "OPTIONS"
|
|
710
|
+
authorization = "NONE"
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
# CORS integration for OPTIONS methods
|
|
714
|
+
resource "aws_api_gateway_integration" "options_integration" {
|
|
715
|
+
for_each = local.resource_ids
|
|
716
|
+
|
|
717
|
+
rest_api_id = module.rest_api.api_id
|
|
718
|
+
resource_id = each.value
|
|
719
|
+
http_method = aws_api_gateway_method.options_method[each.key].http_method
|
|
720
|
+
|
|
721
|
+
type = "MOCK"
|
|
722
|
+
request_templates = {
|
|
723
|
+
"application/json" = "{\"statusCode\": 204}"
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
# OPTIONS method responses
|
|
728
|
+
resource "aws_api_gateway_method_response" "options_response" {
|
|
729
|
+
for_each = local.resource_ids
|
|
730
|
+
|
|
731
|
+
rest_api_id = module.rest_api.api_id
|
|
732
|
+
resource_id = each.value
|
|
733
|
+
http_method = aws_api_gateway_method.options_method[each.key].http_method
|
|
734
|
+
status_code = "204"
|
|
735
|
+
|
|
736
|
+
response_parameters = {
|
|
737
|
+
"method.response.header.Access-Control-Allow-Headers" = true
|
|
738
|
+
"method.response.header.Access-Control-Allow-Methods" = true
|
|
739
|
+
"method.response.header.Access-Control-Allow-Origin" = true
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
# OPTIONS integration responses
|
|
744
|
+
resource "aws_api_gateway_integration_response" "options_integration_response" {
|
|
745
|
+
for_each = local.resource_ids
|
|
746
|
+
|
|
747
|
+
rest_api_id = module.rest_api.api_id
|
|
748
|
+
resource_id = each.value
|
|
749
|
+
http_method = aws_api_gateway_method.options_method[each.key].http_method
|
|
750
|
+
status_code = aws_api_gateway_method_response.options_response[each.key].status_code
|
|
751
|
+
|
|
752
|
+
response_parameters = {
|
|
753
|
+
"method.response.header.Access-Control-Allow-Headers" = "'${join(",", var.cors_allow_headers)}'"
|
|
754
|
+
"method.response.header.Access-Control-Allow-Methods" = "'${join(",", var.cors_allow_methods)}'"
|
|
755
|
+
"method.response.header.Access-Control-Allow-Origin" = "'${join(",", var.cors_allow_origins)}'"
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
depends_on = [aws_api_gateway_integration.options_integration]
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
# API Gateway deployment
|
|
762
|
+
resource "aws_api_gateway_deployment" "api_deployment" {
|
|
763
|
+
rest_api_id = module.rest_api.api_id
|
|
764
|
+
|
|
765
|
+
triggers = {
|
|
766
|
+
redeployment = sha1(jsonencode(concat(
|
|
767
|
+
[for r in aws_api_gateway_resource.path_depth_0 : r.id],
|
|
768
|
+
<%_ for (let depth = 1; depth < maxRestPathDepth; depth++) { _%>
|
|
769
|
+
[for r in aws_api_gateway_resource.path_depth_<%- depth %> : r.id],
|
|
770
|
+
<%_ } _%>
|
|
771
|
+
[for m in aws_api_gateway_method.operation_methods : m.id],
|
|
772
|
+
[for i in aws_api_gateway_integration.lambda_integration : i.id],
|
|
773
|
+
[for m in aws_api_gateway_method.options_method : m.id],
|
|
774
|
+
[for i in aws_api_gateway_integration.options_integration : i.id],
|
|
775
|
+
)))
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
lifecycle {
|
|
779
|
+
create_before_destroy = true
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
depends_on = [
|
|
783
|
+
aws_api_gateway_method.operation_methods,
|
|
784
|
+
aws_api_gateway_integration.lambda_integration,
|
|
785
|
+
aws_api_gateway_method.options_method,
|
|
786
|
+
aws_api_gateway_integration.options_integration,
|
|
787
|
+
aws_api_gateway_method_response.options_response,
|
|
788
|
+
aws_api_gateway_integration_response.options_integration_response,
|
|
789
|
+
<%_ if (auth === 'custom') { _%>
|
|
790
|
+
aws_api_gateway_authorizer.custom_authorizer,
|
|
791
|
+
<%_ } _%>
|
|
792
|
+
]
|
|
793
|
+
}
|
|
794
|
+
<%_ } else { _%>
|
|
467
795
|
# Create proxy resource (captures all paths)
|
|
468
796
|
resource "aws_api_gateway_resource" "proxy_resource" {
|
|
469
797
|
rest_api_id = module.rest_api.api_id
|
|
@@ -605,6 +933,8 @@ resource "aws_api_gateway_deployment" "api_deployment" {
|
|
|
605
933
|
]
|
|
606
934
|
}
|
|
607
935
|
|
|
936
|
+
<%_ } _%>
|
|
937
|
+
|
|
608
938
|
# KMS key for encrypting access logs at rest
|
|
609
939
|
resource "aws_kms_key" "access_logs" {
|
|
610
940
|
description = "<%- apiNameClassName %> API access log encryption"
|
|
@@ -749,6 +1079,18 @@ resource "aws_api_gateway_rest_api_policy" "api_policy" {
|
|
|
749
1079
|
# Lambda permission for API Gateway to invoke the function
|
|
750
1080
|
<%_ } _%>
|
|
751
1081
|
resource "aws_lambda_permission" "api_gateway_invoke" {
|
|
1082
|
+
<%_ if (backend.integrationPattern === 'isolated') { _%>
|
|
1083
|
+
for_each = local.operations
|
|
1084
|
+
|
|
1085
|
+
statement_id = "AllowExecutionFromAPIGateway"
|
|
1086
|
+
action = "lambda:InvokeFunction"
|
|
1087
|
+
function_name = aws_lambda_function.api_lambda[each.key].function_name
|
|
1088
|
+
<%_ if (backend.type === 'fastapi') { _%>
|
|
1089
|
+
qualifier = aws_lambda_alias.live[each.key].name
|
|
1090
|
+
<%_ } _%>
|
|
1091
|
+
principal = "apigateway.amazonaws.com"
|
|
1092
|
+
source_arn = "${module.rest_api.api_execution_arn}/*/${local.permission_source_suffix[each.key]}"
|
|
1093
|
+
<%_ } else { _%>
|
|
752
1094
|
statement_id = "AllowExecutionFromAPIGateway"
|
|
753
1095
|
action = "lambda:InvokeFunction"
|
|
754
1096
|
function_name = aws_lambda_function.api_lambda.function_name
|
|
@@ -757,6 +1099,7 @@ resource "aws_lambda_permission" "api_gateway_invoke" {
|
|
|
757
1099
|
<%_ } _%>
|
|
758
1100
|
principal = "apigateway.amazonaws.com"
|
|
759
1101
|
source_arn = "${module.rest_api.api_execution_arn}/*/*"
|
|
1102
|
+
<%_ } _%>
|
|
760
1103
|
|
|
761
1104
|
<%_ if (backend.type === 'fastapi') { _%>
|
|
762
1105
|
depends_on = [module.rest_api, aws_lambda_alias.live]
|
|
@@ -768,12 +1111,23 @@ resource "aws_lambda_permission" "api_gateway_invoke" {
|
|
|
768
1111
|
<%_ if (backend.type === 'fastapi') { _%>
|
|
769
1112
|
# Lambda permission for API Gateway to invoke with response streaming via alias
|
|
770
1113
|
resource "aws_lambda_permission" "api_gateway_invoke_streaming" {
|
|
1114
|
+
<%_ if (backend.integrationPattern === 'isolated') { _%>
|
|
1115
|
+
for_each = local.operations
|
|
1116
|
+
|
|
1117
|
+
statement_id = "AllowStreamingFromAPIGateway"
|
|
1118
|
+
action = "lambda:InvokeFunctionWithResponseStream"
|
|
1119
|
+
function_name = aws_lambda_function.api_lambda[each.key].function_name
|
|
1120
|
+
qualifier = aws_lambda_alias.live[each.key].name
|
|
1121
|
+
principal = "apigateway.amazonaws.com"
|
|
1122
|
+
source_arn = "${module.rest_api.api_execution_arn}/*/${local.permission_source_suffix[each.key]}"
|
|
1123
|
+
<%_ } else { _%>
|
|
771
1124
|
statement_id = "AllowStreamingFromAPIGateway"
|
|
772
1125
|
action = "lambda:InvokeFunctionWithResponseStream"
|
|
773
1126
|
function_name = aws_lambda_function.api_lambda.function_name
|
|
774
1127
|
qualifier = aws_lambda_alias.live.name
|
|
775
1128
|
principal = "apigateway.amazonaws.com"
|
|
776
1129
|
source_arn = "${module.rest_api.api_execution_arn}/*/*"
|
|
1130
|
+
<%_ } _%>
|
|
777
1131
|
|
|
778
1132
|
depends_on = [module.rest_api, aws_lambda_alias.live]
|
|
779
1133
|
}
|
|
@@ -785,7 +1139,7 @@ module "add_url_to_runtime_config" {
|
|
|
785
1139
|
|
|
786
1140
|
namespace = "connection"
|
|
787
1141
|
key = "apis"
|
|
788
|
-
value = { "<%- apiNameClassName %>" = aws_api_gateway_stage.api_stage.invoke_url }
|
|
1142
|
+
value = { "<%- apiNameClassName %>" = "${aws_api_gateway_stage.api_stage.invoke_url}/" }
|
|
789
1143
|
|
|
790
1144
|
depends_on = [aws_api_gateway_stage.api_stage]
|
|
791
1145
|
}
|
|
@@ -838,6 +1192,86 @@ output "stage_id" {
|
|
|
838
1192
|
value = aws_api_gateway_stage.api_stage.id
|
|
839
1193
|
}
|
|
840
1194
|
|
|
1195
|
+
<%_ if (backend.integrationPattern === 'isolated') { _%>
|
|
1196
|
+
# Lambda Function Outputs, keyed by operation name
|
|
1197
|
+
output "operations" {
|
|
1198
|
+
description = "Names of the API operations, each with its own Lambda function"
|
|
1199
|
+
value = keys(local.operations)
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
output "lambda_function_names" {
|
|
1203
|
+
description = "Name of each operation's Lambda function, keyed by operation name"
|
|
1204
|
+
value = { for op, fn in aws_lambda_function.api_lambda : op => fn.function_name }
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
output "lambda_function_arns" {
|
|
1208
|
+
description = "ARN of each operation's Lambda function, keyed by operation name"
|
|
1209
|
+
value = { for op, fn in aws_lambda_function.api_lambda : op => fn.arn }
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
output "lambda_invoke_arns" {
|
|
1213
|
+
description = "Invoke ARN of each operation's Lambda function, keyed by operation name"
|
|
1214
|
+
value = { for op, fn in aws_lambda_function.api_lambda : op => fn.invoke_arn }
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
output "lambda_qualified_arns" {
|
|
1218
|
+
description = "Qualified ARN of each operation's Lambda function, keyed by operation name"
|
|
1219
|
+
value = { for op, fn in aws_lambda_function.api_lambda : op => fn.qualified_arn }
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
output "lambda_versions" {
|
|
1223
|
+
description = "Version of each operation's Lambda function, keyed by operation name"
|
|
1224
|
+
value = { for op, fn in aws_lambda_function.api_lambda : op => fn.version }
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
output "lambda_source_code_hash" {
|
|
1228
|
+
description = "Base64-encoded SHA256 hash of the Lambda deployment package, shared by every operation"
|
|
1229
|
+
value = data.archive_file.lambda_zip.output_base64sha256
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
# IAM Role Outputs, keyed by operation name
|
|
1233
|
+
output "lambda_execution_role_arns" {
|
|
1234
|
+
description = "ARN of each operation's Lambda execution role, keyed by operation name"
|
|
1235
|
+
value = { for op, role in aws_iam_role.lambda_execution_role : op => role.arn }
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
output "lambda_execution_role_names" {
|
|
1239
|
+
description = "Name of each operation's Lambda execution role, keyed by operation name. Attach additional policies to a specific operation's role by name."
|
|
1240
|
+
value = { for op, role in aws_iam_role.lambda_execution_role : op => role.name }
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
output "security_group_id" {
|
|
1244
|
+
description = "Security group ID shared by the API Lambda functions, for use in ingress rules on resources they must reach (e.g. a database). Null unless enable_vpc is true."
|
|
1245
|
+
value = var.enable_vpc ? aws_security_group.api_lambda[0].id : null
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
# Integration Outputs, keyed by operation name
|
|
1249
|
+
output "integration_ids" {
|
|
1250
|
+
description = "ID of each operation's Lambda integration, keyed by operation name"
|
|
1251
|
+
value = { for op, i in aws_api_gateway_integration.lambda_integration : op => i.id }
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
output "method_ids" {
|
|
1255
|
+
description = "ID of each operation's API Gateway method, keyed by operation name"
|
|
1256
|
+
value = { for op, m in aws_api_gateway_method.operation_methods : op => m.id }
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
output "resource_ids" {
|
|
1260
|
+
description = "ID of each API Gateway resource, keyed by its path"
|
|
1261
|
+
value = local.resource_ids
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
# CloudWatch Log Groups, keyed by operation name
|
|
1265
|
+
output "lambda_log_group_names" {
|
|
1266
|
+
description = "Name of each operation's Lambda CloudWatch log group, keyed by operation name"
|
|
1267
|
+
value = { for op, lg in aws_cloudwatch_log_group.lambda_logs : op => lg.name }
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
output "lambda_log_group_arns" {
|
|
1271
|
+
description = "ARN of each operation's Lambda CloudWatch log group, keyed by operation name"
|
|
1272
|
+
value = { for op, lg in aws_cloudwatch_log_group.lambda_logs : op => lg.arn }
|
|
1273
|
+
}
|
|
1274
|
+
<%_ } else { _%>
|
|
841
1275
|
# Lambda Function Outputs
|
|
842
1276
|
output "lambda_function_name" {
|
|
843
1277
|
description = "Name of the Lambda function"
|
|
@@ -916,3 +1350,4 @@ output "lambda_log_group_arn" {
|
|
|
916
1350
|
description = "ARN of the Lambda CloudWatch log group"
|
|
917
1351
|
value = aws_cloudwatch_log_group.lambda_logs.arn
|
|
918
1352
|
}
|
|
1353
|
+
<%_ } _%>
|
package/src/utils/api-constructs/files/terraform/core/api/rest/rest-api/rest-api.tf.template
CHANGED
|
@@ -200,22 +200,75 @@ resource "aws_wafv2_web_acl" "api_waf" {
|
|
|
200
200
|
}
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
+
resource "random_id" "waf_log_suffix" {
|
|
204
|
+
count = var.enable_waf ? 1 : 0
|
|
205
|
+
byte_length = 4
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
locals {
|
|
209
|
+
waf_log_group_name = var.enable_waf ? "aws-waf-logs-${var.api_name}-${random_id.waf_log_suffix[0].hex}" : null
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
# KMS key for encrypting WAF request logs at rest
|
|
213
|
+
resource "aws_kms_key" "waf_logs" {
|
|
214
|
+
count = var.enable_waf ? 1 : 0
|
|
215
|
+
|
|
216
|
+
description = "${var.api_name} API WAF log encryption"
|
|
217
|
+
deletion_window_in_days = 7
|
|
218
|
+
enable_key_rotation = true
|
|
219
|
+
|
|
220
|
+
policy = jsonencode({
|
|
221
|
+
Version = "2012-10-17"
|
|
222
|
+
Statement = [
|
|
223
|
+
{
|
|
224
|
+
Sid = "Enable IAM User Permissions"
|
|
225
|
+
Effect = "Allow"
|
|
226
|
+
Principal = { AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" }
|
|
227
|
+
Action = "kms:*"
|
|
228
|
+
Resource = "*"
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
Sid = "Allow CloudWatch Logs"
|
|
232
|
+
Effect = "Allow"
|
|
233
|
+
Principal = { Service = "logs.${data.aws_region.current.region}.amazonaws.com" }
|
|
234
|
+
Action = [
|
|
235
|
+
"kms:Encrypt",
|
|
236
|
+
"kms:Decrypt",
|
|
237
|
+
"kms:ReEncrypt*",
|
|
238
|
+
"kms:GenerateDataKey*",
|
|
239
|
+
"kms:DescribeKey"
|
|
240
|
+
]
|
|
241
|
+
Resource = "*"
|
|
242
|
+
Condition = {
|
|
243
|
+
ArnEquals = {
|
|
244
|
+
"kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:${data.aws_region.current.region}:${data.aws_caller_identity.current.account_id}:log-group:${local.waf_log_group_name}"
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
]
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
tags = var.tags
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
resource "aws_kms_alias" "waf_logs" {
|
|
255
|
+
count = var.enable_waf ? 1 : 0
|
|
256
|
+
|
|
257
|
+
name = "alias/${var.api_name}-${random_id.waf_log_suffix[0].hex}-waf-logs"
|
|
258
|
+
target_key_id = aws_kms_key.waf_logs[0].key_id
|
|
259
|
+
}
|
|
260
|
+
|
|
203
261
|
# CloudWatch Log Group for WAF request logs. Name must start with `aws-waf-logs-`.
|
|
204
262
|
resource "aws_cloudwatch_log_group" "api_waf_logs" {
|
|
205
|
-
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
206
263
|
#checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
|
|
207
264
|
count = var.enable_waf ? 1 : 0
|
|
208
265
|
|
|
209
|
-
name =
|
|
266
|
+
name = local.waf_log_group_name
|
|
210
267
|
retention_in_days = 30
|
|
268
|
+
kms_key_id = aws_kms_key.waf_logs[0].arn
|
|
211
269
|
tags = var.tags
|
|
212
270
|
}
|
|
213
271
|
|
|
214
|
-
resource "random_id" "waf_log_suffix" {
|
|
215
|
-
count = var.enable_waf ? 1 : 0
|
|
216
|
-
byte_length = 4
|
|
217
|
-
}
|
|
218
|
-
|
|
219
272
|
resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
|
|
220
273
|
count = var.enable_waf ? 1 : 0
|
|
221
274
|
|