Provisioning AWS API Gateway using Terraform

Sarath Kallatt Sivasankaran
3 min readJan 23, 2021

--

AWS provides two types of API Gateways, one specifically to support REST APIs and other to support all type of HTTP APIs. Visit the documentation to compare and choose one that suits your needs.

In this story we will try to provision an HTTP API Gateway using terraform. A complete example repository is hosted in github.

Let’s start defining API Gateway in the terraform file as follows

resource "aws_apigatewayv2_api" "sample_api_gateway_resource" {
name = var.api_gateway_name
description = var.api_gatway_description
protocol_type = "HTTP"
}

You can either define the variables like api_gateway_name, used in your terrafrom configuration, in configuration files or pass them to terrafrom while you run terraform commands.

You can create a stage and link to API Gateway as follows. A stage is a named reference to a deployment of the API. See documentation for more details

resource "aws_apigatewayv2_stage" "sample_stage_resource" {
api_id = aws_apigatewayv2_api.sample_api_gateway_resource.id
name = var.stage_name
auto_deploy = true
}

You can create a custom domain if you like and link it to your api gateway stage as follows. This assumes that you already have an SSL certificate managed by AWS certificate manager and you might have created it using terraform itself.

data "aws_acm_certificate" "sample_certificate_resource" {
domain = var.api_gateway_domain_name
statuses = ["ISSUED"]
most_recent = true
}
resource "aws_apigatewayv2_domain_name" "sample_domain_resource" {
domain_name = var.api_gateway_domain_name
domain_name_configuration {
certificate_arn = data.aws_acm_certificate.sample_certificate_resource.arn
endpoint_type = "REGIONAL"
security_policy = "TLS_1_2"
}
}
resource "aws_apigatewayv2_api_mapping" "sample_api_mapping_resource" {
api_id = aws_apigatewayv2_api.sample_api_gateway_resource.id
domain_name = aws_apigatewayv2_domain_name.sample_domain_resource.id
stage = aws_apigatewayv2_stage.sample_stage_resource.id
}

Now to route the requests to your api (let’s say for example a lambda: “sample_lambda_resource”), use “aws_apigatewayv2_integration” and “aws_apigatewayv2_route” as follows

data "aws_lambda_function" "sample_lambda_resource" {
function_name = var.integration_lambda_name
}
resource "aws_apigatewayv2_integration" "sample_integration_resource" {
depends_on = [data.aws_lambda_function.sample_lambda_resource.resource]

api_id = aws_apigatewayv2_api.sample_api_gateway_resource.id
integration_type = "AWS_PROXY"

connection_type = "INTERNET"
description = var.integration_description
integration_method = var.http_method
integration_uri = data.aws_lambda_function.sample_lambda_resource.invoke_arn
payload_format_version = "2.0"
}

resource "aws_apigatewayv2_route" "sample_route_resource" {
api_id = aws_apigatewayv2_api.sample_api_gateway_resource.id
route_key = "${var.http_method} /${var.root_route_path}"
authorization_type = "NONE"
target = "integrations/${aws_apigatewayv2_integration.sample_integration_resource.id}"
}

If you need a custom authorizer, use “aws_apigatewayv2_authorizer” and in the route you need to link to the custom authorizer if you want to use it with that route. See below for an example. Here we assume that you have a custom lambda function that is used for authorization (again may be created via terraform)

data "aws_lambda_function" "sample_authorizer_lambda_resource" {
function_name = var.authorization_lambda_name
}
resource "aws_apigatewayv2_authorizer" "sample_authorizer_resource" {
api_id = aws_apigatewayv2_api.sample_api_gateway_resource.id
authorizer_type = "REQUEST"
identity_sources = []
name = var.authorizer_name
authorizer_payload_format_version = "2.0"
authorizer_result_ttl_in_seconds = 300
enable_simple_responses = true
authorizer_uri = aws_lambda_function.sample_authorizer_lambda_resource.invoke_arn
}
resource "aws_apigatewayv2_route" "sample_route2_resource" {
api_id = aws_apigatewayv2_api.sample_api_gateway_resource.id
route_key = "${var.http_method} /${var.root_route_path}"
authorization_type = "CUSTOM"
authorizer_id = aws_apigatewayv2_authorizer.sample_authorizer_resource.id
target = "integrations/${aws_apigatewayv2_integration.sample_integration_resource.id}"
}

And finally if you need to create a route53 entry and link it to your api gateway, see below example

data "aws_route53_zone" "sample_api_zone_resource" {
name = var.domain_name
}
resource "aws_route53_record" "sample_route53_record_resource" {
zone_id = data.aws_route53_zone.sample_api_zone_resource.zone_id
name = aws_apigatewayv2_domain_name.sample_domain_resource.domain_name
type = "CNAME"
ttl = "300"
records = [
aws_apigatewayv2_domain_name.sample_domain_resource.domain_name_configuration[0].target_domain_name
]
}

Visit Terraform AWS API Gateway documentation to see all the supported options

Happy terraforming.

--

--