101 Terraform Patterns - 01 Parameters
Simple Patterns for dealing with a complex Terraform code-base
TLDR: As many as possible defaults in modules, additional defaults in root in variables.tf, environment specific variables in root in *.tfvars & implement consistency
Example repo here.
Dealing with parameters
In a simplified worldview Terraform is basically a complex system for parameter maintenance. So dealing with them consistently is important. If not handled carefully parameters can become obscure and small changes become a challenge. If crucial values are hard-coded in multiple places or if they are referenced multiple times with different names it is time to rethink your implementation.
Defaults
In a simple project three locations matter:
config / env.tfvars
modules / module / variables.tf & main.tf
root: variables.tf
Default values help reduce the number of parameters that need to be passed to a module.
This is the basic pattern:
First, decide which values are reasonable defaults and fix them hard-coded inside modules. This means that you do not expose these variables to the user, this can always be changed later on, but only with a change inside the module. If you hard-code them inside a module you should still use locals in order to make the variables flexible and reusable locally.
For example, inside main.tf
inside the module:
locals {
parameter1 = "This value is locally hardcoded, do this with values that never (or rarely) change"
}
resource null_resource "example" {
triggers = { parameter1 = local.parameter1 }
provisioner "local-exec" {
command = "echo ${local.parameter1}"
}
}
Secondly, define as many as reasonable defaults inside the modules using variables.tf
. This allows you to avoid defining the variable when you call the module, but if needed you can still change the default value, without changing the module, when you want to override the default.
For example, inside variables.tf
inside the module, ‘parameter2’ has no default, but ‘parameter3’ does:
variable "parameter2" {}
variable "parameter3" {
default = "This variables has a default value inside the module"
}
We can do the same with variables in the root of the project. However, in the root project you might want to avoid hard-coding values directly into the module and simply only set defaults for all values in variables.tf
. If these values are overwritten use locals, you can place these in a separate locals.tf
file.
Lastly, your config/env.tfvars
should contain the least amount of variables as possible, and should only focus on the key values that change between environments.
Consistency
Besides the funnel that abstracts away as many as possible parameters it is also a good idea to follow a basic convention. Start with a naming convention, use underscores, use British or American English, and copy the name of the actual variable in the resource whenever possible.
Secondly, always add a simple description inside the variable, this makes debugging a lot simpler since the error will tell you what the variable is meant to do. Also add validations to variables, but this will be dealt with in a future article.
Thirdly, order you variables inside the module and keep that same order when calling the module, this will create a basic flow where you always look for some basic component at the top of a list, for example place a name and the location always at the top and place tags at the bottom, module specific variables are then placed in the middle.
Nota bene
This pattern described here is meant for Terraform implementations that use a basic structure where environments are separated through branches.
There are other Terraform patterns, but most of them only impact the bottom of the funnel. The general advice to place as many parameters as possible inside the abstracted layers (the modules) holds for any implementation.
Additional variables can also be passed inside the CI/CD pipeline, this pattern might be needed if the variables are only known at run time, but should ideally be avoided since you are strongly coupling the CI/CD to your specific implementation.
If you are using a folder structure for environment separation you might want to use Terragrunt, which is out of scope but a good tool.
References
These are some great resources that have helped me and also provide some more insight on some other topics.