State file isolation

Terraform state isolation ensures that each Terraform configuration maintains its own separate and independent state file to prevent interference or conflicts with other configurations.

Terraform configuration files can quickly become large and complicated; the same goes for the state. It’s good practice to employ isolation as much as possible to reduce the blast radius should a problem occur. By blast radius, I mean how much damage is done if an error is introduced to the system. If all your resources are defined in a single configuration, and something impacts that deployment, your entire infrastructure is at risk.

There are two methods to reduce the blast radius; w orkspaces and file structure.

Terraform Workspaces

Workspaces are always in-use, you don’t see it unless you check. In a Terraform configuration folder that has had terraform init applied you can see the workspace you are using as shown below.

$ terraform workspace list
* default

This will create a state file exactly as configured in the backend configuration as per the key value.

terraform {
  backend "s3" {
    bucket = "terraform-state-grinntec-learn"
    key = "project/resources/terraform.tfstate"
    region = "us-east-2"

    dynamodb_table = "terraform-locks"
    encrypt = true
  }
}

state file blob URL

s3://terraform-state-grinntec-learn/project/resources/terraform.tfstate

In the same configuration working directory if you run the following command you will create a new workspace called instance2.

$ terraform workspace new instance2
Created and switched to workspace "instance2"!

You're now on a new, empty workspace. Workspaces isolate their state,
so if you run "terraform plan" Terraform will not see any existing state
for this configuration.

Now if you check the S3 bucket you will see a new data path with env:/{workspace} in the path.

state file blob URL

s3://terraform-state-grinntec-learn/env:/instance2/project/resources/terraform.tfstate

If you run this command you can see which workspace you are in as indicated by the asterix.

terraform workspace list
  default
* instance2

Now if you run terraform plan in the instance2 workspace it will act as if this is a brand new empty deployment as the state file it is referencing is empty. Remember you are using exactly the same Terraform configuration file. You can deploy the resource and it will be created alongside the one created in the default workspace.

So basically, Terraform workspaces allows you to use the exact same Terraform resources and backend configuration file in the same working directory and split the target state file location based on the workspace you are working in which in effect allows you to create multiple deployments.

In my opinion, this is not a good way of working in Terraform for two reasons. First, you likely want to deploy the same resources to allow for multiple environments such as prod and stage. In this case, as your landing zone is the same AWS account, you are deploying production next to development resources which goes against good security and resource management practices. The second reason is that using workspaces could do with being more obvious. There is little indication of which workspace you are in unless you run the command to check, and in a busy or complicated working environment, the chances of deploying to the wrong workspace is high.

File structure

Suppose you were to chop up your infrastructure into smaller individual configurations, so prod, stage, etc. A change made to a dev that causes a problem only affects the development resources as the blast radius is contained by the working directory and the configuration using a stage version of the state file. Also you can use a prod and stage AWS account to further separate your resources as there would be two S3 buckets; one per AWS account.

This does mean there are more files to manage though, but for me that is small penalty to pay if it means I have less chance of affecting my production system due to a simple human error. It adds further simplicity in that you know exactly what system you are working in given the path so using automation tools can also be simpler to configure.

state file blob URL - stage

s3://terraform-state-grinntec-learn/project/stage/resources/terraform.tfstate

state file blob URL - prod

s3://terraform-state-grinntec-learn/project/prod/resources/terraform.tfstate

So instead of having a single working directory and set of Terraform configuration files as below.

/path/to/directory
+-- project
|   +-- main.tf
|   +-- providers.tf
|   +-- variables.tf
|   +-- outputs.tf

You end up with duplicated folders split based on the environment, for example, prod and stage.

/path/to/directory
+-- prod
|   +-- main.tf
|   +-- providers.tf
|   +-- variables.tf
|   +-- outputs.tf
+-- stage
|   +-- main.tf
|   +-- providers.tf
|   +-- variables.tf
|   +-- outputs.tf
Last modified July 21, 2024: update (e2ae86c)