Terraform

How to Manage Different Terraform Versions

How to Manage Different Terraform Versions

Terraform has become more popular than any other IaC tool. With its popularity, new features have been introduced, some of which are breaking existing functionality, creating difficulties for inexperienced users who don’t use versioning. 

As well as new features coming to Terraform, Terraform providers also release new features regularly. These releases can also have breaking changes, and leveraging versioning goes a long way in protecting against those.

We will cover:

  1. Why do we need versioning?
  2. Terraform versions
  3. Terraform provider versions

Why do we need versioning?

You may wonder why you would need to leverage versioning for IaC too.

Well, infrastructure is the first piece of Lego when it comes to deploying your applications. For every bit of code, versioning should be leveraged, and whenever you are using a product, you have to ensure that it sticks to a version that doesn’t incur breaking changes.

Here are four things related to versioning you have to keep in mind when working with Terraform:

  • Terraform Version → Stick to a specific version for your Terraform code that ensures there are no breaking changes.
  • Terraform Code Versioning → Use a VCS. This can be really helpful when it comes to collaboration between teams, and using git as a single source of truth will considerably reduce the chances of human errors.
  • Terraform Provider Version → Stick to a provider version that ensures there are no breaking changes.
  • Terraform Module Versions → Use an exact version of the module.

Now the question becomes, how can you stick to a version that ensures there are no breaking changes? Luckily, when it comes to versioning, the IT world has a widely accepted standard.

Versions are defined in the following fashion: Major.Minor.Patch (e.g. 1.2.7).

In 99.99% of the cases, a Patch change will not affect the current functionality of the product. In some cases, a Minor change will introduce breaking changes, whereas a Major change will most likely bring out breaking changes.

With Terraform, you have the flexibility for both the product and the providers to:

  • Lock in a specific version
  • Exclude a specific version
  • Use a version that is either greater or lower than the version you are specifying
  • Allow only the most upright version to increment

Terraform versions

What is my Terraform version?

To find out which Terraform version you are using, the most simple thing you can do is go to your terminal and run:

terraform -v                                                                

Terraform v1.5.3
on darwin_arm64

Your version of Terraform is out of date! The latest version
is 1.5.4. You can update by downloading from https://www.terraform.io/downloads.html

This Terraform command will show you your current version, the platform on which it is installed, and if you are using a version that is out of date, it will let you know and show you what the latest version is.

What are the latest versions of Terraform and major Terraform providers?

Product Major.Minor Version Release Date Latest
Terraform 1.6 Oct 2023 1.6.3 (Nov 2023)
1.5 Jun 2023 1.5.7 (Sept 2023)
1.4 Mar 2023 1.4.6 (Apr 2023)
1.3 Sep 2022 1.3.9 (Feb 2023)
Terraform Provider AWS 5.24 Oct 2023 5.24.0 (Oct 2023)
5.23 Oct 2023 5.23.1 (Oct 2023)
5.22 Oct 2023 5.22.0 (Oct 2023)
Terraform Provider Azurerm 3.79 Oct 2023 3.79.0 (Oct 2023)
3.78 Oct 2023 3.78.0 (Oct 2023)
3.77 Oct 2023 3.77.0 (Oct 2023)
Terraform Provider GCP 5.4 Oct 2023 5.4.0 (Oct 2023)
5.3 Oct 2023 5.3.0 (Oct 2023)
5.2 Oct 2023 5.2.0 (Oct 2023)

As you can see from the above table, minor versions of the Terraform product have a cycle of a couple of months between them, whereas minor versions of the Terraform providers come out every couple of weeks/days.

This happens because major cloud providers are evolving at a very high rate and are adding new resources or new features to existing resources faster.

Note: New versions of Terraform will be placed under the BUSL license, but everything created before version 1.5.x stays open-source. OpenTofu is an open-source version of Terraform that will expand on Terraform’s existing concepts and offerings. It is a viable alternative to HashiCorp’s Terraform, being forked from Terraform version 1.5.6. OpenTofu retained all the features and functionalities that had made Terraform popular among developers while also introducing improvements and enhancements. OpenTofu is the future of the Terraform ecosystem, and having a truly open-source project to support all your IaC needs is the main priority.

Terraform version constraints

As mentioned before, you can introduce version constraints in multiple ways.

1. Equal constraint

Terraform

