はじめに
◇ 前提
Terraformステートファイルを"リソース毎"や"プロジェクト毎"に管理する方法についてまとめてみました。
Terraformステートを "S3" & "DynamoDB" リモート管理していることを前提に進めるので、ステートファイルのリモート管理について知りたい方は以前書いた記事もよければ参考にしてください。
TerraformステートをS3+DynamoDBでリモート管理する手順
◇ なぜステートファイルを分離するのか
理由:
役割もライフサイクルも異なるリソースを同じステートで管理すると、影響範囲が大きくなりすぎるから
です。
ステートファイルをリモート管理している場合、
- 「ステートファイルのリモート管理用に作成した S3 & DynamoDB」のステートファイル
- 「コンテンツ用に作成する EC2 などのリソース」 のステートファイル
この二つのステートファイルは分けるべきです。
・バックエンド用の S3 / DynamoDB は「Terraform の土台そのもの」
・EC2 や VPC は「アプリケーション側のインフラ」
であり、後者を作り直したい・構成を変えたい、といったタイミングで誤って前者(バックエンド)に影響を与えてしまうと、ステートファイルそのものが読めなくなったり、Lock用のDynamoDBを壊してしまったり、既存環境のterraform destroyや再作成が暴発する、といった事故につながりかねないです。
ステートファイルの分離については大きく分けて
1. ファイルレイアウトによる分離
2. ワークスペースによる分離
の二つの方法があります。
1.ファイルレイアウトによる分離
ファイルレイアウトによるステート分離は非常にシンプルです。
Terraformのコードを用途ごとに別フォルダへ配置するだけで、ステートファイル(tfstate)も自動的に分離され、環境やコンポーネント(インフラを構成する部品)の独立性を保てます。
1.1 ファイルレイアウトによる分離の手順
ファイルレイアウトによるステートファイルの分離を行うために、
まずTerraformの実行環境側のフォルダは以下の構成にします。
terraform/
├── backend/
│ ├── main.tf # ステート管理用の S3 & DynamoDB
│
└── example/
├── main.tf # 実際にリソースとして作成するEC2・VPCなど
実際に、バックエンド用のS3とDynamoDBが存在することを前提に『EC2』をデプロイしてみます。
まず、EC2リソースのコードを書きます。
※EC2リソースは自由に定義してください。
provider "aws" {
region = "ap-northeast-1"
}
# EC2リソースのamiリソースを動的に取得
data "aws_ami" "amazonlinux" {
# 条件に合うAMIが複数ある場合、最新を選択
most_recent = true
# Amazon公式AMIだけを対象にする
owners = ["amazon"]
# t3.microに合わせてアーキを選択
filter {
name = "architecture"
values = ["x86_64"]
}
# 名前パターンで Amazon Linux 2023 系に絞り込む
filter {
name = "name"
values = ["al2023-ami-*-kernel-*"]
}
}
# EC2の設定
resource "aws_instance" "example" {
ami = data.aws_ami.amazonlinux.id
instance_type = "t3.micro"
}
つづいて、同じファイルにバックエンド用のコードを追記します。
バックエンド用に作成した S3 と DynamoDB を指定します。
# ステートのバックエンド設定
terraform {
# バックエンド用 S3 を指定
backend "s3" {
bucket = "terraform-example-20250930-state"
# バックエンド用 S3 & DynamoDB のステート保存先とは違うパスを指定
key = "example/terraform.tfstate"
region = "ap-northeast-1"
# バックエンド用 DynamoDB を指定
dynamodb_table = "terraform-up-and-running-locks"
encrypt = true
}
}
このコードが格納されたフォルダパスに移動して、terraform initとterraform applyを実行します。
※バックエンド用リソースを作成したAWS認証情報と同じ認証情報を使わないと、バックエンドに指定したS3とDynamoDBリソースを見つけられずエラーになります。
PS C:\..\terraform\example> terraform init
PS C:\..\terraform\example> terraform apply
デプロイに成功すると、keyで指定したS3バケットのパスにS3の管理コンソール上でステートファイルが作成されるはずです。
EC2リソースも問題なく作成されています。
1.2 ファイルレイアウトで分離したステートファイルの削除
ファイルレイアウトでステートを分離している場合、不要になったステートファイル(tfstate)は S3 バケットから 手動 で削除できます。
■ 完全に不要なコンポーネントを正しく削除したいとき
複数リソースで構成されたコンポーネント自体を削除する場合は、S3で管理されているステートファイルも要らなくなるので、手動で削除して問題ありません。
安全な方法としておすすめする手順は以下の通りです。
-
terraform destroy:リソースを削除 - S3 のステートファイルを削除(手動)
- ローカルの
.tfファイルとディレクトリを削除
また、ステートファイル名(key)は、後から見ても用途が分かる命名が重要です。
例)
prod/network/terraform.tfstate
prod/database/terraform.tfstate
stage/compute/terraform.tfstate
このように用途と環境が一目で分かる命名にしておくと、
後から「これは何のステートファイルか?」と迷うリスクがなくなります。
1.3 ファイルレイアウトのポイント
この分離の方法は、以下のようなメリットがあります。
- 直感的でわかりやすく、フォルダを分けるだけで運用できる
- ステートがプロジェクト/リソース単位に独立し、安全性が高い
- ディレクトリ毎に AWS 認証情報(profile)を切り替える場合にも便利
ただし、以下のようなデメリットもあります。
-
一括デプロイができない(各ディレクトリで
terraform applyが必要) -
バックエンド設定をディレクトリ毎に記述する必要がある
(S3 バケットの key を手で分ける必要がある) - ステートが分かれるため、別ステートの値がそのまま参照できない
このあたりの問題は、 モジュール化 や terraform_remote_state という別ステートのリソースを参照する方法を駆使すればある程度解消できますが、この記事では扱いません。
2.ワークスペースによる分離
ファイルレイアウトとは別の分離方法として、ワークスペースによる分離もあります。
Terraformでは通常 "default" と呼ばれるワークスペースが使われます。
そのワークスペースを切り替えることで、既存のリソースをそのまま別環境としてデプロイすることができ、その際にステートファイルも自動で分離されます。
一時的に変更内容をテストしたい場合などに使えます。
2.1 現在のワークスペースの確認
現在のワークスペースを確認するにはterraform workspace showコマンドを使います。
PS C:\..\terraform\example> terraform workspace show
default
ワークスペースを切り替えていなければ "default" と表示されると思います。
また、EC2を作成するterraformコードのステートはバックエンドとして
【S3バケット名】:"terraform-example-20250930-state"
【key】:"example/terraform.tfstate"
を指定していました。
# bucket名とkeyの設定だけ抜粋
bucket = "terraform-example-20250930-state"
key = "example/terraform.tfstate"
そのため、S3のバケットにはこのパスでステートファイルが格納されています。
ワークスペースを切り替えると、
このステートファイルのパスが変わります。
2.2 ワークスペースの切替
実際にワークスペースを切り替えてみます。
新しいワークスペースを作成するには、terraform workspace newコマンドを使います。
"example1" というワークスペースを作成してみます。
PS C:\..\terraform\example> terraform workspace new example1
Created and switched to workspace "example1"!
You're now on a new, empty workspace. Workspaces isolate their state,
so if you run "terraform plan" Terraform will not see any existing state
for this configuration.
switched to workspace "example1"と表示されているので、ワークスペースの作成と同時にワークスペースも "example1" に切り替わっています。
2.3 新しいワークスペースでリソースをデプロイ
"example1" のワークスペースでterraform planコマンドを実行してみます。
PS C:\..\terraform\example> terraform workspace new example1
(...)
Plan: 1 to add, 0 to change, 0 to destroy.
TerraformはEC2インスタンスが既に作成されているとは認識せず、EC2を新しく作成しようとしています。
terraform applyコマンドでEC2インスタンスを作成してみます。
PS C:\..\terraform\example> terraform apply
(...)
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
これで、もう一つ新しいEC2インスタンスが作成されました。
2.4 分離したステートファイルの確認
この時、S3バケットを確認すると、以下のようはパスが生成されています。

