Over the past couple of months I’ve been involved in upgrading my favourite automation tool; Terraform, to version 0.12.
I was late to the 0.12 party for a couple of reasons:
- There were over 110 Terraform states to upgrade, across 3 clouds and 20+ Terraform providers
- Many of the states shared the same custom modules
- A couple of Terraform providers took a while to support 0.12
- Some providers for 0.12 only supported the latest version of the platform it’s managing e.g. Kong API
This does not describe how to upgrade Terraform to 0.12 (the official guide is here: https://www.terraform.io/upgrade-guides/0-12.html), but shares some tips and tricks to speed up the process and minimise impact.
Backup your states
Once you run terraform apply with 0.12 on a state, it cannot go back to 0.11, so ensure you’ve backed up your states. You can save your remote state with:
1 |
terraform state pull > STATE |
It is also a good practice to have bucket versioning enabled on your storage bucket (I use at least 10 versions).
Upgrade custom modules
If you’re using many custom modules in your git repository, clone them all into another location i.e. 0.12/ subfolder, and upgrade each one independently.
Yes you can do a terraform init and plan on a module just to validate the syntax:
1 2 3 4 |
terraform_v0.12 init terraform_v0.12 0.12upgrade terraform_v0.12 validate terraform_v0.12 plan |
Specify min version for all providers
If you see the following error message:
Error: Failed to instantiate provider “github” to obtain schema: Incompatible API version with plugin. Plugin version: 4, Client versions: [5]
This indicates that your provider does not support Terraform 0.12.
Many people do not specify a min version for providers, especially the core Terraform providers such as template, http, random, null etc… Upgrade each one and specify a min version e.g.
1 2 3 |
provider "null" { version = ">= 2.1.2" } |
Upgrade states
TLDR;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# ensure you’re up to date with 0.11.x terraform_v0.11 plan && terraform_v0.11 apply # run the official checklist terraform_v0.11 0.12checklist # remove plugins to force a clean init rm -rf .terraform # change modules source to 0.12 in your .tf files # run a clean init terraform_v0.12 init # execute the official upgrade tool terraform_v0.12 0.12upgrade # check for syntax terraform_v0.12 validate # plan until there are no changes terraform_v0.12 plan |
You may see some default values have changed or been added to the new provider, add them to your .tf files until you see no changes or they are minor changes and safe to apply.
The 0.12upgrade command creates a versions.tf file which is a quick way to see whether you’ve upgraded this state yet (including in modules).
Some gotchas
The 0.12upgrade does a great job but isn’t going to be 100% accurate. Some common changes are:
var.version reserved
Remove any var.version as it’s now reserved (I’ve had to remove it in lots of modules relying on a version of a helm chart for example)
Count for enabled
A quick fix for the count where it’s used for true/false is to convert it to 1 or 0:
count = var.enabled ? 1 : 0
Quotes
Some fields in resources need quotes as Terraform thinks it’s a variable or resource property, when it’s not and consequently removes the quotes. In this example credentials.json needs to be quoted
1 2 3 4 5 |
resource “kubernetes_secret” “foo” { data = { "credentials.json" = google_service_account_key.mykey.private_key } } |
Lookups
If you get the error message “Blocks of type “default” are not expected here. Did you mean to define argument default?”.
Lists have changed with 0.12 and are much more powerful, a shortcut to keep it working is to just specify type = any.
1 2 3 4 5 6 7 8 |
variable "node_a_machine_type" { type = any default = { "0" = "custom-1-6912-ext" "1" = "custom-1-6912-ext" "2" = "custom-1-6912-ext" } } |
Happy terraforming !