Terraform Template Gimmicks

While managing multiple environments with Terraform, you might come across this scenario:

  • you have to manage multiple environments
  • all the environments use the same configuration file, but with different values
  • so, the above mentioned configuration file has to be a template
  • some environments tend to be snowflakes, as they have non-default values

You want to accomplish two things:

  • limit the number of variables you maintain for each environment
  • not replicate a snowflake variable to a standard / non-snowflake environment

The solution

The TL;DR version

variables and outputs

The structure of the Terraform code

$ tree 
.
├── common-template-file.tftpl
├── environmentA
│   ├── render-template.tf
│   └── variables.tf
├── environmentB
│   ├── render-template.tf
│   └── variables.tf
├── environmentC
│   ├── render-template.tf
│   └── variables.tf
├── environmentD
│   ├── render-template.tf
│   └── variables.tf
├── environmentE
│   ├── render-template.tf
│   └── variables.tf
└── README.md

So

  • there's a common template for all environments
  • each environment has its own directory
  • each environment has a bunch of .tf files holding the Terraform code
  • in variables.tf each environment has its own custom set of variables

The template and the variables

This is the common template file:

global:
  hostnames: [${hostname}]
  importantStuff:
    enableThis: ${enable_this}
thisWorksIfVariableIsDeclared:
  thisDependsOnTheValueOfVariable: %{ if enable_this == false }0%{ else }1%{ endif }
thisWorksEvenIfVariableIsMissing:
  VarOne: ${try(optional_vars.var_one, 2)}
  VarTwo: ${try(optional_vars.var_two, "bla")}
  VarThree: ${try(optional_vars.var_three, true)}

The are two important sections in the template:

the first one:

  thisDependsOnTheValueOfVariable: %{ if enable_this == false }0%{ else }1%{ endif }
  • enable_this is a variable that must exist in all variables.tf files
  • environmentE doesn't have it and you'll see Terraform failing because of that
  • depending on its value, another variable is set.

the second one:

thisWorksEvenIfVariableIsMissing:
  VarOne: ${try(optional_vars.var_one, 2)}
  VarTwo: ${try(optional_vars.var_two, "bla")}
  VarThree: ${try(optional_vars.var_three, true)}
  • optional_vars is a section (that can have any other name) that must exist in all variables.tf files
  • environmentD doesn't have it and you'll see Terraform failing because of that
  • but any variable nested under optional_vars is optional and can be missing

So, this will work in variables.tf:

template_variables = {
  git_tag       = "some_hash"
  hostname      = "A.foo.bar"
  enable_this   = false
  optional_vars = {}
}

and this will work as well:

template_variables = {
  git_tag     = "C"
  hostname    = "C.foo.bar"
  enable_this = true
  optional_vars = {
    var_one   = "C has both var_one"
    var_three = "... and var_three"
  }
}

but this won't:

template_variables = {
  git_tag     = "some_hash"
  hostname    = "D.foo.bar"
  enable_this = false
}
more ...