diff --git a/README.md b/README.md index c55db8d..97152a9 100644 --- a/README.md +++ b/README.md @@ -1 +1,24 @@ -# aws-tf-modules \ No newline at end of file +# aws-tf-modules + +A collection of opinionated Terraform modules for AWS. + +## Modules + +| Module | Description | +|---|---| +| [acm-certificate](acm-certificate/) | ACM certificate with optional Route53 DNS validation | +| [github-oidc-iam-role](github-oidc-iam-role/) | IAM role for GitHub Actions OIDC authentication | +| [github-oidc-provider](github-oidc-provider/) | GitHub OIDC identity provider | +| [guardrails](guardrails/) | Account-level security guardrails (EBS, S3, IAM) | +| [http-api](http-api/) | API Gateway v2 HTTP API with Lambda integrations and authorizer | +| [http-api-domain](http-api-domain/) | Custom domain name for HTTP APIs (shared across multiple APIs) | +| [http-api-lambda-authorizer](http-api-lambda-authorizer/) | Lambda function pre-configured as an HTTP API REQUEST authorizer | +| [lambda-function](lambda-function/) | Lambda function with IAM role, CloudWatch logging, and permissions | +| [lambda-layer](lambda-layer/) | Lambda layer packaging | +| [ses-domain-identity](ses-domain-identity/) | SES domain identity with DNS records | +| [sqs](sqs/) | SQS queue with optional dead-letter queue | +| [static-site](static-site/) | Static site hosting via S3 + CloudFront + Route53 | + +## HTTP API modules + +The `http-api`, `http-api-domain`, and `http-api-lambda-authorizer` modules are designed to work together. See [http-api/README.md](http-api/README.md) for the complete multi-module wiring example. diff --git a/http-api-domain/README.md b/http-api-domain/README.md new file mode 100644 index 0000000..79142eb --- /dev/null +++ b/http-api-domain/README.md @@ -0,0 +1,71 @@ +# HTTP API Domain + +This module sets up a custom domain name for API Gateway v2 HTTP APIs. + +## Features + +- `aws_apigatewayv2_domain_name` with TLS 1.2 and REGIONAL endpoint +- Optional Route53 A alias record (omit `zone_id` to manage DNS externally) +- Designed to be shared across multiple `http-api` instances at different base paths + +## Design notes + +**Why is the domain separate from the API?** A single domain (`api.mysite.com`) can serve multiple independent APIs mounted at different base paths (`/auth`, `/posts`, `/members`). If the domain lived inside each `http-api` module instance, every API would try to create the same domain resource. Keeping the domain here lets you create it once and map as many APIs to it as you need. + +**Bring-your-own cert.** Use the `acm-certificate` module to provision the ACM certificate, then pass its ARN here. ACM certificates for API Gateway regional endpoints must be in the same AWS region as the API (unlike CloudFront, which requires `us-east-1`). + +## Usage + +See `variables.tf` for the full argument reference. + +```hcl +module "cert" { + source = "github.com/script47/aws-tf-modules//acm-certificate" + domains = ["api.mysite.com"] + zone_id = "Z123456789ABCDEF" + tags = local.tags +} + +module "api_domain" { + source = "github.com/script47/aws-tf-modules//http-api-domain" + + domain_name = "api.mysite.com" + certificate_arn = module.cert.arn + zone_id = "Z123456789ABCDEF" + + tags = { + Project = "my-project" + Environment = "production" + } +} +``` + +Then pass the domain to each `http-api` module: + +```hcl +module "posts_api" { + source = "github.com/script47/aws-tf-modules//http-api" + name = "posts-api" + + domain = { + name = module.api_domain.domain.name + base_path = "posts" + } + + # ... +} + +module "auth_api" { + source = "github.com/script47/aws-tf-modules//http-api" + name = "auth-api" + + domain = { + name = module.api_domain.domain.name + base_path = "auth" + } + + # ... +} +``` + +See `http-api/README.md` for the full multi-module wiring example. diff --git a/http-api-domain/domain.tf b/http-api-domain/domain.tf new file mode 100644 index 0000000..fb56fb6 --- /dev/null +++ b/http-api-domain/domain.tf @@ -0,0 +1,10 @@ +resource "aws_apigatewayv2_domain_name" "this" { + domain_name = var.domain_name + tags = var.tags + + domain_name_configuration { + certificate_arn = var.certificate_arn + endpoint_type = "REGIONAL" + security_policy = "TLS_1_2" + } +} diff --git a/http-api-domain/outputs.tf b/http-api-domain/outputs.tf new file mode 100644 index 0000000..05dda45 --- /dev/null +++ b/http-api-domain/outputs.tf @@ -0,0 +1,8 @@ +output "domain" { + description = "Custom domain details" + value = { + name = aws_apigatewayv2_domain_name.this.domain_name + target = aws_apigatewayv2_domain_name.this.domain_name_configuration[0].target_domain_name + hosted_zone_id = aws_apigatewayv2_domain_name.this.domain_name_configuration[0].hosted_zone_id + } +} diff --git a/http-api-domain/providers.tf b/http-api-domain/providers.tf new file mode 100644 index 0000000..5ec6724 --- /dev/null +++ b/http-api-domain/providers.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.13" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 6" + } + } +} diff --git a/http-api-domain/route53.tf b/http-api-domain/route53.tf new file mode 100644 index 0000000..bbd5e37 --- /dev/null +++ b/http-api-domain/route53.tf @@ -0,0 +1,13 @@ +resource "aws_route53_record" "this" { + count = var.zone_id != null ? 1 : 0 + + zone_id = var.zone_id + name = var.domain_name + type = "A" + + alias { + name = aws_apigatewayv2_domain_name.this.domain_name_configuration[0].target_domain_name + zone_id = aws_apigatewayv2_domain_name.this.domain_name_configuration[0].hosted_zone_id + evaluate_target_health = false + } +} diff --git a/http-api-domain/variables.tf b/http-api-domain/variables.tf new file mode 100644 index 0000000..63a8e11 --- /dev/null +++ b/http-api-domain/variables.tf @@ -0,0 +1,21 @@ +variable "domain_name" { + type = string + description = "The custom domain name (e.g. api.mysite.com)" +} + +variable "certificate_arn" { + type = string + description = "ARN of the ACM certificate for this domain (must be in the same region as the API)" +} + +variable "zone_id" { + type = string + description = "Route53 hosted zone ID. If provided, an A record alias will be created automatically" + default = null +} + +variable "tags" { + type = map(string) + description = "The tags to apply to all resources created" + default = {} +} diff --git a/http-api-lambda-authorizer/README.md b/http-api-lambda-authorizer/README.md new file mode 100644 index 0000000..ce920d5 --- /dev/null +++ b/http-api-lambda-authorizer/README.md @@ -0,0 +1,62 @@ +# HTTP API Lambda Authorizer + +This module sets up a Lambda function pre-configured as an HTTP API Gateway REQUEST authorizer. + +## Features + +- All features of the `lambda-function` module (IAM role, CloudWatch logs, layers, environment variables) +- Purpose-built for HTTP API Gateway v2 REQUEST authorizers +- The `http-api` module owns the Lambda permissions to avoid circular dependencies + +## Design notes + +**REQUEST type only.** The Lambda receives the full HTTP request and is responsible for validating the JWT from the `Authorization: Bearer ` header itself. This is different from the API Gateway-native JWT type, where API GW validates tokens directly against a JWKS endpoint (e.g. Cognito, Auth0). Use this module when you want full control over token validation logic in your Lambda code. + +**No Lambda permissions created here.** When you wire this authorizer into an `http-api` module instance, that module creates the `aws_lambda_permission` granting `apigateway.amazonaws.com` the right to invoke this function - scoped to that API's `execution_arn`. Keeping permissions in the API module avoids a circular dependency: if this module created its own permission, it would need to reference the API's ARN, and the API already references this module's function ARN. + +## Usage + +See `variables.tf` for the full argument reference. + +```hcl +module "authorizer" { + source = "github.com/script47/aws-tf-modules//http-api-lambda-authorizer" + + name = "my-api-authorizer" + src = abspath("${path.module}/../dist/authorizer") + handler = "index.handler" + + logs = { + enabled = true + retention_in_days = 14 + } + + tags = { + Project = "my-project" + Environment = "production" + } +} +``` + +Then pass the function ARN to `http-api`: + +```hcl +module "api" { + source = "github.com/script47/aws-tf-modules//http-api" + name = "my-api" + + authorizer = { + function_arn = module.authorizer.arn + } + + routes = { + "GET /items" = { + function_arn = module.items_fn.arn + } + } + + # ... +} +``` + +See `http-api/README.md` for the full multi-module wiring example. diff --git a/http-api-lambda-authorizer/lambda.tf b/http-api-lambda-authorizer/lambda.tf new file mode 100644 index 0000000..04a4b68 --- /dev/null +++ b/http-api-lambda-authorizer/lambda.tf @@ -0,0 +1,20 @@ +module "fn" { + source = "../lambda-function" + + name = var.name + description = var.description + role_arn = var.role_arn + policy_arns = var.policy_arns + inline_policies = var.inline_policies + layer_arns = var.layer_arns + runtime = var.runtime + architectures = var.architectures + memory = var.memory + timeout = var.timeout + concurrency = var.concurrency + vars = var.vars + src = var.src + handler = var.handler + logs = var.logs + tags = var.tags +} diff --git a/http-api-lambda-authorizer/outputs.tf b/http-api-lambda-authorizer/outputs.tf new file mode 100644 index 0000000..749a264 --- /dev/null +++ b/http-api-lambda-authorizer/outputs.tf @@ -0,0 +1,21 @@ +output "arn" { + value = module.fn.arn +} + +output "function_name" { + value = module.fn.function_name +} + +output "invoke_arn" { + value = module.fn.invoke_arn +} + +output "fn" { + description = "Lambda function details" + value = module.fn.fn +} + +output "log_group" { + description = "CloudWatch log group details (if enabled)" + value = module.fn.log_group +} diff --git a/http-api-lambda-authorizer/providers.tf b/http-api-lambda-authorizer/providers.tf new file mode 100644 index 0000000..6dfd123 --- /dev/null +++ b/http-api-lambda-authorizer/providers.tf @@ -0,0 +1,15 @@ +terraform { + required_version = ">= 1.13" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 6" + } + + archive = { + source = "hashicorp/archive" + version = ">= 2.0.0, < 3.0.0" + } + } +} diff --git a/http-api-lambda-authorizer/variables.tf b/http-api-lambda-authorizer/variables.tf new file mode 100644 index 0000000..6ff6c52 --- /dev/null +++ b/http-api-lambda-authorizer/variables.tf @@ -0,0 +1,97 @@ +variable "name" { + type = string + description = "The function's name" +} + +variable "description" { + type = string + default = "" + description = "The function's description" +} + +variable "role_arn" { + type = string + description = "ARN of the role assumed by the function. If unspecified a role will be created" + default = null +} + +variable "policy_arns" { + type = list(string) + description = "Optional list of policy ARNs to attach to the execution role" + default = [] +} + +variable "inline_policies" { + type = map(any) + description = "Map of inline IAM policy documents" + default = {} +} + +variable "layer_arns" { + type = list(string) + default = [] + description = "ARNs of Lambda layers to attach" +} + +variable "runtime" { + type = string + default = "nodejs24.x" + description = "Lambda runtime environment identifier" +} + +variable "architectures" { + type = set(string) + default = ["arm64"] + description = "A list of the supported architectures" +} + +variable "memory" { + type = number + default = 128 + description = "Allocated memory for the function" +} + +variable "timeout" { + type = number + default = 3 + description = "Lambda execution timeout in seconds" +} + +variable "concurrency" { + type = number + default = -1 + description = "Set the maximum execution concurrency" +} + +variable "vars" { + type = map(string) + default = {} + description = "Environment variables available to the function" +} + +variable "src" { + type = string + description = "The path to your function code" +} + +variable "handler" { + type = string + description = "The function's entrypoint" +} + +variable "logs" { + type = object({ + enabled = optional(bool, true) + format = optional(string, "Text") + retention_in_days = optional(number, 30) + app_log_level = optional(string, null) + system_log_level = optional(string, null) + }) + default = {} +} + +variable "tags" { + type = map(string) + description = "The tags to apply to all resources created" + default = {} +} diff --git a/http-api/README.md b/http-api/README.md new file mode 100644 index 0000000..ecffdd9 --- /dev/null +++ b/http-api/README.md @@ -0,0 +1,182 @@ +# HTTP API + +This module sets up an API Gateway v2 HTTP API backed by Lambda functions. + +## Features + +- HTTP API with optional global CORS configuration +- Lambda proxy integrations (`AWS_PROXY`, payload format 2.0) +- Single REQUEST-type Lambda authorizer applied per-route (opt-out with `authorized = false`) +- All Lambda permissions (`lambda:InvokeFunction`) managed by this module; no configuration needed on the Lambda side +- Base-path mapping onto a shared custom domain (via `http-api-domain`) +- Default execute-api endpoint disabled when a custom domain is configured +- CloudWatch access logging for the `$default` stage + +## Design notes + +**Why is the domain separate?** The `http-api-domain` module owns the `aws_apigatewayv2_domain_name` resource. This module only creates the mapping between the API and a base path on that domain. This allows a single domain (`api.mysite.com`) to serve multiple independent APIs (`/auth`, `/posts`, `/members`) without each API competing to own the domain resource. + +**Why does this module own all Lambda permissions?** Both route integrations and the authorizer Lambda need `aws_lambda_permission` scoped to this API's `execution_arn`. Creating them here (where the ARN is known) avoids a circular dependency: if the authorizer module granted its own permission, it would need to reference this API — and this API references the authorizer. By keeping all permissions here, the authorizer module has no back-reference. + +**`authorized = true` by default.** Routes are protected unless you explicitly opt out. Set `authorized = false` for public endpoints (webhooks, health checks, etc.). If no `authorizer` is configured, this flag has no effect. + +## Usage + +See `variables.tf` for the full argument reference. + +```hcl +module "api" { + source = "github.com/script47/aws-tf-modules//http-api" + + name = "my-api" + description = "My HTTP API" + + cors_config = { + allow_origins = ["https://app.mysite.com"] + allow_methods = ["GET", "POST", "OPTIONS"] + allow_headers = ["Content-Type", "Authorization"] + } + + authorizer = { + function_arn = module.authorizer.arn + # identity_sources defaults to ["$request.header.Authorization"] + } + + routes = { + "GET /items" = { + function_arn = module.list_items_fn.arn + # authorized defaults to true + } + "POST /items" = { + function_arn = module.create_item_fn.arn + } + "POST /webhook" = { + function_arn = module.webhook_fn.arn + authorized = false + } + } + + domain = { + name = module.api_domain.domain.name + base_path = "items" + } + + logs = { + enabled = true + retention_in_days = 30 + } + + tags = { + Project = "my-project" + Environment = "production" + } +} +``` + +## Complete example + +The following wires together all three HTTP API modules: `acm-certificate`, `http-api-domain`, `http-api-lambda-authorizer`, and `http-api`. + +```hcl +locals { + tags = { + Project = "my-project" + Environment = "production" + } +} + +# ACM certificate for the custom domain (same region as the API) +module "cert" { + source = "github.com/script47/aws-tf-modules//acm-certificate" + domains = ["api.mysite.com"] + zone_id = "Z123456789ABCDEF" + tags = local.tags +} + +# Custom domain: created once, shared across all APIs mounted on it +module "api_domain" { + source = "github.com/script47/aws-tf-modules//http-api-domain" + domain_name = "api.mysite.com" + certificate_arn = module.cert.arn + zone_id = "Z123456789ABCDEF" + tags = local.tags +} + +# Authorizer Lambda: validates JWT Bearer tokens in Lambda code +# No Lambda permissions are configured here; http-api handles them +module "authorizer" { + source = "github.com/script47/aws-tf-modules//http-api-lambda-authorizer" + name = "posts-authorizer" + src = abspath("${path.module}/../dist/authorizer") + handler = "index.handler" + tags = local.tags +} + +# Route Lambda functions +module "list_posts_fn" { + source = "github.com/script47/aws-tf-modules//lambda-function" + name = "list-posts" + src = abspath("${path.module}/../dist/list-posts") + handler = "index.handler" + tags = local.tags +} + +module "create_post_fn" { + source = "github.com/script47/aws-tf-modules//lambda-function" + name = "create-post" + src = abspath("${path.module}/../dist/create-post") + handler = "index.handler" + tags = local.tags +} + +module "webhook_fn" { + source = "github.com/script47/aws-tf-modules//lambda-function" + name = "posts-webhook" + src = abspath("${path.module}/../dist/webhook") + handler = "index.handler" + tags = local.tags +} + +# Posts API — mounted at api.mysite.com/posts +module "posts_api" { + source = "github.com/script47/aws-tf-modules//http-api" + + name = "posts-api" + description = "Posts CRUD API" + + authorizer = { + function_arn = module.authorizer.arn + } + + routes = { + "GET /posts" = { + function_arn = module.list_posts_fn.arn + } + "POST /posts" = { + function_arn = module.create_post_fn.arn + } + "POST /webhook" = { + function_arn = module.webhook_fn.arn + authorized = false + } + } + + domain = { + name = module.api_domain.domain.name + base_path = "posts" + } + + logs = { + enabled = true + } + + tags = local.tags +} + +# Outputs +output "api_endpoint" { + value = "https://${module.posts_api.apigw.endpoint}" + # With domain: https://api.mysite.com/posts + # Without domain: module.posts_api.apigw.endpoint +} +``` diff --git a/http-api/apigw.tf b/http-api/apigw.tf new file mode 100644 index 0000000..79a013b --- /dev/null +++ b/http-api/apigw.tf @@ -0,0 +1,96 @@ +resource "aws_apigatewayv2_api" "this" { + name = var.name + description = var.description + protocol_type = "HTTP" + disable_execute_api_endpoint = var.domain != null + tags = var.tags + + dynamic "cors_configuration" { + for_each = var.cors_config != null ? [var.cors_config] : [] + content { + allow_credentials = cors_configuration.value.allow_credentials + allow_headers = cors_configuration.value.allow_headers + allow_methods = cors_configuration.value.allow_methods + allow_origins = cors_configuration.value.allow_origins + expose_headers = cors_configuration.value.expose_headers + max_age = cors_configuration.value.max_age + } + } +} + +resource "aws_apigatewayv2_stage" "this" { + api_id = aws_apigatewayv2_api.this.id + name = "$default" + auto_deploy = true + tags = var.tags + + dynamic "access_log_settings" { + for_each = var.logs.enabled ? [1] : [] + content { + destination_arn = aws_cloudwatch_log_group.this[0].arn + format = var.logs.format + } + } +} + +resource "aws_apigatewayv2_authorizer" "this" { + count = var.authorizer != null ? 1 : 0 + + api_id = aws_apigatewayv2_api.this.id + name = "${var.name}-authorizer" + authorizer_type = "REQUEST" + identity_sources = var.authorizer.identity_sources + authorizer_uri = local.authorizer_uri + authorizer_payload_format_version = var.authorizer.payload_format_version + enable_simple_responses = var.authorizer.enable_simple_responses + authorizer_result_ttl_in_seconds = var.authorizer.result_ttl_in_seconds +} + +resource "aws_apigatewayv2_integration" "this" { + for_each = var.routes + + api_id = aws_apigatewayv2_api.this.id + integration_type = "AWS_PROXY" + integration_method = "POST" + integration_uri = local.route_integration_uris[each.key] + payload_format_version = "2.0" +} + +resource "aws_apigatewayv2_route" "this" { + for_each = var.routes + + api_id = aws_apigatewayv2_api.this.id + route_key = each.key + target = "integrations/${aws_apigatewayv2_integration.this[each.key].id}" + authorization_type = contains(keys(local.authorized_routes), each.key) ? "CUSTOM" : "NONE" + authorizer_id = contains(keys(local.authorized_routes), each.key) ? aws_apigatewayv2_authorizer.this[0].id : null +} + +resource "aws_lambda_permission" "authorizer" { + count = var.authorizer != null ? 1 : 0 + + statement_id = "allow-${var.name}-apigw-authorizer" + action = "lambda:InvokeFunction" + function_name = var.authorizer.function_arn + principal = "apigateway.amazonaws.com" + source_arn = "${aws_apigatewayv2_api.this.execution_arn}/*/*" +} + +resource "aws_lambda_permission" "routes" { + for_each = var.routes + + statement_id = "allow-apigw-${replace(each.key, "/[^a-zA-Z0-9-_]/", "-")}" + action = "lambda:InvokeFunction" + function_name = each.value.function_arn + principal = "apigateway.amazonaws.com" + source_arn = "${aws_apigatewayv2_api.this.execution_arn}/*/*" +} + +resource "aws_apigatewayv2_api_mapping" "this" { + count = var.domain != null ? 1 : 0 + + api_id = aws_apigatewayv2_api.this.id + domain_name = var.domain.name + stage = aws_apigatewayv2_stage.this.id + api_mapping_key = var.domain.base_path +} diff --git a/http-api/cloudwatch.tf b/http-api/cloudwatch.tf new file mode 100644 index 0000000..a954bd2 --- /dev/null +++ b/http-api/cloudwatch.tf @@ -0,0 +1,8 @@ +resource "aws_cloudwatch_log_group" "this" { + count = var.logs.enabled ? 1 : 0 + + name = local.log_group_name + log_group_class = "STANDARD" + retention_in_days = var.logs.retention_in_days + tags = var.tags +} diff --git a/http-api/data.tf b/http-api/data.tf new file mode 100644 index 0000000..2502393 --- /dev/null +++ b/http-api/data.tf @@ -0,0 +1 @@ +data "aws_region" "current" {} diff --git a/http-api/locals.tf b/http-api/locals.tf new file mode 100644 index 0000000..68389ab --- /dev/null +++ b/http-api/locals.tf @@ -0,0 +1,17 @@ +locals { + lambda_uri_prefix = "arn:aws:apigateway:${data.aws_region.current.region}:lambda:path/2015-03-31/functions" + + route_integration_uris = { + for k, r in var.routes : + k => "${local.lambda_uri_prefix}/${r.function_arn}/invocations" + } + + authorizer_uri = var.authorizer != null ? "${local.lambda_uri_prefix}/${var.authorizer.function_arn}/invocations" : null + + authorized_routes = { + for k, r in var.routes : k => r + if r.authorized && var.authorizer != null + } + + log_group_name = "/aws/apigateway/${var.name}" +} diff --git a/http-api/outputs.tf b/http-api/outputs.tf new file mode 100644 index 0000000..3c43fcf --- /dev/null +++ b/http-api/outputs.tf @@ -0,0 +1,15 @@ +output "apigw" { + description = "Core HTTP API attributes" + value = { + id = aws_apigatewayv2_api.this.id + endpoint = aws_apigatewayv2_api.this.api_endpoint + execution_arn = aws_apigatewayv2_api.this.execution_arn + } +} + +output "log_group" { + description = "CloudWatch log group details (if logging enabled)" + value = { + arn = one(aws_cloudwatch_log_group.this[*].arn) + } +} diff --git a/http-api/providers.tf b/http-api/providers.tf new file mode 100644 index 0000000..9213c51 --- /dev/null +++ b/http-api/providers.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.13" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 6" + } + } +} \ No newline at end of file diff --git a/http-api/variables.tf b/http-api/variables.tf new file mode 100644 index 0000000..a2fb0ac --- /dev/null +++ b/http-api/variables.tf @@ -0,0 +1,69 @@ +variable "name" { + type = string + description = "The name of the HTTP API" +} + +variable "description" { + type = string + description = "The description of the HTTP API" + default = "" +} + +variable "cors_config" { + type = object({ + allow_credentials = optional(bool, false) + allow_headers = optional(set(string), ["*"]) + allow_methods = optional(set(string), ["*"]) + allow_origins = optional(set(string), ["*"]) + expose_headers = optional(set(string), []) + max_age = optional(number, 3600) + }) + description = "Optional global CORS configuration for the API" + default = null +} + +variable "routes" { + type = map(object({ + function_arn = string + authorized = optional(bool, true) + })) + description = "Map of route key (e.g. \"GET /items\") to Lambda function ARN. Routes are authorized by default when an authorizer is configured; set authorized = false for public routes." + default = {} +} + +variable "authorizer" { + type = object({ + function_arn = string + identity_sources = optional(set(string), ["$request.header.Authorization"]) + result_ttl_in_seconds = optional(number, 300) + payload_format_version = optional(string, "2.0") + enable_simple_responses = optional(bool, true) + }) + description = "Optional REQUEST-type Lambda authorizer applied to all routes where authorized = true. The Lambda validates the JWT." + default = null +} + +variable "domain" { + type = object({ + name = string + base_path = optional(string, null) + }) + description = "Optional base-path mapping onto an existing aws_apigatewayv2_domain_name. Create the domain with the http-api-domain module. When set, the default execute-api endpoint is disabled." + default = null +} + +variable "logs" { + type = object({ + enabled = optional(bool, false) + retention_in_days = optional(number, 30) + format = optional(string, "{\"requestId\":\"$context.requestId\",\"ip\":\"$context.identity.sourceIp\",\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"routeKey\":\"$context.routeKey\",\"status\":\"$context.status\",\"responseLength\":\"$context.responseLength\",\"integrationLatency\":\"$context.integrationLatency\"}") + }) + description = "Access log configuration for the default stage" + default = {} +} + +variable "tags" { + type = map(string) + description = "The tags to apply to all resources created" + default = {} +}