こんにちは、エン・ジャパン株式会社のプラットフォームチームに所属している山﨑です。
弊社ではプラットフォームの構築にTerraform を利用しており、tfstate の操作を行なうことが多いのですが、操作を誤ると元の状態に戻すことが困難であったり、時間がかかってしまいます。
素早く安全に操作するため、どういった手法があるのか整理するとともに、ユースケースについて紹介します。
tfstate の操作手法の比較
tfstate は重要なファイルなので直接編集するのはなるべくやめましょう。また、ワークロードでは可能な限り Block Syntax(※1)で書くようにし、結果をPlanしたり、レビューを受けたりして安全に操作することを推奨します。
※1)後述しますが、moved
Block やimport
Block、removed
Block など。
tfstate を操作する方法は2つに大別できます。
Terraform CLI はコマンドラインで操作し、Block Syntax はTerraform コードに記載してapply で操作します。
以下で比較します。
操作性の比較
操作方法 | 簡単な操作(※2) | 安全な操作(※3) | 備考 |
---|---|---|---|
Terraform CLI | O | X | ローカルから実行できる |
Block Syntax | X | O | Planで確認できる |
※2)操作するための手順が少ないほど簡単な操作としています。
※3)実行する前に結果が分かるか、レビュー可能か、などの観点から判断している。
利用可能な操作の比較
多くの操作がBlock Syntaxで実施できます。
操作手法 | import | move (同一のtfstate) |
move (別のtfstate) |
remove |
---|---|---|---|---|
Terraform CLI | O | O | O | O |
Block Syntax | O | O | X | O |
操作方法
前提として、tfstate はクラウドに保存されている状態で始めます。tfstate の操作時にはバックアップを取っておきましょう。
Terraform CLI とBlock Syntax の操作方法について紹介します。
Terraform CLI
Terraform CLI を利用すれば、ローカルで全ての操作を行うことができます。
terraform state
tfstateの操作はこのコマンドでほとんど実行可能。
✅ terraform state list
どんなリソースを管理しているのか把握するのに便利。
# input
terraform state list
# output
module.vpc.aws_route_table.api[0]
module.vpc.aws_route_table.api[1]
module.vpc.aws_route_table.private[0]
module.vpc.aws_route_table.private[1]
module.vpc.aws_route_table.public
✅ terraform state show
特定のリソースの状態を把握するときに利用。
# input
terraform state show module.vpc.aws_route.public
# output
resource "aws_route" "public" {
destination_cidr_block = "0.0.0.0/0"
gateway_id = "igw-xxx"
id = "r-rtb-xxx"
origin = "CreateRoute"
route_table_id = "rtb-xxx"
state = "active"
}
✅ terraform state pull
現在の状態を取得。パイプを使えばファイル保存することが可能で、バックアップでよく使う。
# backup.tfstateに現在のtfstateをコピー
terraform state pull > backup.tfstate
✅ terraform state push
このコマンドは既存のtfstateを上書きするため注意すること。
指定したtfstateを既存のtfstateに上書きする。上書きする際にはtfstate のserial を変更しないといけない場合があるので注意。(その場合、直接tfstate を編集することになる)
# new.tfstateを新しいtfstateとする(上書きのため注意)
terraform state push new.tfstate
terraform state mv
Terraform v1.1.0 でmove
Block が導入されたので、まずはそちらの利用を検討しましょう。
tfstateのリソースを操作する。オプションを付けなければ tfstate 内でリソース名を変更し、-state-out
というオプションを付ければ別のファイルに移動(元のファイルからは削除)することが可能。
# リソース名を変更する
terraform state mv aws_instance.before aws_instance.after
# リソースを外に出力する
terraform state mv -state-out output.tfstate aws_instance.before aws_instance.after
terraform state rm
Terraform v1.7.0 でremoved
Block が導入されたので、まずはそちらの利用を検討しましょう。
tfstate からリソースを削除する。実リソースを削除するわけではなく、あくまでtfstate 上から削除するだけ。
# aws_instance.this をtfstate 上から削除する
terraform state rm aws_instance.this
terraform import
Terraform v1.5.0 でimport
Block が導入されたので、まずはそちらの利用を検討しましょう。
import はterraform state import
ではなく、terraform import
コマンドで実行する。
実リソースの情報をtfstate に取り込む。実リソースのID 指定方法はリソースによって異なるため公式ドキュメントを参考にすること。
# S3 Bucket
terraform import aws_s3_bucket.bucket bucket-name
# Route 53 Record
terraform import aws_route53_record.myrecord "Z4KAPRWWNC7JR_dev.example.com_NS"
Block Syntax
tfstate 操作の多くを実行できる。Planで確認したり、レビューしたりすることが可能なので、こちらの利用を推奨
✅ import Block
v1.5.0 から導入されたブロック。v1.7.0 からfor_each
が導入されたので、大量のリソースを一気に取り込むことが可能になった。
importコマンドと同様、公式ドキュメントを参考に適切なid
を指定すること。
import {
to = aws_instance.this
id = "i-12345678"
}
✅ moved Block
v1.1.0 から導入されたブロック。リファクタリングでお世話になる。大量のmoved
blockを作成しないといけない場合は生成AIを活用したりすると一気に作成できる。
moved {
from = aws_instance.before
to = aws_instance.after
}
✅ removed Block
v1.7.0 から導入されたブロック。
importやmoveの構文とは少し異なり、lifecycle
でdestroy = false
を入れる必要がある。lifecycle
を入れない場合やdestroy = true
の場合はリソース自体が削除されるので、そもそもremoved
Block を使う必要がなくなる。
removed {
from = aws_instance.this
lifecycle {
destroy = false
}
}
ユースケース
Block Syntax ではtfstate の操作を行うのが難しいユースケースについて紹介します。
1️⃣ 実行単位を分けたい
実行単位が大きくなってきて分割したい場合、tfstate も分割する必要があります。
複数のtfstate を利用するため、Block Syntax ではなくCLI でtfstate を操作します。
移行元のディレクトリにて
# まずは現状確認(差分0が望ましい)
terraform plan
# バックアップ(backup.tfstate)を取得
terraform state pull > backup.tfstate
# 移行したいリソースをに移動(mv.state)
terraform state mv -state-out=mv.tfstate module.xxx module.xxx
# 移行したいリソースがdestroyされるかどうかを確認
terraform plan
上記操作後、mv.tfstate
を移行先のディレクトリに移動する。
移行先のディレクトリにて
# まずは現状確認(差分が出るはず)
terraform plan
# 先ほど作成したmv.tfstateを管理主体にする
terraform state push mv.tfstate
# 差分が出ないことを確認
terraform plan
上記操作後、mv.tfstate
を削除する。
2️⃣ 既に存在する別のtfstateにリソースを追加したい
こちらのユースケースも複数のtfstate を利用するため、Block Syntax ではなくCLI でtfstate を操作します。
移行元のディレクトリにて
# out.tfstateにaws_instance.thisリソースを移行させる
# そのとき、元のtfstateからaws_instance.thisリソースが消える
terraform state mv -state-out=out.tfstate aws_instance.this aws_instance.this
出力したout.tfstate
を移行先のディレクトリに移動させる。
移行先のディレクトリにて
# out.tfstate にあるaws_instance.this の情報を in.tfstate に移行する
terraform state mv -state=out.tfstate -state-out=in.tfstate aws_instance.this aws_instance.this
# in.tfstate をtfstate に上書きする
terraform state push in.tfstate
まとめ
直接tfstate を編集したり、実行結果が分からないままtfstate の操作を行ってしまうと実際のリソースが消えてしまうわけではありませんが、IaC を維持していくことが困難になります。そのため必ずバックアップを取得した上で、tfstate を安全に操作するようにしましょう。