Optional Terraform Blocks
Since Terraform 0.12.0 there are arguments and blocks;
just two ways of configuring resources.
“An argument assigns a value to a particular name” and a “block is a container for other content” (source).
Not a very enlightening definition, but after all “just” syntax anyway.
Don’t get me wrong, syntax (and therefore UX) is one of the most important aspects of technology in my point of view.
Given some blocks may need to be repeated, Terraform has a way of making them dynamic. Great, but what if Terraform you want to make them optional?
The Problem Link to heading
Writing your own Terraform module is about creating abstraction and this includes sometimes features becoming optional. For example the object lock configuration of a AWS S3 bucket.
An object lock configuration needs to be enabled via the argument called object_lock_enabled. Example code copied from here:
|
|
Fair to assume the argument would also accept a value like Disabled in case there is no need for an object lock?
Yes, but unfortunately not the case. Here is the line in the AWS Terraform provider doing the validation on the argument’s value.
(In this case it was quite useful to not have object locks enabled during development, simply because a bucket with object lock enabled could only be deleted by the root user of that Amazon AWS account.)
On the S3 API REST API docs the feature can be enabled via a HTTP request header:
|
|
My feeling for the English language suggests that True would have been a better value than ObjectLockEnabledForBucket. But also, why not accept: False?
In all honesty, my experience in API design is close to zero. And I can only assume AWS has good reasons for doing what they do.
Maybe it’s quicker to validate only for one possible valid value? If not quicker, it may offer some other advantage.
On YouTube is an interesting talk from the AWS re:invent 2019 conference called Beyond five 9s: Lessons from our highest available data planes about the reliability of AWS’ services. If you’d rather read than watch and listen, have a look here.
The talk also mentions that there are mathematical ways of arguing about the correctness of code, and maybe these considerations feed back into API design 🤷
The Solution Link to heading
“A dynamic block acts much like a for expression, but produces nested blocks instead of a complex typed value.” – https://www.terraform.io/language/expressions/dynamic-blocks
The following way of (mis-)using this ability, renders a block optional. Line 2 has a condition and the value of object_lock_enabled can just stay being Enabled as the content block is simply not being used, if the condition is False.
|
|
Using dynamic blocks for making them optional doesn’t feel super straight forward. Syntactically, but also in terms of realising dynamic blocks can be used for this at all.
I don’t see a reason why a count meta-argument wouldn’t be the better solution. For example as it would just be consistent with how you make resources optional.
Conclusion and Disclaimer Link to heading
I didn’t invent this solution myself, but it took my a while to find it. You can find the very same solution on other places on the internet:
It also seems that only in Terraform version 0.12.0 they fixed fundamental limitation in the conditional ? operator: https://www.hashicorp.com/blog/terraform-0-12-conditional-operator-improvements
But hopefully this article provides some more value by explaining a bit more of the context and use case. Speaking of use cases, I can only assume there are way more cases in which a argument only accepts one value, so I think it’s just a matter of time when I need this workaround again. And luckily now I know where I can find an example on how to use dynamic blocks to make them optional.