terraform {
  required_version = "=1.5"
}

Terraform Provider AWS

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "=5.10.0"
    }
  }
}

In this example, we are pinning the terraform version to 1.5 and the AWS provider version to 5.10.0.

2. Not equal constraint

Terraform 

terraform {
  required_version = "!=1.5"
}

Terraform Provider AWS

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "!=5.10.0"
    }
  }
}

The Terraform version can be anything that is different from 1.5, and the Terraform AWS provider can be anything different from 5.10.0.

In this case, Terraform will try to get the latest version, but if the latest version is the one that is specified in the constraint, it will get the second latest one.

3. Greater/less or equal to constraint

Terraform 

terraform {
  required_version = ">=1.5"
}

Terraform Provider AWS

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = ">=5.10.0"
    }
  }
}

In this case, we are trying to get versions that are greater or equal to the constraint specified for both Terraform and the AWS provider. This works in the same way for the lower constraint.

4. Allow only the most upright version to increment

Terraform 

terraform {
  required_version = "~>1.5.0"
}

Terraform Provider AWS

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "~>5.10.0"
    }
  }
}

Allowing only the most upright version to increment for patches is actually one of the best practices for managing your Terraform and Terraform provider versions.

In this case, for Terraform, only the Patch (0) will be incremented if a new patch version comes up, ensuring there is a minimal chance of a breaking change.

A similar thing happens to the AWS provider; the Patch (0) is incremented only if a new patch version gets released.

5. Breaking changes in minor versions

Historically speaking, Terraform had the biggest changes from version 0.11 to 0.12. Of course, breaking changes appeared in other versions, but this one had a lot to do with the language’s syntax.

0.11 syntax

resource "aws_instance" "example" {
  ami           = "${var.ami}"
  instance_type = "${var.instance_type}"

  tags {
    Name = "${var.instance_name}"
  }
}

variable "ami" {
  default = "ami-0c94855ba95c574c8"
}

variable "instance_type" {
  default = "t2.micro"
}

variable "instance_name" {
  default = "terraform-example"
}

0.12 syntax

resource "aws_instance" "example" {
  ami           = var.ami
  instance_type = var.instance_type

  tags {
    Name = var.instance_name
  }
}

variable "ami" {
  default = "ami-0c94855ba95c574c8"
}

variable "instance_type" {
  default = "t2.micro"
}

variable "instance_name" {
  default = "terraform-example"
}

The syntax for 0.11 became obsolete with the release of 0.12, even though Terraform maintained a degree of backward compatibility and many 0.11 style expressions still worked.

In this case, if you didn’t pin your version by allowing only the Patch version to increment, your code would’ve been in big trouble.

Usually, when a minor version is being released, an upgrade guide also becomes available, as you can see in the image below:

terraform 0.12

Upgrade guides indicate the best way to upgrade your current configuration to support the next version of Terraform, but sometimes they don’t include all the necessary information you need, and you may need to figure things out yourself.

Some guides for versions prior to 1.x, also come with an upgrade script that takes care of some of the breaking changes for the user. These won’t solve your problems completely, but they are still a great starting point.

Here’s an interesting fact: In 0.12, Terraform dynamic blocks and the for_each loops were introduced, becoming two of Terraform’s best features. 

How to upgrade your code / Terraform version

The most important thing to understand is what has changed in a new Terraform version. For that, you should head to Terraform’s Github repository, and go to the Releases page

On the Releases page, you will get information on the latest available versions. Pre-releases are marked with the pre-release tags and usually have an alpha in the tag name. I believe you should avoid using pre-releases as they are unstable, but it is important to note what features are coming up.

terraform version release

This is how I know the terraform test command will be moved out of experimental when 1.6.0 comes up.

Now, let’s look at the changelog for 1.5.0.

In this version, we can see there are no breaking changes, and that makes our lives easier. However, if you want to adopt the latest features, like the check block and the newly configuration-driven import, you will need to upgrade. 

Upgrading your local Terraform version can be done by:

  • Downloading the version you are interested in for your operating system and replacing the one you already have
  • Using tfswitch → a tool you can install locally, that can switch to any terraform version you’d like
  • Using tfenv → an alternative to tfswitch

After you have installed the version you want, you should check if this version has added any breaking changes to the configuration.

