Recently, I had to create a REST API Gateway in AWS with Terraform. The documentation is good but I had to took some time, navigating pages to understand what should I do. So, here is a resume of that research.
Note : For HTTP and Websockets API Gateway, there is another of the Terraform scripts.
The base - API Gateway Rest API
With it's minimal form, the Gateway is quite simple.
resource "aws_api_gateway_rest_api" "example" {
name = "example"
}
But you have a lot of optional elements like :
- description : Quick description of the API
- body : Expose the API services exposed with an OpenAPI Specification.
- endpoint_configuration : Define the endpoint configuration type How to choose your endpoint configuration : https://docs.aws.amazon.com/en_en/apigateway/latest/developerguide/api-gateway-api-endpoint-types.html
- ...
resource "aws_api_gateway_rest_api" "example" {
body = jsonencode({
openapi = "3.0.1"
info = {
title = "example"
version = "1.0"
}
paths = {
"/path1" = {
get = {
x-amazon-apigateway-integration = {
httpMethod = "GET"
payloadFormatVersion = "1.0"
type = "HTTP_PROXY"
uri = "https://ip-ranges.amazonaws.com/ip-ranges.json"
}
}
}
}
})
description = "Example of an API Gateway"
name = "example"
endpoint_configuration {
types = ["REGIONAL"]
}
}
Ingress Policy - API Gateway Rest API Policy
The definition of the Ingress Policy can help you to manage ingress access. It can be really helpful because Security Groups can't be applied to the API Gateway.
In this example, we only open the API Gateway to the IP Ranges: 10.0.0.0/24 and 10.10.0.0/24
resource "aws_api_gateway_rest_api_policy" "test" {
rest_api_id = aws_api_gateway_rest_api.test.id
policy = <<EOF
{
Version = "2012-10-17",
Statement = [{
Effect = "Deny",
Principal = "*",
Action = "execute-api:Invoke",
Resource = "execute-api:/*/*/*"
},
{
Effect = "Allow",
Principal = "*",
Action = "execute-api:Invoke",
Resource = "execute-api:/*/*/*",
Condition = {
NotIpAddress = {
"aws:SourceIp" = ["10.0.0.0/24", "10.10.0.0/24"]
}
}
}
]
}
EOF
Define the exposed resources
Declare paths - API Gateway Resource
The Resource element will define all the path you want to expose.
Let's take an example.
You have the following paths :
- /users
- /add
- /list
- /cards
- /play
- /send
Both /users path expose a method of the same service, but each of /card expose a different one.
To do it, first we have to create a resource for /users and one for /cards.
resource "aws_api_gateway_resource" "usersResource" {
rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id
parent_id = aws_api_gateway_rest_api.MyDemoAPI.root_resource_id # In this case, the parent id should the gateway root_resource_id.
path_part = "users"
}
resource "aws_api_gateway_resource" "cardsResource" {
rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id
parent_id = aws_api_gateway_rest_api.MyDemoAPI.root_resource_id
path_part = "cards"
}
Then, we had to create another resource for each subpath which expose differents endpoints. (You can do others resources for each users path)
resource "aws_api_gateway_resource" "playCardResource" {
rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id
parent_id = aws_api_gateway_resource.cardsResource.id # In this case, the parent id should be the parent aws_api_gateway_resource id.
path_part = "play"
}
resource "aws_api_gateway_resource" "sendCardResource" {
rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id
parent_id = aws_api_gateway_resource.cardsResource.id
path_part = "send"
}
Declare Methods - API Gateway Method
Declare all methods available for each path.
Example where we only allow GET methods for /cards/send
resource "aws_api_gateway_method" "MyDemoMethod" {
rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id
resource_id = aws_api_gateway_resource.sendCardResource.id
http_method = "GET"
authorization = "NONE"
}
If you want, you can define a way to check autorization with Cognito or IAM.
Integrate endpoints - API Gateway Integration
Now, we integrate endpoints with differents services. Here is differents examples.
Lambda
resource "aws_api_gateway_integration" "integration" {
rest_api_id = aws_api_gateway_rest_api.api.id
resource_id = aws_api_gateway_resource.resource.id
http_method = aws_api_gateway_method.method.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.lambda.invoke_arn
}
HTTP
resource "aws_api_gateway_integration" "test" {
rest_api_id = aws_api_gateway_rest_api.api_gateway_pmt_apis.id
resource_id = aws_api_gateway_resource.api_gateway_ressource_all.id
http_method = aws_api_gateway_method.aws_api_gateway_method_root.http_method
type = "HTTP"
uri = "https://www.google.de"
integration_http_method = "GET"
}
Mock
This element can be really useful if you want to quickly expose a method for a frontend service. But to define completly a mock, we will need the two next elements.
resource "aws_api_gateway_integration" "MyDemoIntegration" {
rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id
resource_id = aws_api_gateway_resource.MyDemoResource.id
http_method = aws_api_gateway_method.MyDemoMethod.http_method
type = "MOCK"
}
Declare Mock HTTP Response - API Gateway Method Response
When you want to mock a integration, you have to have to declare its response.
resource "aws_api_gateway_method_response" "response_200" {
rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id
resource_id = aws_api_gateway_resource.MyDemoResource.id
http_method = aws_api_gateway_method.MyDemoMethod.http_method
status_code = "200"
}
Declare Mock HTTP Response Body - API Gateway Integration Response
resource "aws_api_gateway_integration_response" "MyDemoIntegrationResponse" {
rest_api_id = aws_api_gateway_rest_api.MyDemoAPI.id
resource_id = aws_api_gateway_resource.MyDemoResource.id
http_method = aws_api_gateway_method.MyDemoMethod.http_method
status_code = aws_api_gateway_method_response.response_200.status_code
# Transforms the backend JSON response to XML
response_templates = {
"application/xml" = <<EOF
#set($inputRoot = $input.path('$'))
<?xml version="1.0" encoding="UTF-8"?>
<message>
$inputRoot.body
</message>
EOF
}
}
I hope it will help you!
Top comments (0)