Terraform 応用 — state / import / workspace ほか 50+ コマンドを 8 カテゴリで

Terraform の init / plan / apply は誰でも使える。差が出るのは state 操作・既存リソース import・モジュール再構成のような「壊れない手順で構成変更ができるか」。 本記事は応用 50+ コマンドと、現場頻出のリファクタリング手順を整理します。

state 操作は常に危険: リモート state はチーム共有、操作の前に terraform state pull > backup-$(date +%s).tfstate でバックアップを取り、別ブランチで実験してから本番適用すること。

目次

  1. init / plan / apply の応用オプション
  2. state コマンド — 真の主役
  3. 既存リソース import
  4. refresh / taint / replace
  5. workspace
  6. fmt / validate / console
  7. output / show / providers
  8. moved / removed ブロック
  9. リファクタリング・移行レシピ
  10. 用語集

1. init / plan / apply の応用オプション

# init
terraform init                                           # 初期化
terraform init -upgrade                                  # provider バージョン更新を許可
terraform init -reconfigure                              # backend 設定を強制再構成
terraform init -migrate-state                            # backend 切替時の state 移行
terraform init -backend-config=backend.hcl               # backend 設定をファイルで
terraform init -backend=false                            # backend 初期化スキップ(CI 検証)

# plan
terraform plan
terraform plan -out=plan.tfplan                          # 計画ファイル保存
terraform plan -var 'env=prod' -var-file=prod.tfvars
terraform plan -target='module.vpc.aws_vpc.main'         # 対象限定(緊急時のみ)
terraform plan -refresh=false                            # state refresh をスキップ
terraform plan -destroy                                   # destroy のプレビュー
terraform plan -compact-warnings

# apply
terraform apply plan.tfplan                              # plan ファイルを適用(推奨)
terraform apply -auto-approve                            # CI で
terraform apply -parallelism=20                          # 並列度(デフォルト 10)

# destroy
terraform destroy
terraform destroy -target='module.vpc.aws_vpc.main'      # 対象限定
plan -out + apply plan.tfplan の 2 段階は本番運用の鉄則。「plan 時の世界観のまま apply」が保証される。

2. state コマンド

# state 一覧 (アドレス確認の起点)
terraform state list
terraform state list 'module.vpc.*'
terraform state list -id i-xxxxxxxx                      # AWS リソース ID で逆引き

# 特定リソースの中身
terraform state show 'module.vpc.aws_vpc.main'

# リネーム / モジュール移動 (state mv)
terraform state mv aws_s3_bucket.old aws_s3_bucket.new
terraform state mv 'aws_instance.web' 'module.web.aws_instance.this'
terraform state mv 'module.old_vpc' 'module.new_vpc'

# state から削除(実リソースは消えない、Terraform 管理外に)
terraform state rm 'aws_s3_bucket.legacy'

# state を別 backend へコピー
terraform state pull > current.tfstate
terraform state push current.tfstate                     # 復旧 / 移行

# replace-provider(provider 変更)
terraform state replace-provider \
  registry.terraform.io/-/aws \
  registry.terraform.io/hashicorp/aws

3. 既存リソース import

レガシー: import コマンド(CLI 引数)

# まず HCL で resource ブロックだけ書いておく
# resource "aws_s3_bucket" "imported" { bucket = "my-existing-bucket" }

terraform import aws_s3_bucket.imported my-existing-bucket
terraform import 'module.web.aws_instance.this' i-1234567890abcdef0
terraform import 'aws_dynamodb_table.users[0]' users-table

推奨: import block(Terraform 1.5+)

# import.tf
import {
  to = aws_s3_bucket.imported
  id = "my-existing-bucket"
}

# plan / apply で取り込み(plan に「create」ではなく「import」と表示される)
terraform plan
terraform apply

# Terraform 1.5+ は HCL も自動生成
terraform plan -generate-config-out=generated.tf
import block は HCL ファイル化された手順として PR でレビュー可能。CLI import は履歴に残らないので、 新規環境では import block を強く推奨。

