seahaven-door-unlock-api/terraform/apigateway.tf
Adam Moussa c43bee214c
feat(terraform): add hcp terraform for prod door-unlock-api (PLAT-76) (#81)
* feat(terraform): add hcp terraform for prod door-unlock-api

Move deploy off frozen CDK CD onto an HCP workspace that recreates the HTTP API, five Lambdas, disabled EventBridge rules, and alarms in seahaven-prod without a poller VPC.

* docs(readme): link the door unlock api ops page

* fix(terraform): invoke package build via bash

HCP launches the external data source with bash, so the inner build script should not depend on the git executable bit.
2026-08-27 22:03:12 +00:00

131 lines
5 KiB
HCL

resource "aws_apigatewayv2_api" "this" {
name = local.project
protocol_type = "HTTP"
description = "Yealink door unlock and lockdown HTTP API"
}
# Invoke permission is a Lambda resource policy, not AuthorizerCredentialsArn.
# The credentials-role path returned 500 without invoking the authorizer
# (PLAT-102); resource policy matches the route grants.
resource "aws_apigatewayv2_authorizer" "token" {
api_id = aws_apigatewayv2_api.this.id
name = "${local.project}-token-authorizer"
authorizer_type = "REQUEST"
authorizer_uri = aws_lambda_function.authorizer.invoke_arn
authorizer_payload_format_version = "2.0"
authorizer_result_ttl_in_seconds = 300
enable_simple_responses = true
identity_sources = ["$request.querystring.token"]
}
resource "aws_apigatewayv2_integration" "unlock" {
api_id = aws_apigatewayv2_api.this.id
integration_type = "AWS_PROXY"
integration_method = "POST"
integration_uri = aws_lambda_function.unlock.invoke_arn
payload_format_version = "2.0"
timeout_milliseconds = 20000
}
resource "aws_apigatewayv2_integration" "lockdown" {
api_id = aws_apigatewayv2_api.this.id
integration_type = "AWS_PROXY"
integration_method = "POST"
integration_uri = aws_lambda_function.lockdown.invoke_arn
payload_format_version = "2.0"
timeout_milliseconds = 15000
}
resource "aws_apigatewayv2_route" "unlock" {
api_id = aws_apigatewayv2_api.this.id
route_key = "GET /unlock"
target = "integrations/${aws_apigatewayv2_integration.unlock.id}"
authorization_type = "CUSTOM"
authorizer_id = aws_apigatewayv2_authorizer.token.id
}
resource "aws_apigatewayv2_route" "lockdown" {
api_id = aws_apigatewayv2_api.this.id
route_key = "GET /lockdown"
target = "integrations/${aws_apigatewayv2_integration.lockdown.id}"
authorization_type = "CUSTOM"
authorizer_id = aws_apigatewayv2_authorizer.token.id
}
resource "aws_apigatewayv2_route" "lockdown_status" {
api_id = aws_apigatewayv2_api.this.id
route_key = "GET /lockdown/status"
target = "integrations/${aws_apigatewayv2_integration.lockdown.id}"
authorization_type = "CUSTOM"
authorizer_id = aws_apigatewayv2_authorizer.token.id
}
resource "aws_apigatewayv2_stage" "default" {
api_id = aws_apigatewayv2_api.this.id
name = "$default"
auto_deploy = true
access_log_settings {
destination_arn = aws_cloudwatch_log_group.api_access.arn
format = "{\"requestId\":\"$context.requestId\",\"ip\":\"$context.identity.sourceIp\",\"requestTime\":\"$context.requestTime\",\"method\":\"$context.httpMethod\",\"routeKey\":\"$context.routeKey\",\"status\":\"$context.status\",\"protocol\":\"$context.protocol\",\"responseLength\":\"$context.responseLength\",\"integrationError\":\"$context.integrationErrorMessage\"}"
}
default_route_settings {
throttling_burst_limit = 5
throttling_rate_limit = 2
}
depends_on = [
aws_apigatewayv2_route.unlock,
aws_apigatewayv2_route.lockdown,
aws_apigatewayv2_route.lockdown_status,
]
}
resource "aws_lambda_permission" "unlock_route" {
statement_id = "AllowApiGatewayInvokeUnlock"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.unlock.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.this.execution_arn}/*/GET/unlock"
}
resource "aws_lambda_permission" "lockdown_route" {
statement_id = "AllowApiGatewayInvokeLockdown"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.lockdown.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.this.execution_arn}/*/GET/lockdown"
}
resource "aws_lambda_permission" "lockdown_status_route" {
statement_id = "AllowApiGatewayInvokeLockdownStatus"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.lockdown.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.this.execution_arn}/*/GET/lockdown/status"
}
resource "aws_lambda_permission" "authorizer" {
statement_id = "AllowApiGatewayInvokeAuthorizer"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.authorizer.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.this.execution_arn}/authorizers/${aws_apigatewayv2_authorizer.token.id}"
}
resource "aws_apigatewayv2_domain_name" "this" {
domain_name = var.domain_name
domain_name_configuration {
certificate_arn = data.aws_acm_certificate.doorunlock.arn
endpoint_type = "REGIONAL"
security_policy = "TLS_1_2"
}
}
resource "aws_apigatewayv2_api_mapping" "this" {
api_id = aws_apigatewayv2_api.this.id
domain_name = aws_apigatewayv2_domain_name.this.id
stage = aws_apigatewayv2_stage.default.id
}