Originally published on the Consensus Enterprises blog.
Terraform is an essential tool for automating cloud-computing infrastructure and storing it in code (IaC). While there are several ways to navigate between deployment environments (e.g. Dev, Staging & Prod), I’d like to talk about how this can be done with environment variables, and explain why it can’t be done more naturally with Terraform variables.
Background
I originally wrote this as a comment on the feature request Using variables in terraform backend config block, which explains Terraform’s design limitations preventing it from allowing any variables in backend configurations, which is what Terraform uses to determine where to store its state data files (which track the cloud resources it manages).
To illustrate, this is where variables can’t be used, even though the configuration must change when the environment changes:
|
|
Alternatives to using environment variables
Alternatively, it’s possible to have a single backend state configuration that stores data for multiple environments using Workspaces. You can then use Terraform CLI commands to switch between them. However, this solution is not not appropriate for all use cases, as per the documentation:
Workspaces are not appropriate for system decomposition or deployments requiring separate credentials and access controls.
If this isn’t a problem for you, Workspaces is a good option. Otherwise, please keep reading.
Solution
In demonstrating a solution, I’m going to be working with OpenStack, which is the IaaS run by many cloud providers, but the same concept can be applied to other IaaS, such as AWS, Azure, GCP, etc. Here, the backend state data will be stored in OpenStack’s Swift object storage service.
Update (2023-04-11): While the example below is still valid conceptually, Swift can no longer be used as a Terraform backend because support for it has been removed. If you’re still using Swift for this purpose, the official migration documentation is sparse. However, I provide guidance in a more recent article: Moving Terraform State from OpenStack Swift to GitLab.
Typically, you’d download an OpenStack RC file, which contains the credentials needed to access the API. In each of these (e.g. vexxhost-abc-prod-ca.openrc.sh
, where the format is provider-project-environment-region
), I append the following lines to the end:
|
|
The sourced (included) file then looks like:
|
|
The special environment variable here is TF_CLI_ARGS_init
. This is what Terraform uses to configure the backend, if it’s set. So by changing that environment variable, you can change the backend configuration. All that’s needed in the code is the following, basically just the backend type, with details being pulled in from the environment.
In main.tf
(or wherever), the backend definition:
|
|
Switching between environments
To switch between environments, I simply source another OpenStack RC file; I have one for every environment that I care about. With the above set-up, it also switches the Swift object storage containers used for backend state. You can then run terraform init
.
I’ve found that sometimes it’s necessary to delete your .terraform
directory before rerunning terraform init
though, or it’ll get confused and you’ll get strange error messages. So as standard practise, I run the following command to remove it first:
|
|
Summary
- You cannot use Terraform variables to vary the backend state configuration.
- You can, however, use the environment variable
TF_CLI_ARGS_init
instead. - Run a script to set this environment variable with the configuration matching the deployment environment you’d like to work with.
- Simply specify the backend type in the Terraform code, and
TF_CLI_ARGS_init
along with your script(s) will take care of the rest.