Terraform persists the result of every run as state, then uses it as the baseline for subsequent plans and applies. Without proper state design and protection, diffs can break and concurrent runs can collide.
Grounded in the behavior described by the official documentation, this guide focuses on the stable concepts most often tested on the Associate exam and the best practices that apply to both exam prep and real-world operations.
State is the data Terraform uses to map resources declared in configuration to the actual resources on the cloud. It holds each resource's address, attribute values, dependencies, and outputs, forming the basis for diff calculation on the next plan/apply.
Without state, Terraform would have to analyze the current world from scratch every time, and safe mappings during renames or refactors would be nearly impossible. State is not just a cache: it is the core data that keeps runs consistent.
By default, state is stored locally in terraform.tfstate, but real-world setups typically use a remote backend (S3, AzureRM, Terraform Cloud, etc.) for concurrent execution, backups, and access control.
Where state fits during a Terraform run
The backend determines where state lives and how it is locked. For team operations, choose a remote backend that prevents concurrent-run collisions and provides history and access control.
The major options break down as follows. S3 provides locking when paired with DynamoDB. AzureRM uses Blob lease for locking. GCS does not support locking, so single-run enforcement is required. Terraform Cloud/Enterprise delivers locking, history, and RBAC in a single integrated package.
| Backend | Locking | Encryption (At Rest) | Versioning / History |
|---|---|---|---|
| local | None | OS permissions only | Manual backup |
| S3 (+DynamoDB) | Yes (DynamoDB Lock) | SSE-S3 / SSE-KMS | S3 versioning |
| AzureRM (Blob) | Yes (Blob lease) | Storage default encryption | Blob versioning / Soft delete |
| GCS | None | Google-managed keys or CMEK | Object Versioning |
| Terraform Cloud/Enterprise | Yes | Managed by the service | State history and Run logs |
Example S3 backend with locking enabled
terraform {
backend "s3" {
bucket = "my-tf-state"
key = "envs/prod/terraform.tfstate"
region = "ap-northeast-1"
dynamodb_table = "tf-state-lock"
encrypt = true
}
}During plan and apply, Terraform re-reads the current state of real infrastructure and refreshes state. Any past manual changes (drift) surface as a diff. If you only want to refresh the current view, you can use the refresh-only apply mode.
Avoid editing state directly. Use the terraform state subcommands for address changes (mv) and detachments (rm). Reserve push/pull for special cases such as disaster recovery.
Common state operation commands
terraform init
terraform plan
# 状態に登録済みリソースの一覧
terraform state list
# リファクタ時のアドレス変更(計画なしで State を更新)
terraform state mv aws_instance.web[0] aws_instance.app[0]
# 孤児化したエントリの切り離し(実インフラは削除しない)
terraform state rm aws_security_group.legacy
# 現況の再取得のみ(構成は変更せず State を更新)
terraform apply -refresh-onlyState locking prevents multiple concurrent runs from corrupting state. The local backend has no locking, which makes it unsuitable for team use. S3 uses a DynamoDB lock record, AzureRM uses Blob lease, and Terraform Cloud handles locking on the service side.
In CI/CD, in addition to backend-level locking, it is effective to serialize runs within the same workspace. This is especially critical for backends without native locking, such as GCS.
Example DynamoDB lock table creation (for S3 backend)
aws dynamodb create-table \
--table-name tf-state-lock \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUESTState contains not only the attributes and dependencies of created resources but can also include outputs and some sensitive data. Even when variables or outputs carry the sensitive flag, the raw values may still live in state, so always treat state itself as sensitive data.
On remote backends, configure encryption (SSE-KMS, default storage encryption, etc.) and strict IAM/RBAC, and enable versioning and delete protection on the bucket/container to guard against accidental loss or corruption. Never commit state to a VCS — that rule is non-negotiable.
Handling sensitive values (the data may still live in state)
variable "db_password" {
type = string
sensitive = true
}
output "db_password" {
value = var.db_password
sensitive = true
}
# 注: sensitive は UI/ログ表示を抑制するフラグ。State から値が完全に排除されるわけではない。To place existing resources under Terraform management, you must import them into state. The traditional approach uses the terraform import CLI command to register the resource and then handwrites the matching HCL. Recent versions also support an import block written directly in configuration (availability depends on the Terraform version).
When drift occurs, first review the diff with plan, then either converge with apply (treating the configuration as the source of truth) or restore consistency with the state subcommands as needed. Manual state edits remain a last resort.
Import examples (CLI and import block)
# 既存の S3 バケットを State に取り込む(従来の CLI)
terraform import aws_s3_bucket.logs my-logs-bucket
# 設定ファイルに import ブロックを記述する方法(対応バージョンのみ)
import {
id = "my-logs-bucket"
to = aws_s3_bucket.logs
}
resource "aws_s3_bucket" "logs" {
bucket = "my-logs-bucket"
}Associate
問題 1
Which backend configuration most reliably prevents state corruption and concurrent-run collisions in team production operations?
正解: A
The S3 backend, combined with DynamoDB locking, provides mutual exclusion on state. GCS does not support locking, local lacks locking, and running only plan does not prevent collisions.
How should I migrate existing state when changing backends?
Use the -migrate-state flag with terraform init to migrate safely. Verify access permissions, encryption, and versioning settings on both the old and new backends beforehand, and confirm there is no diff via plan after migration.
Is it OK to commit terraform.tfstate to Git or another VCS?
No, this is not recommended. State files can contain sensitive data. The best practice is to use a remote backend protected by access control, encryption, and history.
Is separating prod and dev with workspaces safe?
It can work for small setups, but it is inadequate for permission and audit separation. It is safer to split backends or state paths per environment and isolate IAM/RBAC independently.
Practice with certification-focused question sets
無料で問題を解いてみるNicheeLab Editorial Team
NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.
HCL Syntax: Terraform's Configuration Language (2026)
HCL2 fundamentals for Terraform — blocks, attributes, expres...
Terraform Authoring & Operations Pro: Complete Guide (2026)
Tactics for the Terraform Pro exam — module authoring, works...
Terraform Providers: Plugin Management Fundamentals (2026)
Provider mechanics — required_providers, versions, mirrors, ...
Terraform Resource Blocks: Declarative Infra Units (2026)
Resource block fundamentals — addresses, references, common ...
Terraform Data Sources: Read-Only External Data (2026)
Data source basics — declaration, refresh behavior, dependen...