Install Terraform with tfenv

Using tfenv to manage terraform versions

There will come a time when you will need to change to different versions of Terraform. Similar to tools like rbenv for Ruby or pyenv for Python, there's a popular tool used to install different versions of Terraform called tfenv.

Most tooling for infrastructure operations runs on Intel processors, called the amd64 architecture with in Terraform, but given the recent addition of Macbook M1 laptops, the architecture is arm64. Older versions of Terraform that may be needed will require installing Terraform that is only available on Intel processors (amd64 binaries). This can be installed using tfenv as well.

This article will have cover the following sections:

  1. Installing tfenv on Ubuntu (linux) and mac OS (darwin)
  2. Installing a latest or recent version of Terraform
  3. Installing legacy versions of Terraform on macOS with Arm processor (Macbook M1/M2)
  4. Installing Legacy providers plugins (Ubuntu and Macbook M1/M2)

Installing tfenv

Here’s how you can install tfenv on Ubuntu (linux) or mac OS (darwin).

Installing tfenv on Ubuntu

git clone ${HOME}/.tfenv
export PATH="${HOME}/.tfenv/bin:${PATH}"

# install to appropriate shell startup file, e.g. $HOME/.bashrc
echo 'export PATH="$HOME/.tfenv/bin:$PATH"' >> ${HOME}/.profile

Installing tfenv on mac OS

brew install tfenv

Installing Terraform

Once tfenv is installed and available in the path, you can simply run something like this:

tfenv install 1.3.7

Using tfenv list-remote you can fetch different versions of Terraform that are available for installation. From there, you can use GNU grep to select the desired version. For example, here's how to install the latest version:

LATEST_VERSION=$(tfenv list-remote \
| grep -vP '\d.\d{0,2}.\d{0,2}-.*' \
| head -1

tfenv install ${LATEST_VERSION}

NOTE: On macOS, which has BSD flavor grep, it doesn't support PCRE. To get GNU Grep with support for PCRE, use brew install grep.

You can extract the last minor version by passing in an explicit major version to GNU Grep:

# find latest 0.12
LATEST_0_12=$(tfenv list-remote \
| grep -oP '^0.12.\d{1,2}' \
| sort --version-sort \
| tail -1

tfenv install ${LATEST_0_12}

For the latest version, whatever that is, you can also just run the following:

tfenv install latest

Using the desired Terraform version

When you want to use the a particular version of an installed version terraform, you can select it with the following:

tfenv use 1.3.7

Also, to have the Terraform switch automatically when navigating to the project directory, you can place a .terraform-version to auto-select the version:

mkdir -p  ${HOME}/my_project/.terraform-version
echo '1.3.7' > ${HOME}/my_project/.terraform-version

# change version of Terraform automatically
cd ${HOME}/my_project/.terraform-version

Installing Legacy x64 versions on Mac

If you Macbook sports a arm64 processor, you need to get Rosetta to run amd64 binaries on macOS. Afterward, you can follow these steps below to install legacy versions of terraform.

export TFENV_ARCH="amd64"

# install amd64 terrafrom version since there are no arm64 binaries
tfenv install 0.12.31
tfenv use 0.12.31

Installing Legacy providers

In recent versions of Terraform, this installation will happen automatically when you run terraform init

First, you need to find the appropriate plugin version that matches the supported version of Terraform. In the case of terraform-provider-kubectl, you need to install 1.11.2 of that version of that plugin.

Installing legacy provider on Ubuntu Linux


# setup plugins directory if this does not exist already
mkdir -p ${HOME}/.terraform.d/plugins/${ARCH}/

# download archive and install plugin
pushd ${HOME}/Downloads
curl -sOL ${URL}
unzip ${PKG} && rm LICENSE
mv terraform-provider-kubectl_v${VERS} \

Installing legacy provider on Macbook M1

For current Macbooks running the arm64 processor, you will need to install the legacy provider for amd64 (not arm64). This is how you can do that.

ARCH="darwin_amd64" # install amd64 plugin for legacy terraform 

# setup plugins directory if this does not exist already
mkdir -p ${HOME}/.terraform.d/plugins/${ARCH}/

# download archive
pushd ${HOME}/Downloads
curl -sOL ${URL}
unzip ${PKG} && rm LICENSE
mv terraform-provider-kubectl_v${VERS} \


I hope this is useful to your Terraform adventures. The reason why locking down versions of Terraform is important, is that there are significant language changes that make the code incompatible. The installer dependency chain with Terraform modules and providers will need to match the target version of the terraform command.

In addition to this, the state that is generated from interacting with the provider will likely need to match, so it is important that the infrastructure that created with a particular version of Terraform, also be removed when ultimately terraform destroy is used.

As the infrastructure progresses forward, there are techniques you can use to smoothly transition. One method it to have modular modules that depend on looking up the state in the cloud through data sources, and important existing infrastructure into the state using terraform import. This is definitely material for a future article.

In the meantime, you can use the supported version with tools like tfenv. Another tool, as an alternative to tfenv is asdf. I have not explored supported legacy amd64 legacy binaries on Macbook M1 yet, but assume this can work. The asdf is nice in that it has a plugin architecture to support a variety of languages.

Lastly, about this article, which I am sure might unnerve shell purists, where I use $HOME instead of ~ and I use of curly braces for ${VARIABLES}, when they are not needed, when simply $VARIABLE is preferred: this was done intentionally to make the variables light up in color syntax highlighters on different platforms, and $HOME was used for novice uses that may not be familiar with ~ notation.



Joaquín Menchaca (智裕)

Linux NinjaPants Automation Engineering Mutant — exploring DevOps, o11y, k8s, progressive deployment (ci/cd), cloud native infra, infra as code