Terraform State
Terraform State is the data Terraform uses to map your configuration files to the real infrastructure it manages. In practical terms, it tells Terraform which cloud resources exist, what their current attributes are, and how they relate to the code in your .tf files.
Terraform stores this data in a state file, usually named terraform.tfstate. Without state, Terraform would not know whether an AWS EC2 instance, Google Cloud SQL database, Kubernetes namespace, or Azure storage account was already created by a previous run.
What Terraform State does
Terraform State acts as Terraform’s source of truth for managed infrastructure. It helps Terraform answer questions such as:
- Which resources are managed by this Terraform project?
- What cloud resource ID maps to each Terraform resource block?
- Which dependencies exist between resources?
- What changed since the last
terraform apply? - What needs to be created, updated, replaced, or destroyed?
For example, if your configuration defines an AWS security group named aws_security_group.web, the state file stores the real AWS security group ID, such as sg-0abc123. Terraform uses that mapping during future plans and applies.
How Terraform State works
Terraform updates state during commands such as terraform apply, terraform refresh, terraform import, and some forms of terraform state. The typical workflow looks like this:
- You write or change Terraform configuration.
- You run
terraform plan. - Terraform reads the current state and checks the real infrastructure provider APIs.
- Terraform compares configuration, state, and real infrastructure.
- You run
terraform apply. - Terraform changes infrastructure and updates the state file.
The state file is stored in JSON format, but teams usually should not edit it by hand. Manual edits can break resource mappings and cause Terraform to make unsafe changes.
Local state and remote state
Terraform can store state locally or remotely.
- Local state: Stored on your machine, usually as
terraform.tfstatein the working directory. This is simple for learning, testing, or one-person projects. - Remote state: Stored in a shared backend such as Amazon S3, Google Cloud Storage, Azure Blob Storage, Terraform Cloud, or HashiCorp Consul. This is the normal choice for teams and production infrastructure.
Remote state helps teams avoid overwriting each other’s changes. Many remote backends also support state locking, which prevents two engineers or CI jobs from running terraform apply against the same state at the same time.
Why state matters in real environments
State is critical because cloud resources have provider-specific IDs, generated values, and runtime attributes that do not exist in your source code. Terraform needs to track those values to make accurate changes.
Common examples include:
- An AWS load balancer DNS name generated after creation.
- A Google Cloud service account email.
- An Azure managed identity principal ID.
- A Kubernetes secret, namespace, or deployment tracked through the Terraform Kubernetes provider.
- A database endpoint created by a managed database service.
If you use Terraform to manage Kubernetes objects, state still works the same way. Terraform records which objects it created and how they map to your configuration. For a practical example, see this guide on how to deploy Kubernetes resources using Terraform.
Key parts of Terraform State
A Terraform state file usually contains several important types of data:
- Resource mappings: Links between Terraform resource addresses and real provider resource IDs.
- Resource attributes: Current known values for resources, such as names, IDs, tags, IPs, ARNs, and endpoints.
- Dependencies: Information Terraform uses to order operations safely.
- Provider metadata: Data needed to work with the provider that created or manages each resource.
- Outputs: Values exposed by the Terraform configuration, such as a VPC ID or cluster endpoint.
State locking
State locking prevents concurrent writes to the same state file. This matters in team settings and CI/CD pipelines.
For example, if one engineer runs terraform apply while a GitHub Actions workflow runs another apply against the same environment, both operations could try to update the same state. A backend with locking blocks the second operation until the first one finishes.
Common locking setups include:
- Amazon S3 backend with DynamoDB locking.
- Google Cloud Storage backend with object preconditions.
- Azure Blob Storage backend with blob leases.
- Terraform Cloud remote state and locking.
State drift
State drift happens when real infrastructure no longer matches Terraform’s state or configuration. This usually occurs when someone changes a resource outside Terraform, such as through a cloud console, CLI, or another automation tool.
Examples of drift include:
- An engineer manually opens a security group port in the AWS console.
- A Kubernetes deployment is changed with
kubectl edit. - A database instance size is modified outside Terraform.
- A tag policy tool changes labels or tags after Terraform creates a resource.
terraform plan can detect many drift cases by reading provider APIs and comparing real infrastructure with the stored state and desired configuration.
Sensitive data in state
Terraform State can contain sensitive values. Marking an output or variable as sensitive can hide it from CLI output, but the raw value may still exist in the state file.
State can include data such as:
- Database passwords.
- Private keys.
- API tokens.
- Connection strings.
- Generated secrets from providers.
Because of this, production state should be encrypted at rest, access-controlled, and treated as sensitive infrastructure data. Avoid committing terraform.tfstate files to Git.
Common use cases
Terraform State is used in nearly every Terraform workflow, including:
- Provisioning cloud infrastructure: VPCs, subnets, IAM roles, load balancers, databases, and clusters.
- Managing Kubernetes resources: Namespaces, deployments, service accounts, and Helm releases.
- Sharing infrastructure outputs: Passing a VPC ID, cluster endpoint, or subnet list to another Terraform stack.
- Importing existing resources: Bringing manually created resources under Terraform management with
terraform import. - Team-based infrastructure delivery: Running Terraform through CI/CD with remote state and locking.
Remote state and multiple environments
Teams usually split state by environment, service, or infrastructure layer. For example:
networking-prodfor production VPCs and subnets.eks-prodfor a production Kubernetes cluster.app-stagingfor staging application resources.observability-prodfor monitoring and logging infrastructure.
This limits blast radius. A mistake in an application stack should not directly modify the state for shared networking or production databases.
Tools such as Terragrunt are often used to organize multi-environment Terraform projects and reduce repeated backend configuration. If you are structuring Terraform for Google Cloud, this Terraform starter boilerplate for GCP using Terragrunt shows one way to separate environments and state.
Terraform State versus Terraform configuration
Terraform configuration and Terraform State are related, but they are different things.
- Terraform configuration: The desired infrastructure written in
.tffiles. - Terraform State: Terraform’s recorded view of the infrastructure it manages.
- Real infrastructure: The actual resources running in AWS, Google Cloud, Azure, Kubernetes, or another provider.
A Terraform plan compares all three. If configuration says a VM should be e2-standard-4, state says it was last known as e2-standard-2, and the cloud API reports e2-standard-2, Terraform plans an update.
Simple example
Suppose your Terraform code creates an S3 bucket:
resource "aws_s3_bucket" "logs" {
bucket = "example-prod-logs"
}
After terraform apply, the state records that aws_s3_bucket.logs maps to the real bucket named example-prod-logs. Later, if you add tags to the configuration, Terraform uses state to find the same bucket and update it instead of creating a new one.
Best practices
- Use remote state for shared or production infrastructure.
- Turn on state locking when your backend supports it.
- Encrypt state at rest.
- Restrict access to state using IAM, RBAC, or your cloud provider’s access controls.
- Do not commit state files to source control.
- Split state files by environment or infrastructure boundary to reduce risk.
- Use
terraform statecommands instead of manually editing state JSON. - Review
terraform planbefore applying changes, especially after manual changes in the cloud console.
Limitations and risks
Terraform State is powerful, but it adds operational responsibility. If state is lost, corrupted, exposed, or modified incorrectly, Terraform may lose track of resources or plan unexpected changes.
The main risks are:
- State loss: Terraform may no longer know which resources it manages.
- State corruption: Bad writes or manual edits can break resource mappings.
- Secret exposure: Sensitive values may be stored in plain form inside state.
- Concurrent applies: Multiple writers can cause inconsistent state without locking.
- Large state files: Very large stacks can make plans slower and increase blast radius.
For most teams, the safest setup is remote encrypted state, locking, least-privilege access, and a clear state layout per environment.