I have created an API Gateway v2 which exposes a single AWS Lambda which I intend to use to expose my entire REST API through. So far I have this working just fine by following the hashicorp tutorial.
// S3 bucket for code release and updates.
resource "aws_s3_bucket" "lambda_zips" {
bucket = "tdweb-lambda-zips"
acl = "private"
force_destroy = true
}
data "archive_file" "web_api_lambda" {
type = "zip"
source_dir = "${path.module}/web-api"
output_path = "${path.module}/web-api.zip"
}
resource "aws_s3_object" "web_api" {
bucket = aws_s3_bucket.lambda_zips.id
key = "web-api.zip"
source = data.archive_file.web_api_lambda.output_path
etag = filemd5(data.archive_file.web_api_lambda.output_path)
}
// Lambda and associated IAM roles and permissions.
resource "aws_lambda_function" "web_api" {
function_name = "web_api_lambda"
s3_bucket = aws_s3_bucket.lambda_zips.id
s3_key = aws_s3_object.web_api.key
runtime = "python3.9"
handler = "lambda_function.lambda_handler"
source_code_hash = data.archive_file.web_api_lambda.output_base64sha256
role = aws_iam_role.web_api_lambda.arn
}
resource "aws_cloudwatch_log_group" "web_api_lambda" {
name = "/aws/lambda/${aws_lambda_function.web_api.function_name}"
retention_in_days = 30
}
data "aws_iam_policy_document" "web_api_assume_role" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
}
}
resource "aws_iam_role" "web_api_lambda" {
name = "web_api_lambda_role"
assume_role_policy = data.aws_iam_policy_document.web_api_assume_role.json
}
data "aws_iam_policy_document" "web_api_lambda" {
statement {
effect = "Allow"
actions = [
"logs:CreateLogStream",
"logs:CreateLogDelivery",
"logs:PutLogEvents"
]
resources = ["arn:aws:logs:*:*:*"]
}
statement {
effect = "Allow"
actions = [
"dynamodb:*"
]
resources = ["*"]
}
statement {
effect = "Allow"
actions = [
"s3:*",
]
resources = ["*"]
}
}
resource "aws_iam_role_policy" "web_api_lambda" {
name = "web_api_lambda_policy"
policy = data.aws_iam_policy_document.web_api_lambda.json
role = aws_iam_role.web_api_lambda.id
}
// API Gateway
resource "aws_apigatewayv2_api" "web_api" {
name = "web_api_gateway"
protocol_type = "HTTP"
}
resource "aws_apigatewayv2_stage" "web_api_prod" {
api_id = aws_apigatewayv2_api.web_api.id
name = "prod"
auto_deploy = true
access_log_settings {
destination_arn = aws_cloudwatch_log_group.web_api_gateway.arn
format = jsonencode({
requestId = "$context.requestId"
sourceIp = "$context.identity.sourceIp"
requestTime = "$context.requestTime"
protocol = "$context.protocol"
httpMethod = "$context.httpMethod"
resourcePath = "$context.resourcePath"
routeKey = "$context.routeKey"
status = "$context.status"
responseLength = "$context.responseLength"
integrationErrorMessage = "$context.integrationErrorMessage"
}
)
}
}
resource "aws_apigatewayv2_integration" "web_api" {
api_id = aws_apigatewayv2_api.web_api.id
integration_uri = aws_lambda_function.web_api.invoke_arn
integration_type = "AWS_PROXY"
integration_method = "POST"
}
resource "aws_apigatewayv2_route" "web_api_prod" {
api_id = aws_apigatewayv2_api.web_api.id
route_key = "GET /"
target = "integrations/${aws_apigatewayv2_integration.web_api.id}"
}
resource "aws_cloudwatch_log_group" "web_api_gateway" {
name = "/aws/api_gw/${aws_apigatewayv2_api.web_api.name}"
retention_in_days = 30
}
resource "aws_lambda_permission" "web_api_gateway" {
statement_id = "AllowExecutionFromAPIGateway"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.web_api.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.web_api.execution_arn}/*/*"
}
output "base_url" {
value = aws_apigatewayv2_stage.web_api_prod.invoke_url
}
Now I can do a simple curl of the output base_url and I see the response from my Lambda.
I would like for this API to be exposed to via my domain name. So I've add this:
resource "aws_apigatewayv2_domain_name" "web_api" {
domain_name = "prod.${var.domain_name}"
domain_name_configuration {
certificate_arn = aws_acm_certificate.main.arn
endpoint_type = "REGIONAL"
security_policy = "TLS_1_2"
}
}
resource "aws_apigatewayv2_api_mapping" "web_api" {
api_id = aws_apigatewayv2_api.web_api.id
domain_name = aws_apigatewayv2_domain_name.web_api.id
stage = aws_apigatewayv2_stage.web_api_prod.id
}
The certificate is working with an existing web server running on EC2 and is working. However, then I try to curl prod.mydomainname.com I get an error of:
curl: (6) Could not resolve host: prod.mydomainname.com
I am not sure why it's not exposed.
I missed the route53 entry.
resource "aws_route53_record" "web_api" {
zone_id = aws_route53_zone.external.zone_id
name = aws_apigatewayv2_domain_name.web_api.domain_name
type = "A"
alias {
name = aws_apigatewayv2_domain_name.web_api.domain_name_configuration[0].target_domain_name
zone_id = aws_apigatewayv2_domain_name.web_api.domain_name_configuration[0].hosted_zone_id
evaluate_target_health = false
}
}