@aws/nx-plugin 1.0.0-rc.22 → 1.0.0-rc.24
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/LICENSE-THIRD-PARTY +2088 -971
- package/package.json +25 -10
- package/src/agentcore-gateway/attach-upstream.d.ts +7 -7
- package/src/agentcore-gateway/attach-upstream.js +19 -22
- package/src/agentcore-gateway/attach-upstream.js.map +1 -1
- package/src/agentcore-gateway/gateway-connection/generator.d.ts +3 -3
- package/src/agentcore-gateway/gateway-connection/generator.js +42 -45
- package/src/agentcore-gateway/gateway-connection/generator.js.map +1 -1
- package/src/agentcore-gateway/gateway-connection/schema.d.js +6 -0
- package/src/agentcore-gateway/gateway-connection/schema.d.js.map +1 -0
- package/src/agentcore-gateway/generator.js +89 -72
- package/src/agentcore-gateway/generator.js.map +1 -1
- package/src/agentcore-gateway/mcp-connection/generator.d.ts +2 -2
- package/src/agentcore-gateway/mcp-connection/generator.js +24 -30
- package/src/agentcore-gateway/mcp-connection/generator.js.map +1 -1
- package/src/agentcore-gateway/mcp-connection/schema.d.js +6 -0
- package/src/agentcore-gateway/mcp-connection/schema.d.js.map +1 -0
- package/src/agentcore-gateway/schema.d.js +6 -0
- package/src/agentcore-gateway/schema.d.js.map +1 -0
- package/src/bin/aws-nx-mcp.js +14 -0
- package/src/bin/aws-nx-mcp.js.map +1 -0
- package/src/connection/{agent-serve-local.d.ts → agent-local-dev.d.ts} +5 -5
- package/src/connection/agent-local-dev.js +42 -0
- package/src/connection/agent-local-dev.js.map +1 -0
- package/src/connection/agent-runtime-config.js +9 -14
- package/src/connection/agent-runtime-config.js.map +1 -1
- package/src/connection/generator.js +262 -207
- package/src/connection/generator.js.map +1 -1
- package/src/connection/{serve-local.d.ts → local-dev.d.ts} +3 -3
- package/src/connection/local-dev.js +37 -0
- package/src/connection/local-dev.js.map +1 -0
- package/src/connection/schema.d.js +6 -0
- package/src/connection/schema.d.js.map +1 -0
- package/src/index.js +2 -5
- package/src/index.js.map +1 -1
- package/src/infra/app/__snapshots__/generator.spec.ts.snap +4 -26
- package/src/infra/app/generator.js +133 -123
- package/src/infra/app/generator.js.map +1 -1
- package/src/infra/app/schema.d.js +6 -0
- package/src/infra/app/schema.d.js.map +1 -0
- package/src/license/config-types.js +5 -3
- package/src/license/config-types.js.map +1 -1
- package/src/license/config.d.ts +1 -1
- package/src/license/config.js +267 -242
- package/src/license/config.js.map +1 -1
- package/src/license/dependency-check/check.js +51 -46
- package/src/license/dependency-check/check.js.map +1 -1
- package/src/license/dependency-check/collectors/collector.js +41 -71
- package/src/license/dependency-check/collectors/collector.js.map +1 -1
- package/src/license/dependency-check/collectors/npm-collector.js +34 -41
- package/src/license/dependency-check/collectors/npm-collector.js.map +1 -1
- package/src/license/dependency-check/collectors/python-collector.js +55 -62
- package/src/license/dependency-check/collectors/python-collector.js.map +1 -1
- package/src/license/dependency-check/evaluator.js +40 -68
- package/src/license/dependency-check/evaluator.js.map +1 -1
- package/src/license/dependency-check/executor.js +30 -22
- package/src/license/dependency-check/executor.js.map +1 -1
- package/src/license/dependency-check/pre-approved.js +949 -336
- package/src/license/dependency-check/pre-approved.js.map +1 -1
- package/src/license/dependency-check/types.js +2 -3
- package/src/license/dependency-check/types.js.map +1 -1
- package/src/license/generator.js +55 -60
- package/src/license/generator.js.map +1 -1
- package/src/license/index.js +3 -9
- package/src/license/index.js.map +1 -1
- package/src/license/known-exceptions.js +14 -13
- package/src/license/known-exceptions.js.map +1 -1
- package/src/license/schema.d.js +6 -0
- package/src/license/schema.d.js.map +1 -0
- package/src/license/sync/generator.js +94 -119
- package/src/license/sync/generator.js.map +1 -1
- package/src/license/sync/project-file-sync.js +28 -35
- package/src/license/sync/project-file-sync.js.map +1 -1
- package/src/mcp-server/generator-info.js +130 -160
- package/src/mcp-server/generator-info.js.map +1 -1
- package/src/mcp-server/guide-pipeline.js +184 -206
- package/src/mcp-server/guide-pipeline.js.map +1 -1
- package/src/mcp-server/guide-render.js +10 -25
- package/src/mcp-server/guide-render.js.map +1 -1
- package/src/mcp-server/index.js +6 -10
- package/src/mcp-server/index.js.map +1 -1
- package/src/mcp-server/mdx-ast.js +39 -57
- package/src/mcp-server/mdx-ast.js.map +1 -1
- package/src/mcp-server/option-filter.js +33 -47
- package/src/mcp-server/option-filter.js.map +1 -1
- package/src/mcp-server/schema-registry.js +27 -40
- package/src/mcp-server/schema-registry.js.map +1 -1
- package/src/mcp-server/schema.js +9 -7
- package/src/mcp-server/schema.js.map +1 -1
- package/src/mcp-server/server.js +20 -24
- package/src/mcp-server/server.js.map +1 -1
- package/src/mcp-server/tools/create-workspace-command.js +23 -24
- package/src/mcp-server/tools/create-workspace-command.js.map +1 -1
- package/src/mcp-server/tools/general-guidance.js +28 -25
- package/src/mcp-server/tools/general-guidance.js.map +1 -1
- package/src/mcp-server/tools/generator-guide.js +29 -37
- package/src/mcp-server/tools/generator-guide.js.map +1 -1
- package/src/mcp-server/tools/list-generators.js +19 -25
- package/src/mcp-server/tools/list-generators.js.map +1 -1
- package/src/open-api/ts-client/generator.js +16 -26
- package/src/open-api/ts-client/generator.js.map +1 -1
- package/src/open-api/ts-client/schema.d.js +6 -0
- package/src/open-api/ts-client/schema.d.js.map +1 -0
- package/src/open-api/ts-hooks/generator.js +13 -21
- package/src/open-api/ts-hooks/generator.js.map +1 -1
- package/src/open-api/ts-hooks/schema.d.js +6 -0
- package/src/open-api/ts-hooks/schema.d.js.map +1 -0
- package/src/open-api/ts-metadata/generator.js +12 -20
- package/src/open-api/ts-metadata/generator.js.map +1 -1
- package/src/open-api/ts-metadata/schema.d.js +6 -0
- package/src/open-api/ts-metadata/schema.d.js.map +1 -0
- package/src/open-api/utils/codegen-data/languages.js +42 -60
- package/src/open-api/utils/codegen-data/languages.js.map +1 -1
- package/src/open-api/utils/codegen-data/types.js +31 -30
- package/src/open-api/utils/codegen-data/types.js.map +1 -1
- package/src/open-api/utils/codegen-data.js +253 -269
- package/src/open-api/utils/codegen-data.js.map +1 -1
- package/src/open-api/utils/normalise.js +242 -241
- package/src/open-api/utils/normalise.js.map +1 -1
- package/src/open-api/utils/parse.js +8 -14
- package/src/open-api/utils/parse.js.map +1 -1
- package/src/open-api/utils/refs.js +12 -22
- package/src/open-api/utils/refs.js.map +1 -1
- package/src/open-api/utils/types.js +5 -2
- package/src/open-api/utils/types.js.map +1 -1
- package/src/preset/__snapshots__/generator.spec.ts.snap +31 -6
- package/src/preset/generator.d.ts +1 -1
- package/src/preset/generator.js +130 -112
- package/src/preset/generator.js.map +1 -1
- package/src/preset/schema.d.js +6 -0
- package/src/preset/schema.d.js.map +1 -0
- package/src/preset/schema.d.ts +0 -1
- package/src/preset/schema.json +0 -5
- package/src/py/agent/__snapshots__/generator.constructs.spec.ts.snap +304 -0
- package/src/py/agent/__snapshots__/{generator.spec.ts.snap → generator.frameworks.spec.ts.snap} +18 -845
- package/src/py/agent/__snapshots__/generator.protocols.spec.ts.snap +535 -0
- package/src/py/agent/a2a-connection/__snapshots__/generator.spec.ts.snap +1 -2
- package/src/py/agent/a2a-connection/files/agent-connection/app/__targetAgentSnakeCase___client_strands.py.template +1 -1
- package/src/py/agent/a2a-connection/files/langchain/agent-connection/app/__targetAgentSnakeCase___client_langchain.py.template +1 -1
- package/src/py/agent/a2a-connection/generator.js +70 -75
- package/src/py/agent/a2a-connection/generator.js.map +1 -1
- package/src/py/agent/a2a-connection/schema.d.js +8 -0
- package/src/py/agent/a2a-connection/schema.d.js.map +1 -0
- package/src/py/agent/files/ag-ui-langchain/main.py.template +10 -0
- package/src/py/agent/gateway-connection/__snapshots__/generator.spec.ts.snap +6 -8
- package/src/py/agent/gateway-connection/files/agent-connection/app/__gatewaySnakeCase___client_strands.py.template +3 -3
- package/src/py/agent/gateway-connection/files/langchain/agent-connection/app/__gatewaySnakeCase___client_langchain.py.template +3 -3
- package/src/py/agent/gateway-connection/generator.js +61 -58
- package/src/py/agent/gateway-connection/generator.js.map +1 -1
- package/src/py/agent/gateway-connection/schema.d.js +6 -0
- package/src/py/agent/gateway-connection/schema.d.js.map +1 -0
- package/src/py/agent/generator.js +227 -194
- package/src/py/agent/generator.js.map +1 -1
- package/src/py/agent/mcp-connection/__snapshots__/generator.spec.ts.snap +5 -12
- package/src/py/agent/mcp-connection/files/agent-connection/app/__mcpServerSnakeCase___client_strands.py.template +1 -1
- package/src/py/agent/mcp-connection/files/langchain/agent-connection/app/__mcpServerSnakeCase___client_langchain.py.template +1 -1
- package/src/py/agent/mcp-connection/generator.js +53 -52
- package/src/py/agent/mcp-connection/generator.js.map +1 -1
- package/src/py/agent/mcp-connection/schema.d.js +8 -0
- package/src/py/agent/mcp-connection/schema.d.js.map +1 -0
- package/src/py/agent/react-connection/__snapshots__/generator.spec.ts.snap +3 -2
- package/src/py/agent/react-connection/generator.js +51 -58
- package/src/py/agent/react-connection/generator.js.map +1 -1
- package/src/py/agent/react-connection/{serve-local.d.ts → local-dev.d.ts} +4 -4
- package/src/py/agent/react-connection/local-dev.js +36 -0
- package/src/py/agent/react-connection/local-dev.js.map +1 -0
- package/src/py/agent/react-connection/schema.d.js +8 -0
- package/src/py/agent/react-connection/schema.d.js.map +1 -0
- package/src/py/agent/schema.d.js +6 -0
- package/src/py/agent/schema.d.js.map +1 -0
- package/src/py/api/generator.js +12 -12
- package/src/py/api/generator.js.map +1 -1
- package/src/py/api/schema.d.js +6 -0
- package/src/py/api/schema.d.js.map +1 -0
- package/src/py/dynamodb/__snapshots__/generator.spec.ts.snap +68 -28
- package/src/py/dynamodb/agent-connection/__snapshots__/generator.spec.ts.snap +5 -5
- package/src/py/dynamodb/agent-connection/generator.js +27 -29
- package/src/py/dynamodb/agent-connection/generator.js.map +1 -1
- package/src/py/dynamodb/agent-connection/schema.d.js +8 -0
- package/src/py/dynamodb/agent-connection/schema.d.js.map +1 -0
- package/src/py/dynamodb/fast-api-connection/__snapshots__/generator.spec.ts.snap +3 -3
- package/src/py/dynamodb/fast-api-connection/generator.js +26 -28
- package/src/py/dynamodb/fast-api-connection/generator.js.map +1 -1
- package/src/py/dynamodb/fast-api-connection/schema.d.js +8 -0
- package/src/py/dynamodb/fast-api-connection/schema.d.js.map +1 -0
- package/src/py/dynamodb/files/__name__/client.py.template +5 -5
- package/src/py/dynamodb/files/__name__/entities/base.py.template +2 -2
- package/src/py/dynamodb/files/config.json.template +1 -1
- package/src/py/dynamodb/generator.js +63 -62
- package/src/py/dynamodb/generator.js.map +1 -1
- package/src/py/dynamodb/mcp-server-connection/__snapshots__/generator.spec.ts.snap +5 -5
- package/src/py/dynamodb/mcp-server-connection/generator.js +27 -29
- package/src/py/dynamodb/mcp-server-connection/generator.js.map +1 -1
- package/src/py/dynamodb/mcp-server-connection/schema.d.js +8 -0
- package/src/py/dynamodb/mcp-server-connection/schema.d.js.map +1 -0
- package/src/py/dynamodb/schema.d.js +6 -0
- package/src/py/dynamodb/schema.d.js.map +1 -0
- package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +45 -4632
- package/src/py/fast-api/__snapshots__/generator.terraform.spec.ts.snap +4833 -0
- package/src/py/fast-api/generator.js +79 -85
- package/src/py/fast-api/generator.js.map +1 -1
- package/src/py/fast-api/react/__snapshots__/generator.spec.ts.snap +3 -2
- package/src/py/fast-api/react/generator.js +22 -24
- package/src/py/fast-api/react/generator.js.map +1 -1
- package/src/py/fast-api/react/open-api.js +28 -29
- package/src/py/fast-api/react/open-api.js.map +1 -1
- package/src/py/fast-api/react/schema.d.js +6 -0
- package/src/py/fast-api/react/schema.d.js.map +1 -0
- package/src/py/fast-api/schema.d.js +6 -0
- package/src/py/fast-api/schema.d.js.map +1 -0
- package/src/py/lambda-function/__snapshots__/generator.spec.ts.snap +4 -4
- package/src/py/lambda-function/generator.js +57 -58
- package/src/py/lambda-function/generator.js.map +1 -1
- package/src/py/lambda-function/schema.d.js +6 -0
- package/src/py/lambda-function/schema.d.js.map +1 -0
- package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +5 -5
- package/src/py/mcp-server/generator.js +149 -131
- package/src/py/mcp-server/generator.js.map +1 -1
- package/src/py/mcp-server/schema.d.js +6 -0
- package/src/py/mcp-server/schema.d.js.map +1 -0
- package/src/py/project/generator.d.ts +14 -1
- package/src/py/project/generator.js +137 -88
- package/src/py/project/generator.js.map +1 -1
- package/src/py/project/schema.d.js +6 -0
- package/src/py/project/schema.d.js.map +1 -0
- package/src/sdk/agentcore-gateway.d.ts +6 -0
- package/src/sdk/agentcore-gateway.js +7 -0
- package/src/sdk/agentcore-gateway.js.map +1 -0
- package/src/sdk/connection.d.ts +6 -0
- package/src/sdk/connection.js +6 -0
- package/src/sdk/connection.js.map +1 -0
- package/{sdk → src/sdk}/license.d.ts +4 -4
- package/src/sdk/license.js +7 -0
- package/src/sdk/license.js.map +1 -0
- package/src/sdk/open-api.d.ts +10 -0
- package/src/sdk/open-api.js +8 -0
- package/src/sdk/open-api.js.map +1 -0
- package/src/sdk/py.d.ts +14 -0
- package/src/sdk/py.js +10 -0
- package/src/sdk/py.js.map +1 -0
- package/src/sdk/terraform.d.ts +6 -0
- package/src/sdk/terraform.js +7 -0
- package/src/sdk/terraform.js.map +1 -0
- package/src/sdk/ts.d.ts +33 -0
- package/src/sdk/ts.js +35 -0
- package/src/sdk/ts.js.map +1 -0
- package/{sdk → src/sdk}/utils/ast.d.ts +2 -2
- package/src/sdk/utils/ast.js +7 -0
- package/src/sdk/utils/ast.js.map +1 -0
- package/{sdk → src/sdk}/utils/format.d.ts +1 -1
- package/src/sdk/utils/format.js +6 -0
- package/src/sdk/utils/format.js.map +1 -0
- package/{sdk → src/sdk}/utils/test.d.ts +1 -1
- package/src/sdk/utils/test.js +6 -0
- package/src/sdk/utils/test.js.map +1 -0
- package/src/smithy/project/__snapshots__/generator.spec.ts.snap +2 -8
- package/src/smithy/project/files/build.Dockerfile.template +1 -4
- package/src/smithy/project/generator.js +44 -42
- package/src/smithy/project/generator.js.map +1 -1
- package/src/smithy/project/schema.d.js +6 -0
- package/src/smithy/project/schema.d.js.map +1 -0
- package/src/smithy/react-connection/generator.js +35 -46
- package/src/smithy/react-connection/generator.js.map +1 -1
- package/src/smithy/react-connection/schema.d.js +6 -0
- package/src/smithy/react-connection/schema.d.js.map +1 -0
- package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +395 -72
- package/src/smithy/ts/api/generator.js +121 -111
- package/src/smithy/ts/api/generator.js.map +1 -1
- package/src/smithy/ts/api/schema.d.js +6 -0
- package/src/smithy/ts/api/schema.d.js.map +1 -0
- package/src/terraform/project/files/application/src/providers.tf.template +1 -1
- package/src/terraform/project/generator.js +141 -103
- package/src/terraform/project/generator.js.map +1 -1
- package/src/terraform/project/schema.d.js +9 -0
- package/src/terraform/project/schema.d.js.map +1 -0
- package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +336 -92
- package/src/trpc/backend/generator.js +93 -86
- package/src/trpc/backend/generator.js.map +1 -1
- package/src/trpc/backend/schema.d.js +6 -0
- package/src/trpc/backend/schema.d.js.map +1 -0
- package/src/trpc/react/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/trpc/react/generator.js +59 -58
- package/src/trpc/react/generator.js.map +1 -1
- package/src/trpc/react/schema.d.js +6 -0
- package/src/trpc/react/schema.d.js.map +1 -0
- package/src/ts/agent/__snapshots__/generator.spec.ts.snap +11 -11
- package/src/ts/agent/a2a-connection/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/ts/agent/a2a-connection/files/agent-connection/app/__targetAgentKebabCase__-client-strands.ts.template +1 -1
- package/src/ts/agent/a2a-connection/generator.js +58 -54
- package/src/ts/agent/a2a-connection/generator.js.map +1 -1
- package/src/ts/agent/a2a-connection/schema.d.js +8 -0
- package/src/ts/agent/a2a-connection/schema.d.js.map +1 -0
- package/src/ts/agent/gateway-connection/__snapshots__/generator.spec.ts.snap +5 -5
- package/src/ts/agent/gateway-connection/files/agent-connection/app/__gatewayKebabCase__-client-strands.ts.template +3 -3
- package/src/ts/agent/gateway-connection/generator.js +57 -52
- package/src/ts/agent/gateway-connection/generator.js.map +1 -1
- package/src/ts/agent/gateway-connection/schema.d.js +6 -0
- package/src/ts/agent/gateway-connection/schema.d.js.map +1 -0
- package/src/ts/agent/generator.js +165 -136
- package/src/ts/agent/generator.js.map +1 -1
- package/src/ts/agent/mcp-connection/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/ts/agent/mcp-connection/files/agent-connection/app/__mcpServerKebabCase__-client-strands.ts.template +1 -1
- package/src/ts/agent/mcp-connection/generator.js +48 -46
- package/src/ts/agent/mcp-connection/generator.js.map +1 -1
- package/src/ts/agent/mcp-connection/schema.d.js +8 -0
- package/src/ts/agent/mcp-connection/schema.d.js.map +1 -0
- package/src/ts/agent/react-connection/generator.js +79 -79
- package/src/ts/agent/react-connection/generator.js.map +1 -1
- package/src/ts/agent/react-connection/{serve-local.d.ts → local-dev.d.ts} +2 -2
- package/src/ts/agent/react-connection/local-dev.js +17 -0
- package/src/ts/agent/react-connection/local-dev.js.map +1 -0
- package/src/ts/agent/react-connection/schema.d.js +8 -0
- package/src/ts/agent/react-connection/schema.d.js.map +1 -0
- package/src/ts/agent/schema.d.js +6 -0
- package/src/ts/agent/schema.d.js.map +1 -0
- package/src/ts/api/generator.js +16 -18
- package/src/ts/api/generator.js.map +1 -1
- package/src/ts/api/schema.d.js +6 -0
- package/src/ts/api/schema.d.js.map +1 -0
- package/src/ts/astro-docs/__snapshots__/generator.spec.ts.snap +8 -8
- package/src/ts/astro-docs/generator.js +59 -48
- package/src/ts/astro-docs/generator.js.map +1 -1
- package/src/ts/astro-docs/schema.d.js +6 -0
- package/src/ts/astro-docs/schema.d.js.map +1 -0
- package/src/ts/docs/generator.js +10 -10
- package/src/ts/docs/generator.js.map +1 -1
- package/src/ts/docs/schema.d.js +6 -0
- package/src/ts/docs/schema.d.js.map +1 -0
- package/src/ts/dynamodb/__snapshots__/generator.spec.ts.snap +68 -30
- package/src/ts/dynamodb/agent-connection/__snapshots__/generator.spec.ts.snap +5 -5
- package/src/ts/dynamodb/agent-connection/generator.js +21 -23
- package/src/ts/dynamodb/agent-connection/generator.js.map +1 -1
- package/src/ts/dynamodb/agent-connection/schema.d.js +8 -0
- package/src/ts/dynamodb/agent-connection/schema.d.js.map +1 -0
- package/src/ts/dynamodb/files/config.json.template +1 -1
- package/src/ts/dynamodb/files/src/client.ts.template +7 -7
- package/src/ts/dynamodb/generator.js +64 -63
- package/src/ts/dynamodb/generator.js.map +1 -1
- package/src/ts/dynamodb/mcp-server-connection/__snapshots__/generator.spec.ts.snap +5 -5
- package/src/ts/dynamodb/mcp-server-connection/generator.js +21 -23
- package/src/ts/dynamodb/mcp-server-connection/generator.js.map +1 -1
- package/src/ts/dynamodb/mcp-server-connection/schema.d.js +8 -0
- package/src/ts/dynamodb/mcp-server-connection/schema.d.js.map +1 -0
- package/src/ts/dynamodb/schema.d.js +6 -0
- package/src/ts/dynamodb/schema.d.js.map +1 -0
- package/src/ts/dynamodb/smithy-connection/__snapshots__/generator.spec.ts.snap +3 -3
- package/src/ts/dynamodb/smithy-connection/generator.js +20 -22
- package/src/ts/dynamodb/smithy-connection/generator.js.map +1 -1
- package/src/ts/dynamodb/smithy-connection/schema.d.js +8 -0
- package/src/ts/dynamodb/smithy-connection/schema.d.js.map +1 -0
- package/src/ts/dynamodb/trpc-connection/__snapshots__/generator.spec.ts.snap +3 -3
- package/src/ts/dynamodb/trpc-connection/generator.js +20 -22
- package/src/ts/dynamodb/trpc-connection/generator.js.map +1 -1
- package/src/ts/dynamodb/trpc-connection/schema.d.js +8 -0
- package/src/ts/dynamodb/trpc-connection/schema.d.js.map +1 -0
- package/src/ts/lambda-function/__snapshots__/generator.spec.ts.snap +4 -4
- package/src/ts/lambda-function/generator.js +55 -55
- package/src/ts/lambda-function/generator.js.map +1 -1
- package/src/ts/lambda-function/io.js +83 -52
- package/src/ts/lambda-function/io.js.map +1 -1
- package/src/ts/lambda-function/schema.d.js +6 -0
- package/src/ts/lambda-function/schema.d.js.map +1 -0
- package/src/ts/lib/__snapshots__/generator.spec.ts.snap +23 -36
- package/src/ts/lib/biome.js +37 -37
- package/src/ts/lib/biome.js.map +1 -1
- package/src/ts/lib/generator.js +75 -77
- package/src/ts/lib/generator.js.map +1 -1
- package/src/ts/lib/schema.d.js +6 -0
- package/src/ts/lib/schema.d.js.map +1 -0
- package/src/ts/lib/ts-project-utils.js +65 -70
- package/src/ts/lib/ts-project-utils.js.map +1 -1
- package/src/ts/lib/types.js +5 -2
- package/src/ts/lib/types.js.map +1 -1
- package/src/ts/lib/vitest.js +29 -26
- package/src/ts/lib/vitest.js.map +1 -1
- package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +5 -5
- package/src/ts/mcp-server/generator.js +157 -138
- package/src/ts/mcp-server/generator.js.map +1 -1
- package/src/ts/mcp-server/schema.d.js +6 -0
- package/src/ts/mcp-server/schema.d.js.map +1 -0
- package/src/ts/nx-generator/__snapshots__/generator.spec.ts.snap +14 -9
- package/src/ts/nx-generator/files/local/generator.ts.template +3 -3
- package/src/ts/nx-generator/files/nx-plugin-for-aws/generator/generator.ts.template +4 -4
- package/src/ts/nx-generator/generator.js +84 -83
- package/src/ts/nx-generator/generator.js.map +1 -1
- package/src/ts/nx-generator/schema.d.js +6 -0
- package/src/ts/nx-generator/schema.d.js.map +1 -0
- package/src/ts/nx-plugin/__snapshots__/generator.spec.ts.snap +30 -18
- package/src/ts/nx-plugin/files/mcp-server/server.ts.template +5 -5
- package/src/ts/nx-plugin/files/mcp-server/tools/create-workspace-command.ts.template +2 -2
- package/src/ts/nx-plugin/files/mcp-server/tools/general-guidance.ts.template +1 -1
- package/src/ts/nx-plugin/files/mcp-server/tools/generator-guide.ts.template +2 -2
- package/src/ts/nx-plugin/files/mcp-server/tools/list-generators.ts.template +2 -2
- package/src/ts/nx-plugin/files/mcp-server/utils.ts.template +3 -3
- package/src/ts/nx-plugin/generator.js +49 -48
- package/src/ts/nx-plugin/generator.js.map +1 -1
- package/src/ts/nx-plugin/schema.d.js +9 -0
- package/src/ts/nx-plugin/schema.d.js.map +1 -0
- package/src/ts/nx-plugin/utils.js +32 -44
- package/src/ts/nx-plugin/utils.js.map +1 -1
- package/src/ts/rdb/__snapshots__/generator.spec.ts.snap +34 -30
- package/src/ts/rdb/agent-connection/__snapshots__/generator.spec.ts.snap +6 -6
- package/src/ts/rdb/agent-connection/generator.js +38 -39
- package/src/ts/rdb/agent-connection/generator.js.map +1 -1
- package/src/ts/rdb/agent-connection/schema.d.js +8 -0
- package/src/ts/rdb/agent-connection/schema.d.js.map +1 -0
- package/src/ts/rdb/files/config.json.template +1 -1
- package/src/ts/rdb/files/prisma.config.ts.template +2 -2
- package/src/ts/rdb/files/src/prisma.ts.template +7 -7
- package/src/ts/rdb/files/src/utils.ts.template +3 -0
- package/src/ts/rdb/generator.js +130 -117
- package/src/ts/rdb/generator.js.map +1 -1
- package/src/ts/rdb/mcp-server-connection/__snapshots__/generator.spec.ts.snap +6 -6
- package/src/ts/rdb/mcp-server-connection/generator.js +36 -37
- package/src/ts/rdb/mcp-server-connection/generator.js.map +1 -1
- package/src/ts/rdb/mcp-server-connection/schema.d.js +8 -0
- package/src/ts/rdb/mcp-server-connection/schema.d.js.map +1 -0
- package/src/ts/rdb/schema.d.js +6 -0
- package/src/ts/rdb/schema.d.js.map +1 -0
- package/src/ts/rdb/smithy-connection/__snapshots__/generator.spec.ts.snap +3 -3
- package/src/ts/rdb/smithy-connection/generator.js +46 -43
- package/src/ts/rdb/smithy-connection/generator.js.map +1 -1
- package/src/ts/rdb/smithy-connection/schema.d.js +8 -0
- package/src/ts/rdb/smithy-connection/schema.d.js.map +1 -0
- package/src/ts/rdb/trpc-connection/__snapshots__/generator.spec.ts.snap +3 -3
- package/src/ts/rdb/trpc-connection/generator.js +36 -31
- package/src/ts/rdb/trpc-connection/generator.js.map +1 -1
- package/src/ts/rdb/trpc-connection/schema.d.js +8 -0
- package/src/ts/rdb/trpc-connection/schema.d.js.map +1 -0
- package/src/ts/rdb/utils.js +9 -11
- package/src/ts/rdb/utils.js.map +1 -1
- package/src/ts/react-website/agui/generator.js +69 -61
- package/src/ts/react-website/agui/generator.js.map +1 -1
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +179 -332
- package/src/ts/react-website/app/generator.js +197 -188
- package/src/ts/react-website/app/generator.js.map +1 -1
- package/src/ts/react-website/app/schema.d.js +6 -0
- package/src/ts/react-website/app/schema.d.js.map +1 -0
- package/src/ts/react-website/app/schema.json +6 -6
- package/src/ts/react-website/cognito-auth/__snapshots__/generator.spec.ts.snap +118 -4
- package/src/ts/react-website/cognito-auth/__snapshots__/generator.terraform.spec.ts.snap +27 -7
- package/src/ts/react-website/cognito-auth/files/app/components/CognitoAuth/index.tsx.template +2 -2
- package/src/ts/react-website/cognito-auth/generator.js +60 -60
- package/src/ts/react-website/cognito-auth/generator.js.map +1 -1
- package/src/ts/react-website/cognito-auth/schema.d.js +6 -0
- package/src/ts/react-website/cognito-auth/schema.d.js.map +1 -0
- package/src/ts/react-website/cognito-auth/utils.js +34 -32
- package/src/ts/react-website/cognito-auth/utils.js.map +1 -1
- package/src/ts/react-website/runtime-config/__snapshots__/generator.spec.ts.snap +2 -2
- package/src/ts/react-website/runtime-config/files/app/components/RuntimeConfig/index.tsx.template +2 -2
- package/src/ts/react-website/runtime-config/generator.js +29 -31
- package/src/ts/react-website/runtime-config/generator.js.map +1 -1
- package/src/ts/react-website/runtime-config/schema.d.js +6 -0
- package/src/ts/react-website/runtime-config/schema.d.js.map +1 -0
- package/src/ts/sync/generator.js +60 -59
- package/src/ts/sync/generator.js.map +1 -1
- package/src/ts/website/app/generator.js +10 -10
- package/src/ts/website/app/generator.js.map +1 -1
- package/src/ts/website/app/schema.d.js +6 -0
- package/src/ts/website/app/schema.d.js.map +1 -0
- package/src/ts/website/app/schema.json +6 -6
- package/src/ts/website/auth/generator.js +11 -11
- package/src/ts/website/auth/generator.js.map +1 -1
- package/src/ts/website/auth/schema.d.js +6 -0
- package/src/ts/website/auth/schema.d.js.map +1 -0
- package/src/utils/agent-chat/agent-chat.d.ts +1 -1
- package/src/utils/agent-chat/agent-chat.js +11 -12
- package/src/utils/agent-chat/agent-chat.js.map +1 -1
- package/src/utils/agent-chat/files/a2a/chat.ts.template +1 -1
- package/src/utils/agent-chat/files/ag-ui/chat.ts.template +1 -1
- package/src/utils/agent-chat/files/http-py/chat.ts.template +1 -1
- package/src/utils/agent-chat/files/http-ts/chat.ts.template +1 -1
- package/src/utils/agent-connection/agent-connection.d.ts +9 -3
- package/src/utils/agent-connection/agent-connection.js +133 -147
- package/src/utils/agent-connection/agent-connection.js.map +1 -1
- package/src/utils/agent-connection/files/core-gateway/agentcore-gateway-mcp-transport.ts.template +1 -1
- package/src/utils/agent-connection/files/core-strands/gateway/agentcore-gateway-mcp-client-strands.ts.template +1 -1
- package/src/utils/agent-connection/files/py-core-gateway/agentcore_gateway_mcp_transport.py.template +1 -1
- package/src/utils/agent-connection/files/py-core-langchain/gateway/agentcore_gateway_mcp_client_langchain.py.template +1 -1
- package/src/utils/agent-connection/files/py-core-strands/gateway/agentcore_gateway_mcp_client_strands.py.template +1 -1
- package/src/utils/agent-core-constructs/agent-core-constructs.js +53 -50
- package/src/utils/agent-core-constructs/agent-core-constructs.js.map +1 -1
- package/src/utils/agent-core-constructs/files/terraform/app/agentcore-gateway/__nameKebabCase__/__nameKebabCase__.tf.template +4 -4
- package/src/utils/agent-core-constructs/files/terraform/core/agent-core/runtime.tf.template +5 -5
- package/src/utils/agent-core-constructs/files/terraform/core/agentcore-gateway/gateway.tf.template +2 -2
- package/src/utils/api-constructs/api-constructs.js +30 -31
- package/src/utils/api-constructs/api-constructs.js.map +1 -1
- package/src/utils/api-constructs/files/cdk/core/api/http/http-api.ts.template +14 -6
- package/src/utils/api-constructs/files/cdk/core/api/rest/api-gateway-account/index.js.template +58 -0
- package/src/utils/api-constructs/files/cdk/core/api/rest/api-gateway-account.ts.template +82 -0
- package/src/utils/api-constructs/files/cdk/core/api/rest/rest-api.ts.template +28 -14
- package/src/utils/api-constructs/files/terraform/app/apis/http/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +1 -8
- package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +91 -4
- package/src/utils/api-constructs/files/terraform/core/api/http/http-api/http-api.tf.template +1 -1
- package/src/utils/api-constructs/files/terraform/core/api/rest/api-gateway-account/api-gateway-account.tf.template +83 -0
- package/src/utils/api-constructs/files/terraform/core/api/rest/rest-api/rest-api.tf.template +5 -11
- package/src/utils/api-constructs/open-api-metadata.js +22 -22
- package/src/utils/api-constructs/open-api-metadata.js.map +1 -1
- package/src/utils/ast/website.js +17 -19
- package/src/utils/ast/website.js.map +1 -1
- package/src/utils/ast.js +72 -97
- package/src/utils/ast.js.map +1 -1
- package/src/utils/bundle/bundle.js +50 -47
- package/src/utils/bundle/bundle.js.map +1 -1
- package/src/utils/commands.js +47 -26
- package/src/utils/commands.js.map +1 -1
- package/src/utils/config/index.d.ts +2 -2
- package/src/utils/config/index.js +5 -4
- package/src/utils/config/index.js.map +1 -1
- package/src/utils/config/utils.js +48 -58
- package/src/utils/config/utils.js.map +1 -1
- package/src/utils/connection/open-api/react.d.ts +5 -5
- package/src/utils/connection/open-api/react.js +81 -77
- package/src/utils/connection/open-api/react.js.map +1 -1
- package/src/utils/containers.js +22 -23
- package/src/utils/containers.js.map +1 -1
- package/src/utils/dynamodb-constructs/dynamodb-constructs.js +32 -33
- package/src/utils/dynamodb-constructs/dynamodb-constructs.js.map +1 -1
- package/src/utils/dynamodb-constructs/files/terraform/app/dynamodb/__nameKebabCase__/__nameKebabCase__.tf.template +12 -2
- package/src/utils/dynamodb-constructs/files/terraform/core/dynamodb/dynamodb.tf.template +1 -1
- package/src/utils/files/common/scripts/src/dynamodb/create-local-table.ts.template +2 -2
- package/src/utils/files/common/scripts/src/dynamodb/pull-image.ts.template +1 -1
- package/src/utils/files/common/scripts/src/dynamodb/start-container.ts.template +17 -2
- package/src/utils/files/common/scripts/src/rdb/mysql/wait-for-mysql-db.ts.template +32 -0
- package/src/utils/files/common/scripts/src/rdb/postgres/wait-for-postgres-db.ts.template +32 -0
- package/src/utils/files/common/scripts/src/rdb/{pull-image.ts.template → shared/pull-image.ts.template} +1 -1
- package/src/utils/files/common/scripts/src/rdb/{start-container.ts.template → shared/start-container.ts.template} +16 -1
- package/src/utils/files/terraform/src/core/asset-bucket/asset-bucket.tf.template +2 -2
- package/src/utils/files/terraform/src/core/runtime-config/appconfig/appconfig.tf.template +1 -1
- package/src/utils/files/terraform/src/core/runtime-config/appconfig-deployment/appconfig-deployment.tf.template +3 -3
- package/src/utils/files/terraform/src/core/runtime-config/entry/entry.tf.template +1 -1
- package/src/utils/files/terraform/src/core/runtime-config/read/read.tf.template +2 -2
- package/src/utils/files/website/hooks/sigv4/useSigV4.tsx.template +1 -1
- package/src/utils/format.js +153 -99
- package/src/utils/format.js.map +1 -1
- package/src/utils/fs.js +14 -13
- package/src/utils/fs.js.map +1 -1
- package/src/utils/function-constructs/files/terraform/app/lambda-functions/__nameKebabCase__/__nameKebabCase__.tf.template +2 -2
- package/src/utils/function-constructs/function-constructs.js +21 -26
- package/src/utils/function-constructs/function-constructs.js.map +1 -1
- package/src/utils/generators.js +18 -20
- package/src/utils/generators.js.map +1 -1
- package/src/utils/git.js +19 -34
- package/src/utils/git.js.map +1 -1
- package/src/utils/iac-providers.js +5 -5
- package/src/utils/iac-providers.js.map +1 -1
- package/src/utils/iac.js +12 -14
- package/src/utils/iac.js.map +1 -1
- package/src/utils/identity-constructs/files/cdk/core/user-identity.ts.template +112 -2
- package/src/utils/identity-constructs/files/terraform/core/user-identity/add-callback-url/add-callback-url.tf.template +19 -6
- package/src/utils/identity-constructs/files/terraform/core/user-identity/identity/identity.tf.template +104 -2
- package/src/utils/identity-constructs/files/terraform/core/user-identity/main.tf.template +7 -0
- package/src/utils/identity-constructs/identity-constructs.js +24 -28
- package/src/utils/identity-constructs/identity-constructs.js.map +1 -1
- package/src/utils/js.js +27 -20
- package/src/utils/js.js.map +1 -1
- package/src/utils/mcp.js +34 -41
- package/src/utils/mcp.js.map +1 -1
- package/src/utils/metrics.js +30 -45
- package/src/utils/metrics.js.map +1 -1
- package/src/utils/mock-project-graph.js +8 -12
- package/src/utils/mock-project-graph.js.map +1 -1
- package/src/utils/names.d.ts +16 -0
- package/src/utils/names.js +39 -48
- package/src/utils/names.js.map +1 -1
- package/src/utils/npm-scope.js +9 -17
- package/src/utils/npm-scope.js.map +1 -1
- package/src/utils/nx.d.ts +18 -2
- package/src/utils/nx.js +111 -74
- package/src/utils/nx.js.map +1 -1
- package/src/utils/nxlv-python.js +27 -15
- package/src/utils/nxlv-python.js.map +1 -1
- package/src/utils/object.js +5 -11
- package/src/utils/object.js.map +1 -1
- package/src/utils/paths.js +11 -15
- package/src/utils/paths.js.map +1 -1
- package/src/utils/pkg-manager.js +4 -9
- package/src/utils/pkg-manager.js.map +1 -1
- package/src/utils/pnpm-workspace.js +22 -22
- package/src/utils/pnpm-workspace.js.map +1 -1
- package/src/utils/port.js +26 -36
- package/src/utils/port.js.map +1 -1
- package/src/utils/py.d.ts +14 -1
- package/src/utils/py.js +63 -45
- package/src/utils/py.js.map +1 -1
- package/src/utils/rdb-constructs/files/terraform/app/dbs/__nameKebabCase__/__nameKebabCase__.tf.template +4 -4
- package/src/utils/rdb-constructs/files/terraform/core/rdb/aurora/aurora.tf.template +3 -3
- package/src/utils/rdb-constructs/rdb-constructs.js +31 -33
- package/src/utils/rdb-constructs/rdb-constructs.js.map +1 -1
- package/src/utils/shared-constructs-constants.js +16 -16
- package/src/utils/shared-constructs-constants.js.map +1 -1
- package/src/utils/shared-constructs.js +57 -54
- package/src/utils/shared-constructs.js.map +1 -1
- package/src/utils/shared-dynamodb-scripts.js +16 -14
- package/src/utils/shared-dynamodb-scripts.js.map +1 -1
- package/src/utils/shared-infra-config.js +30 -31
- package/src/utils/shared-infra-config.js.map +1 -1
- package/src/utils/shared-infra-scripts.js +30 -25
- package/src/utils/shared-infra-scripts.js.map +1 -1
- package/src/utils/shared-rdb-scripts.d.ts +8 -3
- package/src/utils/shared-rdb-scripts.js +21 -13
- package/src/utils/shared-rdb-scripts.js.map +1 -1
- package/src/utils/shared-scripts.js +12 -17
- package/src/utils/shared-scripts.js.map +1 -1
- package/src/utils/shared-shadcn.js +64 -60
- package/src/utils/shared-shadcn.js.map +1 -1
- package/src/utils/test.d.ts +8 -0
- package/src/utils/test.js +33 -25
- package/src/utils/test.js.map +1 -1
- package/src/utils/toml.js +7 -15
- package/src/utils/toml.js.map +1 -1
- package/src/utils/versions.d.ts +31 -4
- package/src/utils/versions.js +40 -23
- package/src/utils/versions.js.map +1 -1
- package/src/utils/website-constructs/files/terraform/app/static-websites/__websiteNameKebabCase__/__websiteNameKebabCase__.tf.template +1 -1
- package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template +4 -4
- package/src/utils/website-constructs/website-constructs.js +31 -30
- package/src/utils/website-constructs/website-constructs.js.map +1 -1
- package/bin/aws-nx-mcp.js +0 -17
- package/bin/aws-nx-mcp.js.map +0 -1
- package/sdk/agentcore-gateway.d.ts +0 -6
- package/sdk/agentcore-gateway.js +0 -11
- package/sdk/agentcore-gateway.js.map +0 -1
- package/sdk/connection.d.ts +0 -6
- package/sdk/connection.js +0 -10
- package/sdk/connection.js.map +0 -1
- package/sdk/license.js +0 -14
- package/sdk/license.js.map +0 -1
- package/sdk/open-api.d.ts +0 -10
- package/sdk/open-api.js +0 -15
- package/sdk/open-api.js.map +0 -1
- package/sdk/py.d.ts +0 -14
- package/sdk/py.js +0 -18
- package/sdk/py.js.map +0 -1
- package/sdk/terraform.d.ts +0 -6
- package/sdk/terraform.js +0 -11
- package/sdk/terraform.js.map +0 -1
- package/sdk/ts.d.ts +0 -33
- package/sdk/ts.js +0 -53
- package/sdk/ts.js.map +0 -1
- package/sdk/utils/ast.js +0 -12
- package/sdk/utils/ast.js.map +0 -1
- package/sdk/utils/format.js +0 -10
- package/sdk/utils/format.js.map +0 -1
- package/sdk/utils/test.js +0 -10
- package/sdk/utils/test.js.map +0 -1
- package/src/connection/agent-serve-local.js +0 -48
- package/src/connection/agent-serve-local.js.map +0 -1
- package/src/connection/serve-local.js +0 -41
- package/src/connection/serve-local.js.map +0 -1
- package/src/py/agent/react-connection/serve-local.js +0 -38
- package/src/py/agent/react-connection/serve-local.js.map +0 -1
- package/src/ts/agent/react-connection/serve-local.js +0 -19
- package/src/ts/agent/react-connection/serve-local.js.map +0 -1
- package/src/utils/files/common/scripts/src/rdb/wait-for-db.ts.template +0 -59
- /package/src/agentcore-gateway/files/project/{serve-local.ts.template → local-dev.ts.template} +0 -0
- /package/{bin → src/bin}/aws-nx-mcp.d.ts +0 -0
|
@@ -213,11 +213,12 @@ def echo(message: Annotated[str, Query(max_length=1024)]) -> EchoOutput:
|
|
|
213
213
|
if __name__ == "__main__":
|
|
214
214
|
uvicorn.run("proj_test_api.main:app", port=8000)
|
|
215
215
|
",
|
|
216
|
-
"apps/test_api/scripts/generate_open_api.py": "
|
|
217
|
-
import json
|
|
216
|
+
"apps/test_api/scripts/generate_open_api.py": "import json
|
|
218
217
|
import os
|
|
219
218
|
import sys
|
|
220
219
|
|
|
220
|
+
from proj_test_api.main import app
|
|
221
|
+
|
|
221
222
|
os.makedirs(os.path.dirname(sys.argv[1]), exist_ok=True)
|
|
222
223
|
with open(sys.argv[1], "w") as f:
|
|
223
224
|
json.dump(app.openapi(), f)
|
|
@@ -249,7 +250,7 @@ import {
|
|
|
249
250
|
HttpApiIntegration,
|
|
250
251
|
OperationDetails,
|
|
251
252
|
} from './utils.js';
|
|
252
|
-
import { CfnOutput } from 'aws-cdk-lib';
|
|
253
|
+
import { CfnOutput, Stack } from 'aws-cdk-lib';
|
|
253
254
|
import {
|
|
254
255
|
HttpApi as _HttpApi,
|
|
255
256
|
HttpApiProps as _HttpApiProps,
|
|
@@ -259,6 +260,8 @@ import {
|
|
|
259
260
|
ThrottleSettings,
|
|
260
261
|
} from 'aws-cdk-lib/aws-apigatewayv2';
|
|
261
262
|
import { LogGroup } from 'aws-cdk-lib/aws-logs';
|
|
263
|
+
import { Key } from 'aws-cdk-lib/aws-kms';
|
|
264
|
+
import { ServicePrincipal } from 'aws-cdk-lib/aws-iam';
|
|
262
265
|
import { suppressRules } from '../checkov.js';
|
|
263
266
|
|
|
264
267
|
/**
|
|
@@ -335,12 +338,18 @@ export class HttpApi<
|
|
|
335
338
|
...props,
|
|
336
339
|
});
|
|
337
340
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
341
|
+
// Customer-managed key for access log encryption at rest
|
|
342
|
+
const logsKey = new Key(this, 'AccessLogsKey', {
|
|
343
|
+
enableKeyRotation: true,
|
|
344
|
+
});
|
|
345
|
+
const stack = Stack.of(this);
|
|
346
|
+
logsKey.grantEncryptDecrypt(
|
|
347
|
+
new ServicePrincipal(\`logs.\${stack.region}.amazonaws.com\`),
|
|
343
348
|
);
|
|
349
|
+
|
|
350
|
+
const accessLogGroup = new LogGroup(this, 'AccessLogs', {
|
|
351
|
+
encryptionKey: logsKey,
|
|
352
|
+
});
|
|
344
353
|
suppressRules(
|
|
345
354
|
accessLogGroup,
|
|
346
355
|
['CKV_AWS_66', 'CKV_AWS_338'],
|
|
@@ -1068,7 +1077,9 @@ export class IntegrationBuilder<
|
|
|
1068
1077
|
exports[`fastapi project generator > should set up shared constructs for rest > rest-api.ts 1`] = `
|
|
1069
1078
|
"import { Construct, IConstruct } from 'constructs';
|
|
1070
1079
|
import {
|
|
1080
|
+
AccessLogFormat,
|
|
1071
1081
|
Cors,
|
|
1082
|
+
LogGroupLogDestination,
|
|
1072
1083
|
Resource,
|
|
1073
1084
|
RestApi as _RestApi,
|
|
1074
1085
|
RestApiProps as _RestApiProps,
|
|
@@ -1076,14 +1087,17 @@ import {
|
|
|
1076
1087
|
Stage,
|
|
1077
1088
|
ThrottleSettings,
|
|
1078
1089
|
} from 'aws-cdk-lib/aws-apigateway';
|
|
1079
|
-
import { IAspect,
|
|
1090
|
+
import { IAspect, Stack } from 'aws-cdk-lib';
|
|
1080
1091
|
import {
|
|
1081
1092
|
CfnLoggingConfiguration,
|
|
1082
1093
|
CfnWebACL,
|
|
1083
1094
|
CfnWebACLAssociation,
|
|
1084
1095
|
} from 'aws-cdk-lib/aws-wafv2';
|
|
1085
1096
|
import { LogGroup, RetentionDays } from 'aws-cdk-lib/aws-logs';
|
|
1097
|
+
import { Key } from 'aws-cdk-lib/aws-kms';
|
|
1098
|
+
import { ServicePrincipal } from 'aws-cdk-lib/aws-iam';
|
|
1086
1099
|
import { RuntimeConfig } from '../runtime-config.js';
|
|
1100
|
+
import { ApiGatewayAccount } from './api-gateway-account.js';
|
|
1087
1101
|
import {
|
|
1088
1102
|
ApiIntegrations,
|
|
1089
1103
|
OperationDetails,
|
|
@@ -1168,10 +1182,28 @@ export class RestApi<
|
|
|
1168
1182
|
super(scope, id);
|
|
1169
1183
|
this.integrations = integrations;
|
|
1170
1184
|
|
|
1185
|
+
// Account-level CloudWatch role required for REST API access logging
|
|
1186
|
+
const account = ApiGatewayAccount.ensure(this);
|
|
1187
|
+
|
|
1188
|
+
// KMS key for encrypting logs at rest, usable by CloudWatch Logs
|
|
1189
|
+
const logsKey = new Key(this, 'LogsKey', {
|
|
1190
|
+
enableKeyRotation: true,
|
|
1191
|
+
});
|
|
1192
|
+
logsKey.grantEncryptDecrypt(
|
|
1193
|
+
new ServicePrincipal(\`logs.\${Stack.of(this).region}.amazonaws.com\`),
|
|
1194
|
+
);
|
|
1195
|
+
|
|
1196
|
+
const accessLogs = new LogGroup(this, 'AccessLogs', {
|
|
1197
|
+
retention: RetentionDays.ONE_YEAR,
|
|
1198
|
+
encryptionKey: logsKey,
|
|
1199
|
+
});
|
|
1200
|
+
|
|
1171
1201
|
// Create the API Gateway REST API
|
|
1172
1202
|
this.api = new _RestApi(this, 'Api', {
|
|
1173
1203
|
...props,
|
|
1174
1204
|
deployOptions: {
|
|
1205
|
+
accessLogDestination: new LogGroupLogDestination(accessLogs),
|
|
1206
|
+
accessLogFormat: AccessLogFormat.jsonWithStandardFields(),
|
|
1175
1207
|
...props.deployOptions,
|
|
1176
1208
|
methodOptions: {
|
|
1177
1209
|
...props.deployOptions?.methodOptions,
|
|
@@ -1184,6 +1216,8 @@ export class RestApi<
|
|
|
1184
1216
|
},
|
|
1185
1217
|
},
|
|
1186
1218
|
});
|
|
1219
|
+
// Ensure the account-level role is configured before the stage is created
|
|
1220
|
+
this.api.deploymentStage.node.addDependency(account.resource);
|
|
1187
1221
|
|
|
1188
1222
|
if (enableWaf) {
|
|
1189
1223
|
this.webAcl = new CfnWebACL(this, 'WebAcl', {
|
|
@@ -1250,14 +1284,9 @@ export class RestApi<
|
|
|
1250
1284
|
// \`aws-waf-logs-\` to satisfy the WAFv2 logging destination requirement.
|
|
1251
1285
|
const wafLogGroup = new LogGroup(this, 'WebAclLogs', {
|
|
1252
1286
|
logGroupName: \`aws-waf-logs-\${apiName}-\${this.node.addr.slice(-8)}\`,
|
|
1253
|
-
retention: RetentionDays.
|
|
1254
|
-
|
|
1287
|
+
retention: RetentionDays.ONE_YEAR,
|
|
1288
|
+
encryptionKey: logsKey,
|
|
1255
1289
|
});
|
|
1256
|
-
suppressRules(
|
|
1257
|
-
wafLogGroup,
|
|
1258
|
-
['CKV_AWS_158'],
|
|
1259
|
-
'Using default CloudWatch log encryption for WAF logs',
|
|
1260
|
-
);
|
|
1261
1290
|
|
|
1262
1291
|
new CfnLoggingConfiguration(this, 'WebAclLoggingConfig', {
|
|
1263
1292
|
resourceArn: this.webAcl.attrArn,
|
|
@@ -1271,12 +1300,6 @@ export class RestApi<
|
|
|
1271
1300
|
'Caching not required for this use case',
|
|
1272
1301
|
(c) => c instanceof Stage,
|
|
1273
1302
|
);
|
|
1274
|
-
suppressRules(
|
|
1275
|
-
this.api,
|
|
1276
|
-
['CKV_AWS_76'],
|
|
1277
|
-
'API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement',
|
|
1278
|
-
(c) => c instanceof Stage,
|
|
1279
|
-
);
|
|
1280
1303
|
|
|
1281
1304
|
// Resolve the integration for a given operation.
|
|
1282
1305
|
// If the operation has a dedicated integration, use it; otherwise fall back to $router.
|
|
@@ -2039,4613 +2062,3 @@ export class IntegrationBuilder<
|
|
|
2039
2062
|
}
|
|
2040
2063
|
"
|
|
2041
2064
|
`;
|
|
2042
|
-
|
|
2043
|
-
exports[`fastapi project generator > terraform iac > should generate terraform files for HTTP API with Cognito auth and snapshot them > terraform-http-cognito-files 1`] = `
|
|
2044
|
-
{
|
|
2045
|
-
"http-api.tf": "# Core HTTP API Gateway module
|
|
2046
|
-
# This module creates the API Gateway HTTP API, stage, and logging resources
|
|
2047
|
-
|
|
2048
|
-
terraform {
|
|
2049
|
-
required_version = ">= 1.0"
|
|
2050
|
-
|
|
2051
|
-
required_providers {
|
|
2052
|
-
aws = {
|
|
2053
|
-
source = "hashicorp/aws"
|
|
2054
|
-
version = "~> 6.0"
|
|
2055
|
-
}
|
|
2056
|
-
}
|
|
2057
|
-
}
|
|
2058
|
-
|
|
2059
|
-
# Core HTTP API Gateway Variables
|
|
2060
|
-
|
|
2061
|
-
variable "api_name" {
|
|
2062
|
-
description = "Name of the HTTP API Gateway"
|
|
2063
|
-
type = string
|
|
2064
|
-
}
|
|
2065
|
-
|
|
2066
|
-
variable "api_description" {
|
|
2067
|
-
description = "Description of the HTTP API Gateway"
|
|
2068
|
-
type = string
|
|
2069
|
-
default = "HTTP API Gateway"
|
|
2070
|
-
}
|
|
2071
|
-
|
|
2072
|
-
variable "stage_name" {
|
|
2073
|
-
description = "Name of the API Gateway stage"
|
|
2074
|
-
type = string
|
|
2075
|
-
default = "prod"
|
|
2076
|
-
}
|
|
2077
|
-
|
|
2078
|
-
variable "stage_auto_deploy" {
|
|
2079
|
-
description = "Whether to automatically deploy the API stage"
|
|
2080
|
-
type = bool
|
|
2081
|
-
default = true
|
|
2082
|
-
}
|
|
2083
|
-
|
|
2084
|
-
# CORS Configuration
|
|
2085
|
-
|
|
2086
|
-
variable "cors_allow_credentials" {
|
|
2087
|
-
description = "Whether to allow credentials in CORS requests"
|
|
2088
|
-
type = bool
|
|
2089
|
-
default = false
|
|
2090
|
-
}
|
|
2091
|
-
|
|
2092
|
-
variable "cors_allow_headers" {
|
|
2093
|
-
description = "List of allowed headers for CORS"
|
|
2094
|
-
type = list(string)
|
|
2095
|
-
default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
|
|
2096
|
-
}
|
|
2097
|
-
|
|
2098
|
-
variable "cors_allow_methods" {
|
|
2099
|
-
description = "List of allowed HTTP methods for CORS"
|
|
2100
|
-
type = list(string)
|
|
2101
|
-
default = ["*"]
|
|
2102
|
-
}
|
|
2103
|
-
|
|
2104
|
-
variable "cors_allow_origins" {
|
|
2105
|
-
description = "List of allowed origins for CORS"
|
|
2106
|
-
type = list(string)
|
|
2107
|
-
default = ["*"]
|
|
2108
|
-
}
|
|
2109
|
-
|
|
2110
|
-
variable "cors_expose_headers" {
|
|
2111
|
-
description = "List of headers to expose in CORS responses"
|
|
2112
|
-
type = list(string)
|
|
2113
|
-
default = []
|
|
2114
|
-
}
|
|
2115
|
-
|
|
2116
|
-
variable "cors_max_age" {
|
|
2117
|
-
description = "Maximum age for CORS preflight requests in seconds"
|
|
2118
|
-
type = number
|
|
2119
|
-
default = 86400
|
|
2120
|
-
}
|
|
2121
|
-
|
|
2122
|
-
# Tags
|
|
2123
|
-
|
|
2124
|
-
variable "tags" {
|
|
2125
|
-
description = "Tags to apply to all resources"
|
|
2126
|
-
type = map(string)
|
|
2127
|
-
default = {}
|
|
2128
|
-
}
|
|
2129
|
-
|
|
2130
|
-
# Data sources
|
|
2131
|
-
data "aws_region" "current" {}
|
|
2132
|
-
data "aws_caller_identity" "current" {}
|
|
2133
|
-
|
|
2134
|
-
# Resources
|
|
2135
|
-
|
|
2136
|
-
# KMS key for CloudWatch log group encryption
|
|
2137
|
-
resource "aws_kms_key" "logs_key" {
|
|
2138
|
-
description = "KMS key for CloudWatch log group encryption"
|
|
2139
|
-
deletion_window_in_days = 7
|
|
2140
|
-
enable_key_rotation = true
|
|
2141
|
-
|
|
2142
|
-
policy = jsonencode({
|
|
2143
|
-
Version = "2012-10-17"
|
|
2144
|
-
Statement = [
|
|
2145
|
-
{
|
|
2146
|
-
Sid = "Enable IAM User Permissions"
|
|
2147
|
-
Effect = "Allow"
|
|
2148
|
-
Principal = {
|
|
2149
|
-
AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
|
|
2150
|
-
}
|
|
2151
|
-
Action = "kms:*"
|
|
2152
|
-
Resource = "*"
|
|
2153
|
-
},
|
|
2154
|
-
{
|
|
2155
|
-
Sid = "Allow CloudWatch Logs"
|
|
2156
|
-
Effect = "Allow"
|
|
2157
|
-
Principal = {
|
|
2158
|
-
Service = "logs.\${data.aws_region.current.region}.amazonaws.com"
|
|
2159
|
-
}
|
|
2160
|
-
Action = [
|
|
2161
|
-
"kms:Encrypt",
|
|
2162
|
-
"kms:Decrypt",
|
|
2163
|
-
"kms:ReEncrypt*",
|
|
2164
|
-
"kms:GenerateDataKey*",
|
|
2165
|
-
"kms:DescribeKey"
|
|
2166
|
-
]
|
|
2167
|
-
Resource = "*"
|
|
2168
|
-
Condition = {
|
|
2169
|
-
ArnEquals = {
|
|
2170
|
-
"kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:log-group:/aws/apigateway/\${var.api_name}"
|
|
2171
|
-
}
|
|
2172
|
-
}
|
|
2173
|
-
}
|
|
2174
|
-
]
|
|
2175
|
-
})
|
|
2176
|
-
|
|
2177
|
-
tags = var.tags
|
|
2178
|
-
}
|
|
2179
|
-
|
|
2180
|
-
resource "aws_kms_alias" "logs_key_alias" {
|
|
2181
|
-
name = "alias/\${var.api_name}-api-logs-encryption"
|
|
2182
|
-
target_key_id = aws_kms_key.logs_key.key_id
|
|
2183
|
-
}
|
|
2184
|
-
|
|
2185
|
-
# HTTP API Gateway
|
|
2186
|
-
resource "aws_apigatewayv2_api" "http_api" {
|
|
2187
|
-
name = var.api_name
|
|
2188
|
-
protocol_type = "HTTP"
|
|
2189
|
-
description = var.api_description
|
|
2190
|
-
|
|
2191
|
-
cors_configuration {
|
|
2192
|
-
allow_credentials = var.cors_allow_credentials
|
|
2193
|
-
allow_headers = var.cors_allow_headers
|
|
2194
|
-
allow_methods = var.cors_allow_methods
|
|
2195
|
-
allow_origins = var.cors_allow_origins
|
|
2196
|
-
expose_headers = var.cors_expose_headers
|
|
2197
|
-
max_age = var.cors_max_age
|
|
2198
|
-
}
|
|
2199
|
-
|
|
2200
|
-
tags = var.tags
|
|
2201
|
-
}
|
|
2202
|
-
|
|
2203
|
-
# API Gateway stage
|
|
2204
|
-
resource "aws_apigatewayv2_stage" "api_stage" {
|
|
2205
|
-
api_id = aws_apigatewayv2_api.http_api.id
|
|
2206
|
-
name = var.stage_name
|
|
2207
|
-
auto_deploy = var.stage_auto_deploy
|
|
2208
|
-
|
|
2209
|
-
access_log_settings {
|
|
2210
|
-
destination_arn = aws_cloudwatch_log_group.api_logs.arn
|
|
2211
|
-
format = jsonencode({
|
|
2212
|
-
requestId = "$context.requestId"
|
|
2213
|
-
ip = "$context.identity.sourceIp"
|
|
2214
|
-
requestTime = "$context.requestTime"
|
|
2215
|
-
httpMethod = "$context.httpMethod"
|
|
2216
|
-
routeKey = "$context.routeKey"
|
|
2217
|
-
status = "$context.status"
|
|
2218
|
-
protocol = "$context.protocol"
|
|
2219
|
-
responseLength = "$context.responseLength"
|
|
2220
|
-
error = "$context.error.message"
|
|
2221
|
-
integrationError = "$context.integrationErrorMessage"
|
|
2222
|
-
})
|
|
2223
|
-
}
|
|
2224
|
-
|
|
2225
|
-
default_route_settings {
|
|
2226
|
-
throttling_burst_limit = 5000
|
|
2227
|
-
throttling_rate_limit = 10000
|
|
2228
|
-
}
|
|
2229
|
-
|
|
2230
|
-
tags = var.tags
|
|
2231
|
-
|
|
2232
|
-
depends_on = [aws_cloudwatch_log_group.api_logs]
|
|
2233
|
-
}
|
|
2234
|
-
|
|
2235
|
-
# CloudWatch Log Group for API Gateway
|
|
2236
|
-
resource "aws_cloudwatch_log_group" "api_logs" {
|
|
2237
|
-
#checkov:skip=CKV_AWS_338:Log retention set to forever
|
|
2238
|
-
#checkov:skip=CKV_AWS_66:Log retention set to forever
|
|
2239
|
-
name = "/aws/apigateway/\${var.api_name}"
|
|
2240
|
-
kms_key_id = aws_kms_key.logs_key.arn
|
|
2241
|
-
tags = var.tags
|
|
2242
|
-
}
|
|
2243
|
-
|
|
2244
|
-
# Outputs
|
|
2245
|
-
|
|
2246
|
-
output "api_id" {
|
|
2247
|
-
description = "ID of the HTTP API Gateway"
|
|
2248
|
-
value = aws_apigatewayv2_api.http_api.id
|
|
2249
|
-
}
|
|
2250
|
-
|
|
2251
|
-
output "api_arn" {
|
|
2252
|
-
description = "ARN of the HTTP API Gateway"
|
|
2253
|
-
value = aws_apigatewayv2_api.http_api.arn
|
|
2254
|
-
}
|
|
2255
|
-
|
|
2256
|
-
output "api_endpoint" {
|
|
2257
|
-
description = "Base URL of the HTTP API Gateway"
|
|
2258
|
-
value = aws_apigatewayv2_api.http_api.api_endpoint
|
|
2259
|
-
}
|
|
2260
|
-
|
|
2261
|
-
output "api_execution_arn" {
|
|
2262
|
-
description = "Execution ARN of the HTTP API Gateway"
|
|
2263
|
-
value = aws_apigatewayv2_api.http_api.execution_arn
|
|
2264
|
-
}
|
|
2265
|
-
|
|
2266
|
-
output "stage_id" {
|
|
2267
|
-
description = "ID of the API Gateway stage"
|
|
2268
|
-
value = aws_apigatewayv2_stage.api_stage.id
|
|
2269
|
-
}
|
|
2270
|
-
|
|
2271
|
-
output "stage_arn" {
|
|
2272
|
-
description = "ARN of the API Gateway stage"
|
|
2273
|
-
value = aws_apigatewayv2_stage.api_stage.arn
|
|
2274
|
-
}
|
|
2275
|
-
|
|
2276
|
-
output "stage_execution_arn" {
|
|
2277
|
-
description = "Execution ARN of the API Gateway stage"
|
|
2278
|
-
value = aws_apigatewayv2_stage.api_stage.execution_arn
|
|
2279
|
-
}
|
|
2280
|
-
|
|
2281
|
-
output "stage_invoke_url" {
|
|
2282
|
-
description = "Invoke URL of the API Gateway stage"
|
|
2283
|
-
value = aws_apigatewayv2_stage.api_stage.invoke_url
|
|
2284
|
-
}
|
|
2285
|
-
|
|
2286
|
-
output "api_log_group_name" {
|
|
2287
|
-
description = "Name of the API Gateway CloudWatch log group"
|
|
2288
|
-
value = aws_cloudwatch_log_group.api_logs.name
|
|
2289
|
-
}
|
|
2290
|
-
|
|
2291
|
-
output "api_log_group_arn" {
|
|
2292
|
-
description = "ARN of the API Gateway CloudWatch log group"
|
|
2293
|
-
value = aws_cloudwatch_log_group.api_logs.arn
|
|
2294
|
-
}",
|
|
2295
|
-
"test-api.tf": "terraform {
|
|
2296
|
-
required_version = ">= 1.0"
|
|
2297
|
-
|
|
2298
|
-
required_providers {
|
|
2299
|
-
aws = {
|
|
2300
|
-
source = "hashicorp/aws"
|
|
2301
|
-
version = "~> 6.0"
|
|
2302
|
-
}
|
|
2303
|
-
}
|
|
2304
|
-
}
|
|
2305
|
-
|
|
2306
|
-
# Authentication Configuration
|
|
2307
|
-
variable "user_pool_id" {
|
|
2308
|
-
description = "Cognito User Pool ID for authentication"
|
|
2309
|
-
type = string
|
|
2310
|
-
}
|
|
2311
|
-
|
|
2312
|
-
variable "user_pool_client_ids" {
|
|
2313
|
-
description = "List of Cognito User Pool Client IDs"
|
|
2314
|
-
type = list(string)
|
|
2315
|
-
}
|
|
2316
|
-
|
|
2317
|
-
variable "env" {
|
|
2318
|
-
description = "Environment variables for the Lambda function"
|
|
2319
|
-
type = map(string)
|
|
2320
|
-
default = {}
|
|
2321
|
-
}
|
|
2322
|
-
|
|
2323
|
-
variable "additional_iam_policy_statements" {
|
|
2324
|
-
description = "Additional IAM policy statements for the Lambda function"
|
|
2325
|
-
type = list(object({
|
|
2326
|
-
Effect = string
|
|
2327
|
-
Action = list(string)
|
|
2328
|
-
Resource = list(string)
|
|
2329
|
-
}))
|
|
2330
|
-
default = []
|
|
2331
|
-
}
|
|
2332
|
-
|
|
2333
|
-
variable "asset_bucket_name" {
|
|
2334
|
-
description = "Name of the shared asset S3 bucket used to stage the Lambda deployment zip. Instantiate the \`core/asset-bucket\` module once per deployment and pass its \`bucket_name\` output here."
|
|
2335
|
-
type = string
|
|
2336
|
-
}
|
|
2337
|
-
|
|
2338
|
-
# CORS Configuration (passed to core module)
|
|
2339
|
-
variable "cors_allow_credentials" {
|
|
2340
|
-
description = "Whether to allow credentials in CORS requests"
|
|
2341
|
-
type = bool
|
|
2342
|
-
default = false
|
|
2343
|
-
}
|
|
2344
|
-
|
|
2345
|
-
variable "cors_allow_headers" {
|
|
2346
|
-
description = "List of allowed headers for CORS"
|
|
2347
|
-
type = list(string)
|
|
2348
|
-
default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
|
|
2349
|
-
}
|
|
2350
|
-
|
|
2351
|
-
variable "cors_allow_methods" {
|
|
2352
|
-
description = "List of allowed HTTP methods for CORS"
|
|
2353
|
-
type = list(string)
|
|
2354
|
-
default = ["*"]
|
|
2355
|
-
}
|
|
2356
|
-
|
|
2357
|
-
variable "cors_allow_origins" {
|
|
2358
|
-
description = "List of allowed origins for CORS"
|
|
2359
|
-
type = list(string)
|
|
2360
|
-
default = ["*"]
|
|
2361
|
-
}
|
|
2362
|
-
|
|
2363
|
-
variable "cors_expose_headers" {
|
|
2364
|
-
description = "List of headers to expose in CORS responses"
|
|
2365
|
-
type = list(string)
|
|
2366
|
-
default = []
|
|
2367
|
-
}
|
|
2368
|
-
|
|
2369
|
-
variable "cors_max_age" {
|
|
2370
|
-
description = "Maximum age for CORS preflight requests in seconds"
|
|
2371
|
-
type = number
|
|
2372
|
-
default = 0
|
|
2373
|
-
}
|
|
2374
|
-
|
|
2375
|
-
# Tags
|
|
2376
|
-
variable "tags" {
|
|
2377
|
-
description = "Tags to apply to all resources"
|
|
2378
|
-
type = map(string)
|
|
2379
|
-
default = {}
|
|
2380
|
-
}
|
|
2381
|
-
|
|
2382
|
-
# Get current AWS region and account ID
|
|
2383
|
-
data "aws_region" "current" {}
|
|
2384
|
-
data "aws_caller_identity" "current" {}
|
|
2385
|
-
|
|
2386
|
-
resource "random_string" "suffix" {
|
|
2387
|
-
length = 8
|
|
2388
|
-
special = false
|
|
2389
|
-
upper = false
|
|
2390
|
-
}
|
|
2391
|
-
|
|
2392
|
-
# Resources
|
|
2393
|
-
|
|
2394
|
-
# Create Lambda ZIP file from the bundle directory
|
|
2395
|
-
data "archive_file" "lambda_zip" {
|
|
2396
|
-
type = "zip"
|
|
2397
|
-
source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
|
|
2398
|
-
output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
|
|
2399
|
-
}
|
|
2400
|
-
|
|
2401
|
-
resource "aws_s3_object" "lambda_zip" {
|
|
2402
|
-
bucket = var.asset_bucket_name
|
|
2403
|
-
key = "apis/test-api/\${data.archive_file.lambda_zip.output_sha256}.zip"
|
|
2404
|
-
source = data.archive_file.lambda_zip.output_path
|
|
2405
|
-
source_hash = data.archive_file.lambda_zip.output_base64sha256
|
|
2406
|
-
etag = data.archive_file.lambda_zip.output_md5
|
|
2407
|
-
}
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
# Use the core HTTP API module
|
|
2411
|
-
module "http_api" {
|
|
2412
|
-
source = "../../../core/api/http-api"
|
|
2413
|
-
|
|
2414
|
-
api_name = "TestApi-\${random_string.suffix.result}"
|
|
2415
|
-
api_description = "TestApi HTTP API"
|
|
2416
|
-
stage_name = "$default"
|
|
2417
|
-
stage_auto_deploy = true
|
|
2418
|
-
|
|
2419
|
-
# CORS Configuration
|
|
2420
|
-
cors_allow_credentials = var.cors_allow_credentials
|
|
2421
|
-
cors_allow_headers = var.cors_allow_headers
|
|
2422
|
-
cors_allow_methods = var.cors_allow_methods
|
|
2423
|
-
cors_allow_origins = var.cors_allow_origins
|
|
2424
|
-
cors_expose_headers = var.cors_expose_headers
|
|
2425
|
-
cors_max_age = var.cors_max_age
|
|
2426
|
-
|
|
2427
|
-
# Tags
|
|
2428
|
-
tags = var.tags
|
|
2429
|
-
}
|
|
2430
|
-
|
|
2431
|
-
# Lambda function
|
|
2432
|
-
# This configures a single "router" lambda to serve all requests
|
|
2433
|
-
resource "aws_lambda_function" "api_lambda" {
|
|
2434
|
-
#checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
|
|
2435
|
-
#checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
|
|
2436
|
-
#checkov:skip=CKV_AWS_272:Code signing not required for this use case
|
|
2437
|
-
#checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
|
|
2438
|
-
#checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
|
|
2439
|
-
s3_bucket = aws_s3_object.lambda_zip.bucket
|
|
2440
|
-
s3_key = aws_s3_object.lambda_zip.key
|
|
2441
|
-
s3_object_version = aws_s3_object.lambda_zip.version_id
|
|
2442
|
-
function_name = "TestApiHandler-\${random_string.suffix.result}"
|
|
2443
|
-
role = aws_iam_role.lambda_execution_role.arn
|
|
2444
|
-
handler = "run.sh"
|
|
2445
|
-
runtime = "python3.14"
|
|
2446
|
-
layers = ["arn:aws:lambda:\${data.aws_region.current.region}:753240598075:layer:LambdaAdapterLayerX86:24"]
|
|
2447
|
-
timeout = 30
|
|
2448
|
-
memory_size = 128
|
|
2449
|
-
publish = true
|
|
2450
|
-
|
|
2451
|
-
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
|
|
2452
|
-
|
|
2453
|
-
# Enable X-Ray tracing
|
|
2454
|
-
tracing_config {
|
|
2455
|
-
mode = "Active"
|
|
2456
|
-
}
|
|
2457
|
-
|
|
2458
|
-
# Enable SnapStart for faster cold starts
|
|
2459
|
-
snap_start {
|
|
2460
|
-
apply_on = "PublishedVersions"
|
|
2461
|
-
}
|
|
2462
|
-
|
|
2463
|
-
environment {
|
|
2464
|
-
variables = merge({
|
|
2465
|
-
PORT = "8000"
|
|
2466
|
-
AWS_LWA_INVOKE_MODE = "buffered"
|
|
2467
|
-
AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
|
|
2468
|
-
}, var.env)
|
|
2469
|
-
}
|
|
2470
|
-
|
|
2471
|
-
tags = var.tags
|
|
2472
|
-
}
|
|
2473
|
-
|
|
2474
|
-
# IAM role for Lambda execution
|
|
2475
|
-
resource "aws_iam_role" "lambda_execution_role" {
|
|
2476
|
-
name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
|
|
2477
|
-
|
|
2478
|
-
assume_role_policy = jsonencode({
|
|
2479
|
-
Version = "2012-10-17"
|
|
2480
|
-
Statement = [
|
|
2481
|
-
{
|
|
2482
|
-
Action = "sts:AssumeRole"
|
|
2483
|
-
Effect = "Allow"
|
|
2484
|
-
Principal = {
|
|
2485
|
-
Service = "lambda.amazonaws.com"
|
|
2486
|
-
}
|
|
2487
|
-
}
|
|
2488
|
-
]
|
|
2489
|
-
})
|
|
2490
|
-
|
|
2491
|
-
tags = var.tags
|
|
2492
|
-
}
|
|
2493
|
-
|
|
2494
|
-
# Attach basic execution policy to Lambda role
|
|
2495
|
-
resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
|
|
2496
|
-
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
2497
|
-
role = aws_iam_role.lambda_execution_role.name
|
|
2498
|
-
}
|
|
2499
|
-
|
|
2500
|
-
# Attach X-Ray tracing policy to Lambda role
|
|
2501
|
-
resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
|
|
2502
|
-
policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
|
|
2503
|
-
role = aws_iam_role.lambda_execution_role.name
|
|
2504
|
-
}
|
|
2505
|
-
|
|
2506
|
-
# Additional IAM policies for Lambda (if provided)
|
|
2507
|
-
resource "aws_iam_role_policy" "lambda_additional_policies" {
|
|
2508
|
-
count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
|
|
2509
|
-
name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
|
|
2510
|
-
role = aws_iam_role.lambda_execution_role.id
|
|
2511
|
-
|
|
2512
|
-
policy = jsonencode({
|
|
2513
|
-
Version = "2012-10-17"
|
|
2514
|
-
Statement = var.additional_iam_policy_statements
|
|
2515
|
-
})
|
|
2516
|
-
}
|
|
2517
|
-
|
|
2518
|
-
# CloudWatch Log Group for Lambda
|
|
2519
|
-
resource "aws_cloudwatch_log_group" "lambda_logs" {
|
|
2520
|
-
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
2521
|
-
#checkov:skip=CKV_AWS_338:Log retention set to forever
|
|
2522
|
-
#checkov:skip=CKV_AWS_66:Log retention set to forever
|
|
2523
|
-
name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
|
|
2524
|
-
tags = var.tags
|
|
2525
|
-
}
|
|
2526
|
-
|
|
2527
|
-
# Lambda alias pointing to the latest published version for SnapStart
|
|
2528
|
-
resource "aws_lambda_alias" "live" {
|
|
2529
|
-
name = "live"
|
|
2530
|
-
function_name = aws_lambda_function.api_lambda.function_name
|
|
2531
|
-
function_version = aws_lambda_function.api_lambda.version
|
|
2532
|
-
|
|
2533
|
-
depends_on = [aws_lambda_function.api_lambda]
|
|
2534
|
-
}
|
|
2535
|
-
|
|
2536
|
-
# Cognito User Pool Authorizer
|
|
2537
|
-
resource "aws_apigatewayv2_authorizer" "cognito_authorizer" {
|
|
2538
|
-
api_id = module.http_api.api_id
|
|
2539
|
-
authorizer_type = "JWT"
|
|
2540
|
-
identity_sources = ["$request.header.Authorization"]
|
|
2541
|
-
name = "TestApiAuthorizer-\${random_string.suffix.result}"
|
|
2542
|
-
|
|
2543
|
-
jwt_configuration {
|
|
2544
|
-
audience = var.user_pool_client_ids
|
|
2545
|
-
issuer = "https://cognito-idp.\${data.aws_region.current.region}.amazonaws.com/\${var.user_pool_id}"
|
|
2546
|
-
}
|
|
2547
|
-
}
|
|
2548
|
-
|
|
2549
|
-
# Lambda integration for HTTP API
|
|
2550
|
-
resource "aws_apigatewayv2_integration" "lambda_integration" {
|
|
2551
|
-
api_id = module.http_api.api_id
|
|
2552
|
-
integration_type = "AWS_PROXY"
|
|
2553
|
-
integration_uri = aws_lambda_alias.live.invoke_arn
|
|
2554
|
-
|
|
2555
|
-
payload_format_version = "2.0"
|
|
2556
|
-
timeout_milliseconds = 30000
|
|
2557
|
-
|
|
2558
|
-
depends_on = [aws_lambda_alias.live]
|
|
2559
|
-
}
|
|
2560
|
-
|
|
2561
|
-
# Route for proxy integration (catches all requests)
|
|
2562
|
-
resource "aws_apigatewayv2_route" "proxy_routes" {
|
|
2563
|
-
# NB: OPTIONS is omitted here since API Gateway manages responding to preflight requests
|
|
2564
|
-
# when cors settings are configured
|
|
2565
|
-
for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"])
|
|
2566
|
-
|
|
2567
|
-
api_id = module.http_api.api_id
|
|
2568
|
-
route_key = "\${each.key} /{proxy+}"
|
|
2569
|
-
target = "integrations/\${aws_apigatewayv2_integration.lambda_integration.id}"
|
|
2570
|
-
|
|
2571
|
-
authorization_type = "JWT"
|
|
2572
|
-
authorizer_id = aws_apigatewayv2_authorizer.cognito_authorizer.id
|
|
2573
|
-
|
|
2574
|
-
depends_on = [aws_apigatewayv2_integration.lambda_integration, aws_apigatewayv2_authorizer.cognito_authorizer]
|
|
2575
|
-
}
|
|
2576
|
-
|
|
2577
|
-
# Add API url to runtime config
|
|
2578
|
-
module "add_url_to_runtime_config" {
|
|
2579
|
-
source = "../../../core/runtime-config/entry"
|
|
2580
|
-
|
|
2581
|
-
namespace = "connection"
|
|
2582
|
-
key = "apis"
|
|
2583
|
-
value = { "TestApi" = module.http_api.stage_invoke_url }
|
|
2584
|
-
|
|
2585
|
-
depends_on = [module.http_api]
|
|
2586
|
-
}
|
|
2587
|
-
|
|
2588
|
-
# Lambda permission for API Gateway to invoke the function via alias
|
|
2589
|
-
resource "aws_lambda_permission" "api_gateway_invoke" {
|
|
2590
|
-
statement_id = "AllowExecutionFromAPIGateway"
|
|
2591
|
-
action = "lambda:InvokeFunction"
|
|
2592
|
-
function_name = aws_lambda_function.api_lambda.function_name
|
|
2593
|
-
qualifier = aws_lambda_alias.live.name
|
|
2594
|
-
principal = "apigateway.amazonaws.com"
|
|
2595
|
-
source_arn = "\${module.http_api.api_execution_arn}/*/*"
|
|
2596
|
-
|
|
2597
|
-
depends_on = [module.http_api, aws_lambda_alias.live]
|
|
2598
|
-
}
|
|
2599
|
-
|
|
2600
|
-
# Outputs
|
|
2601
|
-
|
|
2602
|
-
# API Gateway Outputs (from core module)
|
|
2603
|
-
output "api_id" {
|
|
2604
|
-
description = "ID of the HTTP API Gateway"
|
|
2605
|
-
value = module.http_api.api_id
|
|
2606
|
-
}
|
|
2607
|
-
|
|
2608
|
-
output "api_arn" {
|
|
2609
|
-
description = "ARN of the HTTP API Gateway"
|
|
2610
|
-
value = module.http_api.api_arn
|
|
2611
|
-
}
|
|
2612
|
-
|
|
2613
|
-
output "api_endpoint" {
|
|
2614
|
-
description = "Base URL of the HTTP API Gateway"
|
|
2615
|
-
value = module.http_api.api_endpoint
|
|
2616
|
-
}
|
|
2617
|
-
|
|
2618
|
-
output "api_execution_arn" {
|
|
2619
|
-
description = "Execution ARN of the HTTP API Gateway"
|
|
2620
|
-
value = module.http_api.api_execution_arn
|
|
2621
|
-
}
|
|
2622
|
-
|
|
2623
|
-
output "stage_invoke_url" {
|
|
2624
|
-
description = "Invoke URL of the API Gateway stage"
|
|
2625
|
-
value = module.http_api.stage_invoke_url
|
|
2626
|
-
}
|
|
2627
|
-
|
|
2628
|
-
output "stage_arn" {
|
|
2629
|
-
description = "ARN of the API Gateway stage"
|
|
2630
|
-
value = module.http_api.stage_arn
|
|
2631
|
-
}
|
|
2632
|
-
|
|
2633
|
-
output "stage_execution_arn" {
|
|
2634
|
-
description = "Execution ARN of the API Gateway stage"
|
|
2635
|
-
value = module.http_api.stage_execution_arn
|
|
2636
|
-
}
|
|
2637
|
-
|
|
2638
|
-
# Lambda Function Outputs
|
|
2639
|
-
output "lambda_function_name" {
|
|
2640
|
-
description = "Name of the Lambda function"
|
|
2641
|
-
value = aws_lambda_function.api_lambda.function_name
|
|
2642
|
-
}
|
|
2643
|
-
|
|
2644
|
-
output "lambda_function_arn" {
|
|
2645
|
-
description = "ARN of the Lambda function"
|
|
2646
|
-
value = aws_lambda_function.api_lambda.arn
|
|
2647
|
-
}
|
|
2648
|
-
|
|
2649
|
-
output "lambda_invoke_arn" {
|
|
2650
|
-
description = "Invoke ARN of the Lambda function"
|
|
2651
|
-
value = aws_lambda_function.api_lambda.invoke_arn
|
|
2652
|
-
}
|
|
2653
|
-
|
|
2654
|
-
output "lambda_qualified_arn" {
|
|
2655
|
-
description = "Qualified ARN of the Lambda function"
|
|
2656
|
-
value = aws_lambda_function.api_lambda.qualified_arn
|
|
2657
|
-
}
|
|
2658
|
-
|
|
2659
|
-
output "lambda_version" {
|
|
2660
|
-
description = "Version of the Lambda function"
|
|
2661
|
-
value = aws_lambda_function.api_lambda.version
|
|
2662
|
-
}
|
|
2663
|
-
|
|
2664
|
-
output "lambda_source_code_hash" {
|
|
2665
|
-
description = "Base64-encoded SHA256 hash of the Lambda deployment package"
|
|
2666
|
-
value = aws_lambda_function.api_lambda.source_code_hash
|
|
2667
|
-
}
|
|
2668
|
-
|
|
2669
|
-
output "lambda_source_code_size" {
|
|
2670
|
-
description = "Size of the Lambda deployment package in bytes"
|
|
2671
|
-
value = aws_lambda_function.api_lambda.source_code_size
|
|
2672
|
-
}
|
|
2673
|
-
|
|
2674
|
-
# IAM Role Outputs
|
|
2675
|
-
output "lambda_execution_role_arn" {
|
|
2676
|
-
description = "ARN of the Lambda execution role"
|
|
2677
|
-
value = aws_iam_role.lambda_execution_role.arn
|
|
2678
|
-
}
|
|
2679
|
-
|
|
2680
|
-
output "lambda_execution_role_name" {
|
|
2681
|
-
description = "Name of the Lambda execution role"
|
|
2682
|
-
value = aws_iam_role.lambda_execution_role.name
|
|
2683
|
-
}
|
|
2684
|
-
|
|
2685
|
-
# Integration Outputs
|
|
2686
|
-
output "integration_id" {
|
|
2687
|
-
description = "ID of the Lambda integration"
|
|
2688
|
-
value = aws_apigatewayv2_integration.lambda_integration.id
|
|
2689
|
-
}
|
|
2690
|
-
|
|
2691
|
-
# CloudWatch Log Groups
|
|
2692
|
-
output "lambda_log_group_name" {
|
|
2693
|
-
description = "Name of the Lambda CloudWatch log group"
|
|
2694
|
-
value = aws_cloudwatch_log_group.lambda_logs.name
|
|
2695
|
-
}
|
|
2696
|
-
|
|
2697
|
-
output "lambda_log_group_arn" {
|
|
2698
|
-
description = "ARN of the Lambda CloudWatch log group"
|
|
2699
|
-
value = aws_cloudwatch_log_group.lambda_logs.arn
|
|
2700
|
-
}
|
|
2701
|
-
|
|
2702
|
-
output "api_log_group_name" {
|
|
2703
|
-
description = "Name of the API Gateway CloudWatch log group"
|
|
2704
|
-
value = module.http_api.api_log_group_name
|
|
2705
|
-
}
|
|
2706
|
-
|
|
2707
|
-
output "api_log_group_arn" {
|
|
2708
|
-
description = "ARN of the API Gateway CloudWatch log group"
|
|
2709
|
-
value = module.http_api.api_log_group_arn
|
|
2710
|
-
}",
|
|
2711
|
-
}
|
|
2712
|
-
`;
|
|
2713
|
-
|
|
2714
|
-
exports[`fastapi project generator > terraform iac > should generate terraform files for HTTP API with Custom auth and snapshot them > terraform-http-custom-files 1`] = `
|
|
2715
|
-
{
|
|
2716
|
-
"http-api.tf": "# Core HTTP API Gateway module
|
|
2717
|
-
# This module creates the API Gateway HTTP API, stage, and logging resources
|
|
2718
|
-
|
|
2719
|
-
terraform {
|
|
2720
|
-
required_version = ">= 1.0"
|
|
2721
|
-
|
|
2722
|
-
required_providers {
|
|
2723
|
-
aws = {
|
|
2724
|
-
source = "hashicorp/aws"
|
|
2725
|
-
version = "~> 6.0"
|
|
2726
|
-
}
|
|
2727
|
-
}
|
|
2728
|
-
}
|
|
2729
|
-
|
|
2730
|
-
# Core HTTP API Gateway Variables
|
|
2731
|
-
|
|
2732
|
-
variable "api_name" {
|
|
2733
|
-
description = "Name of the HTTP API Gateway"
|
|
2734
|
-
type = string
|
|
2735
|
-
}
|
|
2736
|
-
|
|
2737
|
-
variable "api_description" {
|
|
2738
|
-
description = "Description of the HTTP API Gateway"
|
|
2739
|
-
type = string
|
|
2740
|
-
default = "HTTP API Gateway"
|
|
2741
|
-
}
|
|
2742
|
-
|
|
2743
|
-
variable "stage_name" {
|
|
2744
|
-
description = "Name of the API Gateway stage"
|
|
2745
|
-
type = string
|
|
2746
|
-
default = "prod"
|
|
2747
|
-
}
|
|
2748
|
-
|
|
2749
|
-
variable "stage_auto_deploy" {
|
|
2750
|
-
description = "Whether to automatically deploy the API stage"
|
|
2751
|
-
type = bool
|
|
2752
|
-
default = true
|
|
2753
|
-
}
|
|
2754
|
-
|
|
2755
|
-
# CORS Configuration
|
|
2756
|
-
|
|
2757
|
-
variable "cors_allow_credentials" {
|
|
2758
|
-
description = "Whether to allow credentials in CORS requests"
|
|
2759
|
-
type = bool
|
|
2760
|
-
default = false
|
|
2761
|
-
}
|
|
2762
|
-
|
|
2763
|
-
variable "cors_allow_headers" {
|
|
2764
|
-
description = "List of allowed headers for CORS"
|
|
2765
|
-
type = list(string)
|
|
2766
|
-
default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
|
|
2767
|
-
}
|
|
2768
|
-
|
|
2769
|
-
variable "cors_allow_methods" {
|
|
2770
|
-
description = "List of allowed HTTP methods for CORS"
|
|
2771
|
-
type = list(string)
|
|
2772
|
-
default = ["*"]
|
|
2773
|
-
}
|
|
2774
|
-
|
|
2775
|
-
variable "cors_allow_origins" {
|
|
2776
|
-
description = "List of allowed origins for CORS"
|
|
2777
|
-
type = list(string)
|
|
2778
|
-
default = ["*"]
|
|
2779
|
-
}
|
|
2780
|
-
|
|
2781
|
-
variable "cors_expose_headers" {
|
|
2782
|
-
description = "List of headers to expose in CORS responses"
|
|
2783
|
-
type = list(string)
|
|
2784
|
-
default = []
|
|
2785
|
-
}
|
|
2786
|
-
|
|
2787
|
-
variable "cors_max_age" {
|
|
2788
|
-
description = "Maximum age for CORS preflight requests in seconds"
|
|
2789
|
-
type = number
|
|
2790
|
-
default = 86400
|
|
2791
|
-
}
|
|
2792
|
-
|
|
2793
|
-
# Tags
|
|
2794
|
-
|
|
2795
|
-
variable "tags" {
|
|
2796
|
-
description = "Tags to apply to all resources"
|
|
2797
|
-
type = map(string)
|
|
2798
|
-
default = {}
|
|
2799
|
-
}
|
|
2800
|
-
|
|
2801
|
-
# Data sources
|
|
2802
|
-
data "aws_region" "current" {}
|
|
2803
|
-
data "aws_caller_identity" "current" {}
|
|
2804
|
-
|
|
2805
|
-
# Resources
|
|
2806
|
-
|
|
2807
|
-
# KMS key for CloudWatch log group encryption
|
|
2808
|
-
resource "aws_kms_key" "logs_key" {
|
|
2809
|
-
description = "KMS key for CloudWatch log group encryption"
|
|
2810
|
-
deletion_window_in_days = 7
|
|
2811
|
-
enable_key_rotation = true
|
|
2812
|
-
|
|
2813
|
-
policy = jsonencode({
|
|
2814
|
-
Version = "2012-10-17"
|
|
2815
|
-
Statement = [
|
|
2816
|
-
{
|
|
2817
|
-
Sid = "Enable IAM User Permissions"
|
|
2818
|
-
Effect = "Allow"
|
|
2819
|
-
Principal = {
|
|
2820
|
-
AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
|
|
2821
|
-
}
|
|
2822
|
-
Action = "kms:*"
|
|
2823
|
-
Resource = "*"
|
|
2824
|
-
},
|
|
2825
|
-
{
|
|
2826
|
-
Sid = "Allow CloudWatch Logs"
|
|
2827
|
-
Effect = "Allow"
|
|
2828
|
-
Principal = {
|
|
2829
|
-
Service = "logs.\${data.aws_region.current.region}.amazonaws.com"
|
|
2830
|
-
}
|
|
2831
|
-
Action = [
|
|
2832
|
-
"kms:Encrypt",
|
|
2833
|
-
"kms:Decrypt",
|
|
2834
|
-
"kms:ReEncrypt*",
|
|
2835
|
-
"kms:GenerateDataKey*",
|
|
2836
|
-
"kms:DescribeKey"
|
|
2837
|
-
]
|
|
2838
|
-
Resource = "*"
|
|
2839
|
-
Condition = {
|
|
2840
|
-
ArnEquals = {
|
|
2841
|
-
"kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:log-group:/aws/apigateway/\${var.api_name}"
|
|
2842
|
-
}
|
|
2843
|
-
}
|
|
2844
|
-
}
|
|
2845
|
-
]
|
|
2846
|
-
})
|
|
2847
|
-
|
|
2848
|
-
tags = var.tags
|
|
2849
|
-
}
|
|
2850
|
-
|
|
2851
|
-
resource "aws_kms_alias" "logs_key_alias" {
|
|
2852
|
-
name = "alias/\${var.api_name}-api-logs-encryption"
|
|
2853
|
-
target_key_id = aws_kms_key.logs_key.key_id
|
|
2854
|
-
}
|
|
2855
|
-
|
|
2856
|
-
# HTTP API Gateway
|
|
2857
|
-
resource "aws_apigatewayv2_api" "http_api" {
|
|
2858
|
-
name = var.api_name
|
|
2859
|
-
protocol_type = "HTTP"
|
|
2860
|
-
description = var.api_description
|
|
2861
|
-
|
|
2862
|
-
cors_configuration {
|
|
2863
|
-
allow_credentials = var.cors_allow_credentials
|
|
2864
|
-
allow_headers = var.cors_allow_headers
|
|
2865
|
-
allow_methods = var.cors_allow_methods
|
|
2866
|
-
allow_origins = var.cors_allow_origins
|
|
2867
|
-
expose_headers = var.cors_expose_headers
|
|
2868
|
-
max_age = var.cors_max_age
|
|
2869
|
-
}
|
|
2870
|
-
|
|
2871
|
-
tags = var.tags
|
|
2872
|
-
}
|
|
2873
|
-
|
|
2874
|
-
# API Gateway stage
|
|
2875
|
-
resource "aws_apigatewayv2_stage" "api_stage" {
|
|
2876
|
-
api_id = aws_apigatewayv2_api.http_api.id
|
|
2877
|
-
name = var.stage_name
|
|
2878
|
-
auto_deploy = var.stage_auto_deploy
|
|
2879
|
-
|
|
2880
|
-
access_log_settings {
|
|
2881
|
-
destination_arn = aws_cloudwatch_log_group.api_logs.arn
|
|
2882
|
-
format = jsonencode({
|
|
2883
|
-
requestId = "$context.requestId"
|
|
2884
|
-
ip = "$context.identity.sourceIp"
|
|
2885
|
-
requestTime = "$context.requestTime"
|
|
2886
|
-
httpMethod = "$context.httpMethod"
|
|
2887
|
-
routeKey = "$context.routeKey"
|
|
2888
|
-
status = "$context.status"
|
|
2889
|
-
protocol = "$context.protocol"
|
|
2890
|
-
responseLength = "$context.responseLength"
|
|
2891
|
-
error = "$context.error.message"
|
|
2892
|
-
integrationError = "$context.integrationErrorMessage"
|
|
2893
|
-
})
|
|
2894
|
-
}
|
|
2895
|
-
|
|
2896
|
-
default_route_settings {
|
|
2897
|
-
throttling_burst_limit = 5000
|
|
2898
|
-
throttling_rate_limit = 10000
|
|
2899
|
-
}
|
|
2900
|
-
|
|
2901
|
-
tags = var.tags
|
|
2902
|
-
|
|
2903
|
-
depends_on = [aws_cloudwatch_log_group.api_logs]
|
|
2904
|
-
}
|
|
2905
|
-
|
|
2906
|
-
# CloudWatch Log Group for API Gateway
|
|
2907
|
-
resource "aws_cloudwatch_log_group" "api_logs" {
|
|
2908
|
-
#checkov:skip=CKV_AWS_338:Log retention set to forever
|
|
2909
|
-
#checkov:skip=CKV_AWS_66:Log retention set to forever
|
|
2910
|
-
name = "/aws/apigateway/\${var.api_name}"
|
|
2911
|
-
kms_key_id = aws_kms_key.logs_key.arn
|
|
2912
|
-
tags = var.tags
|
|
2913
|
-
}
|
|
2914
|
-
|
|
2915
|
-
# Outputs
|
|
2916
|
-
|
|
2917
|
-
output "api_id" {
|
|
2918
|
-
description = "ID of the HTTP API Gateway"
|
|
2919
|
-
value = aws_apigatewayv2_api.http_api.id
|
|
2920
|
-
}
|
|
2921
|
-
|
|
2922
|
-
output "api_arn" {
|
|
2923
|
-
description = "ARN of the HTTP API Gateway"
|
|
2924
|
-
value = aws_apigatewayv2_api.http_api.arn
|
|
2925
|
-
}
|
|
2926
|
-
|
|
2927
|
-
output "api_endpoint" {
|
|
2928
|
-
description = "Base URL of the HTTP API Gateway"
|
|
2929
|
-
value = aws_apigatewayv2_api.http_api.api_endpoint
|
|
2930
|
-
}
|
|
2931
|
-
|
|
2932
|
-
output "api_execution_arn" {
|
|
2933
|
-
description = "Execution ARN of the HTTP API Gateway"
|
|
2934
|
-
value = aws_apigatewayv2_api.http_api.execution_arn
|
|
2935
|
-
}
|
|
2936
|
-
|
|
2937
|
-
output "stage_id" {
|
|
2938
|
-
description = "ID of the API Gateway stage"
|
|
2939
|
-
value = aws_apigatewayv2_stage.api_stage.id
|
|
2940
|
-
}
|
|
2941
|
-
|
|
2942
|
-
output "stage_arn" {
|
|
2943
|
-
description = "ARN of the API Gateway stage"
|
|
2944
|
-
value = aws_apigatewayv2_stage.api_stage.arn
|
|
2945
|
-
}
|
|
2946
|
-
|
|
2947
|
-
output "stage_execution_arn" {
|
|
2948
|
-
description = "Execution ARN of the API Gateway stage"
|
|
2949
|
-
value = aws_apigatewayv2_stage.api_stage.execution_arn
|
|
2950
|
-
}
|
|
2951
|
-
|
|
2952
|
-
output "stage_invoke_url" {
|
|
2953
|
-
description = "Invoke URL of the API Gateway stage"
|
|
2954
|
-
value = aws_apigatewayv2_stage.api_stage.invoke_url
|
|
2955
|
-
}
|
|
2956
|
-
|
|
2957
|
-
output "api_log_group_name" {
|
|
2958
|
-
description = "Name of the API Gateway CloudWatch log group"
|
|
2959
|
-
value = aws_cloudwatch_log_group.api_logs.name
|
|
2960
|
-
}
|
|
2961
|
-
|
|
2962
|
-
output "api_log_group_arn" {
|
|
2963
|
-
description = "ARN of the API Gateway CloudWatch log group"
|
|
2964
|
-
value = aws_cloudwatch_log_group.api_logs.arn
|
|
2965
|
-
}",
|
|
2966
|
-
"test-api.tf": "terraform {
|
|
2967
|
-
required_version = ">= 1.0"
|
|
2968
|
-
|
|
2969
|
-
required_providers {
|
|
2970
|
-
aws = {
|
|
2971
|
-
source = "hashicorp/aws"
|
|
2972
|
-
version = "~> 6.0"
|
|
2973
|
-
}
|
|
2974
|
-
}
|
|
2975
|
-
}
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
variable "env" {
|
|
2979
|
-
description = "Environment variables for the Lambda function"
|
|
2980
|
-
type = map(string)
|
|
2981
|
-
default = {}
|
|
2982
|
-
}
|
|
2983
|
-
|
|
2984
|
-
variable "additional_iam_policy_statements" {
|
|
2985
|
-
description = "Additional IAM policy statements for the Lambda function"
|
|
2986
|
-
type = list(object({
|
|
2987
|
-
Effect = string
|
|
2988
|
-
Action = list(string)
|
|
2989
|
-
Resource = list(string)
|
|
2990
|
-
}))
|
|
2991
|
-
default = []
|
|
2992
|
-
}
|
|
2993
|
-
|
|
2994
|
-
variable "asset_bucket_name" {
|
|
2995
|
-
description = "Name of the shared asset S3 bucket used to stage the Lambda deployment zip. Instantiate the \`core/asset-bucket\` module once per deployment and pass its \`bucket_name\` output here."
|
|
2996
|
-
type = string
|
|
2997
|
-
}
|
|
2998
|
-
|
|
2999
|
-
# CORS Configuration (passed to core module)
|
|
3000
|
-
variable "cors_allow_credentials" {
|
|
3001
|
-
description = "Whether to allow credentials in CORS requests"
|
|
3002
|
-
type = bool
|
|
3003
|
-
default = false
|
|
3004
|
-
}
|
|
3005
|
-
|
|
3006
|
-
variable "cors_allow_headers" {
|
|
3007
|
-
description = "List of allowed headers for CORS"
|
|
3008
|
-
type = list(string)
|
|
3009
|
-
default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
|
|
3010
|
-
}
|
|
3011
|
-
|
|
3012
|
-
variable "cors_allow_methods" {
|
|
3013
|
-
description = "List of allowed HTTP methods for CORS"
|
|
3014
|
-
type = list(string)
|
|
3015
|
-
default = ["*"]
|
|
3016
|
-
}
|
|
3017
|
-
|
|
3018
|
-
variable "cors_allow_origins" {
|
|
3019
|
-
description = "List of allowed origins for CORS"
|
|
3020
|
-
type = list(string)
|
|
3021
|
-
default = ["*"]
|
|
3022
|
-
}
|
|
3023
|
-
|
|
3024
|
-
variable "cors_expose_headers" {
|
|
3025
|
-
description = "List of headers to expose in CORS responses"
|
|
3026
|
-
type = list(string)
|
|
3027
|
-
default = []
|
|
3028
|
-
}
|
|
3029
|
-
|
|
3030
|
-
variable "cors_max_age" {
|
|
3031
|
-
description = "Maximum age for CORS preflight requests in seconds"
|
|
3032
|
-
type = number
|
|
3033
|
-
default = 0
|
|
3034
|
-
}
|
|
3035
|
-
|
|
3036
|
-
# Tags
|
|
3037
|
-
variable "tags" {
|
|
3038
|
-
description = "Tags to apply to all resources"
|
|
3039
|
-
type = map(string)
|
|
3040
|
-
default = {}
|
|
3041
|
-
}
|
|
3042
|
-
|
|
3043
|
-
# Get current AWS region and account ID
|
|
3044
|
-
data "aws_region" "current" {}
|
|
3045
|
-
data "aws_caller_identity" "current" {}
|
|
3046
|
-
|
|
3047
|
-
resource "random_string" "suffix" {
|
|
3048
|
-
length = 8
|
|
3049
|
-
special = false
|
|
3050
|
-
upper = false
|
|
3051
|
-
}
|
|
3052
|
-
|
|
3053
|
-
# Resources
|
|
3054
|
-
|
|
3055
|
-
# Create Lambda ZIP file from the bundle directory
|
|
3056
|
-
data "archive_file" "lambda_zip" {
|
|
3057
|
-
type = "zip"
|
|
3058
|
-
source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
|
|
3059
|
-
output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
|
|
3060
|
-
}
|
|
3061
|
-
|
|
3062
|
-
resource "aws_s3_object" "lambda_zip" {
|
|
3063
|
-
bucket = var.asset_bucket_name
|
|
3064
|
-
key = "apis/test-api/\${data.archive_file.lambda_zip.output_sha256}.zip"
|
|
3065
|
-
source = data.archive_file.lambda_zip.output_path
|
|
3066
|
-
source_hash = data.archive_file.lambda_zip.output_base64sha256
|
|
3067
|
-
etag = data.archive_file.lambda_zip.output_md5
|
|
3068
|
-
}
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
# Use the core HTTP API module
|
|
3072
|
-
module "http_api" {
|
|
3073
|
-
source = "../../../core/api/http-api"
|
|
3074
|
-
|
|
3075
|
-
api_name = "TestApi-\${random_string.suffix.result}"
|
|
3076
|
-
api_description = "TestApi HTTP API"
|
|
3077
|
-
stage_name = "$default"
|
|
3078
|
-
stage_auto_deploy = true
|
|
3079
|
-
|
|
3080
|
-
# CORS Configuration
|
|
3081
|
-
cors_allow_credentials = var.cors_allow_credentials
|
|
3082
|
-
cors_allow_headers = var.cors_allow_headers
|
|
3083
|
-
cors_allow_methods = var.cors_allow_methods
|
|
3084
|
-
cors_allow_origins = var.cors_allow_origins
|
|
3085
|
-
cors_expose_headers = var.cors_expose_headers
|
|
3086
|
-
cors_max_age = var.cors_max_age
|
|
3087
|
-
|
|
3088
|
-
# Tags
|
|
3089
|
-
tags = var.tags
|
|
3090
|
-
}
|
|
3091
|
-
|
|
3092
|
-
# Lambda function
|
|
3093
|
-
# This configures a single "router" lambda to serve all requests
|
|
3094
|
-
resource "aws_lambda_function" "api_lambda" {
|
|
3095
|
-
#checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
|
|
3096
|
-
#checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
|
|
3097
|
-
#checkov:skip=CKV_AWS_272:Code signing not required for this use case
|
|
3098
|
-
#checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
|
|
3099
|
-
#checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
|
|
3100
|
-
s3_bucket = aws_s3_object.lambda_zip.bucket
|
|
3101
|
-
s3_key = aws_s3_object.lambda_zip.key
|
|
3102
|
-
s3_object_version = aws_s3_object.lambda_zip.version_id
|
|
3103
|
-
function_name = "TestApiHandler-\${random_string.suffix.result}"
|
|
3104
|
-
role = aws_iam_role.lambda_execution_role.arn
|
|
3105
|
-
handler = "run.sh"
|
|
3106
|
-
runtime = "python3.14"
|
|
3107
|
-
layers = ["arn:aws:lambda:\${data.aws_region.current.region}:753240598075:layer:LambdaAdapterLayerX86:24"]
|
|
3108
|
-
timeout = 30
|
|
3109
|
-
memory_size = 128
|
|
3110
|
-
publish = true
|
|
3111
|
-
|
|
3112
|
-
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
|
|
3113
|
-
|
|
3114
|
-
# Enable X-Ray tracing
|
|
3115
|
-
tracing_config {
|
|
3116
|
-
mode = "Active"
|
|
3117
|
-
}
|
|
3118
|
-
|
|
3119
|
-
# Enable SnapStart for faster cold starts
|
|
3120
|
-
snap_start {
|
|
3121
|
-
apply_on = "PublishedVersions"
|
|
3122
|
-
}
|
|
3123
|
-
|
|
3124
|
-
environment {
|
|
3125
|
-
variables = merge({
|
|
3126
|
-
PORT = "8000"
|
|
3127
|
-
AWS_LWA_INVOKE_MODE = "buffered"
|
|
3128
|
-
AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
|
|
3129
|
-
}, var.env)
|
|
3130
|
-
}
|
|
3131
|
-
|
|
3132
|
-
tags = var.tags
|
|
3133
|
-
}
|
|
3134
|
-
|
|
3135
|
-
# IAM role for Lambda execution
|
|
3136
|
-
resource "aws_iam_role" "lambda_execution_role" {
|
|
3137
|
-
name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
|
|
3138
|
-
|
|
3139
|
-
assume_role_policy = jsonencode({
|
|
3140
|
-
Version = "2012-10-17"
|
|
3141
|
-
Statement = [
|
|
3142
|
-
{
|
|
3143
|
-
Action = "sts:AssumeRole"
|
|
3144
|
-
Effect = "Allow"
|
|
3145
|
-
Principal = {
|
|
3146
|
-
Service = "lambda.amazonaws.com"
|
|
3147
|
-
}
|
|
3148
|
-
}
|
|
3149
|
-
]
|
|
3150
|
-
})
|
|
3151
|
-
|
|
3152
|
-
tags = var.tags
|
|
3153
|
-
}
|
|
3154
|
-
|
|
3155
|
-
# Attach basic execution policy to Lambda role
|
|
3156
|
-
resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
|
|
3157
|
-
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
3158
|
-
role = aws_iam_role.lambda_execution_role.name
|
|
3159
|
-
}
|
|
3160
|
-
|
|
3161
|
-
# Attach X-Ray tracing policy to Lambda role
|
|
3162
|
-
resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
|
|
3163
|
-
policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
|
|
3164
|
-
role = aws_iam_role.lambda_execution_role.name
|
|
3165
|
-
}
|
|
3166
|
-
|
|
3167
|
-
# Additional IAM policies for Lambda (if provided)
|
|
3168
|
-
resource "aws_iam_role_policy" "lambda_additional_policies" {
|
|
3169
|
-
count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
|
|
3170
|
-
name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
|
|
3171
|
-
role = aws_iam_role.lambda_execution_role.id
|
|
3172
|
-
|
|
3173
|
-
policy = jsonencode({
|
|
3174
|
-
Version = "2012-10-17"
|
|
3175
|
-
Statement = var.additional_iam_policy_statements
|
|
3176
|
-
})
|
|
3177
|
-
}
|
|
3178
|
-
|
|
3179
|
-
# CloudWatch Log Group for Lambda
|
|
3180
|
-
resource "aws_cloudwatch_log_group" "lambda_logs" {
|
|
3181
|
-
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
3182
|
-
#checkov:skip=CKV_AWS_338:Log retention set to forever
|
|
3183
|
-
#checkov:skip=CKV_AWS_66:Log retention set to forever
|
|
3184
|
-
name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
|
|
3185
|
-
tags = var.tags
|
|
3186
|
-
}
|
|
3187
|
-
|
|
3188
|
-
# Lambda alias pointing to the latest published version for SnapStart
|
|
3189
|
-
resource "aws_lambda_alias" "live" {
|
|
3190
|
-
name = "live"
|
|
3191
|
-
function_name = aws_lambda_function.api_lambda.function_name
|
|
3192
|
-
function_version = aws_lambda_function.api_lambda.version
|
|
3193
|
-
|
|
3194
|
-
depends_on = [aws_lambda_function.api_lambda]
|
|
3195
|
-
}
|
|
3196
|
-
|
|
3197
|
-
# Custom Lambda Authorizer
|
|
3198
|
-
resource "aws_lambda_function" "authorizer_lambda" {
|
|
3199
|
-
#checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
|
|
3200
|
-
#checkov:skip=CKV_AWS_116:Dead Letter Queue not required for authorizer
|
|
3201
|
-
#checkov:skip=CKV_AWS_272:Code signing not required for this use case
|
|
3202
|
-
#checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
|
|
3203
|
-
#checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
|
|
3204
|
-
s3_bucket = aws_s3_object.authorizer_zip.bucket
|
|
3205
|
-
s3_key = aws_s3_object.authorizer_zip.key
|
|
3206
|
-
s3_object_version = aws_s3_object.authorizer_zip.version_id
|
|
3207
|
-
function_name = "TestApiAuthorizer-\${random_string.suffix.result}"
|
|
3208
|
-
role = aws_iam_role.authorizer_execution_role.arn
|
|
3209
|
-
handler = "proj_test_api.authorizer.handler"
|
|
3210
|
-
runtime = "python3.14"
|
|
3211
|
-
timeout = 10
|
|
3212
|
-
memory_size = 128
|
|
3213
|
-
|
|
3214
|
-
source_code_hash = data.archive_file.authorizer_zip.output_base64sha256
|
|
3215
|
-
|
|
3216
|
-
tracing_config {
|
|
3217
|
-
mode = "Active"
|
|
3218
|
-
}
|
|
3219
|
-
|
|
3220
|
-
tags = var.tags
|
|
3221
|
-
}
|
|
3222
|
-
|
|
3223
|
-
resource "aws_iam_role" "authorizer_execution_role" {
|
|
3224
|
-
name = "TestApiAuthorizer-role-\${random_string.suffix.result}"
|
|
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
|
-
resource "aws_iam_role_policy_attachment" "authorizer_basic_execution" {
|
|
3243
|
-
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
3244
|
-
role = aws_iam_role.authorizer_execution_role.name
|
|
3245
|
-
}
|
|
3246
|
-
|
|
3247
|
-
resource "aws_iam_role_policy_attachment" "authorizer_xray_execution" {
|
|
3248
|
-
policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
|
|
3249
|
-
role = aws_iam_role.authorizer_execution_role.name
|
|
3250
|
-
}
|
|
3251
|
-
|
|
3252
|
-
resource "aws_cloudwatch_log_group" "authorizer_logs" {
|
|
3253
|
-
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
3254
|
-
#checkov:skip=CKV_AWS_338:Log retention set to forever
|
|
3255
|
-
#checkov:skip=CKV_AWS_66:Log retention set to forever
|
|
3256
|
-
name = "/aws/lambda/TestApiAuthorizer-\${random_string.suffix.result}"
|
|
3257
|
-
tags = var.tags
|
|
3258
|
-
}
|
|
3259
|
-
|
|
3260
|
-
data "archive_file" "authorizer_zip" {
|
|
3261
|
-
type = "zip"
|
|
3262
|
-
source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
|
|
3263
|
-
output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/authorizer.zip"
|
|
3264
|
-
}
|
|
3265
|
-
|
|
3266
|
-
resource "aws_s3_object" "authorizer_zip" {
|
|
3267
|
-
bucket = var.asset_bucket_name
|
|
3268
|
-
key = "apis/test-api/authorizer-\${data.archive_file.authorizer_zip.output_sha256}.zip"
|
|
3269
|
-
source = data.archive_file.authorizer_zip.output_path
|
|
3270
|
-
source_hash = data.archive_file.authorizer_zip.output_base64sha256
|
|
3271
|
-
etag = data.archive_file.authorizer_zip.output_md5
|
|
3272
|
-
}
|
|
3273
|
-
|
|
3274
|
-
resource "aws_apigatewayv2_authorizer" "custom_authorizer" {
|
|
3275
|
-
api_id = module.http_api.api_id
|
|
3276
|
-
authorizer_type = "REQUEST"
|
|
3277
|
-
authorizer_uri = aws_lambda_function.authorizer_lambda.invoke_arn
|
|
3278
|
-
authorizer_payload_format_version = "2.0"
|
|
3279
|
-
authorizer_result_ttl_in_seconds = 0
|
|
3280
|
-
enable_simple_responses = true
|
|
3281
|
-
name = "TestApiAuthorizer-\${random_string.suffix.result}"
|
|
3282
|
-
}
|
|
3283
|
-
|
|
3284
|
-
resource "aws_lambda_permission" "authorizer_invoke" {
|
|
3285
|
-
statement_id = "AllowAPIGatewayInvokeAuthorizer"
|
|
3286
|
-
action = "lambda:InvokeFunction"
|
|
3287
|
-
function_name = aws_lambda_function.authorizer_lambda.function_name
|
|
3288
|
-
principal = "apigateway.amazonaws.com"
|
|
3289
|
-
source_arn = "\${module.http_api.api_execution_arn}/authorizers/\${aws_apigatewayv2_authorizer.custom_authorizer.id}"
|
|
3290
|
-
}
|
|
3291
|
-
|
|
3292
|
-
# Lambda integration for HTTP API
|
|
3293
|
-
resource "aws_apigatewayv2_integration" "lambda_integration" {
|
|
3294
|
-
api_id = module.http_api.api_id
|
|
3295
|
-
integration_type = "AWS_PROXY"
|
|
3296
|
-
integration_uri = aws_lambda_alias.live.invoke_arn
|
|
3297
|
-
|
|
3298
|
-
payload_format_version = "2.0"
|
|
3299
|
-
timeout_milliseconds = 30000
|
|
3300
|
-
|
|
3301
|
-
depends_on = [aws_lambda_alias.live]
|
|
3302
|
-
}
|
|
3303
|
-
|
|
3304
|
-
# Route for proxy integration (catches all requests)
|
|
3305
|
-
resource "aws_apigatewayv2_route" "proxy_routes" {
|
|
3306
|
-
# NB: OPTIONS is omitted here since API Gateway manages responding to preflight requests
|
|
3307
|
-
# when cors settings are configured
|
|
3308
|
-
for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"])
|
|
3309
|
-
|
|
3310
|
-
api_id = module.http_api.api_id
|
|
3311
|
-
route_key = "\${each.key} /{proxy+}"
|
|
3312
|
-
target = "integrations/\${aws_apigatewayv2_integration.lambda_integration.id}"
|
|
3313
|
-
|
|
3314
|
-
authorization_type = "CUSTOM"
|
|
3315
|
-
authorizer_id = aws_apigatewayv2_authorizer.custom_authorizer.id
|
|
3316
|
-
|
|
3317
|
-
depends_on = [aws_apigatewayv2_integration.lambda_integration, aws_apigatewayv2_authorizer.custom_authorizer]
|
|
3318
|
-
}
|
|
3319
|
-
|
|
3320
|
-
# Add API url to runtime config
|
|
3321
|
-
module "add_url_to_runtime_config" {
|
|
3322
|
-
source = "../../../core/runtime-config/entry"
|
|
3323
|
-
|
|
3324
|
-
namespace = "connection"
|
|
3325
|
-
key = "apis"
|
|
3326
|
-
value = { "TestApi" = module.http_api.stage_invoke_url }
|
|
3327
|
-
|
|
3328
|
-
depends_on = [module.http_api]
|
|
3329
|
-
}
|
|
3330
|
-
|
|
3331
|
-
# Lambda permission for API Gateway to invoke the function via alias
|
|
3332
|
-
resource "aws_lambda_permission" "api_gateway_invoke" {
|
|
3333
|
-
statement_id = "AllowExecutionFromAPIGateway"
|
|
3334
|
-
action = "lambda:InvokeFunction"
|
|
3335
|
-
function_name = aws_lambda_function.api_lambda.function_name
|
|
3336
|
-
qualifier = aws_lambda_alias.live.name
|
|
3337
|
-
principal = "apigateway.amazonaws.com"
|
|
3338
|
-
source_arn = "\${module.http_api.api_execution_arn}/*/*"
|
|
3339
|
-
|
|
3340
|
-
depends_on = [module.http_api, aws_lambda_alias.live]
|
|
3341
|
-
}
|
|
3342
|
-
|
|
3343
|
-
# Outputs
|
|
3344
|
-
|
|
3345
|
-
# API Gateway Outputs (from core module)
|
|
3346
|
-
output "api_id" {
|
|
3347
|
-
description = "ID of the HTTP API Gateway"
|
|
3348
|
-
value = module.http_api.api_id
|
|
3349
|
-
}
|
|
3350
|
-
|
|
3351
|
-
output "api_arn" {
|
|
3352
|
-
description = "ARN of the HTTP API Gateway"
|
|
3353
|
-
value = module.http_api.api_arn
|
|
3354
|
-
}
|
|
3355
|
-
|
|
3356
|
-
output "api_endpoint" {
|
|
3357
|
-
description = "Base URL of the HTTP API Gateway"
|
|
3358
|
-
value = module.http_api.api_endpoint
|
|
3359
|
-
}
|
|
3360
|
-
|
|
3361
|
-
output "api_execution_arn" {
|
|
3362
|
-
description = "Execution ARN of the HTTP API Gateway"
|
|
3363
|
-
value = module.http_api.api_execution_arn
|
|
3364
|
-
}
|
|
3365
|
-
|
|
3366
|
-
output "stage_invoke_url" {
|
|
3367
|
-
description = "Invoke URL of the API Gateway stage"
|
|
3368
|
-
value = module.http_api.stage_invoke_url
|
|
3369
|
-
}
|
|
3370
|
-
|
|
3371
|
-
output "stage_arn" {
|
|
3372
|
-
description = "ARN of the API Gateway stage"
|
|
3373
|
-
value = module.http_api.stage_arn
|
|
3374
|
-
}
|
|
3375
|
-
|
|
3376
|
-
output "stage_execution_arn" {
|
|
3377
|
-
description = "Execution ARN of the API Gateway stage"
|
|
3378
|
-
value = module.http_api.stage_execution_arn
|
|
3379
|
-
}
|
|
3380
|
-
|
|
3381
|
-
# Lambda Function Outputs
|
|
3382
|
-
output "lambda_function_name" {
|
|
3383
|
-
description = "Name of the Lambda function"
|
|
3384
|
-
value = aws_lambda_function.api_lambda.function_name
|
|
3385
|
-
}
|
|
3386
|
-
|
|
3387
|
-
output "lambda_function_arn" {
|
|
3388
|
-
description = "ARN of the Lambda function"
|
|
3389
|
-
value = aws_lambda_function.api_lambda.arn
|
|
3390
|
-
}
|
|
3391
|
-
|
|
3392
|
-
output "lambda_invoke_arn" {
|
|
3393
|
-
description = "Invoke ARN of the Lambda function"
|
|
3394
|
-
value = aws_lambda_function.api_lambda.invoke_arn
|
|
3395
|
-
}
|
|
3396
|
-
|
|
3397
|
-
output "lambda_qualified_arn" {
|
|
3398
|
-
description = "Qualified ARN of the Lambda function"
|
|
3399
|
-
value = aws_lambda_function.api_lambda.qualified_arn
|
|
3400
|
-
}
|
|
3401
|
-
|
|
3402
|
-
output "lambda_version" {
|
|
3403
|
-
description = "Version of the Lambda function"
|
|
3404
|
-
value = aws_lambda_function.api_lambda.version
|
|
3405
|
-
}
|
|
3406
|
-
|
|
3407
|
-
output "lambda_source_code_hash" {
|
|
3408
|
-
description = "Base64-encoded SHA256 hash of the Lambda deployment package"
|
|
3409
|
-
value = aws_lambda_function.api_lambda.source_code_hash
|
|
3410
|
-
}
|
|
3411
|
-
|
|
3412
|
-
output "lambda_source_code_size" {
|
|
3413
|
-
description = "Size of the Lambda deployment package in bytes"
|
|
3414
|
-
value = aws_lambda_function.api_lambda.source_code_size
|
|
3415
|
-
}
|
|
3416
|
-
|
|
3417
|
-
# IAM Role Outputs
|
|
3418
|
-
output "lambda_execution_role_arn" {
|
|
3419
|
-
description = "ARN of the Lambda execution role"
|
|
3420
|
-
value = aws_iam_role.lambda_execution_role.arn
|
|
3421
|
-
}
|
|
3422
|
-
|
|
3423
|
-
output "lambda_execution_role_name" {
|
|
3424
|
-
description = "Name of the Lambda execution role"
|
|
3425
|
-
value = aws_iam_role.lambda_execution_role.name
|
|
3426
|
-
}
|
|
3427
|
-
|
|
3428
|
-
# Integration Outputs
|
|
3429
|
-
output "integration_id" {
|
|
3430
|
-
description = "ID of the Lambda integration"
|
|
3431
|
-
value = aws_apigatewayv2_integration.lambda_integration.id
|
|
3432
|
-
}
|
|
3433
|
-
|
|
3434
|
-
# CloudWatch Log Groups
|
|
3435
|
-
output "lambda_log_group_name" {
|
|
3436
|
-
description = "Name of the Lambda CloudWatch log group"
|
|
3437
|
-
value = aws_cloudwatch_log_group.lambda_logs.name
|
|
3438
|
-
}
|
|
3439
|
-
|
|
3440
|
-
output "lambda_log_group_arn" {
|
|
3441
|
-
description = "ARN of the Lambda CloudWatch log group"
|
|
3442
|
-
value = aws_cloudwatch_log_group.lambda_logs.arn
|
|
3443
|
-
}
|
|
3444
|
-
|
|
3445
|
-
output "api_log_group_name" {
|
|
3446
|
-
description = "Name of the API Gateway CloudWatch log group"
|
|
3447
|
-
value = module.http_api.api_log_group_name
|
|
3448
|
-
}
|
|
3449
|
-
|
|
3450
|
-
output "api_log_group_arn" {
|
|
3451
|
-
description = "ARN of the API Gateway CloudWatch log group"
|
|
3452
|
-
value = module.http_api.api_log_group_arn
|
|
3453
|
-
}",
|
|
3454
|
-
}
|
|
3455
|
-
`;
|
|
3456
|
-
|
|
3457
|
-
exports[`fastapi project generator > terraform iac > should generate terraform files for HTTP API with IAM auth and snapshot them > terraform-http-iam-files 1`] = `
|
|
3458
|
-
{
|
|
3459
|
-
"http-api.tf": "# Core HTTP API Gateway module
|
|
3460
|
-
# This module creates the API Gateway HTTP API, stage, and logging resources
|
|
3461
|
-
|
|
3462
|
-
terraform {
|
|
3463
|
-
required_version = ">= 1.0"
|
|
3464
|
-
|
|
3465
|
-
required_providers {
|
|
3466
|
-
aws = {
|
|
3467
|
-
source = "hashicorp/aws"
|
|
3468
|
-
version = "~> 6.0"
|
|
3469
|
-
}
|
|
3470
|
-
}
|
|
3471
|
-
}
|
|
3472
|
-
|
|
3473
|
-
# Core HTTP API Gateway Variables
|
|
3474
|
-
|
|
3475
|
-
variable "api_name" {
|
|
3476
|
-
description = "Name of the HTTP API Gateway"
|
|
3477
|
-
type = string
|
|
3478
|
-
}
|
|
3479
|
-
|
|
3480
|
-
variable "api_description" {
|
|
3481
|
-
description = "Description of the HTTP API Gateway"
|
|
3482
|
-
type = string
|
|
3483
|
-
default = "HTTP API Gateway"
|
|
3484
|
-
}
|
|
3485
|
-
|
|
3486
|
-
variable "stage_name" {
|
|
3487
|
-
description = "Name of the API Gateway stage"
|
|
3488
|
-
type = string
|
|
3489
|
-
default = "prod"
|
|
3490
|
-
}
|
|
3491
|
-
|
|
3492
|
-
variable "stage_auto_deploy" {
|
|
3493
|
-
description = "Whether to automatically deploy the API stage"
|
|
3494
|
-
type = bool
|
|
3495
|
-
default = true
|
|
3496
|
-
}
|
|
3497
|
-
|
|
3498
|
-
# CORS Configuration
|
|
3499
|
-
|
|
3500
|
-
variable "cors_allow_credentials" {
|
|
3501
|
-
description = "Whether to allow credentials in CORS requests"
|
|
3502
|
-
type = bool
|
|
3503
|
-
default = false
|
|
3504
|
-
}
|
|
3505
|
-
|
|
3506
|
-
variable "cors_allow_headers" {
|
|
3507
|
-
description = "List of allowed headers for CORS"
|
|
3508
|
-
type = list(string)
|
|
3509
|
-
default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
|
|
3510
|
-
}
|
|
3511
|
-
|
|
3512
|
-
variable "cors_allow_methods" {
|
|
3513
|
-
description = "List of allowed HTTP methods for CORS"
|
|
3514
|
-
type = list(string)
|
|
3515
|
-
default = ["*"]
|
|
3516
|
-
}
|
|
3517
|
-
|
|
3518
|
-
variable "cors_allow_origins" {
|
|
3519
|
-
description = "List of allowed origins for CORS"
|
|
3520
|
-
type = list(string)
|
|
3521
|
-
default = ["*"]
|
|
3522
|
-
}
|
|
3523
|
-
|
|
3524
|
-
variable "cors_expose_headers" {
|
|
3525
|
-
description = "List of headers to expose in CORS responses"
|
|
3526
|
-
type = list(string)
|
|
3527
|
-
default = []
|
|
3528
|
-
}
|
|
3529
|
-
|
|
3530
|
-
variable "cors_max_age" {
|
|
3531
|
-
description = "Maximum age for CORS preflight requests in seconds"
|
|
3532
|
-
type = number
|
|
3533
|
-
default = 86400
|
|
3534
|
-
}
|
|
3535
|
-
|
|
3536
|
-
# Tags
|
|
3537
|
-
|
|
3538
|
-
variable "tags" {
|
|
3539
|
-
description = "Tags to apply to all resources"
|
|
3540
|
-
type = map(string)
|
|
3541
|
-
default = {}
|
|
3542
|
-
}
|
|
3543
|
-
|
|
3544
|
-
# Data sources
|
|
3545
|
-
data "aws_region" "current" {}
|
|
3546
|
-
data "aws_caller_identity" "current" {}
|
|
3547
|
-
|
|
3548
|
-
# Resources
|
|
3549
|
-
|
|
3550
|
-
# KMS key for CloudWatch log group encryption
|
|
3551
|
-
resource "aws_kms_key" "logs_key" {
|
|
3552
|
-
description = "KMS key for CloudWatch log group encryption"
|
|
3553
|
-
deletion_window_in_days = 7
|
|
3554
|
-
enable_key_rotation = true
|
|
3555
|
-
|
|
3556
|
-
policy = jsonencode({
|
|
3557
|
-
Version = "2012-10-17"
|
|
3558
|
-
Statement = [
|
|
3559
|
-
{
|
|
3560
|
-
Sid = "Enable IAM User Permissions"
|
|
3561
|
-
Effect = "Allow"
|
|
3562
|
-
Principal = {
|
|
3563
|
-
AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
|
|
3564
|
-
}
|
|
3565
|
-
Action = "kms:*"
|
|
3566
|
-
Resource = "*"
|
|
3567
|
-
},
|
|
3568
|
-
{
|
|
3569
|
-
Sid = "Allow CloudWatch Logs"
|
|
3570
|
-
Effect = "Allow"
|
|
3571
|
-
Principal = {
|
|
3572
|
-
Service = "logs.\${data.aws_region.current.region}.amazonaws.com"
|
|
3573
|
-
}
|
|
3574
|
-
Action = [
|
|
3575
|
-
"kms:Encrypt",
|
|
3576
|
-
"kms:Decrypt",
|
|
3577
|
-
"kms:ReEncrypt*",
|
|
3578
|
-
"kms:GenerateDataKey*",
|
|
3579
|
-
"kms:DescribeKey"
|
|
3580
|
-
]
|
|
3581
|
-
Resource = "*"
|
|
3582
|
-
Condition = {
|
|
3583
|
-
ArnEquals = {
|
|
3584
|
-
"kms:EncryptionContext:aws:logs:arn" = "arn:aws:logs:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:log-group:/aws/apigateway/\${var.api_name}"
|
|
3585
|
-
}
|
|
3586
|
-
}
|
|
3587
|
-
}
|
|
3588
|
-
]
|
|
3589
|
-
})
|
|
3590
|
-
|
|
3591
|
-
tags = var.tags
|
|
3592
|
-
}
|
|
3593
|
-
|
|
3594
|
-
resource "aws_kms_alias" "logs_key_alias" {
|
|
3595
|
-
name = "alias/\${var.api_name}-api-logs-encryption"
|
|
3596
|
-
target_key_id = aws_kms_key.logs_key.key_id
|
|
3597
|
-
}
|
|
3598
|
-
|
|
3599
|
-
# HTTP API Gateway
|
|
3600
|
-
resource "aws_apigatewayv2_api" "http_api" {
|
|
3601
|
-
name = var.api_name
|
|
3602
|
-
protocol_type = "HTTP"
|
|
3603
|
-
description = var.api_description
|
|
3604
|
-
|
|
3605
|
-
cors_configuration {
|
|
3606
|
-
allow_credentials = var.cors_allow_credentials
|
|
3607
|
-
allow_headers = var.cors_allow_headers
|
|
3608
|
-
allow_methods = var.cors_allow_methods
|
|
3609
|
-
allow_origins = var.cors_allow_origins
|
|
3610
|
-
expose_headers = var.cors_expose_headers
|
|
3611
|
-
max_age = var.cors_max_age
|
|
3612
|
-
}
|
|
3613
|
-
|
|
3614
|
-
tags = var.tags
|
|
3615
|
-
}
|
|
3616
|
-
|
|
3617
|
-
# API Gateway stage
|
|
3618
|
-
resource "aws_apigatewayv2_stage" "api_stage" {
|
|
3619
|
-
api_id = aws_apigatewayv2_api.http_api.id
|
|
3620
|
-
name = var.stage_name
|
|
3621
|
-
auto_deploy = var.stage_auto_deploy
|
|
3622
|
-
|
|
3623
|
-
access_log_settings {
|
|
3624
|
-
destination_arn = aws_cloudwatch_log_group.api_logs.arn
|
|
3625
|
-
format = jsonencode({
|
|
3626
|
-
requestId = "$context.requestId"
|
|
3627
|
-
ip = "$context.identity.sourceIp"
|
|
3628
|
-
requestTime = "$context.requestTime"
|
|
3629
|
-
httpMethod = "$context.httpMethod"
|
|
3630
|
-
routeKey = "$context.routeKey"
|
|
3631
|
-
status = "$context.status"
|
|
3632
|
-
protocol = "$context.protocol"
|
|
3633
|
-
responseLength = "$context.responseLength"
|
|
3634
|
-
error = "$context.error.message"
|
|
3635
|
-
integrationError = "$context.integrationErrorMessage"
|
|
3636
|
-
})
|
|
3637
|
-
}
|
|
3638
|
-
|
|
3639
|
-
default_route_settings {
|
|
3640
|
-
throttling_burst_limit = 5000
|
|
3641
|
-
throttling_rate_limit = 10000
|
|
3642
|
-
}
|
|
3643
|
-
|
|
3644
|
-
tags = var.tags
|
|
3645
|
-
|
|
3646
|
-
depends_on = [aws_cloudwatch_log_group.api_logs]
|
|
3647
|
-
}
|
|
3648
|
-
|
|
3649
|
-
# CloudWatch Log Group for API Gateway
|
|
3650
|
-
resource "aws_cloudwatch_log_group" "api_logs" {
|
|
3651
|
-
#checkov:skip=CKV_AWS_338:Log retention set to forever
|
|
3652
|
-
#checkov:skip=CKV_AWS_66:Log retention set to forever
|
|
3653
|
-
name = "/aws/apigateway/\${var.api_name}"
|
|
3654
|
-
kms_key_id = aws_kms_key.logs_key.arn
|
|
3655
|
-
tags = var.tags
|
|
3656
|
-
}
|
|
3657
|
-
|
|
3658
|
-
# Outputs
|
|
3659
|
-
|
|
3660
|
-
output "api_id" {
|
|
3661
|
-
description = "ID of the HTTP API Gateway"
|
|
3662
|
-
value = aws_apigatewayv2_api.http_api.id
|
|
3663
|
-
}
|
|
3664
|
-
|
|
3665
|
-
output "api_arn" {
|
|
3666
|
-
description = "ARN of the HTTP API Gateway"
|
|
3667
|
-
value = aws_apigatewayv2_api.http_api.arn
|
|
3668
|
-
}
|
|
3669
|
-
|
|
3670
|
-
output "api_endpoint" {
|
|
3671
|
-
description = "Base URL of the HTTP API Gateway"
|
|
3672
|
-
value = aws_apigatewayv2_api.http_api.api_endpoint
|
|
3673
|
-
}
|
|
3674
|
-
|
|
3675
|
-
output "api_execution_arn" {
|
|
3676
|
-
description = "Execution ARN of the HTTP API Gateway"
|
|
3677
|
-
value = aws_apigatewayv2_api.http_api.execution_arn
|
|
3678
|
-
}
|
|
3679
|
-
|
|
3680
|
-
output "stage_id" {
|
|
3681
|
-
description = "ID of the API Gateway stage"
|
|
3682
|
-
value = aws_apigatewayv2_stage.api_stage.id
|
|
3683
|
-
}
|
|
3684
|
-
|
|
3685
|
-
output "stage_arn" {
|
|
3686
|
-
description = "ARN of the API Gateway stage"
|
|
3687
|
-
value = aws_apigatewayv2_stage.api_stage.arn
|
|
3688
|
-
}
|
|
3689
|
-
|
|
3690
|
-
output "stage_execution_arn" {
|
|
3691
|
-
description = "Execution ARN of the API Gateway stage"
|
|
3692
|
-
value = aws_apigatewayv2_stage.api_stage.execution_arn
|
|
3693
|
-
}
|
|
3694
|
-
|
|
3695
|
-
output "stage_invoke_url" {
|
|
3696
|
-
description = "Invoke URL of the API Gateway stage"
|
|
3697
|
-
value = aws_apigatewayv2_stage.api_stage.invoke_url
|
|
3698
|
-
}
|
|
3699
|
-
|
|
3700
|
-
output "api_log_group_name" {
|
|
3701
|
-
description = "Name of the API Gateway CloudWatch log group"
|
|
3702
|
-
value = aws_cloudwatch_log_group.api_logs.name
|
|
3703
|
-
}
|
|
3704
|
-
|
|
3705
|
-
output "api_log_group_arn" {
|
|
3706
|
-
description = "ARN of the API Gateway CloudWatch log group"
|
|
3707
|
-
value = aws_cloudwatch_log_group.api_logs.arn
|
|
3708
|
-
}",
|
|
3709
|
-
"test-api.tf": "terraform {
|
|
3710
|
-
required_version = ">= 1.0"
|
|
3711
|
-
|
|
3712
|
-
required_providers {
|
|
3713
|
-
aws = {
|
|
3714
|
-
source = "hashicorp/aws"
|
|
3715
|
-
version = "~> 6.0"
|
|
3716
|
-
}
|
|
3717
|
-
}
|
|
3718
|
-
}
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
variable "env" {
|
|
3722
|
-
description = "Environment variables for the Lambda function"
|
|
3723
|
-
type = map(string)
|
|
3724
|
-
default = {}
|
|
3725
|
-
}
|
|
3726
|
-
|
|
3727
|
-
variable "additional_iam_policy_statements" {
|
|
3728
|
-
description = "Additional IAM policy statements for the Lambda function"
|
|
3729
|
-
type = list(object({
|
|
3730
|
-
Effect = string
|
|
3731
|
-
Action = list(string)
|
|
3732
|
-
Resource = list(string)
|
|
3733
|
-
}))
|
|
3734
|
-
default = []
|
|
3735
|
-
}
|
|
3736
|
-
|
|
3737
|
-
variable "asset_bucket_name" {
|
|
3738
|
-
description = "Name of the shared asset S3 bucket used to stage the Lambda deployment zip. Instantiate the \`core/asset-bucket\` module once per deployment and pass its \`bucket_name\` output here."
|
|
3739
|
-
type = string
|
|
3740
|
-
}
|
|
3741
|
-
|
|
3742
|
-
# CORS Configuration (passed to core module)
|
|
3743
|
-
variable "cors_allow_credentials" {
|
|
3744
|
-
description = "Whether to allow credentials in CORS requests"
|
|
3745
|
-
type = bool
|
|
3746
|
-
default = false
|
|
3747
|
-
}
|
|
3748
|
-
|
|
3749
|
-
variable "cors_allow_headers" {
|
|
3750
|
-
description = "List of allowed headers for CORS"
|
|
3751
|
-
type = list(string)
|
|
3752
|
-
default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
|
|
3753
|
-
}
|
|
3754
|
-
|
|
3755
|
-
variable "cors_allow_methods" {
|
|
3756
|
-
description = "List of allowed HTTP methods for CORS"
|
|
3757
|
-
type = list(string)
|
|
3758
|
-
default = ["*"]
|
|
3759
|
-
}
|
|
3760
|
-
|
|
3761
|
-
variable "cors_allow_origins" {
|
|
3762
|
-
description = "List of allowed origins for CORS"
|
|
3763
|
-
type = list(string)
|
|
3764
|
-
default = ["*"]
|
|
3765
|
-
}
|
|
3766
|
-
|
|
3767
|
-
variable "cors_expose_headers" {
|
|
3768
|
-
description = "List of headers to expose in CORS responses"
|
|
3769
|
-
type = list(string)
|
|
3770
|
-
default = []
|
|
3771
|
-
}
|
|
3772
|
-
|
|
3773
|
-
variable "cors_max_age" {
|
|
3774
|
-
description = "Maximum age for CORS preflight requests in seconds"
|
|
3775
|
-
type = number
|
|
3776
|
-
default = 0
|
|
3777
|
-
}
|
|
3778
|
-
|
|
3779
|
-
# Tags
|
|
3780
|
-
variable "tags" {
|
|
3781
|
-
description = "Tags to apply to all resources"
|
|
3782
|
-
type = map(string)
|
|
3783
|
-
default = {}
|
|
3784
|
-
}
|
|
3785
|
-
|
|
3786
|
-
# Get current AWS region and account ID
|
|
3787
|
-
data "aws_region" "current" {}
|
|
3788
|
-
data "aws_caller_identity" "current" {}
|
|
3789
|
-
|
|
3790
|
-
resource "random_string" "suffix" {
|
|
3791
|
-
length = 8
|
|
3792
|
-
special = false
|
|
3793
|
-
upper = false
|
|
3794
|
-
}
|
|
3795
|
-
|
|
3796
|
-
# Resources
|
|
3797
|
-
|
|
3798
|
-
# Create Lambda ZIP file from the bundle directory
|
|
3799
|
-
data "archive_file" "lambda_zip" {
|
|
3800
|
-
type = "zip"
|
|
3801
|
-
source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
|
|
3802
|
-
output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
|
|
3803
|
-
}
|
|
3804
|
-
|
|
3805
|
-
resource "aws_s3_object" "lambda_zip" {
|
|
3806
|
-
bucket = var.asset_bucket_name
|
|
3807
|
-
key = "apis/test-api/\${data.archive_file.lambda_zip.output_sha256}.zip"
|
|
3808
|
-
source = data.archive_file.lambda_zip.output_path
|
|
3809
|
-
source_hash = data.archive_file.lambda_zip.output_base64sha256
|
|
3810
|
-
etag = data.archive_file.lambda_zip.output_md5
|
|
3811
|
-
}
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
# Use the core HTTP API module
|
|
3815
|
-
module "http_api" {
|
|
3816
|
-
source = "../../../core/api/http-api"
|
|
3817
|
-
|
|
3818
|
-
api_name = "TestApi-\${random_string.suffix.result}"
|
|
3819
|
-
api_description = "TestApi HTTP API"
|
|
3820
|
-
stage_name = "$default"
|
|
3821
|
-
stage_auto_deploy = true
|
|
3822
|
-
|
|
3823
|
-
# CORS Configuration
|
|
3824
|
-
cors_allow_credentials = var.cors_allow_credentials
|
|
3825
|
-
cors_allow_headers = var.cors_allow_headers
|
|
3826
|
-
cors_allow_methods = var.cors_allow_methods
|
|
3827
|
-
cors_allow_origins = var.cors_allow_origins
|
|
3828
|
-
cors_expose_headers = var.cors_expose_headers
|
|
3829
|
-
cors_max_age = var.cors_max_age
|
|
3830
|
-
|
|
3831
|
-
# Tags
|
|
3832
|
-
tags = var.tags
|
|
3833
|
-
}
|
|
3834
|
-
|
|
3835
|
-
# Lambda function
|
|
3836
|
-
# This configures a single "router" lambda to serve all requests
|
|
3837
|
-
resource "aws_lambda_function" "api_lambda" {
|
|
3838
|
-
#checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
|
|
3839
|
-
#checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
|
|
3840
|
-
#checkov:skip=CKV_AWS_272:Code signing not required for this use case
|
|
3841
|
-
#checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
|
|
3842
|
-
#checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
|
|
3843
|
-
s3_bucket = aws_s3_object.lambda_zip.bucket
|
|
3844
|
-
s3_key = aws_s3_object.lambda_zip.key
|
|
3845
|
-
s3_object_version = aws_s3_object.lambda_zip.version_id
|
|
3846
|
-
function_name = "TestApiHandler-\${random_string.suffix.result}"
|
|
3847
|
-
role = aws_iam_role.lambda_execution_role.arn
|
|
3848
|
-
handler = "run.sh"
|
|
3849
|
-
runtime = "python3.14"
|
|
3850
|
-
layers = ["arn:aws:lambda:\${data.aws_region.current.region}:753240598075:layer:LambdaAdapterLayerX86:24"]
|
|
3851
|
-
timeout = 30
|
|
3852
|
-
memory_size = 128
|
|
3853
|
-
publish = true
|
|
3854
|
-
|
|
3855
|
-
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
|
|
3856
|
-
|
|
3857
|
-
# Enable X-Ray tracing
|
|
3858
|
-
tracing_config {
|
|
3859
|
-
mode = "Active"
|
|
3860
|
-
}
|
|
3861
|
-
|
|
3862
|
-
# Enable SnapStart for faster cold starts
|
|
3863
|
-
snap_start {
|
|
3864
|
-
apply_on = "PublishedVersions"
|
|
3865
|
-
}
|
|
3866
|
-
|
|
3867
|
-
environment {
|
|
3868
|
-
variables = merge({
|
|
3869
|
-
PORT = "8000"
|
|
3870
|
-
AWS_LWA_INVOKE_MODE = "buffered"
|
|
3871
|
-
AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
|
|
3872
|
-
}, var.env)
|
|
3873
|
-
}
|
|
3874
|
-
|
|
3875
|
-
tags = var.tags
|
|
3876
|
-
}
|
|
3877
|
-
|
|
3878
|
-
# IAM role for Lambda execution
|
|
3879
|
-
resource "aws_iam_role" "lambda_execution_role" {
|
|
3880
|
-
name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
|
|
3881
|
-
|
|
3882
|
-
assume_role_policy = jsonencode({
|
|
3883
|
-
Version = "2012-10-17"
|
|
3884
|
-
Statement = [
|
|
3885
|
-
{
|
|
3886
|
-
Action = "sts:AssumeRole"
|
|
3887
|
-
Effect = "Allow"
|
|
3888
|
-
Principal = {
|
|
3889
|
-
Service = "lambda.amazonaws.com"
|
|
3890
|
-
}
|
|
3891
|
-
}
|
|
3892
|
-
]
|
|
3893
|
-
})
|
|
3894
|
-
|
|
3895
|
-
tags = var.tags
|
|
3896
|
-
}
|
|
3897
|
-
|
|
3898
|
-
# Attach basic execution policy to Lambda role
|
|
3899
|
-
resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
|
|
3900
|
-
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
3901
|
-
role = aws_iam_role.lambda_execution_role.name
|
|
3902
|
-
}
|
|
3903
|
-
|
|
3904
|
-
# Attach X-Ray tracing policy to Lambda role
|
|
3905
|
-
resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
|
|
3906
|
-
policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
|
|
3907
|
-
role = aws_iam_role.lambda_execution_role.name
|
|
3908
|
-
}
|
|
3909
|
-
|
|
3910
|
-
# Additional IAM policies for Lambda (if provided)
|
|
3911
|
-
resource "aws_iam_role_policy" "lambda_additional_policies" {
|
|
3912
|
-
count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
|
|
3913
|
-
name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
|
|
3914
|
-
role = aws_iam_role.lambda_execution_role.id
|
|
3915
|
-
|
|
3916
|
-
policy = jsonencode({
|
|
3917
|
-
Version = "2012-10-17"
|
|
3918
|
-
Statement = var.additional_iam_policy_statements
|
|
3919
|
-
})
|
|
3920
|
-
}
|
|
3921
|
-
|
|
3922
|
-
# CloudWatch Log Group for Lambda
|
|
3923
|
-
resource "aws_cloudwatch_log_group" "lambda_logs" {
|
|
3924
|
-
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
3925
|
-
#checkov:skip=CKV_AWS_338:Log retention set to forever
|
|
3926
|
-
#checkov:skip=CKV_AWS_66:Log retention set to forever
|
|
3927
|
-
name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
|
|
3928
|
-
tags = var.tags
|
|
3929
|
-
}
|
|
3930
|
-
|
|
3931
|
-
# Lambda alias pointing to the latest published version for SnapStart
|
|
3932
|
-
resource "aws_lambda_alias" "live" {
|
|
3933
|
-
name = "live"
|
|
3934
|
-
function_name = aws_lambda_function.api_lambda.function_name
|
|
3935
|
-
function_version = aws_lambda_function.api_lambda.version
|
|
3936
|
-
|
|
3937
|
-
depends_on = [aws_lambda_function.api_lambda]
|
|
3938
|
-
}
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
# Lambda integration for HTTP API
|
|
3942
|
-
resource "aws_apigatewayv2_integration" "lambda_integration" {
|
|
3943
|
-
api_id = module.http_api.api_id
|
|
3944
|
-
integration_type = "AWS_PROXY"
|
|
3945
|
-
integration_uri = aws_lambda_alias.live.invoke_arn
|
|
3946
|
-
|
|
3947
|
-
payload_format_version = "2.0"
|
|
3948
|
-
timeout_milliseconds = 30000
|
|
3949
|
-
|
|
3950
|
-
depends_on = [aws_lambda_alias.live]
|
|
3951
|
-
}
|
|
3952
|
-
|
|
3953
|
-
# Route for proxy integration (catches all requests)
|
|
3954
|
-
resource "aws_apigatewayv2_route" "proxy_routes" {
|
|
3955
|
-
# NB: OPTIONS is omitted here since API Gateway manages responding to preflight requests
|
|
3956
|
-
# when cors settings are configured
|
|
3957
|
-
for_each = toset(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"])
|
|
3958
|
-
|
|
3959
|
-
api_id = module.http_api.api_id
|
|
3960
|
-
route_key = "\${each.key} /{proxy+}"
|
|
3961
|
-
target = "integrations/\${aws_apigatewayv2_integration.lambda_integration.id}"
|
|
3962
|
-
|
|
3963
|
-
authorization_type = "AWS_IAM"
|
|
3964
|
-
|
|
3965
|
-
depends_on = [aws_apigatewayv2_integration.lambda_integration]
|
|
3966
|
-
}
|
|
3967
|
-
|
|
3968
|
-
# Add API url to runtime config
|
|
3969
|
-
module "add_url_to_runtime_config" {
|
|
3970
|
-
source = "../../../core/runtime-config/entry"
|
|
3971
|
-
|
|
3972
|
-
namespace = "connection"
|
|
3973
|
-
key = "apis"
|
|
3974
|
-
value = { "TestApi" = module.http_api.stage_invoke_url }
|
|
3975
|
-
|
|
3976
|
-
depends_on = [module.http_api]
|
|
3977
|
-
}
|
|
3978
|
-
|
|
3979
|
-
# Lambda permission for API Gateway to invoke the function via alias
|
|
3980
|
-
resource "aws_lambda_permission" "api_gateway_invoke" {
|
|
3981
|
-
statement_id = "AllowExecutionFromAPIGateway"
|
|
3982
|
-
action = "lambda:InvokeFunction"
|
|
3983
|
-
function_name = aws_lambda_function.api_lambda.function_name
|
|
3984
|
-
qualifier = aws_lambda_alias.live.name
|
|
3985
|
-
principal = "apigateway.amazonaws.com"
|
|
3986
|
-
source_arn = "\${module.http_api.api_execution_arn}/*/*"
|
|
3987
|
-
|
|
3988
|
-
depends_on = [module.http_api, aws_lambda_alias.live]
|
|
3989
|
-
}
|
|
3990
|
-
|
|
3991
|
-
# Outputs
|
|
3992
|
-
|
|
3993
|
-
# API Gateway Outputs (from core module)
|
|
3994
|
-
output "api_id" {
|
|
3995
|
-
description = "ID of the HTTP API Gateway"
|
|
3996
|
-
value = module.http_api.api_id
|
|
3997
|
-
}
|
|
3998
|
-
|
|
3999
|
-
output "api_arn" {
|
|
4000
|
-
description = "ARN of the HTTP API Gateway"
|
|
4001
|
-
value = module.http_api.api_arn
|
|
4002
|
-
}
|
|
4003
|
-
|
|
4004
|
-
output "api_endpoint" {
|
|
4005
|
-
description = "Base URL of the HTTP API Gateway"
|
|
4006
|
-
value = module.http_api.api_endpoint
|
|
4007
|
-
}
|
|
4008
|
-
|
|
4009
|
-
output "api_execution_arn" {
|
|
4010
|
-
description = "Execution ARN of the HTTP API Gateway"
|
|
4011
|
-
value = module.http_api.api_execution_arn
|
|
4012
|
-
}
|
|
4013
|
-
|
|
4014
|
-
output "stage_invoke_url" {
|
|
4015
|
-
description = "Invoke URL of the API Gateway stage"
|
|
4016
|
-
value = module.http_api.stage_invoke_url
|
|
4017
|
-
}
|
|
4018
|
-
|
|
4019
|
-
output "stage_arn" {
|
|
4020
|
-
description = "ARN of the API Gateway stage"
|
|
4021
|
-
value = module.http_api.stage_arn
|
|
4022
|
-
}
|
|
4023
|
-
|
|
4024
|
-
output "stage_execution_arn" {
|
|
4025
|
-
description = "Execution ARN of the API Gateway stage"
|
|
4026
|
-
value = module.http_api.stage_execution_arn
|
|
4027
|
-
}
|
|
4028
|
-
|
|
4029
|
-
# Lambda Function Outputs
|
|
4030
|
-
output "lambda_function_name" {
|
|
4031
|
-
description = "Name of the Lambda function"
|
|
4032
|
-
value = aws_lambda_function.api_lambda.function_name
|
|
4033
|
-
}
|
|
4034
|
-
|
|
4035
|
-
output "lambda_function_arn" {
|
|
4036
|
-
description = "ARN of the Lambda function"
|
|
4037
|
-
value = aws_lambda_function.api_lambda.arn
|
|
4038
|
-
}
|
|
4039
|
-
|
|
4040
|
-
output "lambda_invoke_arn" {
|
|
4041
|
-
description = "Invoke ARN of the Lambda function"
|
|
4042
|
-
value = aws_lambda_function.api_lambda.invoke_arn
|
|
4043
|
-
}
|
|
4044
|
-
|
|
4045
|
-
output "lambda_qualified_arn" {
|
|
4046
|
-
description = "Qualified ARN of the Lambda function"
|
|
4047
|
-
value = aws_lambda_function.api_lambda.qualified_arn
|
|
4048
|
-
}
|
|
4049
|
-
|
|
4050
|
-
output "lambda_version" {
|
|
4051
|
-
description = "Version of the Lambda function"
|
|
4052
|
-
value = aws_lambda_function.api_lambda.version
|
|
4053
|
-
}
|
|
4054
|
-
|
|
4055
|
-
output "lambda_source_code_hash" {
|
|
4056
|
-
description = "Base64-encoded SHA256 hash of the Lambda deployment package"
|
|
4057
|
-
value = aws_lambda_function.api_lambda.source_code_hash
|
|
4058
|
-
}
|
|
4059
|
-
|
|
4060
|
-
output "lambda_source_code_size" {
|
|
4061
|
-
description = "Size of the Lambda deployment package in bytes"
|
|
4062
|
-
value = aws_lambda_function.api_lambda.source_code_size
|
|
4063
|
-
}
|
|
4064
|
-
|
|
4065
|
-
# IAM Role Outputs
|
|
4066
|
-
output "lambda_execution_role_arn" {
|
|
4067
|
-
description = "ARN of the Lambda execution role"
|
|
4068
|
-
value = aws_iam_role.lambda_execution_role.arn
|
|
4069
|
-
}
|
|
4070
|
-
|
|
4071
|
-
output "lambda_execution_role_name" {
|
|
4072
|
-
description = "Name of the Lambda execution role"
|
|
4073
|
-
value = aws_iam_role.lambda_execution_role.name
|
|
4074
|
-
}
|
|
4075
|
-
|
|
4076
|
-
# Integration Outputs
|
|
4077
|
-
output "integration_id" {
|
|
4078
|
-
description = "ID of the Lambda integration"
|
|
4079
|
-
value = aws_apigatewayv2_integration.lambda_integration.id
|
|
4080
|
-
}
|
|
4081
|
-
|
|
4082
|
-
# CloudWatch Log Groups
|
|
4083
|
-
output "lambda_log_group_name" {
|
|
4084
|
-
description = "Name of the Lambda CloudWatch log group"
|
|
4085
|
-
value = aws_cloudwatch_log_group.lambda_logs.name
|
|
4086
|
-
}
|
|
4087
|
-
|
|
4088
|
-
output "lambda_log_group_arn" {
|
|
4089
|
-
description = "ARN of the Lambda CloudWatch log group"
|
|
4090
|
-
value = aws_cloudwatch_log_group.lambda_logs.arn
|
|
4091
|
-
}
|
|
4092
|
-
|
|
4093
|
-
output "api_log_group_name" {
|
|
4094
|
-
description = "Name of the API Gateway CloudWatch log group"
|
|
4095
|
-
value = module.http_api.api_log_group_name
|
|
4096
|
-
}
|
|
4097
|
-
|
|
4098
|
-
output "api_log_group_arn" {
|
|
4099
|
-
description = "ARN of the API Gateway CloudWatch log group"
|
|
4100
|
-
value = module.http_api.api_log_group_arn
|
|
4101
|
-
}",
|
|
4102
|
-
}
|
|
4103
|
-
`;
|
|
4104
|
-
|
|
4105
|
-
exports[`fastapi project generator > terraform iac > should generate terraform files for REST API with Cognito auth and snapshot them > terraform-rest-cognito-files 1`] = `
|
|
4106
|
-
{
|
|
4107
|
-
"rest-api.tf": "# Core REST API Gateway module
|
|
4108
|
-
# This module creates the API Gateway REST API, deployment, stage, and logging resources
|
|
4109
|
-
|
|
4110
|
-
terraform {
|
|
4111
|
-
required_version = ">= 1.0"
|
|
4112
|
-
|
|
4113
|
-
required_providers {
|
|
4114
|
-
aws = {
|
|
4115
|
-
source = "hashicorp/aws"
|
|
4116
|
-
version = "~> 6.33"
|
|
4117
|
-
}
|
|
4118
|
-
random = {
|
|
4119
|
-
source = "hashicorp/random"
|
|
4120
|
-
version = "~> 3.1"
|
|
4121
|
-
}
|
|
4122
|
-
}
|
|
4123
|
-
}
|
|
4124
|
-
|
|
4125
|
-
# Core REST API Gateway Variables
|
|
4126
|
-
|
|
4127
|
-
variable "api_name" {
|
|
4128
|
-
description = "Name of the REST API Gateway"
|
|
4129
|
-
type = string
|
|
4130
|
-
}
|
|
4131
|
-
|
|
4132
|
-
variable "api_description" {
|
|
4133
|
-
description = "Description of the REST API Gateway"
|
|
4134
|
-
type = string
|
|
4135
|
-
default = "REST API Gateway"
|
|
4136
|
-
}
|
|
4137
|
-
|
|
4138
|
-
variable "stage_name" {
|
|
4139
|
-
description = "Name of the API Gateway stage"
|
|
4140
|
-
type = string
|
|
4141
|
-
default = "prod"
|
|
4142
|
-
}
|
|
4143
|
-
|
|
4144
|
-
variable "stage_auto_deploy" {
|
|
4145
|
-
description = "Whether to automatically deploy the API stage"
|
|
4146
|
-
type = bool
|
|
4147
|
-
default = true
|
|
4148
|
-
}
|
|
4149
|
-
|
|
4150
|
-
variable "enable_waf" {
|
|
4151
|
-
description = "Whether to enable AWS WAFv2 with the default managed ruleset (AWSManagedRulesCommonRuleSet and AWSManagedRulesKnownBadInputsRuleSet). The Web ACL is created here and associated with the stage in the consuming module."
|
|
4152
|
-
type = bool
|
|
4153
|
-
default = true
|
|
4154
|
-
}
|
|
4155
|
-
|
|
4156
|
-
# CORS Configuration
|
|
4157
|
-
|
|
4158
|
-
variable "cors_allow_headers" {
|
|
4159
|
-
description = "List of allowed headers for CORS"
|
|
4160
|
-
type = list(string)
|
|
4161
|
-
default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
|
|
4162
|
-
}
|
|
4163
|
-
|
|
4164
|
-
variable "cors_allow_methods" {
|
|
4165
|
-
description = "List of allowed HTTP methods for CORS"
|
|
4166
|
-
type = list(string)
|
|
4167
|
-
default = ["*"]
|
|
4168
|
-
}
|
|
4169
|
-
|
|
4170
|
-
variable "cors_allow_origins" {
|
|
4171
|
-
description = "List of allowed origins for CORS"
|
|
4172
|
-
type = list(string)
|
|
4173
|
-
default = ["*"]
|
|
4174
|
-
}
|
|
4175
|
-
|
|
4176
|
-
# Tags
|
|
4177
|
-
|
|
4178
|
-
variable "tags" {
|
|
4179
|
-
description = "Tags to apply to all resources"
|
|
4180
|
-
type = map(string)
|
|
4181
|
-
default = {}
|
|
4182
|
-
}
|
|
4183
|
-
|
|
4184
|
-
# Data sources
|
|
4185
|
-
data "aws_region" "current" {}
|
|
4186
|
-
data "aws_caller_identity" "current" {}
|
|
4187
|
-
|
|
4188
|
-
# Resources
|
|
4189
|
-
|
|
4190
|
-
# Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
|
|
4191
|
-
|
|
4192
|
-
# REST API Gateway
|
|
4193
|
-
resource "aws_api_gateway_rest_api" "rest_api" {
|
|
4194
|
-
name = var.api_name
|
|
4195
|
-
description = var.api_description
|
|
4196
|
-
|
|
4197
|
-
endpoint_configuration {
|
|
4198
|
-
types = ["REGIONAL"]
|
|
4199
|
-
}
|
|
4200
|
-
|
|
4201
|
-
lifecycle {
|
|
4202
|
-
create_before_destroy = true
|
|
4203
|
-
}
|
|
4204
|
-
|
|
4205
|
-
tags = var.tags
|
|
4206
|
-
}
|
|
4207
|
-
|
|
4208
|
-
# Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
|
|
4209
|
-
# after all methods and integrations are defined
|
|
4210
|
-
|
|
4211
|
-
# Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
|
|
4212
|
-
|
|
4213
|
-
# Gateway Response for CORS (4XX errors)
|
|
4214
|
-
resource "aws_api_gateway_gateway_response" "cors_4xx" {
|
|
4215
|
-
rest_api_id = aws_api_gateway_rest_api.rest_api.id
|
|
4216
|
-
response_type = "DEFAULT_4XX"
|
|
4217
|
-
|
|
4218
|
-
response_parameters = {
|
|
4219
|
-
"gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
|
|
4220
|
-
"gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
|
|
4221
|
-
"gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
|
|
4222
|
-
}
|
|
4223
|
-
}
|
|
4224
|
-
|
|
4225
|
-
# Gateway Response for CORS (5XX errors)
|
|
4226
|
-
resource "aws_api_gateway_gateway_response" "cors_5xx" {
|
|
4227
|
-
rest_api_id = aws_api_gateway_rest_api.rest_api.id
|
|
4228
|
-
response_type = "DEFAULT_5XX"
|
|
4229
|
-
|
|
4230
|
-
response_parameters = {
|
|
4231
|
-
"gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
|
|
4232
|
-
"gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
|
|
4233
|
-
"gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
|
|
4234
|
-
}
|
|
4235
|
-
}
|
|
4236
|
-
|
|
4237
|
-
resource "aws_wafv2_web_acl" "api_waf" {
|
|
4238
|
-
#checkov:skip=CKV2_AWS_31:Logging configuration is defined below in aws_wafv2_web_acl_logging_configuration.api_waf_logging; Checkov does not resolve the separate resource
|
|
4239
|
-
count = var.enable_waf ? 1 : 0
|
|
4240
|
-
|
|
4241
|
-
name = "\${var.api_name}-waf"
|
|
4242
|
-
scope = "REGIONAL"
|
|
4243
|
-
|
|
4244
|
-
default_action {
|
|
4245
|
-
allow {}
|
|
4246
|
-
}
|
|
4247
|
-
|
|
4248
|
-
rule {
|
|
4249
|
-
name = "CRSRule"
|
|
4250
|
-
priority = 0
|
|
4251
|
-
|
|
4252
|
-
override_action {
|
|
4253
|
-
none {}
|
|
4254
|
-
}
|
|
4255
|
-
|
|
4256
|
-
statement {
|
|
4257
|
-
managed_rule_group_statement {
|
|
4258
|
-
name = "AWSManagedRulesCommonRuleSet"
|
|
4259
|
-
vendor_name = "AWS"
|
|
4260
|
-
|
|
4261
|
-
# Count instead of Block: the CRS 8 KB body limit is too restrictive for most APIs.
|
|
4262
|
-
rule_action_override {
|
|
4263
|
-
name = "SizeRestrictions_BODY"
|
|
4264
|
-
action_to_use {
|
|
4265
|
-
count {}
|
|
4266
|
-
}
|
|
4267
|
-
}
|
|
4268
|
-
}
|
|
4269
|
-
}
|
|
4270
|
-
|
|
4271
|
-
visibility_config {
|
|
4272
|
-
cloudwatch_metrics_enabled = true
|
|
4273
|
-
metric_name = "\${var.api_name}WebAcl-CRS"
|
|
4274
|
-
sampled_requests_enabled = true
|
|
4275
|
-
}
|
|
4276
|
-
}
|
|
4277
|
-
|
|
4278
|
-
rule {
|
|
4279
|
-
name = "KnownBadInputsRule"
|
|
4280
|
-
priority = 1
|
|
4281
|
-
|
|
4282
|
-
override_action {
|
|
4283
|
-
none {}
|
|
4284
|
-
}
|
|
4285
|
-
|
|
4286
|
-
statement {
|
|
4287
|
-
managed_rule_group_statement {
|
|
4288
|
-
name = "AWSManagedRulesKnownBadInputsRuleSet"
|
|
4289
|
-
vendor_name = "AWS"
|
|
4290
|
-
}
|
|
4291
|
-
}
|
|
4292
|
-
|
|
4293
|
-
visibility_config {
|
|
4294
|
-
cloudwatch_metrics_enabled = true
|
|
4295
|
-
metric_name = "\${var.api_name}WebAcl-KnownBadInputs"
|
|
4296
|
-
sampled_requests_enabled = true
|
|
4297
|
-
}
|
|
4298
|
-
}
|
|
4299
|
-
|
|
4300
|
-
visibility_config {
|
|
4301
|
-
cloudwatch_metrics_enabled = true
|
|
4302
|
-
metric_name = "\${var.api_name}WebAcl"
|
|
4303
|
-
sampled_requests_enabled = true
|
|
4304
|
-
}
|
|
4305
|
-
|
|
4306
|
-
tags = var.tags
|
|
4307
|
-
|
|
4308
|
-
lifecycle {
|
|
4309
|
-
create_before_destroy = true
|
|
4310
|
-
}
|
|
4311
|
-
}
|
|
4312
|
-
|
|
4313
|
-
# CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
|
|
4314
|
-
resource "aws_cloudwatch_log_group" "api_waf_logs" {
|
|
4315
|
-
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
4316
|
-
#checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
|
|
4317
|
-
count = var.enable_waf ? 1 : 0
|
|
4318
|
-
|
|
4319
|
-
name = "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}"
|
|
4320
|
-
retention_in_days = 30
|
|
4321
|
-
tags = var.tags
|
|
4322
|
-
}
|
|
4323
|
-
|
|
4324
|
-
resource "random_id" "waf_log_suffix" {
|
|
4325
|
-
count = var.enable_waf ? 1 : 0
|
|
4326
|
-
byte_length = 4
|
|
4327
|
-
}
|
|
4328
|
-
|
|
4329
|
-
resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
|
|
4330
|
-
count = var.enable_waf ? 1 : 0
|
|
4331
|
-
|
|
4332
|
-
log_destination_configs = [aws_cloudwatch_log_group.api_waf_logs[0].arn]
|
|
4333
|
-
resource_arn = aws_wafv2_web_acl.api_waf[0].arn
|
|
4334
|
-
}
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
# Outputs
|
|
4338
|
-
|
|
4339
|
-
output "api_id" {
|
|
4340
|
-
description = "ID of the REST API Gateway"
|
|
4341
|
-
value = aws_api_gateway_rest_api.rest_api.id
|
|
4342
|
-
}
|
|
4343
|
-
|
|
4344
|
-
output "api_arn" {
|
|
4345
|
-
description = "ARN of the REST API Gateway"
|
|
4346
|
-
value = aws_api_gateway_rest_api.rest_api.arn
|
|
4347
|
-
}
|
|
4348
|
-
|
|
4349
|
-
output "api_endpoint" {
|
|
4350
|
-
description = "Base URL of the REST API Gateway"
|
|
4351
|
-
value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
|
|
4352
|
-
}
|
|
4353
|
-
|
|
4354
|
-
output "api_execution_arn" {
|
|
4355
|
-
description = "Execution ARN of the REST API Gateway"
|
|
4356
|
-
value = aws_api_gateway_rest_api.rest_api.execution_arn
|
|
4357
|
-
}
|
|
4358
|
-
|
|
4359
|
-
output "api_root_resource_id" {
|
|
4360
|
-
description = "Root resource ID of the REST API Gateway"
|
|
4361
|
-
value = aws_api_gateway_rest_api.rest_api.root_resource_id
|
|
4362
|
-
}
|
|
4363
|
-
|
|
4364
|
-
output "waf_web_acl_arn" {
|
|
4365
|
-
description = "ARN of the WAFv2 Web ACL associated with the API, or null if WAF is disabled"
|
|
4366
|
-
value = var.enable_waf ? aws_wafv2_web_acl.api_waf[0].arn : null
|
|
4367
|
-
}
|
|
4368
|
-
|
|
4369
|
-
# Note: Stage and deployment outputs are provided by the consuming module
|
|
4370
|
-
|
|
4371
|
-
# Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
|
|
4372
|
-
"test-api.tf": "terraform {
|
|
4373
|
-
required_version = ">= 1.0"
|
|
4374
|
-
|
|
4375
|
-
required_providers {
|
|
4376
|
-
aws = {
|
|
4377
|
-
source = "hashicorp/aws"
|
|
4378
|
-
version = "~> 6.33"
|
|
4379
|
-
}
|
|
4380
|
-
}
|
|
4381
|
-
}
|
|
4382
|
-
|
|
4383
|
-
# Authentication Configuration
|
|
4384
|
-
variable "user_pool_id" {
|
|
4385
|
-
description = "Cognito User Pool ID for authentication"
|
|
4386
|
-
type = string
|
|
4387
|
-
}
|
|
4388
|
-
|
|
4389
|
-
variable "user_pool_client_ids" {
|
|
4390
|
-
description = "List of Cognito User Pool Client IDs"
|
|
4391
|
-
type = list(string)
|
|
4392
|
-
}
|
|
4393
|
-
|
|
4394
|
-
variable "env" {
|
|
4395
|
-
description = "Environment variables for the Lambda function"
|
|
4396
|
-
type = map(string)
|
|
4397
|
-
default = {}
|
|
4398
|
-
}
|
|
4399
|
-
|
|
4400
|
-
variable "additional_iam_policy_statements" {
|
|
4401
|
-
description = "Additional IAM policy statements for the Lambda function"
|
|
4402
|
-
type = list(object({
|
|
4403
|
-
Effect = string
|
|
4404
|
-
Action = list(string)
|
|
4405
|
-
Resource = list(string)
|
|
4406
|
-
}))
|
|
4407
|
-
default = []
|
|
4408
|
-
}
|
|
4409
|
-
|
|
4410
|
-
variable "asset_bucket_name" {
|
|
4411
|
-
description = "Name of the shared asset S3 bucket used to stage the Lambda deployment zip. Instantiate the \`core/asset-bucket\` module once per deployment and pass its \`bucket_name\` output here."
|
|
4412
|
-
type = string
|
|
4413
|
-
}
|
|
4414
|
-
|
|
4415
|
-
# CORS Configuration (passed to core module)
|
|
4416
|
-
|
|
4417
|
-
variable "cors_allow_headers" {
|
|
4418
|
-
description = "List of allowed headers for CORS"
|
|
4419
|
-
type = list(string)
|
|
4420
|
-
default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
|
|
4421
|
-
}
|
|
4422
|
-
|
|
4423
|
-
variable "cors_allow_methods" {
|
|
4424
|
-
description = "List of allowed HTTP methods for CORS"
|
|
4425
|
-
type = list(string)
|
|
4426
|
-
default = ["*"]
|
|
4427
|
-
}
|
|
4428
|
-
|
|
4429
|
-
variable "cors_allow_origins" {
|
|
4430
|
-
description = "List of allowed origins for CORS"
|
|
4431
|
-
type = list(string)
|
|
4432
|
-
default = ["*"]
|
|
4433
|
-
}
|
|
4434
|
-
|
|
4435
|
-
# WAF Configuration
|
|
4436
|
-
variable "enable_waf" {
|
|
4437
|
-
description = "Whether to enable AWS WAFv2 with the default managed ruleset on the API stage"
|
|
4438
|
-
type = bool
|
|
4439
|
-
default = true
|
|
4440
|
-
}
|
|
4441
|
-
|
|
4442
|
-
# Tags
|
|
4443
|
-
variable "tags" {
|
|
4444
|
-
description = "Tags to apply to all resources"
|
|
4445
|
-
type = map(string)
|
|
4446
|
-
default = {}
|
|
4447
|
-
}
|
|
4448
|
-
|
|
4449
|
-
# Get current AWS region and account ID
|
|
4450
|
-
data "aws_region" "current" {}
|
|
4451
|
-
data "aws_caller_identity" "current" {}
|
|
4452
|
-
|
|
4453
|
-
resource "random_string" "suffix" {
|
|
4454
|
-
length = 8
|
|
4455
|
-
special = false
|
|
4456
|
-
upper = false
|
|
4457
|
-
}
|
|
4458
|
-
|
|
4459
|
-
# Resources
|
|
4460
|
-
|
|
4461
|
-
# Create Lambda ZIP file from the bundle directory
|
|
4462
|
-
data "archive_file" "lambda_zip" {
|
|
4463
|
-
type = "zip"
|
|
4464
|
-
source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
|
|
4465
|
-
output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
|
|
4466
|
-
}
|
|
4467
|
-
|
|
4468
|
-
resource "aws_s3_object" "lambda_zip" {
|
|
4469
|
-
bucket = var.asset_bucket_name
|
|
4470
|
-
key = "apis/test-api/\${data.archive_file.lambda_zip.output_sha256}.zip"
|
|
4471
|
-
source = data.archive_file.lambda_zip.output_path
|
|
4472
|
-
source_hash = data.archive_file.lambda_zip.output_base64sha256
|
|
4473
|
-
etag = data.archive_file.lambda_zip.output_md5
|
|
4474
|
-
}
|
|
4475
|
-
|
|
4476
|
-
# Use the core REST API module
|
|
4477
|
-
module "rest_api" {
|
|
4478
|
-
source = "../../../core/api/rest-api"
|
|
4479
|
-
|
|
4480
|
-
api_name = "TestApi-\${random_string.suffix.result}"
|
|
4481
|
-
api_description = "TestApi REST API"
|
|
4482
|
-
stage_name = "prod"
|
|
4483
|
-
stage_auto_deploy = true
|
|
4484
|
-
|
|
4485
|
-
# WAF Configuration
|
|
4486
|
-
enable_waf = var.enable_waf
|
|
4487
|
-
|
|
4488
|
-
# CORS Configuration
|
|
4489
|
-
cors_allow_headers = var.cors_allow_headers
|
|
4490
|
-
cors_allow_methods = var.cors_allow_methods
|
|
4491
|
-
cors_allow_origins = var.cors_allow_origins
|
|
4492
|
-
|
|
4493
|
-
# Tags
|
|
4494
|
-
tags = var.tags
|
|
4495
|
-
}
|
|
4496
|
-
|
|
4497
|
-
resource "aws_wafv2_web_acl_association" "api_waf_association" {
|
|
4498
|
-
count = var.enable_waf ? 1 : 0
|
|
4499
|
-
|
|
4500
|
-
resource_arn = aws_api_gateway_stage.api_stage.arn
|
|
4501
|
-
web_acl_arn = module.rest_api.waf_web_acl_arn
|
|
4502
|
-
|
|
4503
|
-
depends_on = [aws_api_gateway_stage.api_stage]
|
|
4504
|
-
}
|
|
4505
|
-
|
|
4506
|
-
# Lambda function
|
|
4507
|
-
resource "aws_lambda_function" "api_lambda" {
|
|
4508
|
-
#checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
|
|
4509
|
-
#checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
|
|
4510
|
-
#checkov:skip=CKV_AWS_272:Code signing not required for this use case
|
|
4511
|
-
#checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
|
|
4512
|
-
#checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
|
|
4513
|
-
s3_bucket = aws_s3_object.lambda_zip.bucket
|
|
4514
|
-
s3_key = aws_s3_object.lambda_zip.key
|
|
4515
|
-
s3_object_version = aws_s3_object.lambda_zip.version_id
|
|
4516
|
-
function_name = "TestApiHandler-\${random_string.suffix.result}"
|
|
4517
|
-
role = aws_iam_role.lambda_execution_role.arn
|
|
4518
|
-
handler = "run.sh"
|
|
4519
|
-
runtime = "python3.14"
|
|
4520
|
-
layers = ["arn:aws:lambda:\${data.aws_region.current.region}:753240598075:layer:LambdaAdapterLayerX86:24"]
|
|
4521
|
-
timeout = 30
|
|
4522
|
-
memory_size = 128
|
|
4523
|
-
publish = true
|
|
4524
|
-
|
|
4525
|
-
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
|
|
4526
|
-
|
|
4527
|
-
# Enable X-Ray tracing
|
|
4528
|
-
tracing_config {
|
|
4529
|
-
mode = "Active"
|
|
4530
|
-
}
|
|
4531
|
-
|
|
4532
|
-
# Enable SnapStart for faster cold starts
|
|
4533
|
-
snap_start {
|
|
4534
|
-
apply_on = "PublishedVersions"
|
|
4535
|
-
}
|
|
4536
|
-
|
|
4537
|
-
environment {
|
|
4538
|
-
variables = merge({
|
|
4539
|
-
PORT = "8000"
|
|
4540
|
-
AWS_LWA_INVOKE_MODE = "response_stream"
|
|
4541
|
-
AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
|
|
4542
|
-
}, var.env)
|
|
4543
|
-
}
|
|
4544
|
-
|
|
4545
|
-
tags = var.tags
|
|
4546
|
-
}
|
|
4547
|
-
|
|
4548
|
-
# IAM role for Lambda execution
|
|
4549
|
-
resource "aws_iam_role" "lambda_execution_role" {
|
|
4550
|
-
name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
|
|
4551
|
-
|
|
4552
|
-
assume_role_policy = jsonencode({
|
|
4553
|
-
Version = "2012-10-17"
|
|
4554
|
-
Statement = [
|
|
4555
|
-
{
|
|
4556
|
-
Action = "sts:AssumeRole"
|
|
4557
|
-
Effect = "Allow"
|
|
4558
|
-
Principal = {
|
|
4559
|
-
Service = "lambda.amazonaws.com"
|
|
4560
|
-
}
|
|
4561
|
-
}
|
|
4562
|
-
]
|
|
4563
|
-
})
|
|
4564
|
-
|
|
4565
|
-
tags = var.tags
|
|
4566
|
-
}
|
|
4567
|
-
|
|
4568
|
-
# Attach basic execution policy to Lambda role
|
|
4569
|
-
resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
|
|
4570
|
-
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
4571
|
-
role = aws_iam_role.lambda_execution_role.name
|
|
4572
|
-
}
|
|
4573
|
-
|
|
4574
|
-
# Attach X-Ray tracing policy to Lambda role
|
|
4575
|
-
resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
|
|
4576
|
-
policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
|
|
4577
|
-
role = aws_iam_role.lambda_execution_role.name
|
|
4578
|
-
}
|
|
4579
|
-
|
|
4580
|
-
# Additional IAM policies for Lambda (if provided)
|
|
4581
|
-
resource "aws_iam_role_policy" "lambda_additional_policies" {
|
|
4582
|
-
count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
|
|
4583
|
-
name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
|
|
4584
|
-
role = aws_iam_role.lambda_execution_role.id
|
|
4585
|
-
|
|
4586
|
-
policy = jsonencode({
|
|
4587
|
-
Version = "2012-10-17"
|
|
4588
|
-
Statement = var.additional_iam_policy_statements
|
|
4589
|
-
})
|
|
4590
|
-
}
|
|
4591
|
-
|
|
4592
|
-
# CloudWatch Log Group for Lambda
|
|
4593
|
-
resource "aws_cloudwatch_log_group" "lambda_logs" {
|
|
4594
|
-
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
4595
|
-
#checkov:skip=CKV_AWS_338:Log retention set to forever
|
|
4596
|
-
#checkov:skip=CKV_AWS_66:Log retention set to forever
|
|
4597
|
-
name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
|
|
4598
|
-
tags = var.tags
|
|
4599
|
-
}
|
|
4600
|
-
|
|
4601
|
-
# Lambda alias pointing to the latest published version for SnapStart
|
|
4602
|
-
resource "aws_lambda_alias" "live" {
|
|
4603
|
-
name = "live"
|
|
4604
|
-
function_name = aws_lambda_function.api_lambda.function_name
|
|
4605
|
-
function_version = aws_lambda_function.api_lambda.version
|
|
4606
|
-
|
|
4607
|
-
depends_on = [aws_lambda_function.api_lambda]
|
|
4608
|
-
}
|
|
4609
|
-
|
|
4610
|
-
# Cognito User Pool Authorizer
|
|
4611
|
-
resource "aws_api_gateway_authorizer" "cognito_authorizer" {
|
|
4612
|
-
name = "TestApiAuthorizer-\${random_string.suffix.result}"
|
|
4613
|
-
rest_api_id = module.rest_api.api_id
|
|
4614
|
-
type = "COGNITO_USER_POOLS"
|
|
4615
|
-
provider_arns = ["arn:aws:cognito-idp:\${data.aws_region.current.region}:\${data.aws_caller_identity.current.account_id}:userpool/\${var.user_pool_id}"]
|
|
4616
|
-
identity_source = "method.request.header.Authorization"
|
|
4617
|
-
}
|
|
4618
|
-
|
|
4619
|
-
# Create proxy resource (captures all paths)
|
|
4620
|
-
resource "aws_api_gateway_resource" "proxy_resource" {
|
|
4621
|
-
rest_api_id = module.rest_api.api_id
|
|
4622
|
-
parent_id = module.rest_api.api_root_resource_id
|
|
4623
|
-
path_part = "{proxy+}"
|
|
4624
|
-
}
|
|
4625
|
-
|
|
4626
|
-
# Lambda integration for REST API
|
|
4627
|
-
resource "aws_api_gateway_integration" "lambda_integration" {
|
|
4628
|
-
rest_api_id = module.rest_api.api_id
|
|
4629
|
-
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
4630
|
-
http_method = aws_api_gateway_method.proxy_method.http_method
|
|
4631
|
-
|
|
4632
|
-
integration_http_method = "POST"
|
|
4633
|
-
type = "AWS_PROXY"
|
|
4634
|
-
# Use the response streaming invocation path with the alias ARN for SnapStart
|
|
4635
|
-
uri = "arn:aws:apigateway:\${data.aws_region.current.region}:lambda:path/2021-11-15/functions/\${aws_lambda_alias.live.arn}/response-streaming-invocations"
|
|
4636
|
-
response_transfer_mode = "STREAM"
|
|
4637
|
-
|
|
4638
|
-
depends_on = [aws_lambda_alias.live]
|
|
4639
|
-
}
|
|
4640
|
-
|
|
4641
|
-
# Method for proxy integration
|
|
4642
|
-
resource "aws_api_gateway_method" "proxy_method" {
|
|
4643
|
-
#checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
|
|
4644
|
-
rest_api_id = module.rest_api.api_id
|
|
4645
|
-
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
4646
|
-
http_method = "ANY"
|
|
4647
|
-
|
|
4648
|
-
authorization = "COGNITO_USER_POOLS"
|
|
4649
|
-
authorizer_id = aws_api_gateway_authorizer.cognito_authorizer.id
|
|
4650
|
-
# Accept access tokens from both sign-in flows: 'openid' (Cognito hosted UI)
|
|
4651
|
-
# and 'aws.cognito.signin.user.admin' (Cognito admin/SRP auth APIs).
|
|
4652
|
-
authorization_scopes = ["openid", "aws.cognito.signin.user.admin"]
|
|
4653
|
-
|
|
4654
|
-
request_parameters = {
|
|
4655
|
-
"method.request.path.proxy" = true
|
|
4656
|
-
}
|
|
4657
|
-
|
|
4658
|
-
depends_on = [aws_api_gateway_authorizer.cognito_authorizer]
|
|
4659
|
-
}
|
|
4660
|
-
|
|
4661
|
-
# OPTIONS method for CORS preflight
|
|
4662
|
-
resource "aws_api_gateway_method" "options_method" {
|
|
4663
|
-
#checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
|
|
4664
|
-
#checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
|
|
4665
|
-
rest_api_id = module.rest_api.api_id
|
|
4666
|
-
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
4667
|
-
http_method = "OPTIONS"
|
|
4668
|
-
authorization = "NONE"
|
|
4669
|
-
}
|
|
4670
|
-
|
|
4671
|
-
# CORS integration for OPTIONS method
|
|
4672
|
-
resource "aws_api_gateway_integration" "options_integration" {
|
|
4673
|
-
rest_api_id = module.rest_api.api_id
|
|
4674
|
-
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
4675
|
-
http_method = aws_api_gateway_method.options_method.http_method
|
|
4676
|
-
|
|
4677
|
-
type = "MOCK"
|
|
4678
|
-
request_templates = {
|
|
4679
|
-
"application/json" = "{\\"statusCode\\": 204}"
|
|
4680
|
-
}
|
|
4681
|
-
}
|
|
4682
|
-
|
|
4683
|
-
# OPTIONS method response
|
|
4684
|
-
resource "aws_api_gateway_method_response" "options_response" {
|
|
4685
|
-
rest_api_id = module.rest_api.api_id
|
|
4686
|
-
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
4687
|
-
http_method = aws_api_gateway_method.options_method.http_method
|
|
4688
|
-
status_code = "204"
|
|
4689
|
-
|
|
4690
|
-
response_parameters = {
|
|
4691
|
-
"method.response.header.Access-Control-Allow-Headers" = true
|
|
4692
|
-
"method.response.header.Access-Control-Allow-Methods" = true
|
|
4693
|
-
"method.response.header.Access-Control-Allow-Origin" = true
|
|
4694
|
-
}
|
|
4695
|
-
}
|
|
4696
|
-
|
|
4697
|
-
# OPTIONS integration response
|
|
4698
|
-
resource "aws_api_gateway_integration_response" "options_integration_response" {
|
|
4699
|
-
rest_api_id = module.rest_api.api_id
|
|
4700
|
-
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
4701
|
-
http_method = aws_api_gateway_method.options_method.http_method
|
|
4702
|
-
status_code = aws_api_gateway_method_response.options_response.status_code
|
|
4703
|
-
|
|
4704
|
-
response_parameters = {
|
|
4705
|
-
"method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
|
|
4706
|
-
"method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
|
|
4707
|
-
"method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
|
|
4708
|
-
}
|
|
4709
|
-
}
|
|
4710
|
-
|
|
4711
|
-
# API Gateway deployment
|
|
4712
|
-
resource "aws_api_gateway_deployment" "api_deployment" {
|
|
4713
|
-
rest_api_id = module.rest_api.api_id
|
|
4714
|
-
|
|
4715
|
-
triggers = {
|
|
4716
|
-
redeployment = sha1(jsonencode([
|
|
4717
|
-
aws_api_gateway_resource.proxy_resource.id,
|
|
4718
|
-
aws_api_gateway_method.proxy_method.id,
|
|
4719
|
-
aws_api_gateway_integration.lambda_integration.id,
|
|
4720
|
-
aws_api_gateway_method.options_method.id,
|
|
4721
|
-
aws_api_gateway_integration.options_integration.id,
|
|
4722
|
-
]))
|
|
4723
|
-
}
|
|
4724
|
-
|
|
4725
|
-
lifecycle {
|
|
4726
|
-
create_before_destroy = true
|
|
4727
|
-
}
|
|
4728
|
-
|
|
4729
|
-
depends_on = [
|
|
4730
|
-
aws_api_gateway_method.proxy_method,
|
|
4731
|
-
aws_api_gateway_integration.lambda_integration,
|
|
4732
|
-
aws_api_gateway_method.options_method,
|
|
4733
|
-
aws_api_gateway_integration.options_integration,
|
|
4734
|
-
aws_api_gateway_method_response.options_response,
|
|
4735
|
-
aws_api_gateway_integration_response.options_integration_response,
|
|
4736
|
-
]
|
|
4737
|
-
}
|
|
4738
|
-
|
|
4739
|
-
# API Gateway stage
|
|
4740
|
-
resource "aws_api_gateway_stage" "api_stage" {
|
|
4741
|
-
#checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
|
|
4742
|
-
#checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
|
|
4743
|
-
#checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
|
|
4744
|
-
#checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
|
|
4745
|
-
#checkov:skip=CKV2_AWS_77:WAFv2 Web ACL is attached via aws_wafv2_web_acl_association when var.enable_waf is true (default) and includes AWSManagedRulesKnownBadInputsRuleSet for Log4j protection; Checkov does not track the association across modules
|
|
4746
|
-
deployment_id = aws_api_gateway_deployment.api_deployment.id
|
|
4747
|
-
rest_api_id = module.rest_api.api_id
|
|
4748
|
-
stage_name = "prod"
|
|
4749
|
-
xray_tracing_enabled = true
|
|
4750
|
-
|
|
4751
|
-
tags = var.tags
|
|
4752
|
-
|
|
4753
|
-
depends_on = [aws_api_gateway_deployment.api_deployment]
|
|
4754
|
-
}
|
|
4755
|
-
|
|
4756
|
-
# API Gateway Resource Policy
|
|
4757
|
-
resource "aws_api_gateway_rest_api_policy" "api_policy" {
|
|
4758
|
-
rest_api_id = module.rest_api.api_id
|
|
4759
|
-
|
|
4760
|
-
policy = jsonencode({
|
|
4761
|
-
Version = "2012-10-17"
|
|
4762
|
-
Statement = [
|
|
4763
|
-
{
|
|
4764
|
-
# Allow all callers to invoke the API in the resource policy, since auth is handled by Cognito
|
|
4765
|
-
Effect = "Allow"
|
|
4766
|
-
Principal = "*"
|
|
4767
|
-
Action = "execute-api:Invoke"
|
|
4768
|
-
Resource = "execute-api:/*"
|
|
4769
|
-
}
|
|
4770
|
-
]
|
|
4771
|
-
})
|
|
4772
|
-
}
|
|
4773
|
-
|
|
4774
|
-
# Lambda permission for API Gateway to invoke the function via alias
|
|
4775
|
-
resource "aws_lambda_permission" "api_gateway_invoke" {
|
|
4776
|
-
statement_id = "AllowExecutionFromAPIGateway"
|
|
4777
|
-
action = "lambda:InvokeFunction"
|
|
4778
|
-
function_name = aws_lambda_function.api_lambda.function_name
|
|
4779
|
-
qualifier = aws_lambda_alias.live.name
|
|
4780
|
-
principal = "apigateway.amazonaws.com"
|
|
4781
|
-
source_arn = "\${module.rest_api.api_execution_arn}/*/*"
|
|
4782
|
-
|
|
4783
|
-
depends_on = [module.rest_api, aws_lambda_alias.live]
|
|
4784
|
-
}
|
|
4785
|
-
|
|
4786
|
-
# Lambda permission for API Gateway to invoke with response streaming via alias
|
|
4787
|
-
resource "aws_lambda_permission" "api_gateway_invoke_streaming" {
|
|
4788
|
-
statement_id = "AllowStreamingFromAPIGateway"
|
|
4789
|
-
action = "lambda:InvokeFunctionWithResponseStream"
|
|
4790
|
-
function_name = aws_lambda_function.api_lambda.function_name
|
|
4791
|
-
qualifier = aws_lambda_alias.live.name
|
|
4792
|
-
principal = "apigateway.amazonaws.com"
|
|
4793
|
-
source_arn = "\${module.rest_api.api_execution_arn}/*/*"
|
|
4794
|
-
|
|
4795
|
-
depends_on = [module.rest_api, aws_lambda_alias.live]
|
|
4796
|
-
}
|
|
4797
|
-
|
|
4798
|
-
# Add API url to runtime config
|
|
4799
|
-
module "add_url_to_runtime_config" {
|
|
4800
|
-
source = "../../../core/runtime-config/entry"
|
|
4801
|
-
|
|
4802
|
-
namespace = "connection"
|
|
4803
|
-
key = "apis"
|
|
4804
|
-
value = { "TestApi" = aws_api_gateway_stage.api_stage.invoke_url }
|
|
4805
|
-
|
|
4806
|
-
depends_on = [aws_api_gateway_stage.api_stage]
|
|
4807
|
-
}
|
|
4808
|
-
|
|
4809
|
-
# Outputs
|
|
4810
|
-
|
|
4811
|
-
# API Gateway Outputs (from core module)
|
|
4812
|
-
output "api_id" {
|
|
4813
|
-
description = "ID of the REST API Gateway"
|
|
4814
|
-
value = module.rest_api.api_id
|
|
4815
|
-
}
|
|
4816
|
-
|
|
4817
|
-
output "api_arn" {
|
|
4818
|
-
description = "ARN of the REST API Gateway"
|
|
4819
|
-
value = module.rest_api.api_arn
|
|
4820
|
-
}
|
|
4821
|
-
|
|
4822
|
-
output "api_endpoint" {
|
|
4823
|
-
description = "Base URL of the REST API Gateway"
|
|
4824
|
-
value = module.rest_api.api_endpoint
|
|
4825
|
-
}
|
|
4826
|
-
|
|
4827
|
-
output "api_execution_arn" {
|
|
4828
|
-
description = "Execution ARN of the REST API Gateway"
|
|
4829
|
-
value = module.rest_api.api_execution_arn
|
|
4830
|
-
}
|
|
4831
|
-
|
|
4832
|
-
output "stage_invoke_url" {
|
|
4833
|
-
description = "Invoke URL of the API Gateway stage"
|
|
4834
|
-
value = aws_api_gateway_stage.api_stage.invoke_url
|
|
4835
|
-
}
|
|
4836
|
-
|
|
4837
|
-
output "stage_arn" {
|
|
4838
|
-
description = "ARN of the API Gateway stage"
|
|
4839
|
-
value = aws_api_gateway_stage.api_stage.arn
|
|
4840
|
-
}
|
|
4841
|
-
|
|
4842
|
-
output "stage_execution_arn" {
|
|
4843
|
-
description = "Execution ARN of the API Gateway stage"
|
|
4844
|
-
value = aws_api_gateway_stage.api_stage.execution_arn
|
|
4845
|
-
}
|
|
4846
|
-
|
|
4847
|
-
output "deployment_id" {
|
|
4848
|
-
description = "ID of the API Gateway deployment"
|
|
4849
|
-
value = aws_api_gateway_deployment.api_deployment.id
|
|
4850
|
-
}
|
|
4851
|
-
|
|
4852
|
-
output "stage_id" {
|
|
4853
|
-
description = "ID of the API Gateway stage"
|
|
4854
|
-
value = aws_api_gateway_stage.api_stage.id
|
|
4855
|
-
}
|
|
4856
|
-
|
|
4857
|
-
# Lambda Function Outputs
|
|
4858
|
-
output "lambda_function_name" {
|
|
4859
|
-
description = "Name of the Lambda function"
|
|
4860
|
-
value = aws_lambda_function.api_lambda.function_name
|
|
4861
|
-
}
|
|
4862
|
-
|
|
4863
|
-
output "lambda_function_arn" {
|
|
4864
|
-
description = "ARN of the Lambda function"
|
|
4865
|
-
value = aws_lambda_function.api_lambda.arn
|
|
4866
|
-
}
|
|
4867
|
-
|
|
4868
|
-
output "lambda_invoke_arn" {
|
|
4869
|
-
description = "Invoke ARN of the Lambda function"
|
|
4870
|
-
value = aws_lambda_function.api_lambda.invoke_arn
|
|
4871
|
-
}
|
|
4872
|
-
|
|
4873
|
-
output "lambda_qualified_arn" {
|
|
4874
|
-
description = "Qualified ARN of the Lambda function"
|
|
4875
|
-
value = aws_lambda_function.api_lambda.qualified_arn
|
|
4876
|
-
}
|
|
4877
|
-
|
|
4878
|
-
output "lambda_version" {
|
|
4879
|
-
description = "Version of the Lambda function"
|
|
4880
|
-
value = aws_lambda_function.api_lambda.version
|
|
4881
|
-
}
|
|
4882
|
-
|
|
4883
|
-
output "lambda_source_code_hash" {
|
|
4884
|
-
description = "Base64-encoded SHA256 hash of the Lambda deployment package"
|
|
4885
|
-
value = aws_lambda_function.api_lambda.source_code_hash
|
|
4886
|
-
}
|
|
4887
|
-
|
|
4888
|
-
output "lambda_source_code_size" {
|
|
4889
|
-
description = "Size of the Lambda deployment package in bytes"
|
|
4890
|
-
value = aws_lambda_function.api_lambda.source_code_size
|
|
4891
|
-
}
|
|
4892
|
-
|
|
4893
|
-
# IAM Role Outputs
|
|
4894
|
-
output "lambda_execution_role_arn" {
|
|
4895
|
-
description = "ARN of the Lambda execution role"
|
|
4896
|
-
value = aws_iam_role.lambda_execution_role.arn
|
|
4897
|
-
}
|
|
4898
|
-
|
|
4899
|
-
output "lambda_execution_role_name" {
|
|
4900
|
-
description = "Name of the Lambda execution role"
|
|
4901
|
-
value = aws_iam_role.lambda_execution_role.name
|
|
4902
|
-
}
|
|
4903
|
-
|
|
4904
|
-
# Integration Outputs
|
|
4905
|
-
output "integration_id" {
|
|
4906
|
-
description = "ID of the Lambda integration"
|
|
4907
|
-
value = aws_api_gateway_integration.lambda_integration.id
|
|
4908
|
-
}
|
|
4909
|
-
|
|
4910
|
-
output "proxy_resource_id" {
|
|
4911
|
-
description = "ID of the proxy resource"
|
|
4912
|
-
value = aws_api_gateway_resource.proxy_resource.id
|
|
4913
|
-
}
|
|
4914
|
-
|
|
4915
|
-
output "proxy_method_id" {
|
|
4916
|
-
description = "ID of the proxy method"
|
|
4917
|
-
value = aws_api_gateway_method.proxy_method.id
|
|
4918
|
-
}
|
|
4919
|
-
|
|
4920
|
-
# CloudWatch Log Groups
|
|
4921
|
-
output "lambda_log_group_name" {
|
|
4922
|
-
description = "Name of the Lambda CloudWatch log group"
|
|
4923
|
-
value = aws_cloudwatch_log_group.lambda_logs.name
|
|
4924
|
-
}
|
|
4925
|
-
|
|
4926
|
-
output "lambda_log_group_arn" {
|
|
4927
|
-
description = "ARN of the Lambda CloudWatch log group"
|
|
4928
|
-
value = aws_cloudwatch_log_group.lambda_logs.arn
|
|
4929
|
-
}
|
|
4930
|
-
",
|
|
4931
|
-
}
|
|
4932
|
-
`;
|
|
4933
|
-
|
|
4934
|
-
exports[`fastapi project generator > terraform iac > should generate terraform files for REST API with IAM auth and snapshot them > terraform-rest-iam-files 1`] = `
|
|
4935
|
-
{
|
|
4936
|
-
"rest-api.tf": "# Core REST API Gateway module
|
|
4937
|
-
# This module creates the API Gateway REST API, deployment, stage, and logging resources
|
|
4938
|
-
|
|
4939
|
-
terraform {
|
|
4940
|
-
required_version = ">= 1.0"
|
|
4941
|
-
|
|
4942
|
-
required_providers {
|
|
4943
|
-
aws = {
|
|
4944
|
-
source = "hashicorp/aws"
|
|
4945
|
-
version = "~> 6.33"
|
|
4946
|
-
}
|
|
4947
|
-
random = {
|
|
4948
|
-
source = "hashicorp/random"
|
|
4949
|
-
version = "~> 3.1"
|
|
4950
|
-
}
|
|
4951
|
-
}
|
|
4952
|
-
}
|
|
4953
|
-
|
|
4954
|
-
# Core REST API Gateway Variables
|
|
4955
|
-
|
|
4956
|
-
variable "api_name" {
|
|
4957
|
-
description = "Name of the REST API Gateway"
|
|
4958
|
-
type = string
|
|
4959
|
-
}
|
|
4960
|
-
|
|
4961
|
-
variable "api_description" {
|
|
4962
|
-
description = "Description of the REST API Gateway"
|
|
4963
|
-
type = string
|
|
4964
|
-
default = "REST API Gateway"
|
|
4965
|
-
}
|
|
4966
|
-
|
|
4967
|
-
variable "stage_name" {
|
|
4968
|
-
description = "Name of the API Gateway stage"
|
|
4969
|
-
type = string
|
|
4970
|
-
default = "prod"
|
|
4971
|
-
}
|
|
4972
|
-
|
|
4973
|
-
variable "stage_auto_deploy" {
|
|
4974
|
-
description = "Whether to automatically deploy the API stage"
|
|
4975
|
-
type = bool
|
|
4976
|
-
default = true
|
|
4977
|
-
}
|
|
4978
|
-
|
|
4979
|
-
variable "enable_waf" {
|
|
4980
|
-
description = "Whether to enable AWS WAFv2 with the default managed ruleset (AWSManagedRulesCommonRuleSet and AWSManagedRulesKnownBadInputsRuleSet). The Web ACL is created here and associated with the stage in the consuming module."
|
|
4981
|
-
type = bool
|
|
4982
|
-
default = true
|
|
4983
|
-
}
|
|
4984
|
-
|
|
4985
|
-
# CORS Configuration
|
|
4986
|
-
|
|
4987
|
-
variable "cors_allow_headers" {
|
|
4988
|
-
description = "List of allowed headers for CORS"
|
|
4989
|
-
type = list(string)
|
|
4990
|
-
default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
|
|
4991
|
-
}
|
|
4992
|
-
|
|
4993
|
-
variable "cors_allow_methods" {
|
|
4994
|
-
description = "List of allowed HTTP methods for CORS"
|
|
4995
|
-
type = list(string)
|
|
4996
|
-
default = ["*"]
|
|
4997
|
-
}
|
|
4998
|
-
|
|
4999
|
-
variable "cors_allow_origins" {
|
|
5000
|
-
description = "List of allowed origins for CORS"
|
|
5001
|
-
type = list(string)
|
|
5002
|
-
default = ["*"]
|
|
5003
|
-
}
|
|
5004
|
-
|
|
5005
|
-
# Tags
|
|
5006
|
-
|
|
5007
|
-
variable "tags" {
|
|
5008
|
-
description = "Tags to apply to all resources"
|
|
5009
|
-
type = map(string)
|
|
5010
|
-
default = {}
|
|
5011
|
-
}
|
|
5012
|
-
|
|
5013
|
-
# Data sources
|
|
5014
|
-
data "aws_region" "current" {}
|
|
5015
|
-
data "aws_caller_identity" "current" {}
|
|
5016
|
-
|
|
5017
|
-
# Resources
|
|
5018
|
-
|
|
5019
|
-
# Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
|
|
5020
|
-
|
|
5021
|
-
# REST API Gateway
|
|
5022
|
-
resource "aws_api_gateway_rest_api" "rest_api" {
|
|
5023
|
-
name = var.api_name
|
|
5024
|
-
description = var.api_description
|
|
5025
|
-
|
|
5026
|
-
endpoint_configuration {
|
|
5027
|
-
types = ["REGIONAL"]
|
|
5028
|
-
}
|
|
5029
|
-
|
|
5030
|
-
lifecycle {
|
|
5031
|
-
create_before_destroy = true
|
|
5032
|
-
}
|
|
5033
|
-
|
|
5034
|
-
tags = var.tags
|
|
5035
|
-
}
|
|
5036
|
-
|
|
5037
|
-
# Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
|
|
5038
|
-
# after all methods and integrations are defined
|
|
5039
|
-
|
|
5040
|
-
# Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
|
|
5041
|
-
|
|
5042
|
-
# Gateway Response for CORS (4XX errors)
|
|
5043
|
-
resource "aws_api_gateway_gateway_response" "cors_4xx" {
|
|
5044
|
-
rest_api_id = aws_api_gateway_rest_api.rest_api.id
|
|
5045
|
-
response_type = "DEFAULT_4XX"
|
|
5046
|
-
|
|
5047
|
-
response_parameters = {
|
|
5048
|
-
"gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
|
|
5049
|
-
"gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
|
|
5050
|
-
"gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
|
|
5051
|
-
}
|
|
5052
|
-
}
|
|
5053
|
-
|
|
5054
|
-
# Gateway Response for CORS (5XX errors)
|
|
5055
|
-
resource "aws_api_gateway_gateway_response" "cors_5xx" {
|
|
5056
|
-
rest_api_id = aws_api_gateway_rest_api.rest_api.id
|
|
5057
|
-
response_type = "DEFAULT_5XX"
|
|
5058
|
-
|
|
5059
|
-
response_parameters = {
|
|
5060
|
-
"gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
|
|
5061
|
-
"gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
|
|
5062
|
-
"gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
|
|
5063
|
-
}
|
|
5064
|
-
}
|
|
5065
|
-
|
|
5066
|
-
resource "aws_wafv2_web_acl" "api_waf" {
|
|
5067
|
-
#checkov:skip=CKV2_AWS_31:Logging configuration is defined below in aws_wafv2_web_acl_logging_configuration.api_waf_logging; Checkov does not resolve the separate resource
|
|
5068
|
-
count = var.enable_waf ? 1 : 0
|
|
5069
|
-
|
|
5070
|
-
name = "\${var.api_name}-waf"
|
|
5071
|
-
scope = "REGIONAL"
|
|
5072
|
-
|
|
5073
|
-
default_action {
|
|
5074
|
-
allow {}
|
|
5075
|
-
}
|
|
5076
|
-
|
|
5077
|
-
rule {
|
|
5078
|
-
name = "CRSRule"
|
|
5079
|
-
priority = 0
|
|
5080
|
-
|
|
5081
|
-
override_action {
|
|
5082
|
-
none {}
|
|
5083
|
-
}
|
|
5084
|
-
|
|
5085
|
-
statement {
|
|
5086
|
-
managed_rule_group_statement {
|
|
5087
|
-
name = "AWSManagedRulesCommonRuleSet"
|
|
5088
|
-
vendor_name = "AWS"
|
|
5089
|
-
|
|
5090
|
-
# Count instead of Block: the CRS 8 KB body limit is too restrictive for most APIs.
|
|
5091
|
-
rule_action_override {
|
|
5092
|
-
name = "SizeRestrictions_BODY"
|
|
5093
|
-
action_to_use {
|
|
5094
|
-
count {}
|
|
5095
|
-
}
|
|
5096
|
-
}
|
|
5097
|
-
}
|
|
5098
|
-
}
|
|
5099
|
-
|
|
5100
|
-
visibility_config {
|
|
5101
|
-
cloudwatch_metrics_enabled = true
|
|
5102
|
-
metric_name = "\${var.api_name}WebAcl-CRS"
|
|
5103
|
-
sampled_requests_enabled = true
|
|
5104
|
-
}
|
|
5105
|
-
}
|
|
5106
|
-
|
|
5107
|
-
rule {
|
|
5108
|
-
name = "KnownBadInputsRule"
|
|
5109
|
-
priority = 1
|
|
5110
|
-
|
|
5111
|
-
override_action {
|
|
5112
|
-
none {}
|
|
5113
|
-
}
|
|
5114
|
-
|
|
5115
|
-
statement {
|
|
5116
|
-
managed_rule_group_statement {
|
|
5117
|
-
name = "AWSManagedRulesKnownBadInputsRuleSet"
|
|
5118
|
-
vendor_name = "AWS"
|
|
5119
|
-
}
|
|
5120
|
-
}
|
|
5121
|
-
|
|
5122
|
-
visibility_config {
|
|
5123
|
-
cloudwatch_metrics_enabled = true
|
|
5124
|
-
metric_name = "\${var.api_name}WebAcl-KnownBadInputs"
|
|
5125
|
-
sampled_requests_enabled = true
|
|
5126
|
-
}
|
|
5127
|
-
}
|
|
5128
|
-
|
|
5129
|
-
visibility_config {
|
|
5130
|
-
cloudwatch_metrics_enabled = true
|
|
5131
|
-
metric_name = "\${var.api_name}WebAcl"
|
|
5132
|
-
sampled_requests_enabled = true
|
|
5133
|
-
}
|
|
5134
|
-
|
|
5135
|
-
tags = var.tags
|
|
5136
|
-
|
|
5137
|
-
lifecycle {
|
|
5138
|
-
create_before_destroy = true
|
|
5139
|
-
}
|
|
5140
|
-
}
|
|
5141
|
-
|
|
5142
|
-
# CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
|
|
5143
|
-
resource "aws_cloudwatch_log_group" "api_waf_logs" {
|
|
5144
|
-
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
5145
|
-
#checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
|
|
5146
|
-
count = var.enable_waf ? 1 : 0
|
|
5147
|
-
|
|
5148
|
-
name = "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}"
|
|
5149
|
-
retention_in_days = 30
|
|
5150
|
-
tags = var.tags
|
|
5151
|
-
}
|
|
5152
|
-
|
|
5153
|
-
resource "random_id" "waf_log_suffix" {
|
|
5154
|
-
count = var.enable_waf ? 1 : 0
|
|
5155
|
-
byte_length = 4
|
|
5156
|
-
}
|
|
5157
|
-
|
|
5158
|
-
resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
|
|
5159
|
-
count = var.enable_waf ? 1 : 0
|
|
5160
|
-
|
|
5161
|
-
log_destination_configs = [aws_cloudwatch_log_group.api_waf_logs[0].arn]
|
|
5162
|
-
resource_arn = aws_wafv2_web_acl.api_waf[0].arn
|
|
5163
|
-
}
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
# Outputs
|
|
5167
|
-
|
|
5168
|
-
output "api_id" {
|
|
5169
|
-
description = "ID of the REST API Gateway"
|
|
5170
|
-
value = aws_api_gateway_rest_api.rest_api.id
|
|
5171
|
-
}
|
|
5172
|
-
|
|
5173
|
-
output "api_arn" {
|
|
5174
|
-
description = "ARN of the REST API Gateway"
|
|
5175
|
-
value = aws_api_gateway_rest_api.rest_api.arn
|
|
5176
|
-
}
|
|
5177
|
-
|
|
5178
|
-
output "api_endpoint" {
|
|
5179
|
-
description = "Base URL of the REST API Gateway"
|
|
5180
|
-
value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
|
|
5181
|
-
}
|
|
5182
|
-
|
|
5183
|
-
output "api_execution_arn" {
|
|
5184
|
-
description = "Execution ARN of the REST API Gateway"
|
|
5185
|
-
value = aws_api_gateway_rest_api.rest_api.execution_arn
|
|
5186
|
-
}
|
|
5187
|
-
|
|
5188
|
-
output "api_root_resource_id" {
|
|
5189
|
-
description = "Root resource ID of the REST API Gateway"
|
|
5190
|
-
value = aws_api_gateway_rest_api.rest_api.root_resource_id
|
|
5191
|
-
}
|
|
5192
|
-
|
|
5193
|
-
output "waf_web_acl_arn" {
|
|
5194
|
-
description = "ARN of the WAFv2 Web ACL associated with the API, or null if WAF is disabled"
|
|
5195
|
-
value = var.enable_waf ? aws_wafv2_web_acl.api_waf[0].arn : null
|
|
5196
|
-
}
|
|
5197
|
-
|
|
5198
|
-
# Note: Stage and deployment outputs are provided by the consuming module
|
|
5199
|
-
|
|
5200
|
-
# Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
|
|
5201
|
-
"test-api.tf": "terraform {
|
|
5202
|
-
required_version = ">= 1.0"
|
|
5203
|
-
|
|
5204
|
-
required_providers {
|
|
5205
|
-
aws = {
|
|
5206
|
-
source = "hashicorp/aws"
|
|
5207
|
-
version = "~> 6.33"
|
|
5208
|
-
}
|
|
5209
|
-
}
|
|
5210
|
-
}
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
variable "env" {
|
|
5214
|
-
description = "Environment variables for the Lambda function"
|
|
5215
|
-
type = map(string)
|
|
5216
|
-
default = {}
|
|
5217
|
-
}
|
|
5218
|
-
|
|
5219
|
-
variable "additional_iam_policy_statements" {
|
|
5220
|
-
description = "Additional IAM policy statements for the Lambda function"
|
|
5221
|
-
type = list(object({
|
|
5222
|
-
Effect = string
|
|
5223
|
-
Action = list(string)
|
|
5224
|
-
Resource = list(string)
|
|
5225
|
-
}))
|
|
5226
|
-
default = []
|
|
5227
|
-
}
|
|
5228
|
-
|
|
5229
|
-
variable "asset_bucket_name" {
|
|
5230
|
-
description = "Name of the shared asset S3 bucket used to stage the Lambda deployment zip. Instantiate the \`core/asset-bucket\` module once per deployment and pass its \`bucket_name\` output here."
|
|
5231
|
-
type = string
|
|
5232
|
-
}
|
|
5233
|
-
|
|
5234
|
-
# CORS Configuration (passed to core module)
|
|
5235
|
-
|
|
5236
|
-
variable "cors_allow_headers" {
|
|
5237
|
-
description = "List of allowed headers for CORS"
|
|
5238
|
-
type = list(string)
|
|
5239
|
-
default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
|
|
5240
|
-
}
|
|
5241
|
-
|
|
5242
|
-
variable "cors_allow_methods" {
|
|
5243
|
-
description = "List of allowed HTTP methods for CORS"
|
|
5244
|
-
type = list(string)
|
|
5245
|
-
default = ["*"]
|
|
5246
|
-
}
|
|
5247
|
-
|
|
5248
|
-
variable "cors_allow_origins" {
|
|
5249
|
-
description = "List of allowed origins for CORS"
|
|
5250
|
-
type = list(string)
|
|
5251
|
-
default = ["*"]
|
|
5252
|
-
}
|
|
5253
|
-
|
|
5254
|
-
# WAF Configuration
|
|
5255
|
-
variable "enable_waf" {
|
|
5256
|
-
description = "Whether to enable AWS WAFv2 with the default managed ruleset on the API stage"
|
|
5257
|
-
type = bool
|
|
5258
|
-
default = true
|
|
5259
|
-
}
|
|
5260
|
-
|
|
5261
|
-
# Tags
|
|
5262
|
-
variable "tags" {
|
|
5263
|
-
description = "Tags to apply to all resources"
|
|
5264
|
-
type = map(string)
|
|
5265
|
-
default = {}
|
|
5266
|
-
}
|
|
5267
|
-
|
|
5268
|
-
# Get current AWS region and account ID
|
|
5269
|
-
data "aws_region" "current" {}
|
|
5270
|
-
data "aws_caller_identity" "current" {}
|
|
5271
|
-
|
|
5272
|
-
resource "random_string" "suffix" {
|
|
5273
|
-
length = 8
|
|
5274
|
-
special = false
|
|
5275
|
-
upper = false
|
|
5276
|
-
}
|
|
5277
|
-
|
|
5278
|
-
# Resources
|
|
5279
|
-
|
|
5280
|
-
# Create Lambda ZIP file from the bundle directory
|
|
5281
|
-
data "archive_file" "lambda_zip" {
|
|
5282
|
-
type = "zip"
|
|
5283
|
-
source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
|
|
5284
|
-
output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
|
|
5285
|
-
}
|
|
5286
|
-
|
|
5287
|
-
resource "aws_s3_object" "lambda_zip" {
|
|
5288
|
-
bucket = var.asset_bucket_name
|
|
5289
|
-
key = "apis/test-api/\${data.archive_file.lambda_zip.output_sha256}.zip"
|
|
5290
|
-
source = data.archive_file.lambda_zip.output_path
|
|
5291
|
-
source_hash = data.archive_file.lambda_zip.output_base64sha256
|
|
5292
|
-
etag = data.archive_file.lambda_zip.output_md5
|
|
5293
|
-
}
|
|
5294
|
-
|
|
5295
|
-
# Use the core REST API module
|
|
5296
|
-
module "rest_api" {
|
|
5297
|
-
source = "../../../core/api/rest-api"
|
|
5298
|
-
|
|
5299
|
-
api_name = "TestApi-\${random_string.suffix.result}"
|
|
5300
|
-
api_description = "TestApi REST API"
|
|
5301
|
-
stage_name = "prod"
|
|
5302
|
-
stage_auto_deploy = true
|
|
5303
|
-
|
|
5304
|
-
# WAF Configuration
|
|
5305
|
-
enable_waf = var.enable_waf
|
|
5306
|
-
|
|
5307
|
-
# CORS Configuration
|
|
5308
|
-
cors_allow_headers = var.cors_allow_headers
|
|
5309
|
-
cors_allow_methods = var.cors_allow_methods
|
|
5310
|
-
cors_allow_origins = var.cors_allow_origins
|
|
5311
|
-
|
|
5312
|
-
# Tags
|
|
5313
|
-
tags = var.tags
|
|
5314
|
-
}
|
|
5315
|
-
|
|
5316
|
-
resource "aws_wafv2_web_acl_association" "api_waf_association" {
|
|
5317
|
-
count = var.enable_waf ? 1 : 0
|
|
5318
|
-
|
|
5319
|
-
resource_arn = aws_api_gateway_stage.api_stage.arn
|
|
5320
|
-
web_acl_arn = module.rest_api.waf_web_acl_arn
|
|
5321
|
-
|
|
5322
|
-
depends_on = [aws_api_gateway_stage.api_stage]
|
|
5323
|
-
}
|
|
5324
|
-
|
|
5325
|
-
# Lambda function
|
|
5326
|
-
resource "aws_lambda_function" "api_lambda" {
|
|
5327
|
-
#checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
|
|
5328
|
-
#checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
|
|
5329
|
-
#checkov:skip=CKV_AWS_272:Code signing not required for this use case
|
|
5330
|
-
#checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
|
|
5331
|
-
#checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
|
|
5332
|
-
s3_bucket = aws_s3_object.lambda_zip.bucket
|
|
5333
|
-
s3_key = aws_s3_object.lambda_zip.key
|
|
5334
|
-
s3_object_version = aws_s3_object.lambda_zip.version_id
|
|
5335
|
-
function_name = "TestApiHandler-\${random_string.suffix.result}"
|
|
5336
|
-
role = aws_iam_role.lambda_execution_role.arn
|
|
5337
|
-
handler = "run.sh"
|
|
5338
|
-
runtime = "python3.14"
|
|
5339
|
-
layers = ["arn:aws:lambda:\${data.aws_region.current.region}:753240598075:layer:LambdaAdapterLayerX86:24"]
|
|
5340
|
-
timeout = 30
|
|
5341
|
-
memory_size = 128
|
|
5342
|
-
publish = true
|
|
5343
|
-
|
|
5344
|
-
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
|
|
5345
|
-
|
|
5346
|
-
# Enable X-Ray tracing
|
|
5347
|
-
tracing_config {
|
|
5348
|
-
mode = "Active"
|
|
5349
|
-
}
|
|
5350
|
-
|
|
5351
|
-
# Enable SnapStart for faster cold starts
|
|
5352
|
-
snap_start {
|
|
5353
|
-
apply_on = "PublishedVersions"
|
|
5354
|
-
}
|
|
5355
|
-
|
|
5356
|
-
environment {
|
|
5357
|
-
variables = merge({
|
|
5358
|
-
PORT = "8000"
|
|
5359
|
-
AWS_LWA_INVOKE_MODE = "response_stream"
|
|
5360
|
-
AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
|
|
5361
|
-
}, var.env)
|
|
5362
|
-
}
|
|
5363
|
-
|
|
5364
|
-
tags = var.tags
|
|
5365
|
-
}
|
|
5366
|
-
|
|
5367
|
-
# IAM role for Lambda execution
|
|
5368
|
-
resource "aws_iam_role" "lambda_execution_role" {
|
|
5369
|
-
name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
|
|
5370
|
-
|
|
5371
|
-
assume_role_policy = jsonencode({
|
|
5372
|
-
Version = "2012-10-17"
|
|
5373
|
-
Statement = [
|
|
5374
|
-
{
|
|
5375
|
-
Action = "sts:AssumeRole"
|
|
5376
|
-
Effect = "Allow"
|
|
5377
|
-
Principal = {
|
|
5378
|
-
Service = "lambda.amazonaws.com"
|
|
5379
|
-
}
|
|
5380
|
-
}
|
|
5381
|
-
]
|
|
5382
|
-
})
|
|
5383
|
-
|
|
5384
|
-
tags = var.tags
|
|
5385
|
-
}
|
|
5386
|
-
|
|
5387
|
-
# Attach basic execution policy to Lambda role
|
|
5388
|
-
resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
|
|
5389
|
-
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
5390
|
-
role = aws_iam_role.lambda_execution_role.name
|
|
5391
|
-
}
|
|
5392
|
-
|
|
5393
|
-
# Attach X-Ray tracing policy to Lambda role
|
|
5394
|
-
resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
|
|
5395
|
-
policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
|
|
5396
|
-
role = aws_iam_role.lambda_execution_role.name
|
|
5397
|
-
}
|
|
5398
|
-
|
|
5399
|
-
# Additional IAM policies for Lambda (if provided)
|
|
5400
|
-
resource "aws_iam_role_policy" "lambda_additional_policies" {
|
|
5401
|
-
count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
|
|
5402
|
-
name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
|
|
5403
|
-
role = aws_iam_role.lambda_execution_role.id
|
|
5404
|
-
|
|
5405
|
-
policy = jsonencode({
|
|
5406
|
-
Version = "2012-10-17"
|
|
5407
|
-
Statement = var.additional_iam_policy_statements
|
|
5408
|
-
})
|
|
5409
|
-
}
|
|
5410
|
-
|
|
5411
|
-
# CloudWatch Log Group for Lambda
|
|
5412
|
-
resource "aws_cloudwatch_log_group" "lambda_logs" {
|
|
5413
|
-
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
5414
|
-
#checkov:skip=CKV_AWS_338:Log retention set to forever
|
|
5415
|
-
#checkov:skip=CKV_AWS_66:Log retention set to forever
|
|
5416
|
-
name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
|
|
5417
|
-
tags = var.tags
|
|
5418
|
-
}
|
|
5419
|
-
|
|
5420
|
-
# Lambda alias pointing to the latest published version for SnapStart
|
|
5421
|
-
resource "aws_lambda_alias" "live" {
|
|
5422
|
-
name = "live"
|
|
5423
|
-
function_name = aws_lambda_function.api_lambda.function_name
|
|
5424
|
-
function_version = aws_lambda_function.api_lambda.version
|
|
5425
|
-
|
|
5426
|
-
depends_on = [aws_lambda_function.api_lambda]
|
|
5427
|
-
}
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
# Create proxy resource (captures all paths)
|
|
5431
|
-
resource "aws_api_gateway_resource" "proxy_resource" {
|
|
5432
|
-
rest_api_id = module.rest_api.api_id
|
|
5433
|
-
parent_id = module.rest_api.api_root_resource_id
|
|
5434
|
-
path_part = "{proxy+}"
|
|
5435
|
-
}
|
|
5436
|
-
|
|
5437
|
-
# Lambda integration for REST API
|
|
5438
|
-
resource "aws_api_gateway_integration" "lambda_integration" {
|
|
5439
|
-
rest_api_id = module.rest_api.api_id
|
|
5440
|
-
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
5441
|
-
http_method = aws_api_gateway_method.proxy_method.http_method
|
|
5442
|
-
|
|
5443
|
-
integration_http_method = "POST"
|
|
5444
|
-
type = "AWS_PROXY"
|
|
5445
|
-
# Use the response streaming invocation path with the alias ARN for SnapStart
|
|
5446
|
-
uri = "arn:aws:apigateway:\${data.aws_region.current.region}:lambda:path/2021-11-15/functions/\${aws_lambda_alias.live.arn}/response-streaming-invocations"
|
|
5447
|
-
response_transfer_mode = "STREAM"
|
|
5448
|
-
|
|
5449
|
-
depends_on = [aws_lambda_alias.live]
|
|
5450
|
-
}
|
|
5451
|
-
|
|
5452
|
-
# Method for proxy integration
|
|
5453
|
-
resource "aws_api_gateway_method" "proxy_method" {
|
|
5454
|
-
#checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
|
|
5455
|
-
rest_api_id = module.rest_api.api_id
|
|
5456
|
-
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
5457
|
-
http_method = "ANY"
|
|
5458
|
-
|
|
5459
|
-
authorization = "AWS_IAM"
|
|
5460
|
-
|
|
5461
|
-
request_parameters = {
|
|
5462
|
-
"method.request.path.proxy" = true
|
|
5463
|
-
}
|
|
5464
|
-
|
|
5465
|
-
depends_on = []
|
|
5466
|
-
}
|
|
5467
|
-
|
|
5468
|
-
# OPTIONS method for CORS preflight
|
|
5469
|
-
resource "aws_api_gateway_method" "options_method" {
|
|
5470
|
-
#checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
|
|
5471
|
-
#checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
|
|
5472
|
-
rest_api_id = module.rest_api.api_id
|
|
5473
|
-
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
5474
|
-
http_method = "OPTIONS"
|
|
5475
|
-
authorization = "NONE"
|
|
5476
|
-
}
|
|
5477
|
-
|
|
5478
|
-
# CORS integration for OPTIONS method
|
|
5479
|
-
resource "aws_api_gateway_integration" "options_integration" {
|
|
5480
|
-
rest_api_id = module.rest_api.api_id
|
|
5481
|
-
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
5482
|
-
http_method = aws_api_gateway_method.options_method.http_method
|
|
5483
|
-
|
|
5484
|
-
type = "MOCK"
|
|
5485
|
-
request_templates = {
|
|
5486
|
-
"application/json" = "{\\"statusCode\\": 204}"
|
|
5487
|
-
}
|
|
5488
|
-
}
|
|
5489
|
-
|
|
5490
|
-
# OPTIONS method response
|
|
5491
|
-
resource "aws_api_gateway_method_response" "options_response" {
|
|
5492
|
-
rest_api_id = module.rest_api.api_id
|
|
5493
|
-
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
5494
|
-
http_method = aws_api_gateway_method.options_method.http_method
|
|
5495
|
-
status_code = "204"
|
|
5496
|
-
|
|
5497
|
-
response_parameters = {
|
|
5498
|
-
"method.response.header.Access-Control-Allow-Headers" = true
|
|
5499
|
-
"method.response.header.Access-Control-Allow-Methods" = true
|
|
5500
|
-
"method.response.header.Access-Control-Allow-Origin" = true
|
|
5501
|
-
}
|
|
5502
|
-
}
|
|
5503
|
-
|
|
5504
|
-
# OPTIONS integration response
|
|
5505
|
-
resource "aws_api_gateway_integration_response" "options_integration_response" {
|
|
5506
|
-
rest_api_id = module.rest_api.api_id
|
|
5507
|
-
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
5508
|
-
http_method = aws_api_gateway_method.options_method.http_method
|
|
5509
|
-
status_code = aws_api_gateway_method_response.options_response.status_code
|
|
5510
|
-
|
|
5511
|
-
response_parameters = {
|
|
5512
|
-
"method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
|
|
5513
|
-
"method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
|
|
5514
|
-
"method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
|
|
5515
|
-
}
|
|
5516
|
-
}
|
|
5517
|
-
|
|
5518
|
-
# API Gateway deployment
|
|
5519
|
-
resource "aws_api_gateway_deployment" "api_deployment" {
|
|
5520
|
-
rest_api_id = module.rest_api.api_id
|
|
5521
|
-
|
|
5522
|
-
triggers = {
|
|
5523
|
-
redeployment = sha1(jsonencode([
|
|
5524
|
-
aws_api_gateway_resource.proxy_resource.id,
|
|
5525
|
-
aws_api_gateway_method.proxy_method.id,
|
|
5526
|
-
aws_api_gateway_integration.lambda_integration.id,
|
|
5527
|
-
aws_api_gateway_method.options_method.id,
|
|
5528
|
-
aws_api_gateway_integration.options_integration.id,
|
|
5529
|
-
]))
|
|
5530
|
-
}
|
|
5531
|
-
|
|
5532
|
-
lifecycle {
|
|
5533
|
-
create_before_destroy = true
|
|
5534
|
-
}
|
|
5535
|
-
|
|
5536
|
-
depends_on = [
|
|
5537
|
-
aws_api_gateway_method.proxy_method,
|
|
5538
|
-
aws_api_gateway_integration.lambda_integration,
|
|
5539
|
-
aws_api_gateway_method.options_method,
|
|
5540
|
-
aws_api_gateway_integration.options_integration,
|
|
5541
|
-
aws_api_gateway_method_response.options_response,
|
|
5542
|
-
aws_api_gateway_integration_response.options_integration_response,
|
|
5543
|
-
]
|
|
5544
|
-
}
|
|
5545
|
-
|
|
5546
|
-
# API Gateway stage
|
|
5547
|
-
resource "aws_api_gateway_stage" "api_stage" {
|
|
5548
|
-
#checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
|
|
5549
|
-
#checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
|
|
5550
|
-
#checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
|
|
5551
|
-
#checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
|
|
5552
|
-
#checkov:skip=CKV2_AWS_77:WAFv2 Web ACL is attached via aws_wafv2_web_acl_association when var.enable_waf is true (default) and includes AWSManagedRulesKnownBadInputsRuleSet for Log4j protection; Checkov does not track the association across modules
|
|
5553
|
-
deployment_id = aws_api_gateway_deployment.api_deployment.id
|
|
5554
|
-
rest_api_id = module.rest_api.api_id
|
|
5555
|
-
stage_name = "prod"
|
|
5556
|
-
xray_tracing_enabled = true
|
|
5557
|
-
|
|
5558
|
-
tags = var.tags
|
|
5559
|
-
|
|
5560
|
-
depends_on = [aws_api_gateway_deployment.api_deployment]
|
|
5561
|
-
}
|
|
5562
|
-
|
|
5563
|
-
# API Gateway Resource Policy
|
|
5564
|
-
resource "aws_api_gateway_rest_api_policy" "api_policy" {
|
|
5565
|
-
rest_api_id = module.rest_api.api_id
|
|
5566
|
-
|
|
5567
|
-
policy = jsonencode({
|
|
5568
|
-
Version = "2012-10-17"
|
|
5569
|
-
Statement = [
|
|
5570
|
-
{
|
|
5571
|
-
# Grant any AWS credentials from the account to call the API
|
|
5572
|
-
# Machine to machine fine-grained access can be defined here using more specific principals
|
|
5573
|
-
# (eg roles or users) and resources (eg which api paths may be invoked by which principal) if required
|
|
5574
|
-
Effect = "Allow"
|
|
5575
|
-
Principal = {
|
|
5576
|
-
AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
|
|
5577
|
-
}
|
|
5578
|
-
Action = "execute-api:Invoke"
|
|
5579
|
-
Resource = "execute-api:/*"
|
|
5580
|
-
},
|
|
5581
|
-
{
|
|
5582
|
-
# Open up OPTIONS to allow browsers to make unauthenticated preflight requests
|
|
5583
|
-
Effect = "Allow"
|
|
5584
|
-
Principal = "*"
|
|
5585
|
-
Action = "execute-api:Invoke"
|
|
5586
|
-
Resource = "execute-api:/*/OPTIONS/*"
|
|
5587
|
-
}
|
|
5588
|
-
]
|
|
5589
|
-
})
|
|
5590
|
-
}
|
|
5591
|
-
|
|
5592
|
-
# Lambda permission for API Gateway to invoke the function via alias
|
|
5593
|
-
resource "aws_lambda_permission" "api_gateway_invoke" {
|
|
5594
|
-
statement_id = "AllowExecutionFromAPIGateway"
|
|
5595
|
-
action = "lambda:InvokeFunction"
|
|
5596
|
-
function_name = aws_lambda_function.api_lambda.function_name
|
|
5597
|
-
qualifier = aws_lambda_alias.live.name
|
|
5598
|
-
principal = "apigateway.amazonaws.com"
|
|
5599
|
-
source_arn = "\${module.rest_api.api_execution_arn}/*/*"
|
|
5600
|
-
|
|
5601
|
-
depends_on = [module.rest_api, aws_lambda_alias.live]
|
|
5602
|
-
}
|
|
5603
|
-
|
|
5604
|
-
# Lambda permission for API Gateway to invoke with response streaming via alias
|
|
5605
|
-
resource "aws_lambda_permission" "api_gateway_invoke_streaming" {
|
|
5606
|
-
statement_id = "AllowStreamingFromAPIGateway"
|
|
5607
|
-
action = "lambda:InvokeFunctionWithResponseStream"
|
|
5608
|
-
function_name = aws_lambda_function.api_lambda.function_name
|
|
5609
|
-
qualifier = aws_lambda_alias.live.name
|
|
5610
|
-
principal = "apigateway.amazonaws.com"
|
|
5611
|
-
source_arn = "\${module.rest_api.api_execution_arn}/*/*"
|
|
5612
|
-
|
|
5613
|
-
depends_on = [module.rest_api, aws_lambda_alias.live]
|
|
5614
|
-
}
|
|
5615
|
-
|
|
5616
|
-
# Add API url to runtime config
|
|
5617
|
-
module "add_url_to_runtime_config" {
|
|
5618
|
-
source = "../../../core/runtime-config/entry"
|
|
5619
|
-
|
|
5620
|
-
namespace = "connection"
|
|
5621
|
-
key = "apis"
|
|
5622
|
-
value = { "TestApi" = aws_api_gateway_stage.api_stage.invoke_url }
|
|
5623
|
-
|
|
5624
|
-
depends_on = [aws_api_gateway_stage.api_stage]
|
|
5625
|
-
}
|
|
5626
|
-
|
|
5627
|
-
# Outputs
|
|
5628
|
-
|
|
5629
|
-
# API Gateway Outputs (from core module)
|
|
5630
|
-
output "api_id" {
|
|
5631
|
-
description = "ID of the REST API Gateway"
|
|
5632
|
-
value = module.rest_api.api_id
|
|
5633
|
-
}
|
|
5634
|
-
|
|
5635
|
-
output "api_arn" {
|
|
5636
|
-
description = "ARN of the REST API Gateway"
|
|
5637
|
-
value = module.rest_api.api_arn
|
|
5638
|
-
}
|
|
5639
|
-
|
|
5640
|
-
output "api_endpoint" {
|
|
5641
|
-
description = "Base URL of the REST API Gateway"
|
|
5642
|
-
value = module.rest_api.api_endpoint
|
|
5643
|
-
}
|
|
5644
|
-
|
|
5645
|
-
output "api_execution_arn" {
|
|
5646
|
-
description = "Execution ARN of the REST API Gateway"
|
|
5647
|
-
value = module.rest_api.api_execution_arn
|
|
5648
|
-
}
|
|
5649
|
-
|
|
5650
|
-
output "stage_invoke_url" {
|
|
5651
|
-
description = "Invoke URL of the API Gateway stage"
|
|
5652
|
-
value = aws_api_gateway_stage.api_stage.invoke_url
|
|
5653
|
-
}
|
|
5654
|
-
|
|
5655
|
-
output "stage_arn" {
|
|
5656
|
-
description = "ARN of the API Gateway stage"
|
|
5657
|
-
value = aws_api_gateway_stage.api_stage.arn
|
|
5658
|
-
}
|
|
5659
|
-
|
|
5660
|
-
output "stage_execution_arn" {
|
|
5661
|
-
description = "Execution ARN of the API Gateway stage"
|
|
5662
|
-
value = aws_api_gateway_stage.api_stage.execution_arn
|
|
5663
|
-
}
|
|
5664
|
-
|
|
5665
|
-
output "deployment_id" {
|
|
5666
|
-
description = "ID of the API Gateway deployment"
|
|
5667
|
-
value = aws_api_gateway_deployment.api_deployment.id
|
|
5668
|
-
}
|
|
5669
|
-
|
|
5670
|
-
output "stage_id" {
|
|
5671
|
-
description = "ID of the API Gateway stage"
|
|
5672
|
-
value = aws_api_gateway_stage.api_stage.id
|
|
5673
|
-
}
|
|
5674
|
-
|
|
5675
|
-
# Lambda Function Outputs
|
|
5676
|
-
output "lambda_function_name" {
|
|
5677
|
-
description = "Name of the Lambda function"
|
|
5678
|
-
value = aws_lambda_function.api_lambda.function_name
|
|
5679
|
-
}
|
|
5680
|
-
|
|
5681
|
-
output "lambda_function_arn" {
|
|
5682
|
-
description = "ARN of the Lambda function"
|
|
5683
|
-
value = aws_lambda_function.api_lambda.arn
|
|
5684
|
-
}
|
|
5685
|
-
|
|
5686
|
-
output "lambda_invoke_arn" {
|
|
5687
|
-
description = "Invoke ARN of the Lambda function"
|
|
5688
|
-
value = aws_lambda_function.api_lambda.invoke_arn
|
|
5689
|
-
}
|
|
5690
|
-
|
|
5691
|
-
output "lambda_qualified_arn" {
|
|
5692
|
-
description = "Qualified ARN of the Lambda function"
|
|
5693
|
-
value = aws_lambda_function.api_lambda.qualified_arn
|
|
5694
|
-
}
|
|
5695
|
-
|
|
5696
|
-
output "lambda_version" {
|
|
5697
|
-
description = "Version of the Lambda function"
|
|
5698
|
-
value = aws_lambda_function.api_lambda.version
|
|
5699
|
-
}
|
|
5700
|
-
|
|
5701
|
-
output "lambda_source_code_hash" {
|
|
5702
|
-
description = "Base64-encoded SHA256 hash of the Lambda deployment package"
|
|
5703
|
-
value = aws_lambda_function.api_lambda.source_code_hash
|
|
5704
|
-
}
|
|
5705
|
-
|
|
5706
|
-
output "lambda_source_code_size" {
|
|
5707
|
-
description = "Size of the Lambda deployment package in bytes"
|
|
5708
|
-
value = aws_lambda_function.api_lambda.source_code_size
|
|
5709
|
-
}
|
|
5710
|
-
|
|
5711
|
-
# IAM Role Outputs
|
|
5712
|
-
output "lambda_execution_role_arn" {
|
|
5713
|
-
description = "ARN of the Lambda execution role"
|
|
5714
|
-
value = aws_iam_role.lambda_execution_role.arn
|
|
5715
|
-
}
|
|
5716
|
-
|
|
5717
|
-
output "lambda_execution_role_name" {
|
|
5718
|
-
description = "Name of the Lambda execution role"
|
|
5719
|
-
value = aws_iam_role.lambda_execution_role.name
|
|
5720
|
-
}
|
|
5721
|
-
|
|
5722
|
-
# Integration Outputs
|
|
5723
|
-
output "integration_id" {
|
|
5724
|
-
description = "ID of the Lambda integration"
|
|
5725
|
-
value = aws_api_gateway_integration.lambda_integration.id
|
|
5726
|
-
}
|
|
5727
|
-
|
|
5728
|
-
output "proxy_resource_id" {
|
|
5729
|
-
description = "ID of the proxy resource"
|
|
5730
|
-
value = aws_api_gateway_resource.proxy_resource.id
|
|
5731
|
-
}
|
|
5732
|
-
|
|
5733
|
-
output "proxy_method_id" {
|
|
5734
|
-
description = "ID of the proxy method"
|
|
5735
|
-
value = aws_api_gateway_method.proxy_method.id
|
|
5736
|
-
}
|
|
5737
|
-
|
|
5738
|
-
# CloudWatch Log Groups
|
|
5739
|
-
output "lambda_log_group_name" {
|
|
5740
|
-
description = "Name of the Lambda CloudWatch log group"
|
|
5741
|
-
value = aws_cloudwatch_log_group.lambda_logs.name
|
|
5742
|
-
}
|
|
5743
|
-
|
|
5744
|
-
output "lambda_log_group_arn" {
|
|
5745
|
-
description = "ARN of the Lambda CloudWatch log group"
|
|
5746
|
-
value = aws_cloudwatch_log_group.lambda_logs.arn
|
|
5747
|
-
}
|
|
5748
|
-
",
|
|
5749
|
-
}
|
|
5750
|
-
`;
|
|
5751
|
-
|
|
5752
|
-
exports[`fastapi project generator > terraform iac > should generate terraform files for REST API with None auth and snapshot them > terraform-rest-none-files 1`] = `
|
|
5753
|
-
{
|
|
5754
|
-
"rest-api.tf": "# Core REST API Gateway module
|
|
5755
|
-
# This module creates the API Gateway REST API, deployment, stage, and logging resources
|
|
5756
|
-
|
|
5757
|
-
terraform {
|
|
5758
|
-
required_version = ">= 1.0"
|
|
5759
|
-
|
|
5760
|
-
required_providers {
|
|
5761
|
-
aws = {
|
|
5762
|
-
source = "hashicorp/aws"
|
|
5763
|
-
version = "~> 6.33"
|
|
5764
|
-
}
|
|
5765
|
-
random = {
|
|
5766
|
-
source = "hashicorp/random"
|
|
5767
|
-
version = "~> 3.1"
|
|
5768
|
-
}
|
|
5769
|
-
}
|
|
5770
|
-
}
|
|
5771
|
-
|
|
5772
|
-
# Core REST API Gateway Variables
|
|
5773
|
-
|
|
5774
|
-
variable "api_name" {
|
|
5775
|
-
description = "Name of the REST API Gateway"
|
|
5776
|
-
type = string
|
|
5777
|
-
}
|
|
5778
|
-
|
|
5779
|
-
variable "api_description" {
|
|
5780
|
-
description = "Description of the REST API Gateway"
|
|
5781
|
-
type = string
|
|
5782
|
-
default = "REST API Gateway"
|
|
5783
|
-
}
|
|
5784
|
-
|
|
5785
|
-
variable "stage_name" {
|
|
5786
|
-
description = "Name of the API Gateway stage"
|
|
5787
|
-
type = string
|
|
5788
|
-
default = "prod"
|
|
5789
|
-
}
|
|
5790
|
-
|
|
5791
|
-
variable "stage_auto_deploy" {
|
|
5792
|
-
description = "Whether to automatically deploy the API stage"
|
|
5793
|
-
type = bool
|
|
5794
|
-
default = true
|
|
5795
|
-
}
|
|
5796
|
-
|
|
5797
|
-
variable "enable_waf" {
|
|
5798
|
-
description = "Whether to enable AWS WAFv2 with the default managed ruleset (AWSManagedRulesCommonRuleSet and AWSManagedRulesKnownBadInputsRuleSet). The Web ACL is created here and associated with the stage in the consuming module."
|
|
5799
|
-
type = bool
|
|
5800
|
-
default = true
|
|
5801
|
-
}
|
|
5802
|
-
|
|
5803
|
-
# CORS Configuration
|
|
5804
|
-
|
|
5805
|
-
variable "cors_allow_headers" {
|
|
5806
|
-
description = "List of allowed headers for CORS"
|
|
5807
|
-
type = list(string)
|
|
5808
|
-
default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
|
|
5809
|
-
}
|
|
5810
|
-
|
|
5811
|
-
variable "cors_allow_methods" {
|
|
5812
|
-
description = "List of allowed HTTP methods for CORS"
|
|
5813
|
-
type = list(string)
|
|
5814
|
-
default = ["*"]
|
|
5815
|
-
}
|
|
5816
|
-
|
|
5817
|
-
variable "cors_allow_origins" {
|
|
5818
|
-
description = "List of allowed origins for CORS"
|
|
5819
|
-
type = list(string)
|
|
5820
|
-
default = ["*"]
|
|
5821
|
-
}
|
|
5822
|
-
|
|
5823
|
-
# Tags
|
|
5824
|
-
|
|
5825
|
-
variable "tags" {
|
|
5826
|
-
description = "Tags to apply to all resources"
|
|
5827
|
-
type = map(string)
|
|
5828
|
-
default = {}
|
|
5829
|
-
}
|
|
5830
|
-
|
|
5831
|
-
# Data sources
|
|
5832
|
-
data "aws_region" "current" {}
|
|
5833
|
-
data "aws_caller_identity" "current" {}
|
|
5834
|
-
|
|
5835
|
-
# Resources
|
|
5836
|
-
|
|
5837
|
-
# Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
|
|
5838
|
-
|
|
5839
|
-
# REST API Gateway
|
|
5840
|
-
resource "aws_api_gateway_rest_api" "rest_api" {
|
|
5841
|
-
name = var.api_name
|
|
5842
|
-
description = var.api_description
|
|
5843
|
-
|
|
5844
|
-
endpoint_configuration {
|
|
5845
|
-
types = ["REGIONAL"]
|
|
5846
|
-
}
|
|
5847
|
-
|
|
5848
|
-
lifecycle {
|
|
5849
|
-
create_before_destroy = true
|
|
5850
|
-
}
|
|
5851
|
-
|
|
5852
|
-
tags = var.tags
|
|
5853
|
-
}
|
|
5854
|
-
|
|
5855
|
-
# Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
|
|
5856
|
-
# after all methods and integrations are defined
|
|
5857
|
-
|
|
5858
|
-
# Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
|
|
5859
|
-
|
|
5860
|
-
# Gateway Response for CORS (4XX errors)
|
|
5861
|
-
resource "aws_api_gateway_gateway_response" "cors_4xx" {
|
|
5862
|
-
rest_api_id = aws_api_gateway_rest_api.rest_api.id
|
|
5863
|
-
response_type = "DEFAULT_4XX"
|
|
5864
|
-
|
|
5865
|
-
response_parameters = {
|
|
5866
|
-
"gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
|
|
5867
|
-
"gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
|
|
5868
|
-
"gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
|
|
5869
|
-
}
|
|
5870
|
-
}
|
|
5871
|
-
|
|
5872
|
-
# Gateway Response for CORS (5XX errors)
|
|
5873
|
-
resource "aws_api_gateway_gateway_response" "cors_5xx" {
|
|
5874
|
-
rest_api_id = aws_api_gateway_rest_api.rest_api.id
|
|
5875
|
-
response_type = "DEFAULT_5XX"
|
|
5876
|
-
|
|
5877
|
-
response_parameters = {
|
|
5878
|
-
"gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
|
|
5879
|
-
"gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
|
|
5880
|
-
"gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
|
|
5881
|
-
}
|
|
5882
|
-
}
|
|
5883
|
-
|
|
5884
|
-
resource "aws_wafv2_web_acl" "api_waf" {
|
|
5885
|
-
#checkov:skip=CKV2_AWS_31:Logging configuration is defined below in aws_wafv2_web_acl_logging_configuration.api_waf_logging; Checkov does not resolve the separate resource
|
|
5886
|
-
count = var.enable_waf ? 1 : 0
|
|
5887
|
-
|
|
5888
|
-
name = "\${var.api_name}-waf"
|
|
5889
|
-
scope = "REGIONAL"
|
|
5890
|
-
|
|
5891
|
-
default_action {
|
|
5892
|
-
allow {}
|
|
5893
|
-
}
|
|
5894
|
-
|
|
5895
|
-
rule {
|
|
5896
|
-
name = "CRSRule"
|
|
5897
|
-
priority = 0
|
|
5898
|
-
|
|
5899
|
-
override_action {
|
|
5900
|
-
none {}
|
|
5901
|
-
}
|
|
5902
|
-
|
|
5903
|
-
statement {
|
|
5904
|
-
managed_rule_group_statement {
|
|
5905
|
-
name = "AWSManagedRulesCommonRuleSet"
|
|
5906
|
-
vendor_name = "AWS"
|
|
5907
|
-
|
|
5908
|
-
# Count instead of Block: the CRS 8 KB body limit is too restrictive for most APIs.
|
|
5909
|
-
rule_action_override {
|
|
5910
|
-
name = "SizeRestrictions_BODY"
|
|
5911
|
-
action_to_use {
|
|
5912
|
-
count {}
|
|
5913
|
-
}
|
|
5914
|
-
}
|
|
5915
|
-
}
|
|
5916
|
-
}
|
|
5917
|
-
|
|
5918
|
-
visibility_config {
|
|
5919
|
-
cloudwatch_metrics_enabled = true
|
|
5920
|
-
metric_name = "\${var.api_name}WebAcl-CRS"
|
|
5921
|
-
sampled_requests_enabled = true
|
|
5922
|
-
}
|
|
5923
|
-
}
|
|
5924
|
-
|
|
5925
|
-
rule {
|
|
5926
|
-
name = "KnownBadInputsRule"
|
|
5927
|
-
priority = 1
|
|
5928
|
-
|
|
5929
|
-
override_action {
|
|
5930
|
-
none {}
|
|
5931
|
-
}
|
|
5932
|
-
|
|
5933
|
-
statement {
|
|
5934
|
-
managed_rule_group_statement {
|
|
5935
|
-
name = "AWSManagedRulesKnownBadInputsRuleSet"
|
|
5936
|
-
vendor_name = "AWS"
|
|
5937
|
-
}
|
|
5938
|
-
}
|
|
5939
|
-
|
|
5940
|
-
visibility_config {
|
|
5941
|
-
cloudwatch_metrics_enabled = true
|
|
5942
|
-
metric_name = "\${var.api_name}WebAcl-KnownBadInputs"
|
|
5943
|
-
sampled_requests_enabled = true
|
|
5944
|
-
}
|
|
5945
|
-
}
|
|
5946
|
-
|
|
5947
|
-
visibility_config {
|
|
5948
|
-
cloudwatch_metrics_enabled = true
|
|
5949
|
-
metric_name = "\${var.api_name}WebAcl"
|
|
5950
|
-
sampled_requests_enabled = true
|
|
5951
|
-
}
|
|
5952
|
-
|
|
5953
|
-
tags = var.tags
|
|
5954
|
-
|
|
5955
|
-
lifecycle {
|
|
5956
|
-
create_before_destroy = true
|
|
5957
|
-
}
|
|
5958
|
-
}
|
|
5959
|
-
|
|
5960
|
-
# CloudWatch Log Group for WAF request logs. Name must start with \`aws-waf-logs-\`.
|
|
5961
|
-
resource "aws_cloudwatch_log_group" "api_waf_logs" {
|
|
5962
|
-
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
5963
|
-
#checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
|
|
5964
|
-
count = var.enable_waf ? 1 : 0
|
|
5965
|
-
|
|
5966
|
-
name = "aws-waf-logs-\${var.api_name}-\${random_id.waf_log_suffix[0].hex}"
|
|
5967
|
-
retention_in_days = 30
|
|
5968
|
-
tags = var.tags
|
|
5969
|
-
}
|
|
5970
|
-
|
|
5971
|
-
resource "random_id" "waf_log_suffix" {
|
|
5972
|
-
count = var.enable_waf ? 1 : 0
|
|
5973
|
-
byte_length = 4
|
|
5974
|
-
}
|
|
5975
|
-
|
|
5976
|
-
resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
|
|
5977
|
-
count = var.enable_waf ? 1 : 0
|
|
5978
|
-
|
|
5979
|
-
log_destination_configs = [aws_cloudwatch_log_group.api_waf_logs[0].arn]
|
|
5980
|
-
resource_arn = aws_wafv2_web_acl.api_waf[0].arn
|
|
5981
|
-
}
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
# Outputs
|
|
5985
|
-
|
|
5986
|
-
output "api_id" {
|
|
5987
|
-
description = "ID of the REST API Gateway"
|
|
5988
|
-
value = aws_api_gateway_rest_api.rest_api.id
|
|
5989
|
-
}
|
|
5990
|
-
|
|
5991
|
-
output "api_arn" {
|
|
5992
|
-
description = "ARN of the REST API Gateway"
|
|
5993
|
-
value = aws_api_gateway_rest_api.rest_api.arn
|
|
5994
|
-
}
|
|
5995
|
-
|
|
5996
|
-
output "api_endpoint" {
|
|
5997
|
-
description = "Base URL of the REST API Gateway"
|
|
5998
|
-
value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
|
|
5999
|
-
}
|
|
6000
|
-
|
|
6001
|
-
output "api_execution_arn" {
|
|
6002
|
-
description = "Execution ARN of the REST API Gateway"
|
|
6003
|
-
value = aws_api_gateway_rest_api.rest_api.execution_arn
|
|
6004
|
-
}
|
|
6005
|
-
|
|
6006
|
-
output "api_root_resource_id" {
|
|
6007
|
-
description = "Root resource ID of the REST API Gateway"
|
|
6008
|
-
value = aws_api_gateway_rest_api.rest_api.root_resource_id
|
|
6009
|
-
}
|
|
6010
|
-
|
|
6011
|
-
output "waf_web_acl_arn" {
|
|
6012
|
-
description = "ARN of the WAFv2 Web ACL associated with the API, or null if WAF is disabled"
|
|
6013
|
-
value = var.enable_waf ? aws_wafv2_web_acl.api_waf[0].arn : null
|
|
6014
|
-
}
|
|
6015
|
-
|
|
6016
|
-
# Note: Stage and deployment outputs are provided by the consuming module
|
|
6017
|
-
|
|
6018
|
-
# Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
|
|
6019
|
-
"test-api.tf": "terraform {
|
|
6020
|
-
required_version = ">= 1.0"
|
|
6021
|
-
|
|
6022
|
-
required_providers {
|
|
6023
|
-
aws = {
|
|
6024
|
-
source = "hashicorp/aws"
|
|
6025
|
-
version = "~> 6.33"
|
|
6026
|
-
}
|
|
6027
|
-
}
|
|
6028
|
-
}
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
variable "env" {
|
|
6032
|
-
description = "Environment variables for the Lambda function"
|
|
6033
|
-
type = map(string)
|
|
6034
|
-
default = {}
|
|
6035
|
-
}
|
|
6036
|
-
|
|
6037
|
-
variable "additional_iam_policy_statements" {
|
|
6038
|
-
description = "Additional IAM policy statements for the Lambda function"
|
|
6039
|
-
type = list(object({
|
|
6040
|
-
Effect = string
|
|
6041
|
-
Action = list(string)
|
|
6042
|
-
Resource = list(string)
|
|
6043
|
-
}))
|
|
6044
|
-
default = []
|
|
6045
|
-
}
|
|
6046
|
-
|
|
6047
|
-
variable "asset_bucket_name" {
|
|
6048
|
-
description = "Name of the shared asset S3 bucket used to stage the Lambda deployment zip. Instantiate the \`core/asset-bucket\` module once per deployment and pass its \`bucket_name\` output here."
|
|
6049
|
-
type = string
|
|
6050
|
-
}
|
|
6051
|
-
|
|
6052
|
-
# CORS Configuration (passed to core module)
|
|
6053
|
-
|
|
6054
|
-
variable "cors_allow_headers" {
|
|
6055
|
-
description = "List of allowed headers for CORS"
|
|
6056
|
-
type = list(string)
|
|
6057
|
-
default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
|
|
6058
|
-
}
|
|
6059
|
-
|
|
6060
|
-
variable "cors_allow_methods" {
|
|
6061
|
-
description = "List of allowed HTTP methods for CORS"
|
|
6062
|
-
type = list(string)
|
|
6063
|
-
default = ["*"]
|
|
6064
|
-
}
|
|
6065
|
-
|
|
6066
|
-
variable "cors_allow_origins" {
|
|
6067
|
-
description = "List of allowed origins for CORS"
|
|
6068
|
-
type = list(string)
|
|
6069
|
-
default = ["*"]
|
|
6070
|
-
}
|
|
6071
|
-
|
|
6072
|
-
# WAF Configuration
|
|
6073
|
-
variable "enable_waf" {
|
|
6074
|
-
description = "Whether to enable AWS WAFv2 with the default managed ruleset on the API stage"
|
|
6075
|
-
type = bool
|
|
6076
|
-
default = true
|
|
6077
|
-
}
|
|
6078
|
-
|
|
6079
|
-
# Tags
|
|
6080
|
-
variable "tags" {
|
|
6081
|
-
description = "Tags to apply to all resources"
|
|
6082
|
-
type = map(string)
|
|
6083
|
-
default = {}
|
|
6084
|
-
}
|
|
6085
|
-
|
|
6086
|
-
# Get current AWS region and account ID
|
|
6087
|
-
data "aws_region" "current" {}
|
|
6088
|
-
data "aws_caller_identity" "current" {}
|
|
6089
|
-
|
|
6090
|
-
resource "random_string" "suffix" {
|
|
6091
|
-
length = 8
|
|
6092
|
-
special = false
|
|
6093
|
-
upper = false
|
|
6094
|
-
}
|
|
6095
|
-
|
|
6096
|
-
# Resources
|
|
6097
|
-
|
|
6098
|
-
# Create Lambda ZIP file from the bundle directory
|
|
6099
|
-
data "archive_file" "lambda_zip" {
|
|
6100
|
-
type = "zip"
|
|
6101
|
-
source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
|
|
6102
|
-
output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
|
|
6103
|
-
}
|
|
6104
|
-
|
|
6105
|
-
resource "aws_s3_object" "lambda_zip" {
|
|
6106
|
-
bucket = var.asset_bucket_name
|
|
6107
|
-
key = "apis/test-api/\${data.archive_file.lambda_zip.output_sha256}.zip"
|
|
6108
|
-
source = data.archive_file.lambda_zip.output_path
|
|
6109
|
-
source_hash = data.archive_file.lambda_zip.output_base64sha256
|
|
6110
|
-
etag = data.archive_file.lambda_zip.output_md5
|
|
6111
|
-
}
|
|
6112
|
-
|
|
6113
|
-
# Use the core REST API module
|
|
6114
|
-
module "rest_api" {
|
|
6115
|
-
source = "../../../core/api/rest-api"
|
|
6116
|
-
|
|
6117
|
-
api_name = "TestApi-\${random_string.suffix.result}"
|
|
6118
|
-
api_description = "TestApi REST API"
|
|
6119
|
-
stage_name = "prod"
|
|
6120
|
-
stage_auto_deploy = true
|
|
6121
|
-
|
|
6122
|
-
# WAF Configuration
|
|
6123
|
-
enable_waf = var.enable_waf
|
|
6124
|
-
|
|
6125
|
-
# CORS Configuration
|
|
6126
|
-
cors_allow_headers = var.cors_allow_headers
|
|
6127
|
-
cors_allow_methods = var.cors_allow_methods
|
|
6128
|
-
cors_allow_origins = var.cors_allow_origins
|
|
6129
|
-
|
|
6130
|
-
# Tags
|
|
6131
|
-
tags = var.tags
|
|
6132
|
-
}
|
|
6133
|
-
|
|
6134
|
-
resource "aws_wafv2_web_acl_association" "api_waf_association" {
|
|
6135
|
-
count = var.enable_waf ? 1 : 0
|
|
6136
|
-
|
|
6137
|
-
resource_arn = aws_api_gateway_stage.api_stage.arn
|
|
6138
|
-
web_acl_arn = module.rest_api.waf_web_acl_arn
|
|
6139
|
-
|
|
6140
|
-
depends_on = [aws_api_gateway_stage.api_stage]
|
|
6141
|
-
}
|
|
6142
|
-
|
|
6143
|
-
# Lambda function
|
|
6144
|
-
resource "aws_lambda_function" "api_lambda" {
|
|
6145
|
-
#checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
|
|
6146
|
-
#checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
|
|
6147
|
-
#checkov:skip=CKV_AWS_272:Code signing not required for this use case
|
|
6148
|
-
#checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
|
|
6149
|
-
#checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
|
|
6150
|
-
s3_bucket = aws_s3_object.lambda_zip.bucket
|
|
6151
|
-
s3_key = aws_s3_object.lambda_zip.key
|
|
6152
|
-
s3_object_version = aws_s3_object.lambda_zip.version_id
|
|
6153
|
-
function_name = "TestApiHandler-\${random_string.suffix.result}"
|
|
6154
|
-
role = aws_iam_role.lambda_execution_role.arn
|
|
6155
|
-
handler = "run.sh"
|
|
6156
|
-
runtime = "python3.14"
|
|
6157
|
-
layers = ["arn:aws:lambda:\${data.aws_region.current.region}:753240598075:layer:LambdaAdapterLayerX86:24"]
|
|
6158
|
-
timeout = 30
|
|
6159
|
-
memory_size = 128
|
|
6160
|
-
publish = true
|
|
6161
|
-
|
|
6162
|
-
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
|
|
6163
|
-
|
|
6164
|
-
# Enable X-Ray tracing
|
|
6165
|
-
tracing_config {
|
|
6166
|
-
mode = "Active"
|
|
6167
|
-
}
|
|
6168
|
-
|
|
6169
|
-
# Enable SnapStart for faster cold starts
|
|
6170
|
-
snap_start {
|
|
6171
|
-
apply_on = "PublishedVersions"
|
|
6172
|
-
}
|
|
6173
|
-
|
|
6174
|
-
environment {
|
|
6175
|
-
variables = merge({
|
|
6176
|
-
PORT = "8000"
|
|
6177
|
-
AWS_LWA_INVOKE_MODE = "response_stream"
|
|
6178
|
-
AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
|
|
6179
|
-
}, var.env)
|
|
6180
|
-
}
|
|
6181
|
-
|
|
6182
|
-
tags = var.tags
|
|
6183
|
-
}
|
|
6184
|
-
|
|
6185
|
-
# IAM role for Lambda execution
|
|
6186
|
-
resource "aws_iam_role" "lambda_execution_role" {
|
|
6187
|
-
name = "TestApiHandler-execution-role-\${random_string.suffix.result}"
|
|
6188
|
-
|
|
6189
|
-
assume_role_policy = jsonencode({
|
|
6190
|
-
Version = "2012-10-17"
|
|
6191
|
-
Statement = [
|
|
6192
|
-
{
|
|
6193
|
-
Action = "sts:AssumeRole"
|
|
6194
|
-
Effect = "Allow"
|
|
6195
|
-
Principal = {
|
|
6196
|
-
Service = "lambda.amazonaws.com"
|
|
6197
|
-
}
|
|
6198
|
-
}
|
|
6199
|
-
]
|
|
6200
|
-
})
|
|
6201
|
-
|
|
6202
|
-
tags = var.tags
|
|
6203
|
-
}
|
|
6204
|
-
|
|
6205
|
-
# Attach basic execution policy to Lambda role
|
|
6206
|
-
resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
|
|
6207
|
-
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
6208
|
-
role = aws_iam_role.lambda_execution_role.name
|
|
6209
|
-
}
|
|
6210
|
-
|
|
6211
|
-
# Attach X-Ray tracing policy to Lambda role
|
|
6212
|
-
resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
|
|
6213
|
-
policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
|
|
6214
|
-
role = aws_iam_role.lambda_execution_role.name
|
|
6215
|
-
}
|
|
6216
|
-
|
|
6217
|
-
# Additional IAM policies for Lambda (if provided)
|
|
6218
|
-
resource "aws_iam_role_policy" "lambda_additional_policies" {
|
|
6219
|
-
count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
|
|
6220
|
-
name = "TestApiHandler-additional-policies-\${random_string.suffix.result}"
|
|
6221
|
-
role = aws_iam_role.lambda_execution_role.id
|
|
6222
|
-
|
|
6223
|
-
policy = jsonencode({
|
|
6224
|
-
Version = "2012-10-17"
|
|
6225
|
-
Statement = var.additional_iam_policy_statements
|
|
6226
|
-
})
|
|
6227
|
-
}
|
|
6228
|
-
|
|
6229
|
-
# CloudWatch Log Group for Lambda
|
|
6230
|
-
resource "aws_cloudwatch_log_group" "lambda_logs" {
|
|
6231
|
-
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
6232
|
-
#checkov:skip=CKV_AWS_338:Log retention set to forever
|
|
6233
|
-
#checkov:skip=CKV_AWS_66:Log retention set to forever
|
|
6234
|
-
name = "/aws/lambda/TestApiHandler-\${random_string.suffix.result}"
|
|
6235
|
-
tags = var.tags
|
|
6236
|
-
}
|
|
6237
|
-
|
|
6238
|
-
# Lambda alias pointing to the latest published version for SnapStart
|
|
6239
|
-
resource "aws_lambda_alias" "live" {
|
|
6240
|
-
name = "live"
|
|
6241
|
-
function_name = aws_lambda_function.api_lambda.function_name
|
|
6242
|
-
function_version = aws_lambda_function.api_lambda.version
|
|
6243
|
-
|
|
6244
|
-
depends_on = [aws_lambda_function.api_lambda]
|
|
6245
|
-
}
|
|
6246
|
-
|
|
6247
|
-
# Custom Lambda Authorizer
|
|
6248
|
-
resource "aws_lambda_function" "authorizer_lambda" {
|
|
6249
|
-
#checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
|
|
6250
|
-
#checkov:skip=CKV_AWS_116:Dead Letter Queue not required for authorizer
|
|
6251
|
-
#checkov:skip=CKV_AWS_272:Code signing not required for this use case
|
|
6252
|
-
#checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
|
|
6253
|
-
#checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
|
|
6254
|
-
s3_bucket = aws_s3_object.authorizer_zip.bucket
|
|
6255
|
-
s3_key = aws_s3_object.authorizer_zip.key
|
|
6256
|
-
s3_object_version = aws_s3_object.authorizer_zip.version_id
|
|
6257
|
-
function_name = "TestApiAuthorizer-\${random_string.suffix.result}"
|
|
6258
|
-
role = aws_iam_role.authorizer_execution_role.arn
|
|
6259
|
-
handler = "proj_test_api.authorizer.handler"
|
|
6260
|
-
runtime = "python3.14"
|
|
6261
|
-
timeout = 10
|
|
6262
|
-
memory_size = 128
|
|
6263
|
-
|
|
6264
|
-
source_code_hash = data.archive_file.authorizer_zip.output_base64sha256
|
|
6265
|
-
|
|
6266
|
-
tracing_config {
|
|
6267
|
-
mode = "Active"
|
|
6268
|
-
}
|
|
6269
|
-
|
|
6270
|
-
tags = var.tags
|
|
6271
|
-
}
|
|
6272
|
-
|
|
6273
|
-
resource "aws_iam_role" "authorizer_execution_role" {
|
|
6274
|
-
name = "TestApiAuthorizer-role-\${random_string.suffix.result}"
|
|
6275
|
-
|
|
6276
|
-
assume_role_policy = jsonencode({
|
|
6277
|
-
Version = "2012-10-17"
|
|
6278
|
-
Statement = [
|
|
6279
|
-
{
|
|
6280
|
-
Action = "sts:AssumeRole"
|
|
6281
|
-
Effect = "Allow"
|
|
6282
|
-
Principal = {
|
|
6283
|
-
Service = "lambda.amazonaws.com"
|
|
6284
|
-
}
|
|
6285
|
-
}
|
|
6286
|
-
]
|
|
6287
|
-
})
|
|
6288
|
-
|
|
6289
|
-
tags = var.tags
|
|
6290
|
-
}
|
|
6291
|
-
|
|
6292
|
-
resource "aws_iam_role_policy_attachment" "authorizer_basic_execution" {
|
|
6293
|
-
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
6294
|
-
role = aws_iam_role.authorizer_execution_role.name
|
|
6295
|
-
}
|
|
6296
|
-
|
|
6297
|
-
resource "aws_iam_role_policy_attachment" "authorizer_xray_execution" {
|
|
6298
|
-
policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
|
|
6299
|
-
role = aws_iam_role.authorizer_execution_role.name
|
|
6300
|
-
}
|
|
6301
|
-
|
|
6302
|
-
resource "aws_cloudwatch_log_group" "authorizer_logs" {
|
|
6303
|
-
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
6304
|
-
#checkov:skip=CKV_AWS_338:Log retention set to forever
|
|
6305
|
-
#checkov:skip=CKV_AWS_66:Log retention set to forever
|
|
6306
|
-
name = "/aws/lambda/TestApiAuthorizer-\${random_string.suffix.result}"
|
|
6307
|
-
tags = var.tags
|
|
6308
|
-
}
|
|
6309
|
-
|
|
6310
|
-
data "archive_file" "authorizer_zip" {
|
|
6311
|
-
type = "zip"
|
|
6312
|
-
source_dir = "\${path.module}/../../../../../../../dist/apps/test_api/bundle-x86"
|
|
6313
|
-
output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/authorizer.zip"
|
|
6314
|
-
}
|
|
6315
|
-
|
|
6316
|
-
resource "aws_s3_object" "authorizer_zip" {
|
|
6317
|
-
bucket = var.asset_bucket_name
|
|
6318
|
-
key = "apis/test-api/authorizer-\${data.archive_file.authorizer_zip.output_sha256}.zip"
|
|
6319
|
-
source = data.archive_file.authorizer_zip.output_path
|
|
6320
|
-
source_hash = data.archive_file.authorizer_zip.output_base64sha256
|
|
6321
|
-
etag = data.archive_file.authorizer_zip.output_md5
|
|
6322
|
-
}
|
|
6323
|
-
|
|
6324
|
-
resource "aws_api_gateway_authorizer" "custom_authorizer" {
|
|
6325
|
-
name = "TestApiAuthorizer-\${random_string.suffix.result}"
|
|
6326
|
-
rest_api_id = module.rest_api.api_id
|
|
6327
|
-
type = "TOKEN"
|
|
6328
|
-
authorizer_uri = aws_lambda_function.authorizer_lambda.invoke_arn
|
|
6329
|
-
identity_source = "method.request.header.Authorization"
|
|
6330
|
-
}
|
|
6331
|
-
|
|
6332
|
-
resource "aws_lambda_permission" "authorizer_invoke" {
|
|
6333
|
-
statement_id = "AllowAPIGatewayInvokeAuthorizer"
|
|
6334
|
-
action = "lambda:InvokeFunction"
|
|
6335
|
-
function_name = aws_lambda_function.authorizer_lambda.function_name
|
|
6336
|
-
principal = "apigateway.amazonaws.com"
|
|
6337
|
-
source_arn = "\${module.rest_api.api_execution_arn}/authorizers/\${aws_api_gateway_authorizer.custom_authorizer.id}"
|
|
6338
|
-
}
|
|
6339
|
-
|
|
6340
|
-
# Create proxy resource (captures all paths)
|
|
6341
|
-
resource "aws_api_gateway_resource" "proxy_resource" {
|
|
6342
|
-
rest_api_id = module.rest_api.api_id
|
|
6343
|
-
parent_id = module.rest_api.api_root_resource_id
|
|
6344
|
-
path_part = "{proxy+}"
|
|
6345
|
-
}
|
|
6346
|
-
|
|
6347
|
-
# Lambda integration for REST API
|
|
6348
|
-
resource "aws_api_gateway_integration" "lambda_integration" {
|
|
6349
|
-
rest_api_id = module.rest_api.api_id
|
|
6350
|
-
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
6351
|
-
http_method = aws_api_gateway_method.proxy_method.http_method
|
|
6352
|
-
|
|
6353
|
-
integration_http_method = "POST"
|
|
6354
|
-
type = "AWS_PROXY"
|
|
6355
|
-
# Use the response streaming invocation path with the alias ARN for SnapStart
|
|
6356
|
-
uri = "arn:aws:apigateway:\${data.aws_region.current.region}:lambda:path/2021-11-15/functions/\${aws_lambda_alias.live.arn}/response-streaming-invocations"
|
|
6357
|
-
response_transfer_mode = "STREAM"
|
|
6358
|
-
|
|
6359
|
-
depends_on = [aws_lambda_alias.live]
|
|
6360
|
-
}
|
|
6361
|
-
|
|
6362
|
-
# Method for proxy integration
|
|
6363
|
-
resource "aws_api_gateway_method" "proxy_method" {
|
|
6364
|
-
#checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
|
|
6365
|
-
rest_api_id = module.rest_api.api_id
|
|
6366
|
-
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
6367
|
-
http_method = "ANY"
|
|
6368
|
-
|
|
6369
|
-
authorization = "CUSTOM"
|
|
6370
|
-
authorizer_id = aws_api_gateway_authorizer.custom_authorizer.id
|
|
6371
|
-
|
|
6372
|
-
request_parameters = {
|
|
6373
|
-
"method.request.path.proxy" = true
|
|
6374
|
-
}
|
|
6375
|
-
|
|
6376
|
-
depends_on = [aws_api_gateway_authorizer.custom_authorizer]
|
|
6377
|
-
}
|
|
6378
|
-
|
|
6379
|
-
# OPTIONS method for CORS preflight
|
|
6380
|
-
resource "aws_api_gateway_method" "options_method" {
|
|
6381
|
-
#checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
|
|
6382
|
-
#checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
|
|
6383
|
-
rest_api_id = module.rest_api.api_id
|
|
6384
|
-
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
6385
|
-
http_method = "OPTIONS"
|
|
6386
|
-
authorization = "NONE"
|
|
6387
|
-
}
|
|
6388
|
-
|
|
6389
|
-
# CORS integration for OPTIONS method
|
|
6390
|
-
resource "aws_api_gateway_integration" "options_integration" {
|
|
6391
|
-
rest_api_id = module.rest_api.api_id
|
|
6392
|
-
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
6393
|
-
http_method = aws_api_gateway_method.options_method.http_method
|
|
6394
|
-
|
|
6395
|
-
type = "MOCK"
|
|
6396
|
-
request_templates = {
|
|
6397
|
-
"application/json" = "{\\"statusCode\\": 204}"
|
|
6398
|
-
}
|
|
6399
|
-
}
|
|
6400
|
-
|
|
6401
|
-
# OPTIONS method response
|
|
6402
|
-
resource "aws_api_gateway_method_response" "options_response" {
|
|
6403
|
-
rest_api_id = module.rest_api.api_id
|
|
6404
|
-
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
6405
|
-
http_method = aws_api_gateway_method.options_method.http_method
|
|
6406
|
-
status_code = "204"
|
|
6407
|
-
|
|
6408
|
-
response_parameters = {
|
|
6409
|
-
"method.response.header.Access-Control-Allow-Headers" = true
|
|
6410
|
-
"method.response.header.Access-Control-Allow-Methods" = true
|
|
6411
|
-
"method.response.header.Access-Control-Allow-Origin" = true
|
|
6412
|
-
}
|
|
6413
|
-
}
|
|
6414
|
-
|
|
6415
|
-
# OPTIONS integration response
|
|
6416
|
-
resource "aws_api_gateway_integration_response" "options_integration_response" {
|
|
6417
|
-
rest_api_id = module.rest_api.api_id
|
|
6418
|
-
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
6419
|
-
http_method = aws_api_gateway_method.options_method.http_method
|
|
6420
|
-
status_code = aws_api_gateway_method_response.options_response.status_code
|
|
6421
|
-
|
|
6422
|
-
response_parameters = {
|
|
6423
|
-
"method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
|
|
6424
|
-
"method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
|
|
6425
|
-
"method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
|
|
6426
|
-
}
|
|
6427
|
-
}
|
|
6428
|
-
|
|
6429
|
-
# API Gateway deployment
|
|
6430
|
-
resource "aws_api_gateway_deployment" "api_deployment" {
|
|
6431
|
-
rest_api_id = module.rest_api.api_id
|
|
6432
|
-
|
|
6433
|
-
triggers = {
|
|
6434
|
-
redeployment = sha1(jsonencode([
|
|
6435
|
-
aws_api_gateway_resource.proxy_resource.id,
|
|
6436
|
-
aws_api_gateway_method.proxy_method.id,
|
|
6437
|
-
aws_api_gateway_integration.lambda_integration.id,
|
|
6438
|
-
aws_api_gateway_method.options_method.id,
|
|
6439
|
-
aws_api_gateway_integration.options_integration.id,
|
|
6440
|
-
]))
|
|
6441
|
-
}
|
|
6442
|
-
|
|
6443
|
-
lifecycle {
|
|
6444
|
-
create_before_destroy = true
|
|
6445
|
-
}
|
|
6446
|
-
|
|
6447
|
-
depends_on = [
|
|
6448
|
-
aws_api_gateway_method.proxy_method,
|
|
6449
|
-
aws_api_gateway_integration.lambda_integration,
|
|
6450
|
-
aws_api_gateway_method.options_method,
|
|
6451
|
-
aws_api_gateway_integration.options_integration,
|
|
6452
|
-
aws_api_gateway_method_response.options_response,
|
|
6453
|
-
aws_api_gateway_integration_response.options_integration_response,
|
|
6454
|
-
aws_api_gateway_authorizer.custom_authorizer,
|
|
6455
|
-
]
|
|
6456
|
-
}
|
|
6457
|
-
|
|
6458
|
-
# API Gateway stage
|
|
6459
|
-
resource "aws_api_gateway_stage" "api_stage" {
|
|
6460
|
-
#checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
|
|
6461
|
-
#checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
|
|
6462
|
-
#checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
|
|
6463
|
-
#checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
|
|
6464
|
-
#checkov:skip=CKV2_AWS_77:WAFv2 Web ACL is attached via aws_wafv2_web_acl_association when var.enable_waf is true (default) and includes AWSManagedRulesKnownBadInputsRuleSet for Log4j protection; Checkov does not track the association across modules
|
|
6465
|
-
deployment_id = aws_api_gateway_deployment.api_deployment.id
|
|
6466
|
-
rest_api_id = module.rest_api.api_id
|
|
6467
|
-
stage_name = "prod"
|
|
6468
|
-
xray_tracing_enabled = true
|
|
6469
|
-
|
|
6470
|
-
tags = var.tags
|
|
6471
|
-
|
|
6472
|
-
depends_on = [aws_api_gateway_deployment.api_deployment]
|
|
6473
|
-
}
|
|
6474
|
-
|
|
6475
|
-
# API Gateway Resource Policy
|
|
6476
|
-
resource "aws_api_gateway_rest_api_policy" "api_policy" {
|
|
6477
|
-
rest_api_id = module.rest_api.api_id
|
|
6478
|
-
|
|
6479
|
-
policy = jsonencode({
|
|
6480
|
-
Version = "2012-10-17"
|
|
6481
|
-
Statement = [
|
|
6482
|
-
{
|
|
6483
|
-
# Allow all callers to invoke the API in the resource policy, since auth is handled by the Lambda Authorizer
|
|
6484
|
-
Effect = "Allow"
|
|
6485
|
-
Principal = "*"
|
|
6486
|
-
Action = "execute-api:Invoke"
|
|
6487
|
-
Resource = "execute-api:/*"
|
|
6488
|
-
}
|
|
6489
|
-
]
|
|
6490
|
-
})
|
|
6491
|
-
}
|
|
6492
|
-
|
|
6493
|
-
# Lambda permission for API Gateway to invoke the function via alias
|
|
6494
|
-
resource "aws_lambda_permission" "api_gateway_invoke" {
|
|
6495
|
-
statement_id = "AllowExecutionFromAPIGateway"
|
|
6496
|
-
action = "lambda:InvokeFunction"
|
|
6497
|
-
function_name = aws_lambda_function.api_lambda.function_name
|
|
6498
|
-
qualifier = aws_lambda_alias.live.name
|
|
6499
|
-
principal = "apigateway.amazonaws.com"
|
|
6500
|
-
source_arn = "\${module.rest_api.api_execution_arn}/*/*"
|
|
6501
|
-
|
|
6502
|
-
depends_on = [module.rest_api, aws_lambda_alias.live]
|
|
6503
|
-
}
|
|
6504
|
-
|
|
6505
|
-
# Lambda permission for API Gateway to invoke with response streaming via alias
|
|
6506
|
-
resource "aws_lambda_permission" "api_gateway_invoke_streaming" {
|
|
6507
|
-
statement_id = "AllowStreamingFromAPIGateway"
|
|
6508
|
-
action = "lambda:InvokeFunctionWithResponseStream"
|
|
6509
|
-
function_name = aws_lambda_function.api_lambda.function_name
|
|
6510
|
-
qualifier = aws_lambda_alias.live.name
|
|
6511
|
-
principal = "apigateway.amazonaws.com"
|
|
6512
|
-
source_arn = "\${module.rest_api.api_execution_arn}/*/*"
|
|
6513
|
-
|
|
6514
|
-
depends_on = [module.rest_api, aws_lambda_alias.live]
|
|
6515
|
-
}
|
|
6516
|
-
|
|
6517
|
-
# Add API url to runtime config
|
|
6518
|
-
module "add_url_to_runtime_config" {
|
|
6519
|
-
source = "../../../core/runtime-config/entry"
|
|
6520
|
-
|
|
6521
|
-
namespace = "connection"
|
|
6522
|
-
key = "apis"
|
|
6523
|
-
value = { "TestApi" = aws_api_gateway_stage.api_stage.invoke_url }
|
|
6524
|
-
|
|
6525
|
-
depends_on = [aws_api_gateway_stage.api_stage]
|
|
6526
|
-
}
|
|
6527
|
-
|
|
6528
|
-
# Outputs
|
|
6529
|
-
|
|
6530
|
-
# API Gateway Outputs (from core module)
|
|
6531
|
-
output "api_id" {
|
|
6532
|
-
description = "ID of the REST API Gateway"
|
|
6533
|
-
value = module.rest_api.api_id
|
|
6534
|
-
}
|
|
6535
|
-
|
|
6536
|
-
output "api_arn" {
|
|
6537
|
-
description = "ARN of the REST API Gateway"
|
|
6538
|
-
value = module.rest_api.api_arn
|
|
6539
|
-
}
|
|
6540
|
-
|
|
6541
|
-
output "api_endpoint" {
|
|
6542
|
-
description = "Base URL of the REST API Gateway"
|
|
6543
|
-
value = module.rest_api.api_endpoint
|
|
6544
|
-
}
|
|
6545
|
-
|
|
6546
|
-
output "api_execution_arn" {
|
|
6547
|
-
description = "Execution ARN of the REST API Gateway"
|
|
6548
|
-
value = module.rest_api.api_execution_arn
|
|
6549
|
-
}
|
|
6550
|
-
|
|
6551
|
-
output "stage_invoke_url" {
|
|
6552
|
-
description = "Invoke URL of the API Gateway stage"
|
|
6553
|
-
value = aws_api_gateway_stage.api_stage.invoke_url
|
|
6554
|
-
}
|
|
6555
|
-
|
|
6556
|
-
output "stage_arn" {
|
|
6557
|
-
description = "ARN of the API Gateway stage"
|
|
6558
|
-
value = aws_api_gateway_stage.api_stage.arn
|
|
6559
|
-
}
|
|
6560
|
-
|
|
6561
|
-
output "stage_execution_arn" {
|
|
6562
|
-
description = "Execution ARN of the API Gateway stage"
|
|
6563
|
-
value = aws_api_gateway_stage.api_stage.execution_arn
|
|
6564
|
-
}
|
|
6565
|
-
|
|
6566
|
-
output "deployment_id" {
|
|
6567
|
-
description = "ID of the API Gateway deployment"
|
|
6568
|
-
value = aws_api_gateway_deployment.api_deployment.id
|
|
6569
|
-
}
|
|
6570
|
-
|
|
6571
|
-
output "stage_id" {
|
|
6572
|
-
description = "ID of the API Gateway stage"
|
|
6573
|
-
value = aws_api_gateway_stage.api_stage.id
|
|
6574
|
-
}
|
|
6575
|
-
|
|
6576
|
-
# Lambda Function Outputs
|
|
6577
|
-
output "lambda_function_name" {
|
|
6578
|
-
description = "Name of the Lambda function"
|
|
6579
|
-
value = aws_lambda_function.api_lambda.function_name
|
|
6580
|
-
}
|
|
6581
|
-
|
|
6582
|
-
output "lambda_function_arn" {
|
|
6583
|
-
description = "ARN of the Lambda function"
|
|
6584
|
-
value = aws_lambda_function.api_lambda.arn
|
|
6585
|
-
}
|
|
6586
|
-
|
|
6587
|
-
output "lambda_invoke_arn" {
|
|
6588
|
-
description = "Invoke ARN of the Lambda function"
|
|
6589
|
-
value = aws_lambda_function.api_lambda.invoke_arn
|
|
6590
|
-
}
|
|
6591
|
-
|
|
6592
|
-
output "lambda_qualified_arn" {
|
|
6593
|
-
description = "Qualified ARN of the Lambda function"
|
|
6594
|
-
value = aws_lambda_function.api_lambda.qualified_arn
|
|
6595
|
-
}
|
|
6596
|
-
|
|
6597
|
-
output "lambda_version" {
|
|
6598
|
-
description = "Version of the Lambda function"
|
|
6599
|
-
value = aws_lambda_function.api_lambda.version
|
|
6600
|
-
}
|
|
6601
|
-
|
|
6602
|
-
output "lambda_source_code_hash" {
|
|
6603
|
-
description = "Base64-encoded SHA256 hash of the Lambda deployment package"
|
|
6604
|
-
value = aws_lambda_function.api_lambda.source_code_hash
|
|
6605
|
-
}
|
|
6606
|
-
|
|
6607
|
-
output "lambda_source_code_size" {
|
|
6608
|
-
description = "Size of the Lambda deployment package in bytes"
|
|
6609
|
-
value = aws_lambda_function.api_lambda.source_code_size
|
|
6610
|
-
}
|
|
6611
|
-
|
|
6612
|
-
# IAM Role Outputs
|
|
6613
|
-
output "lambda_execution_role_arn" {
|
|
6614
|
-
description = "ARN of the Lambda execution role"
|
|
6615
|
-
value = aws_iam_role.lambda_execution_role.arn
|
|
6616
|
-
}
|
|
6617
|
-
|
|
6618
|
-
output "lambda_execution_role_name" {
|
|
6619
|
-
description = "Name of the Lambda execution role"
|
|
6620
|
-
value = aws_iam_role.lambda_execution_role.name
|
|
6621
|
-
}
|
|
6622
|
-
|
|
6623
|
-
# Integration Outputs
|
|
6624
|
-
output "integration_id" {
|
|
6625
|
-
description = "ID of the Lambda integration"
|
|
6626
|
-
value = aws_api_gateway_integration.lambda_integration.id
|
|
6627
|
-
}
|
|
6628
|
-
|
|
6629
|
-
output "proxy_resource_id" {
|
|
6630
|
-
description = "ID of the proxy resource"
|
|
6631
|
-
value = aws_api_gateway_resource.proxy_resource.id
|
|
6632
|
-
}
|
|
6633
|
-
|
|
6634
|
-
output "proxy_method_id" {
|
|
6635
|
-
description = "ID of the proxy method"
|
|
6636
|
-
value = aws_api_gateway_method.proxy_method.id
|
|
6637
|
-
}
|
|
6638
|
-
|
|
6639
|
-
# CloudWatch Log Groups
|
|
6640
|
-
output "lambda_log_group_name" {
|
|
6641
|
-
description = "Name of the Lambda CloudWatch log group"
|
|
6642
|
-
value = aws_cloudwatch_log_group.lambda_logs.name
|
|
6643
|
-
}
|
|
6644
|
-
|
|
6645
|
-
output "lambda_log_group_arn" {
|
|
6646
|
-
description = "ARN of the Lambda CloudWatch log group"
|
|
6647
|
-
value = aws_cloudwatch_log_group.lambda_logs.arn
|
|
6648
|
-
}
|
|
6649
|
-
",
|
|
6650
|
-
}
|
|
6651
|
-
`;
|