新しいワークスペース "example1" に切り替えてリソースをデプロイすると、バックエンドのS3バケット内にenv:/フォルダが作成され、その中にワークスペースのフォルダが生成されます。
つまり、
ワークスペースを切り替えてリソースをデプロイすると、env:/という階層が掘られてステートファイルパスが切り替えること になります。
既存のインフラに影響を与えず、何らかの実験を一時的に行ったりしたい時などにこのワークスペースの仕組みが使えます。
2.5 使い終わったワークスペースの削除方法
ワークスペースは次のコマンド手順での削除できます。
terraform workspace select <workspace_name>
terraform destroy
terraform workspace select default
terraform workspace delete <workspace_name>
① 削除したいワークスペースに移動:terraform workspace select <workspace_name>
② リソースを削除:terraform destroy
③ defaultワークスペースに移動:terraform workspace select default
④ ワークスペース削除:terraform workspace delete <workspace_name>
2.6 ワークスペース関連のコマンド
さいごに、ワークスペース関連のコマンドを以下表にまとめます。
| 内容 | コマンド |
|---|---|
| 現在のワークスペースの確認: | terraform workspace show |
| ワークスペースの新規作成: | terraform workspace new |
| ワークスペースリストの確認: | terraform workspace list |
| ワークスペース間の移動: | terraform workspace select |
| ワークスペースの削除: | terraform workspace delete |



