1. Introduction

Terraform workspaces use the same configuration for their corresponding resources. However, each workspace has a different state file.

So, what if we want to manage a resource from a workspace different from the origin workspace? Can we transfer a state from one workspace to another?

In this tutorial, we discuss how to migrate states between workspaces in Terraform.

2. Create a Resource

To illustrate the migration of states between workspaces, we’ll create a file called baeldung.tf containing the following configuration:

provider "aws" {
  region = "us-east-1"
}

resource "aws_instance" "baeldung" {
  instance_type               = "t2.micro"
  ami                         = "ami-0574da719dca65348"
  associate_public_ip_address = true
}

To create the resource, we’ll run terraform init in the working directory and then apply the configuration:

$ terraform init && terraform apply -auto-approve
Initializing the backend...
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using previously-installed hashicorp/aws v5.60.0

Terraform has been successfully initialized!

...truncated...

Terraform will perform the following actions:

  # aws_instance.baeldung will be created
  + resource "aws_instance" "baeldung" {
      + ami                                  = "ami-0574da719dca65348"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = true
...truncated...
    }

Plan: 1 to add, 0 to change, 0 to destroy.
aws_instance.baeldung: Creating...
aws_instance.baeldung: Still creating... [10s elapsed]
aws_instance.baeldung: Still creating... [20s elapsed]
aws_instance.baeldung: Still creating... [30s elapsed]
aws_instance.baeldung: Creation complete after 32s [id=i-05b783b91246bf8b3]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Using the -auto-approve flag with terraform apply automates plan approval before applying.

Since we did not create, switch, or specify a workspace when running the terraform commands, all we’ve done so far is in the default workspace. Let’s confirm this using terraform workspace show:

$ terraform workspace show
default

3. Download the State Using terraform state pull

Now that we’ve created a resource in the default workspace, we have a state that we can migrate to another workspace.

That said, if the origin workspace uses a remote state, we have to download the state file before migration, and we can do this with the terraform state pull command:

$ terraform state pull > baeldung.tfstate

Let’s run ls in the directory to which we downloaded the state to confirm success:

$ ls baeldung.tfstate
baeldung.tfstate

If working locally, the state file would already be in the working directory with the name terraform.tfstate, so we wouldn’t have to download it.

4. Initialize the Workspace With the Desired State File

Now, after downloading or locating the state file, we’ll initialize the destination workspace with it. To do this, we use the workspace creation command, terraform workspace new, with the -state flag:

$ terraform workspace new -state=baeldung.tfstate ws2
Created and switched to workspace "ws2"!

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.

terraform workspace new not only creates a new workspace but also switches to that workspace. That said, we can always get into another workspace using terraform workspace select.

When running terraform workspace new with the -state flag, we pass the path of the state file to -state.

5. Run terraform plan

5.1. Checking the Workspace

To confirm that we’ve migrated the default workspace’s state to the ws2 workspace, let’s run terraform plan in the ws2 workspace:

$ terraform plan
aws_instance.baeldung: Refreshing state... [id=i-05b783b91246bf8b3]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are
needed.

The state in the default workspace and ws2 are the same, hence running terraform plan returns the message. In other words, workspaces with the same state are synced with the resource created by that state.

However, any change to the state in either workspace may change this.

5.2. Planning a Change in the Workspaces

If we make a little change to the configuration and apply it in ws2, the state in that workspace changes. Let’s illustrate this by changing the resource name in the configuration:

...truncated...
resource "aws_instance" "baeldung2" {
 ...truncated...
}

Now, let’s run terraform plan:

$ terraform plan
aws_instance.baeldung: Refreshing state... [id=i-05b783b91246bf8b3]

...truncated...

Terraform will perform the following actions:

  # aws_instance.baeldung will be destroyed
  # (because aws_instance.baeldung is not in configuration)
...truncated...

  # aws_instance.baeldung2 will be created
 ...truncated...

Plan: 1 to add, 0 to change, 1 to destroy.

...truncated...

As seen above, after altering the configuration, running terraform plan in the ws2 workspace shows that the resource we created in the default workspace will be deleted and replaced with a resource matching the new configuration.

This further confirms that the workspaces have the same state and control the same resources following state migration.

5.3. Applying a Configuration Change in the Workspaces

If we terraform apply the new configuration in ws2, the state in the default workspace will not be in sync with the new resource.

In other words, the default workspace will not track the resource if we change the resource while in the ws**2 workspace and vice-versa.

Let’s apply the new configuration in ws2 to demonstrate the effect of the change on the default workspace:

$ terraform apply -auto-approve
...truncated...

Plan: 1 to add, 0 to change, 1 to destroy.
aws_instance.baeldung: Destroying... [id=i-037072caed2681dc2]
aws_instance.baeldung2: Creating...
...truncated...
aws_instance.baeldung2: Creation complete after 32s [id=i-05763aa9b259b5371]
aws_instance.baeldung: Destruction complete after 41s

Apply complete! Resources: 1 added, 0 changed, 1 destroyed.

Now, let’s switch to the default workspace and preview its application of the changed configuration:

$ terraform workspace select default && terraform plan
Switched to workspace "default".
...truncated...

Terraform will perform the following actions:

  # aws_instance.baeldung2 will be created
  + resource "aws_instance" "baeldung2" {
...truncated...
    }

Plan: 1 to add, 0 to change, 0 to destroy.
...truncated...

As shown above, the default workspace lost connection with the resource after we applied the altered configuration in ws2. So, instead of the confirmation message, “Your infrastructure matches the configuration.”, it previews the creation of the same resource already in the ws2 workspace.

6. Conclusion

In this article, we saw how we can migrate a state from one workspace to another by initializing the destination workspace with the state file.

After migration, both workspaces will have the same state.

However, if the resource is altered in either workspace, the other workspace will go out of sync with the resource.