Stage 4 · Provision
Terraform Fundamentals
State Management
Remote backends, locking, workspaces, and state migration — keeping infrastructure state safe and collaborative.
Local State
By default, Terraform stores state in a local file called terraform.tfstate in your working directory. This works for personal projects but breaks in team environments — there is no locking, no backup, and no sharing mechanism.
$ terraform show # Readable output of current state
$ terraform state list # List all managed resources
$ terraform state show aws_instance.web # Show a specific resourceThese commands read the state file and display its contents. terraform show gives a human-readable view. terraform state list shows every resource address Terraform manages.
Remote Backends
Remote backends store state in a shared location — an S3 bucket, Azure Storage account, GCS bucket, or Terraform Cloud. This enables team collaboration, state locking, and versioning.
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/vpc/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}This backend stores state in an S3 bucket with server-side encryption. The dynamodb_table enables state locking — a DynamoDB item acts as a mutex so only one person can run terraform at a time.
terraform {
backend "azurerm" {
resource_group_name = "terraform-state-rg"
storage_account_name = "tfstate"
container_name = "tfstate"
key = "prod.tfstate"
}
}The Azure backend stores state in a Blob Storage container. State is encrypted at rest by default. Locking uses Azure blob leases.
State Locking
When two people run terraform apply at the same time without locking, state corruption occurs. Locking prevents concurrent modifications. Most remote backends support locking natively.
$ terraform apply # Automatically acquires lock
$ terraform force-unlock LOCK_ID # Force release if lock is stuckLocking is automatic with remote backends. If a process crashes and leaves a stale lock, use force-unlock with the lock ID shown in the error message. Never force-unlock while someone else is actively running Terraform.
Only use force-unlock when you are certain no other process is running. A stale lock from a crashed CI job is safe to unlock. A lock from a colleague who stepped away is not.
Workspaces
Workspaces let you use the same configuration with different state files. The default workspace is called default. You can create additional workspaces for staging, production, or any other environment.
$ terraform workspace list # List all workspaces
$ terraform workspace new staging # Create and switch to staging
$ terraform workspace select prod # Switch to prod workspace
$ terraform workspace show # Show current workspace
$ terraform workspace delete staging # Delete a workspaceEach workspace has its own state file. The workspace name appears in the state file and in terraform output. Use workspace-specific variables to customize behavior per environment.
Workspaces share the same code and configuration, which makes them simple but inflexible. If prod needs different providers, different modules, or different variable defaults than dev, workspaces become a limitation. Use directory-per-environment or Terragrunt instead.
State Migration
When you change backends, Terraform cannot automatically move state. You must manually migrate it using terraform init with the -migrate-state flag.
$ terraform init -migrate-state
Backend "s3" configuration changed.
Do you want to copy existing state to the new backend?
Pre-existing state will be written to the new backend and
then deleted from the old backend.
Enter a value: yesTerraform copies your state to the new backend and removes the local copy. If migration fails, check that the target bucket exists and that your credentials are correct.
# backend.tf — values provided via CLI or backend config file
terraform {
backend "s3" {}
}
# Run with:
$ terraform init \
-backend-config="bucket=my-terraform-state" \
-backend-config="key=prod/vpc/terraform.tfstate" \
-backend-config="region=us-east-1" \
-backend-config="dynamodb_table=terraform-locks"Partial configuration allows you to keep sensitive values out of code. You pass them at init time via flags or a .tfbackend file. This is essential for CI/CD pipelines where backend credentials vary per environment.
Best Practices
- Always use a remote backend for any team project.
- Enable encryption at rest for state files.
- Use state locking to prevent concurrent modifications.
- Keep state files small by splitting into multiple backends per component.
- Use terraform state mv and terraform state rm carefully — they modify state directly.
- Back up state before running destructive state operations.
- Store backend configuration in a separate file and use partial configuration for sensitive values.
A state file with thousands of resources slows down plan and apply operations. Split state by component (networking, compute, database) or by team. Each backend can reference a different state file.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.