Terraform – using variables to initialize configuration

6 months ago I didn’t have a clue what Terraform was. In my previous workplaces I always worked with infrastructure already in place and hardly any changes were ever required. So I was very excited when I started a new job, because I knew I would have a chance to be exposed to more modern approaches to building infrastructure, such as Infrastructure as Code.

Recently I have been digging my teeth into modifying existing Terraform scripts in order to facilitate the migration of our microservices infrastructure from one AWS account to another. One task was focused on refactoring Terraform scripts to reduce code duplication (the provider is AWS). Below are some things that I learnt during this process.

The remote backend is used to manage Terraform state. This is to ensure that the state is stored somewhere else than your local machine, and that all team members have access to it and are actually working with the latest state of infrastructure. Backends also might support state locking – so if a colleague is amending the state, it will ensure that noone else can make changes at the same time (unless they force unlock the state).

When you deploy to multiple environments, it is good practice to place environment variables into separate files and pass them into Terraform commands. In this way, the rest of the code can be easily reused for each environment thereby ensuring that environments mimic each other. An example of difference in variables for each environment is that you might want to use a t2.small EC2 instance in development and c4.xlarge in production.
It’s also good practice to separate backend initialization variables from the variables used elsewhere.

Say, I have a backend initialization block in HCL (I love and hate tech acronyms, there are so many of them; this one stands for Hashicorp Configuration Language):

As an aside: this means that I’m storing the state in an AWS S3 bucket called “my-tf-bucket” (which should already exist before you execute any Terraform commands); key is where the state is stored for the whole Terraform definition; dynamodb_table is a table used for locking the state and checking consistency. So, if I want to reuse the following code for all environments, I can create a variables file with the following content:

After that we can rewrite the backend part like so:

And to initialize the backend we need to run:

terraform init --backend-config PATH/TO/ENVIRONMENT_VARS_FILE -reconfigure

You need to add  the reconfigure flag in order to prevent migration of any existing states. I suppose if you’re starting from scratch, you probably don’t need to add this flag.

It would certainly be easier if it was possible to interpolate variables in the backend block. Unfortunately, it is not allowed, and I think there have been some requests to Hashicorp to make it possible – we shall see!