4. refresh / taint / replace

# refresh — 実リソースの現状で state を更新(plan 内で自動実行されるが、明示も可能)
terraform refresh                                        # ※ 単体コマンドは v1.5+ で deprecated
terraform apply -refresh-only                            # 推奨: refresh だけする apply

# taint — 次回 apply で強制再作成(v0.15+ 非推奨、replace 推奨)
terraform taint aws_instance.web
terraform untaint aws_instance.web

# replace(推奨)— 強制再作成
terraform plan -replace='aws_instance.web'
terraform apply -replace='aws_instance.web'
terraform apply -replace='module.web.aws_instance.this'

5. workspace

1 つのコードベースで複数 state を持つ機能。dev/stg/prod の軽い分離に使えるが、本番では「環境別 envs ディレクトリ」のほうが推奨。

terraform workspace list
terraform workspace show
terraform workspace new dev
terraform workspace new prod
terraform workspace select prod
terraform workspace delete dev

# HCL から workspace 名を参照
# locals {
#   env = terraform.workspace
# }

6. fmt / validate / console

# フォーマット
terraform fmt                                            # カレント
terraform fmt -recursive                                 # 再帰
terraform fmt -check                                     # CI 検証用(差分があれば exit 1)
terraform fmt -diff                                      # 差分表示

# 構文 / 型チェック
terraform validate
terraform validate -json                                 # JSON 形式

# console — 対話シェル(式評価)
terraform console
> var.env
> aws_s3_bucket.web.arn
> jsonencode({ env = "prod" })
> cidrsubnet("10.0.0.0/16", 8, 1)
> exit

7. output / show / providers

# output
terraform output                                         # 全 output
terraform output -raw bucket_name                        # 単一値(クォート無し)
terraform output -json | jq '.bucket_name.value'

# show
terraform show                                           # 現 state の人間可読表示
terraform show -json                                     # JSON
terraform show plan.tfplan                               # plan ファイルの内容

# providers
terraform providers                                      # 使用中 provider 一覧
terraform providers schema -json | jq '.provider_schemas | keys'
terraform providers lock -platform=darwin_amd64 -platform=linux_amd64

# version
terraform version
terraform -version

8. moved / removed ブロック

state の手動 mv をしなくても、HCL で構成変更を表現できる。レビュー可能で安全。

# リソース or モジュールの場所を変えた
moved {
  from = aws_s3_bucket.legacy
  to   = aws_s3_bucket.web
}
moved {
  from = module.old_vpc
  to   = module.new_vpc
}

# Terraform 1.7+ : リソースを管理から外す
removed {
  from = aws_s3_bucket.legacy
  lifecycle {
    destroy = false                       # 実リソースは残す
  }
}

# plan で「moved」「removed」が認識される

リファクタリング・移行レシピ

レシピ 1 — リソースを別モジュールに移動

# 1) コードを書き換え(resource を新モジュール内に移す)
# 2) moved ブロックを追加
moved {
  from = aws_instance.web
  to   = module.web.aws_instance.this
}
# 3) plan で「No changes」になれば成功
terraform plan
# 4) moved ブロックは残しておくか、十分時間が経ってから削除

レシピ 2 — 既存 AWS リソースを取り込む

# 1) HCL の resource ブロックだけ先に書く(属性は仮で OK)
# 2) import ブロックを追加
import {
  to = aws_s3_bucket.imported
  id = "my-bucket-name"
}
# 3) plan -generate-config-out で属性を自動生成
terraform plan -generate-config-out=generated.tf
# 4) 生成された HCL を resource ブロックに反映
# 5) apply で取り込み完了

レシピ 3 — backend を S3 に移行

# 1) backend "s3" {...} を terraform ブロックに追加
# 2) init で移行を促される
terraform init -migrate-state
# Yes で local → S3 にコピー

レシピ 4 — provider バージョンアップ

