D6 · Terraform State

State, import, and workspace commands

State commands let you inspect and manipulate the without destroying infrastructure. brings existing resources into the state. allow multiple environments with the same configuration.

terraform state list and show

# List all resources in the state
terraform state list

# Example output:
# aws_vpc.main
# aws_subnet.public[0]
# aws_subnet.public[1]
# aws_subnet.private["us-east-1a"]
# module.eks.aws_eks_cluster.this
# data.aws_ami.ubuntu

# Filter by prefix
terraform state list module.eks
# module.eks.aws_eks_cluster.this
# module.eks.aws_iam_role.cluster

# View the detailed attributes of a specific resource
terraform state show aws_vpc.main
terraform state show 'aws_subnet.private["us-east-1a"]'  # quotes needed for for_each

# View in JSON format (all resources)
terraform show -json | jq '.values.root_module.resources[]'

💡 Quotes in for_each addresses

Addresses of resources created with for_each include the key in brackets and quotes: aws_subnet.private["us-east-1a"]. In the shell you must escape the quotes or wrap the address in single quotes.

terraform state mv and rm

terraform state mv — move/rename

# Rename a resource in the state
# (useful when you change the local name in the code without recreating)
terraform state mv   aws_instance.old_name   aws_instance.new_name

# Move a resource into a module
terraform state mv   aws_vpc.main   module.networking.aws_vpc.main

# Move a resource from a module to the root
terraform state mv   module.old.aws_s3_bucket.data   aws_s3_bucket.data

# ⚠️ After mv, also update the .tf code
# so it matches the new address

terraform state rm — remove from the state

# Remove a resource from the state WITHOUT destroying it in the cloud
# Useful when you no longer want Terraform to manage it
terraform state rm aws_instance.legacy

# Remove all resources of a module from the state
terraform state rm module.old_module

# ⚠️ The resource keeps existing in the cloud
# It just stops being tracked by Terraform
# If you later run apply, Terraform will IGNORE that resource
# (it will not destroy it because it's not in the state)
CommandEffect on stateEffect on real infra
terraform state mvChanges the resource address in tfstateNone — the real resource is untouched
terraform state rmRemoves the resource from tfstateNone — the real resource keeps existing
terraform destroyRemoves the resource from tfstate🔥 DESTROYS the resource in the cloud
terraform apply (add)Adds the resource to tfstateCreates the resource in the cloud

terraform state pull and push

# Download the remote state to stdout (useful for inspection or backup)
terraform state pull > backup-state.json

# View the remote state without saving it
terraform state pull | jq '.resources[].type' | sort | uniq

# Push a local state to the remote backend
# ⚠️ DANGEROUS: overwrites the remote state
terraform state push backup-state.json

# terraform state push requires that the serial of the state being pushed
# be greater than the current serial, unless you use -force (very dangerous)

⚠️ terraform state push can corrupt the state

terraform state push overwrites the remote state. Use it only in disaster recovery situations, never as a normal workflow. If the pushed state is outdated, it can cause Terraform to incorrectly recreate or destroy resources.

terraform import and import block

Import lets you bring existing in the cloud (created manually or with other tools) into the Terraform state so it can manage them.

Steps to import with terraform import (classic CLI)

# Step 1: Define the resource block in your .tf code
# (with the arguments the resource already has)
resource "aws_instance" "legacy" {
  # You must define the resource before importing
  ami           = "ami-0c55b159cbfafe1f0"  # get these values from the console
  instance_type = "t3.micro"
}

# Step 2: Import the resource using its real ID in the cloud
terraform import aws_instance.legacy i-0123456789abcdef0

# Step 3: Run terraform plan to see the differences
terraform plan
# May show ~ changes if the resource block doesn't match exactly

# Step 4: Adjust the .tf code until terraform plan shows "No changes"

import block (Terraform 1.5+ — modern form)

# Import block in the HCL code — more declarative and reusable
import {
  to = aws_instance.legacy          # address in Terraform
  id = "i-0123456789abcdef0"       # real ID in the cloud
}

resource "aws_instance" "legacy" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"
}

# Run:
terraform plan   # shows the import plan
terraform apply  # performs the import

# Bonus: terraform 1.5+ can GENERATE the resource block automatically:
terraform plan -generate-config-out=generated.tf

💡 How do I know the import ID?

Each resource type has its own import ID format. The provider documentation in the registry specifies the format for each resource in the "Import" section. For example: aws_instance uses the Instance ID (i-xxx), aws_s3_bucket uses the bucket name.

CLI Workspaces

CLI workspaces let you maintain multiple independent states with the same configuration. Each workspace has its own tfstate.

# Workspace commands
terraform workspace list       # list all workspaces
terraform workspace show       # show the active workspace
terraform workspace new dev    # create workspace "dev" and activate it
terraform workspace select prod  # switch to the "prod" workspace
terraform workspace delete dev   # delete workspace "dev" (cannot be active)

# terraform.workspace — special variable with the active workspace's name
resource "aws_instance" "web" {
  instance_type = terraform.workspace == "prod" ? "t3.large" : "t3.micro"

  tags = {
    Environment = terraform.workspace
  }
}

# State storage per workspace:
# Local backend:    terraform.tfstate.d/<workspace>/terraform.tfstate
# S3 backend:       <bucket>/<key>.tfstate.d/<workspace>/terraform.tfstate
# HCP backend:      separate workspace in the organization
FeatureCLI WorkspacesHCP Terraform Workspaces
What are they?Multiple states of the same working directoryFull execution environments with config, variables, runs, history
Separate state✅ Yes✅ Yes
Separate variables❌ No — they share the code's variables✅ Yes — each workspace has its own variables
Granular access❌ No✅ Yes — per-workspace permissions
Run history❌ No✅ Yes
Recommended forSimple environments, quick testsProduction environments, team collaboration

Did you get this topic?

Test what you just learned

An engineer runs terraform state rm aws_s3_bucket.legacy to stop managing a bucket with Terraform. What will happen to the S3 bucket in AWS after this command?

Ready to test "State Commands, Import and Workspaces"?

Put what you learned to the test with original Terraform Associate exam questions.

Start Terraform Associate mock exam

Unlimited mock exams with the Pro plan

Log in to track your progress.