Create an AWS API Gateway resource based on conditions
You may know Terraform, the tool to create infrastructure as a code. I use it for several AWS experiments, I do. It is declarative and uses HCL language to declare resources to create. Let’s see how one can create resources based on a condition.
I wrote scripts to create API Gateway for my project. The API Gateway service is tricky, and in Terraform one uses several resources to make it work.
You start with adding the
resource, which defines the API Gateway itself. Handler paths are represented
as a tree structure. The root of the
tree matches to the empty path. The root node ID is returned from the
parameter of the
I use Terraform Modules in my scripts to reduce complexity. Modules are the same as functions in other programming languages. It helps to reuse code and reduce duplicates.
The Need of a Condition
I have a module to define API Gateway handlers. That module accepts a handler path and the
root_resource_id parameter. In the module, I have to decide either
to create new resource
for non-empty path, or to use the base
root_resource_id instead, for the empty one.
In a pseudo-code the problem looks as follows:
A Condition Implementation
I head that story from my friend Mikhail Kuzmin several years ago. I have no idea,
how I recalled that, but still. He told me something about the
count parameter is helpful
to implement a condition in Terraform.
Also, I found that in Terraform we have
ternary operator expression,
condition ? foo : bar, which helps me to extract the right resource ID at the end.
The overall condition for a resource did not look trivial. I decided to extract it as a dedicated module from the very beginning. That is what I created:
I do several tricks in that module. The first trick is to set
to zero when I need no resource created. Otherwise, I put
count = 1, which is the default.
The second trick is in the
hardler_id output parameters. I select
either the created ID or the
Ternary Expression and Complexity
You may want to ask, why is it so complicated, me too. I started with the more trivial variant of the second condition:
And then, I found out that Terraform computes both expressions in the ternary
expression. It differs from the semantics we got used from C-like languages. And so,
I had to have a non-empty list in the second expression. I use the
join two lists, and
list("") to create a new list with one element.
concat(aws_api_gateway_resource.handler.*.id, list("") does the trick
making a list contain at least one element, even if the
count was equal to
That is how I found the workable condition expression from the full example above.
Avoiding Ternary Operator
I was speaking with a colleague on that, and realized, the code can be simplified. Instead of the conditional operator, now I join two lists and pick the first element:
aws_api_gateway_resource.handler.*.id gives me an empty list if
count = 0.
That works the same way, but better and shorter. We have only one real condition in
the code now.
I got yet another crazy idea. What if I wish to support
long/path/to/create in my module.
What shall I do? The idea was to call the same module recursively for all needed path parts
to build the resources tree.
There are several problems, I came across. The first one. It is not possible to have a
count parameter on Terraform module usage.
I tried to include the same module from itself. It turned out, Terraform does not
support such inclusion and starts an infinite resolution in
terraform init call.
What if I fix
It is not possible too. One is not allowed to use a non-constant expression for the
parameter of a module.
I recalled and implemented the common pattern in Terraform to handle a conditional resources creation and applied it for AWS API Gateway resources. It plays well for my project, and I hope it will help you too.
Note. It cost me hours of endless debugging. One needs to call api_gateway_deployment after any change in the API Gateway configuration is done. That is hard to code all dependencies in Terraform correctly for it. It is even harder if you have modules around.comments powered by Disqus