# 1) required_providers で version 制約を更新
# 2) lock ファイル更新
terraform init -upgrade
# 3) .terraform.lock.hcl の差分をコミット
git diff .terraform.lock.hcl
# 4) plan で破壊的変更がないか確認

レシピ 5 — 一部リソースだけ destroy & 再作成

terraform plan -replace='aws_db_instance.main'
terraform apply -replace='aws_db_instance.main'

レシピ 6 — state を緊急バックアップ

terraform state pull > "backup-$(date +%Y%m%d-%H%M%S).tfstate"

# 何かあったら復旧
terraform state push backup-20260426-093000.tfstate

レシピ 7 — drift 検出(実リソースが手動変更されてないか)

terraform apply -refresh-only -auto-approve
terraform plan                                # 差分が出れば手動変更が入った印

レシピ 8 — 重い plan の高速化(target で小さく)

terraform plan -target='module.vpc'                 # 緊急時のみ。通常は使わない
terraform plan -refresh=false                        # 既存 state に頼り refresh をスキップ

コマンド早見表

# 基本
terraform init -upgrade
terraform plan -out=plan.tfplan
terraform apply plan.tfplan

# state
terraform state list
terraform state mv FROM TO
terraform state rm ADDR
terraform state pull > backup.tfstate

# import
import { to = X; id = "..." }   # block 推奨
terraform plan -generate-config-out=gen.tf

# 強制再作成
terraform apply -replace=ADDR

# refactor
moved { from = X; to = Y }
removed { from = X; lifecycle { destroy = false } }

# console
terraform console
> cidrsubnet("10.0.0.0/16", 8, 1)

# fmt / validate
terraform fmt -recursive -check
terraform validate

用語集

state (terraform.tfstate)
Terraform が「実リソースと HCL の対応」を記録する JSON。Local / S3 / Remote 等の backend で保管。チーム共有では Lock 必須。
backend
state の保管先設定。S3 + DynamoDB Lock の組合わせが AWS 標準。
state lock
同時 apply 防止のためのロック。S3 backend では DynamoDB テーブル使用。Lock 残留時は terraform force-unlock LOCK_ID(極力使わない)。
provider
aws / google / kubernetes 等のリソース提供プラグイン。required_providers でバージョン固定、lock ファイルで再現性確保。
module
HCL の再利用単位。inputs (variables) → outputs の関数のように使う。modules / envs パターンが定番。
plan ファイル (.tfplan)
terraform plan -out で生成されるバイナリ。apply 時にこれを渡せば「plan 時点の世界観で適用」が保証される。
state mv / state rm
state mv = state 内のアドレス変更(実リソース無変更)。state rm = state から外す(実リソース無変更、管理外に)。
import / import block
既存リソースを Terraform 管理下に取り込む操作。block 形式(v1.5+)が PR レビュー可能で推奨。
moved block
リソースの HCL 上のアドレス移動を宣言。state mv コマンド代替で、HCL に履歴が残る安全な方法。
removed block (v1.7+)
リソースを Terraform 管理外にする宣言。lifecycle.destroy = false で実リソースは残せる。
refresh / refresh-only apply
実リソースの現状で state を更新する操作。drift 検出に使う。terraform refresh 単独は非推奨、apply -refresh-only 推奨。
taint / replace
強制再作成。taint は v0.15+ 非推奨、apply -replace=ADDR 推奨。
workspace
1 コードベース複数 state の機能。dev/prod の軽い分離向け。本番運用は環境別ディレクトリ + 別 backend が推奨。
console
HCL 関数 / 変数 / リソース属性を対話評価するシェル。cidrsubnet や jsonencode の動作確認に。
drift
実リソースが Terraform 管理外で変更された差分。apply -refresh-only + plan で検出。
lifecycle ブロック
create_before_destroy / prevent_destroy / ignore_changes / replace_triggered_by。リソースのライフサイクル制御。
terraform.lock.hcl
provider のバージョン + ハッシュをロックするファイル。再現性のためコミット必須。