I am building a Terraform module that deploys a REST API in AWS API Gateway. The users of this module will provide input like this:
api_resources = {
resource1 = {
api_endpoint = "/pets/{petID}"
http_method = "GET"
},
resource2 = {
api_endpoint = "/pets"
http_method = "GET"
},
resource3 = {
api_endpoint = "/toys"
http_method = "GET"
},
resource4 = {
api_endpoint = "/pets"
http_method = "POST"
}
}
In my module, this input will be deployed using the aws_api_gateway_resource Terraform resource. It takes the following arguments:
resource "aws_api_gateway_resource" "resource" {
rest_api_id = # ID of the parent REST API resource.
parent_id = # ID of the immediate parent of this "part" of the API endpoint.
path_part = # The rightmost "part" of the endpoint URL.
}
Official documentation: Link.
Example: For the input /pets/{petID}, the path_part above will be {petID} & the parent_id will be the ID of the Terraform resource that created the pets path_part.
So something like this:
resource "aws_api_gateway_resource" "pets_resource" {
rest_api_id = aws_api_gateway_rest_api.rest_api.id
parent_id = aws_api_gateway_rest_api.rest_api.root_resource_id
path_part = "pets"
}
resource "aws_api_gateway_resource" "petID_resource" {
rest_api_id = aws_api_gateway_rest_api.rest_api.id
parent_id = aws_api_gateway_resource.pets_resource.id
path_part = "{petID}"
}
Note: The aws_api_gateway_rest_api already exists elsewhere:
resource "aws_api_gateway_rest_api" "rest_api" {
name = "my-api"
}
In order to do all this dynamically based on user input, I have:
- Extracted all API endpoints from the input.
- Looped over them and created one
aws_api_gateway_resourceresource for each.
Like this:
locals {
api_endpoints = toset([
for key, value in var.api_resources :
trimprefix(value.api_endpoint, "/")
])
}
resource "aws_api_gateway_resource" "resource" {
rest_api_id = aws_api_gateway_rest_api.rest_api.id
parent_id = aws_api_gateway_rest_api.rest_api.root_resource_id
for_each = local.api_endpoints # pets/{petID}, pets, toys
path_part = each.key
}
This works well for top-level resources /pets & /toys as seen in this Terraform plan:
Terraform will perform the following actions:
# aws_api_gateway_resource.resource["pets"] will be created
+ resource "aws_api_gateway_resource" "resource" {
+ id = (known after apply)
+ parent_id = "e79wlf30x5"
+ path = (known after apply)
+ path_part = "pets"
+ rest_api_id = "yrpm6dx4z8"
}
# aws_api_gateway_resource.resource["pets/{petID}"] will be created
+ resource "aws_api_gateway_resource" "resource" {
+ id = (known after apply)
+ parent_id = "e79wlf30x5"
+ path = (known after apply)
+ path_part = "pets/{petID}"
+ rest_api_id = "yrpm6dx4z8"
}
# aws_api_gateway_resource.resource["toys"] will be created
+ resource "aws_api_gateway_resource" "resource" {
+ id = (known after apply)
+ parent_id = "e79wlf30x5"
+ path = (known after apply)
+ path_part = "toys"
+ rest_api_id = "yrpm6dx4z8"
}
Plan: 3 to add, 0 to change, 0 to destroy.
How can I make it work for nested resources like /pets/{petID}? Creation of the /pets/{petID} resource in the above plan will fail! The challenge is setting the correct parent_id for aws_api_gateway_resource for nested resources. And this needs to work for any level of nesting.
Note: There exists a data source that can return the ID of any URL path like this:
data "aws_api_gateway_resource" "pets_resource" {
rest_api_id = aws_api_gateway_rest_api.rest_api.id
path = "/pets"
}
I just don't know how to put it all together!