This also depends on the version you currently have, so if we take a look at Terraform 1.5.0 again, we see that it didn’t add any breaking changes from 1.4.0, but if you come from 0.12, for example, you will need to go through the changelog of each minor release to understand what has happened.

Usually, you will need to migrate to a new minor version step by step until you reach the version you desire, but if you reach 1.0.x, you can potentially jump to the latest v1.x minor version because of the Compatibility Promises for 1.x.

Sometimes, you may run a terraform plan/apply and see deprecation messages related to your code. It is best to fix these as soon as possible because otherwise when you are upgrading to a newer version in a couple of months, you may notice these are becoming obsolete.

Which Terraform version to use?

In an ideal world, we would all use the latest stable version of Terraform, but in big organizations, this is clearly impossible due to time constraints and technical debt. 

Whenever you are in charge of a Terraform project, it is important to have an upgrade strategy for all the code that is written. This can seem time-consuming, but if one of your engineers is in charge of overlooking this bi-weekly for less than 30 minutes, it will be a great time saver in the end.

When the big syntax change happened from 0.11 to 0.12, I had already implemented over 25 Terraform modules for the company I was working with at that time. As I am really focused on reusability, all these modules were using count, and you had the flexibility to create as many resources of a kind you wanted and create relationships between them easily.

As 0.12 approached, and I knew about the syntax changes and also tried them in the pre-release, I knew what the upgrade process would look like and how much time the upgrade would take. Later on, in 0.12.6 when for_each was added, I replaced all count occurrences with for_each, and again I had to go through the process of ensuring that everything was working properly.

I always aim to use the latest stable version of Terraform (Patch included), but I am happy to use at least the latest stable minor version. This is a challenging process, but it’s better to go one step at a time rather than undergoing a big upgrade from a very old version to the latest one.

Terraform provider versions

Products are evolving at different paces and have different APIs and different features. That’s why you need a provider for interacting with a tool via Terraform.

Identifying providers in a Terraform configuration

Whenever you run terraform init, you will see all the providers you have in a configuration. The output will be different depending on whether there is a .terraform directory and a .terraform.lock.hcl file created already. 

For a first run, for example, the output for running terraform init on an AWS configuration would be similar to:

terraform init

Initializing the backend...

Initializing provider plugins...
- Finding hashicorp/aws versions matching "5.10.0"...
- Installing hashicorp/aws v5.10.0...
- Installed hashicorp/aws v5.10.0 (signed by HashiCorp)

Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run "terraform init" in the future.

If you are running this a second time, the output will be similar to:

terraform init     

Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using previously-installed hashicorp/aws v5.10.0

You can also run the terraform providers command to get the versions you are using for your providers:

terraform providers

Providers required by configuration:
.
└── provider[registry.terraform.io/hashicorp/aws] 5.10.0

How to upgrade to a new provider version

To upgrade to a new provider version, the same process applies as for upgrading your Terraform version. Let’s suppose we are using the AWS provider, and we would like to upgrade to the latest major version from the second to the latest one. To do the upgrade, we would:

  • First, go to the Changelog of the latest Major version, which is 5.x.
  • Identify if you are using any of the resources, data sources, or provider configurations impacted by the change and make the necessary changes to your code.
  • Increment the required provider version in your terraform configuration and run terraform init -upgrade.

Best practices for Terraform versioning

The most important best practices when it comes to your Terraform and Terraform providers’ versions are:

  • Pin versions to only increment the patch to minimize the chance of breaking changes.
  • Whenever you see deprecations, take time to solve them, as what is deprecated today may become obsolete tomorrow and break your code.
  • Always version your Terraform configurations and Terraform modules.
  • Have an upgrade strategy for both Terraform and your Terraform providers
  • Always follow Terraform’s GitHub repository and the repository for the provider you are using to be prepared for the next updates (weekly or bi-weekly basis should be just fine).

Key Points

In this post, we’ve dived deep into Terraform versions and Terraform provider versions and gone through the process of upgrading them while learning some best practices to keep your infrastructure safe.

If you need a specialized platform that manages your IaC workflow and helps with your Terraform versions, Spacelift is your answer.  Start a free trial or book a demo with our engineering team to discuss your options in more detail.

Manage Terraform Better and Faster

If you are struggling with Terraform automation and management, check out Spacelift. It helps you manage Terraform state, build more complex workflows, and adds several must-have capabilities for end-to-end infrastructure management.

Start free trial