tfstateを分割したい
Terraformはリソース数が多くなってくるとplanやapplyの実行時間が長くなってくる。
planやapplyの実行時間が長くなると手元での確認やCI/CDに時間がかかるようになり、開発効率や開発体験が悪くなる。
そのためtfstateを分割してそれぞれのtfstateを小さくし、planやapplyの実行時間を短くしたい。
terraform_remote_state
terraform_remote_state
は他のtfstateのoutputの値をdata resourceとして参照する機能。
この機能を使ってtfstateを分割できる。
tfstateを分割する
元のコード
簡単な例として以下のコードを考える。
コードの構成は以下の通り。
.
└── original
├── main.tf
└── provider.tf
data "aws_caller_identity" "current" {}
resource "aws_s3_bucket" "example" {
bucket = "example-bucket-${data.aws_caller_identity.current.account_id}"
}
resource "aws_s3_object" "object" {
bucket = aws_s3_bucket.example.bucket
key = "test.txt"
source = "test.txt"
etag = file("test.txt")
}
S3バケットとその中にオブジェクトを置いている。
providerは以下のように設定している。
terraform {
required_version = ">= 0.12"
backend "s3" {
bucket = "terraform-example-bucket"
key = "terraform/original.tfstate"
region = "ap-northeast-1"
}
}
applyするとS3バケットとオブジェクトが作成され、terraform-example-bucket
バケットのterraform/original.tfstate
にtfstateが保存される。
このコードからS3のオブジェクトを別のtfstateに分割する。
outputブロックを追加する
S3バケットリソースを外部から参照できるようにoutput
ブロックを追加する。
コードはoriginalディレクトリ以下にoutput.tfを作成して以下のように記述する。
output "example_bucket" {
value = aws_s3_bucket.example.bucket
}
.
└── original
├── main.tf
├── output.tf // 追加
└── provider.tf
分割先のコードを追加する
新しいディレクトリを作成し、移動先のコードを追加する。
.
├── original
│ ├── main.tf
│ ├── output.tf
│ └── provider.tf
└── sub
└── provider.tf
subディレクトリ以下にprovider.tfを作成し、以下のように記述する。
terraform {
required_version = ">= 0.12"
backend "s3" {
bucket = "terraform-example-bucket"
key = "terraform/sub.tfstate"
region = "ap-northeast-1"
}
}
terraform_remote_state で参照する
subディレクトリ以下にmain.tfを作成し、originalのtfstateのoutputを参照する。
```text
.
├── original
│ ├── main.tf
│ ├── output.tf
│ └── provider.tf
└── sub
├── main.tf // 追加
└── provider.tf
data "terraform_remote_state" "original" {
backend = "s3"
config = {
bucket = "terraform-example-bucket"
key = "terraform/original.tfstate"
region = "ap-northeast-1"
}
}
tfstateがS3に保存されている場合はbackend
に"s3"
を指定する。
terraform_remote_state
のconfig
の値は以下の通り。
- bucket: 参照するtfstateが保存されているS3バケット
- key: 参照するtfstateのパス
- region: S3バケットのリージョン
apply後にstateを確認するとdata resourceとしてS3バケットの情報が取得できる。
$ terraform state show data.terraform_remote_state.original
# data.terraform_remote_state.original:
data "terraform_remote_state" "original" {
backend = "s3"
config = {
bucket = "terraform-example-bucket"
key = "terraform/original.tfstate"
region = "ap-northeast-1"
}
outputs = {
example_bucket = "example-bucket-XXXXXXXXXXXX"
}
}
参考: tfstateをローカルに保存している場合の terraform_remote_state の設定
tfstateをローカルに保存している場合はbackend
に"local"
を指定する。
data "terraform_remote_state" "original" {
backend = "local"
config = {
path = "path/to/tfstate"
}
}
terraform_remote_stateで参照した値を使ってリソースを移動する
terraform_remote_state
で参照した値を使ってリソースを移動する。
まずsubディレクトリ以下のmain.tfにoriginal/main.tfのaws_s3_objectリソースをコピーする。
resource "aws_s3_object" "object" {
# bucket = aws_s3_bucket.example.bucket
bucket = data.terraform_remote_state.original.outputs.example_bucket
key = "test.txt"
source = "test.txt"
etag = file("test.txt")
}
ただしaws_s3_bucket.example.bucket
だった部分をterraform_remote_state
dataブロックで参照するように変更する。
オブジェクト自体はすでに存在するのでimport
ブロックを追加してリソースをインポートする。
import {
id = "example-bucket-XXXXXXXXXXXX/test.txt"
to = aws_s3_object.object
}
元のコードからリソースを削除する
importブロックを追加する
実リソースが削除されてしまわないようにremovedブロックでtfstateからのみ削除されるようにする。
removed {
from = aws_s3_object.object
lifecycle {
destroy = false
}
}
destroy = false
を指定してリソース自体は削除されないようにしている。
terraform plan
コマンドで実際にリソースが削除されないかを必ず確認しておく。
# aws_s3_object.object will no longer be managed by Terraform, but will not be destroyed
リソースを削除する
移動したリソースが正しく動作するか確認したら元のコードからリソースを削除する。
resource "aws_s3_bucket" "example" {
bucket = "example-bucket-${data.aws_caller_identity.current.account_id}"
}
- resource "aws_s3_object" "object" {
- bucket = aws_s3_bucket.example.bucket
- key = "test.txt"
- source = "test.txt"
- etag = file("test.txt")
- }
removedブロックを追加したので、applyするとtfstateのみから削除される。