Managing Terraform Versions Like a Pro

May 28, 2019
productivity hacks

Last week Terraform 0.12 finally came out, and it’s huge. It’s a pretty big release disguised as a point release, that may very well break your codebase here and there. Therefore I wanted to dive into how you can easily run multiple versions of Terraform on your system, so you can gradually change from 0.11.x to 0.12. Or perhaps you just want to test-drive new versions or test your custom providers against multiple versions of Terraform.

Solution 1: Run Terraform in a container

This is how I managed my Terraform install for the past 3 years. I dislike installing things, but what I found more important was that every time somebody on my team got a new version of Terraform through brew upgrade, nobody else could run Terraform anymore because the statefile had been changed. By putting Terraform in a Docker container, we could specify exactly which version we ran, and we could customize how we’d ran it (passing environment variables, adding custom providers, etcetera).

The upside was that everyone would certainly be on the same version of Terraform. The obvious downside was that we now had to maintain our own Docker image and associated shell script to run Terraform this way. And up to about Terraform 0.10.x it was definitely worth it, if not just because of custom providers. But then Terraform was basically slimmed down, and providers were split off into their own projects. You no longer had to bundle your custom provider with Terraform itself. Great!

Solution 2: Use TFEnv

Recently I discovered TFEnv and started to use that. TFEnv does a few really neat things:

  1. It allows you to install multiple versions of Terraform in parallel
  2. It allows you to pick the one you want to use
  3. It allows you to drop a file next to your Terraform code to specify which version of Terraform it needs

Installing TFEnv

You can check the link above for installation instructions, but for Homebrew/Linuxbrew users, here’s what you need to do. First, you need to uninstall or unlink any existing version of Terraform, or installation of TFEnv will fail. That’s because TFEnv will install a wrapper for Terraform in /usr/local/bin/terraform

# Unlink, but keep
$ brew unlink terraform

# Just remove
$ brew remove terraform

Then, you can install TFEnv using the familiar brew install tfenv command. Now, if you run Terraform you’ll get an error that no versions are installed. So let’s install the most recent releases of Terraform.

Installing Terraform using TFEnv

$ tfenv install 0.11.14
[INFO] Installing Terraform v0.11.14

...

[INFO] Installation of terraform v0.11.14 successful
[INFO] Switching to v0.11.14
[INFO] Switching completed

$ tfenv install 0.12
[INFO] Installing Terraform v0.12.0

...

[INFO] Installation of terraform v0.12.0 successful
[INFO] Switching to v0.12.0
[INFO] Switching completed

$ tfenv list
* 0.12.0 (set by /usr/local/Cellar/tfenv/0.6.0/version)
  0.11.14

Awesome! Now, if I want to switch to Terraform 0.11.14, all I need to do is run tfenv use 0.11.14 and I’m set. Except, I’ll forget that, and screw up my state file. Again. So let’s fix that.

Specify which version of Terraform to use

What we really want is a way to specify which version of Terraform should be used for a specific set of code. It would be even better if there was some sort of hierarchy so that we can just specify which version of Terraform to use when nothing is defined. A bit like this:

# Let's stick with Terraform 0.11.14 by default
$ echo "0.11.14" > ${HOME}/.terraform-version

# But we want to use 0.12.0 for a specific project
$ echo "0.12.0" > ${HOME}/code/project-foo/infra/.terraform-version

# So in our ~ we should get Terraform 0.11.14
$ cd ~ && terraform version
Terraform v0.11.14

# But in our project, it should be 0.12.0
$ cd ${HOME}/code/project-foo/infra && terraform version
Terraform v0.12.0

Neat! But there’s some more magic we can apply here. In the example above we have a fixed default version of 0.11.14. But what if 0.11.15 comes out? Chances are, that what we really want here is the latest 0.11.x release. To get that, we can do this:

$ echo "latest:^0.11" > ${HOME}/.terraform-version

$ tfenv install

What if I don’t have version x.y.z installed?

So let’s say that you dropped a .terraform-version file in your project that demands version 0.11.13, but it’s not installed. How would that work? Let me show you:

$ echo "0.11.13" > ${HOME}/code/project-foo/infra/.terraform-version

$ cd ${HOME}/code/project-foo/infra/.terraform-version
$ terraform version
tfenv: tfenv-version-name: [ERROR] version `0.11.13' is not installed (set by /Users/benny/code/project-foo/infra/.terraform-version
/.terraform-version)
/usr/local/Cellar/tfenv/0.6.0/libexec/tfenv-exec: line 22: /usr/local/Cellar/tfenv/0.6.0/versions//terraform: No such file or directory

$ tfenv install
...

$ terraform version
Terraform v0.11.13

TL;DR

What are the main takeaways here:

  1. You can use TFEnv to install and manage multiple versions of Terraform.
  2. To install, run: brew remove terraform; brew install tfenv
  3. Install Terraform versions using tfenv install 0.11.14
  4. Switch to the version you like using tfenv use 0.11.14
  5. You can use .terraform-version files to specify which version of Terraform should be used

Happy Terraforming!