What you'll learn
- Explain remote state and modules
- Describe what Terraform state records and why it can hold secrets
- List what a team backend needs: sharing, locking, encryption and versioning
- Rename or import resources with moved and import blocks instead of editing state
Explain what Terraform state is, why teams keep it in a locked remote backend, and how modules package reusable infrastructure.
Why it matters
Terraform felt simple in the previous lesson because one person ran it on one laptop. On a team, two questions appear quickly. Where does Terraform's record of what it manages live, so that everyone sees the same thing? And how do you avoid copying the same 200 lines of network code into every environment? The answers are remote stateTerraform's record mapping each resource address in code to a real object and its last known attributes. and modules. Both come up in nearly every cloud engineering interview and in the Terraform Associate exam.
What state is
Terraform keeps a state file that maps each resource address in your code (docker_container.web) to a real object (a container ID, an AWS instance ID) together with its last known attributes. Terraform uses state to:
- know which real objects it owns, so it updates them instead of creating duplicates;
- work out the plan quickly without asking the API about every attribute;
- track dependencies, so it destroys things in the right order.
By default state is a local file, terraform.tfstate. That is fine for practice and wrong for a team:
- If two people have different copies, they will make conflicting changes.
- If the laptop dies, Terraform forgets what it created, and the resources keep running (and billing) with nothing tracking them.
- State often contains secrets in plain text: database passwords, private keys, connection strings. It must not live in Git or on shared drives.
Remote backends and locking
A backendThe setting that tells Terraform where to store state, such as an S3 bucket or HCP Terraform. tells Terraform where to store state. Teams use a remote backend:
terraform {
backend "s3" {
bucket = "acme-terraform-state"
key = "networking/prod.tfstate"
region = "us-east-1"
encrypt = true
use_lockfile = true
}
}Common choices are an S3 bucket (AWS), an Azure Storage container (azurerm), a Google Cloud Storage bucket (gcs), or HCP Terraform. A good backend gives you:
- One shared copy that everyone and every CI job reads.
- LockingPreventing two Terraform runs from writing state at the same time and corrupting it., so two
applyruns cannot write at the same time and corrupt state. (The S3 backend supports a lock file in the bucket in recent Terraform versions; older setups used a DynamoDB table.) - Encryption and access control, because of the secrets inside.
- Versioning, so a damaged state can be restored.
Split state by environment and by area (for example networking/prod, app/staging). One giant state file makes every plan slow and every mistake large; this split is often called limiting the blast radiusHow much a single mistake can affect, kept small by splitting state by environment and area..
Working with state safely
Never edit the state file by hand. Use commands:
| Command | Use |
|---|---|
terraform state list | List resource addresses in state |
terraform state show <addr> | Show the recorded attributes of one resource |
moved { from = ... to = ... } block | Rename or move a resource in code without destroying it |
import { to = ... id = ... } block or terraform import | Bring an existing, hand-made resource under Terraform |
terraform plan -refresh-only | See drift: differences between state and reality |
The moved block is worth remembering. If you rename aws_instance.web to aws_instance.app without it, Terraform plans to destroy the old one and create a new one.
Modules
A moduleA folder of .tf files that takes variables as inputs and returns outputs, like a function. is a folder of .tf files. The folder you run Terraform in is the root module; folders it calls are child modules. A module is used like a function: it takes variables as inputs and returns outputs.
# modules/web/variables.tf
variable "name" { type = string }
variable "external_port" { type = number }
# modules/web/main.tf
resource "docker_container" "this" {
name = var.name
image = "nginx:1.27-alpine"
ports {
internal = 80
external = var.external_port
}
}
# modules/web/outputs.tf
output "url" { value = "http://localhost:${var.external_port}" }# main.tf (root module)
module "web_staging" {
source = "./modules/web"
name = "web-staging"
external_port = 8081
}
module "web_prod" {
source = "./modules/web"
name = "web-prod"
external_port = 8082
}
output "staging_url" { value = module.web_staging.url }The two environments now share one tested definition and differ only in their inputs. Resource addresses inside a module are prefixed, for example module.web_prod.docker_container.this.
Modules can come from a local path, a Git URL with a tag, or a registry such as the public Terraform Registry (terraform-aws-modules/vpc/aws). Pin a version (or a Git tag) for anything not local, and run terraform init again after adding or changing a module source.
Good modules are small and focused (a network, a service, a database), expose only the inputs callers need, and document their outputs. A module that wraps a single resource with no added value is usually not worth it.
Common mistakes
- Keeping team state on a laptop or in Git.
- No locking, so two pipelines run
applyat once. - One state file for every environment.
- Renaming resources without
movedblocks and accidentally destroying them. - Pulling registry modules without pinning a version.
Quick recap
- State maps code to real objects; it can hold secrets.
- Teams use an encrypted, versioned, locked remote backend, split by environment.
- Change state with commands and
moved/importblocks, never by hand. - Modules take variables, return outputs, and let environments share one definition.
Try it
This lesson has no hands-on activity. Take the quiz in the Do tab.
Key terms (6)
- state
- Terraform's record mapping each resource address in code to a real object and its last known attributes.
- backend
- The setting that tells Terraform where to store state, such as an S3 bucket or HCP Terraform.
- Locking
- Preventing two Terraform runs from writing state at the same time and corrupting it.
- blast radius
- How much a single mistake can affect, kept small by splitting state by environment and area.
- moved
- A block that tells Terraform a resource was renamed or moved in code, so it is not destroyed and recreated.
- module
- A folder of .tf files that takes variables as inputs and returns outputs, like a function.