Terraform state is the shared record that connects configuration to real infrastructure. This guide explains how to store it remotely, protect it with encryption and locking, separate environments, recover from common state problems, and establish a team workflow that reduces accidental changes and concurrent applies.
Overview
Terraform compares your configuration with its state file and the resources managed by a provider. The state file contains resource addresses, identifiers, relationships, and other values Terraform needs to create an execution plan. It may also contain sensitive information, so treating it like ordinary source code is unsafe.
A local terraform.tfstate file can work for a personal experiment, but it becomes fragile when multiple people or automation run Terraform. Team workflows need a remote backend: a supported storage system that keeps state in a shared location and coordinates access. A well-designed backend provides durable storage, access control, encryption, versioning or recovery options where available, and state locking when supported.
State management is separate from infrastructure design. A well-written module does not compensate for an unprotected state file, and a remote backend does not make every workflow safe by itself. Teams still need clear ownership, reviewed plans, controlled credentials, and a recovery procedure.
Step-by-step workflow
1. Define the state boundary
Start by deciding what one state file should represent. A practical boundary is a deployable unit with a consistent lifecycle, such as a production network, an application platform, or a shared data service. Avoid placing an entire organization in one state file: a small change could create a large plan, increase the blast radius, and force unrelated teams to coordinate every apply.
Separate environments such as development, staging, and production. You can use separate directories with separate backend configuration, or use workspaces where that model fits the team. Directories often make environment-specific variables, permissions, and review boundaries more visible. Whichever approach you choose, document the mapping between source code, backend location, cloud account or subscription, and environment.
2. Select and prepare a remote backend
Choose a backend supported by your Terraform version and cloud platform. Common patterns include object storage in a cloud account, a managed Terraform service, or another organization-approved remote system. Evaluate each option against four questions:
- Can the team restrict who reads and writes each state location?
- Does it support encryption at rest and secure transport?
- Does it provide locking, version history, or a practical recovery path?
- Can CI/CD authenticate without placing long-lived secrets in the repository?
Create the backend's storage and access policy before migrating state. Keep backend resources separate from the state they protect when possible; otherwise, an error in the main configuration could make it difficult to repair the storage used for recovery. Apply least-privilege IAM principles to both human and automation identities. The Cloud IAM Best Practices guide provides a useful framework for reviewing those permissions.
3. Configure locking deliberately
Locking prevents two Terraform operations from modifying the same state at the same time. Enable the locking mechanism supported by your selected backend and verify it with a controlled test. Backend capabilities and configuration details can change across Terraform and platform versions, so use the current backend documentation rather than copying an old configuration unchanged.
Do not treat a lock error as an inconvenience to bypass. First determine whether another plan or apply is genuinely running. Check the relevant CI job, deployment history, and backend metadata. Only use a force-unlock operation when you have confirmed that the original process has stopped and the lock is stale. Removing an active lock can allow concurrent writes and leave state or infrastructure inconsistent.
4. Migrate local state carefully
Commit the backend configuration separately from unrelated resource changes when practical. Run terraform init and follow Terraform's migration prompt only after confirming the destination and access permissions. Before migration, make a copy of the local state file and record the Terraform version, working directory, and target environment.
After migration, run terraform state pull from the configured backend and confirm that the expected resources are present. Then run terraform plan without making changes. A plan that proposes to recreate many existing resources is a stop signal: check the backend key, workspace, provider configuration, credentials, and state migration result before applying anything.
5. Establish the plan-and-apply path
Use pull requests to review configuration changes and have automation generate a plan for the same commit that reviewers inspect. Apply only the approved plan, or regenerate it immediately before applying if your system cannot persist an approved plan safely. Production applies should use a controlled identity and an explicit approval step rather than a developer's personal credentials.
Keep state operations in one defined workflow. Do not allow a local workstation and CI to apply to the same state without coordination. For a practical pipeline pattern, see the Terraform CI/CD tutorial.
Tools and handoffs
A safe Terraform team workflow has clear handoffs:
- Developer: changes modules or environment configuration, formats and validates the code, and opens a pull request.
- CI: initializes the correct backend, runs validation and plan, and publishes the plan output for review without exposing sensitive values in logs.
- Reviewer: checks resource replacements, permission changes, networking changes, data-store changes, and unexpected drift.
- Approver or release process: authorizes the apply for the intended environment.
- Deployment runner: applies with a narrowly scoped identity and records the result.
- Operations owner: verifies the infrastructure and documents exceptions, drift, or recovery actions.
Use separate backend locations and credentials for each environment. Never commit state files, backend credentials, provider secrets, or generated plan files unless you have verified that they contain no sensitive data and your security policy explicitly permits it. State can expose values that are marked sensitive in Terraform output, so protect the storage location and restrict download access.
For AWS, Azure, and Google Cloud, the exact storage, identity, and locking setup differs. The durable design principles are the same: isolated state boundaries, encryption, least privilege, reliable locking, auditability, and a tested recovery path.
Quality checks
Before declaring Terraform state management complete, run this checklist:
- Backend configuration points to the intended account, project, subscription, region, and state key.
- Each environment has a clearly documented state location and ownership.
- Remote state access is encrypted and limited to the required users and automation roles.
- Locking is enabled where supported and the team knows how to investigate stale locks.
- Backend storage has an appropriate recovery or versioning mechanism.
- CI plans and applies with the expected Terraform, provider, and module versions.
- Pull requests expose destructive actions and large replacement plans for review.
- State files, credentials, and plan artifacts are excluded from source control and uncontrolled logs.
- The team has practiced restoring or inspecting a prior state version without applying changes.
When state appears wrong, stop before editing it. Capture a backup with terraform state pull, identify the affected address, and compare configuration, provider data, and recent deployment history. Use terraform state mv when a resource address changed but the real object should remain managed. Use terraform state rm only when you intentionally want Terraform to stop managing an object; it does not delete the remote resource. For serious corruption or accidental deletion, restore through the backend's recovery process and test the resulting plan before any apply.
When to revisit
Review this workflow whenever Terraform, a provider, your cloud platform, or your CI system changes its backend, locking, authentication, or state-recovery behavior. Revisit it after an account or subscription restructure, a repository migration, a new environment, a security review, or an incident involving concurrent applies or unexpected drift.
Make the review actionable: verify the backend configuration against current documentation, run a read-only plan for every environment, test that the intended identity can read and lock state but cannot access unrelated environments, and confirm that recovery instructions still work. Assign an owner and record the last review date in the repository documentation.
If your current state is local, begin with one non-production environment. Back it up, create the remote backend, migrate it, inspect the pulled state, and run a no-change plan. Once that path is repeatable, apply the same controls to production with an explicit approval and a documented rollback or recovery plan.