Terraform文を見ているとlifecycleなるまたよくわからないキーワードが出てきたので調べてみた。
※知ったかぶりはダサいからわからないことは「わからない」と言うと「調べてこい」おじさんが出てくるのでIT業界は面倒だね。。。
Terraformのlifecycleとは
・tfファイルのresourceブロックに記載する構文。
・記載されたリソースに変更が発生する際の挙動を変更できる。
lifecycleで指定できるオプション
公式はこちら。
developer.hashicorp.com
・create_befor_destroy:新しいリソースを作成し、破棄する。先に作成するのでリソースが重複する状態が発生する。
・prevent_destroy:リソースの破棄を防ぐ。破棄しようとするとエラーとなる。
・ignore_changes:リソースの指定属性の変更を無視する。作成後Terraformもしくは手動で変更されても無視する。
・replace_triggered_by:Terraform1.2で追加された機能。参照されたアイテムのいずれかが変更されたときにリソースを置き換える。
※ほぼ名前の通りですね。
やること
AWSのリソース(今回はVPCとSubnet)をTerraformで作成し、lifecycleを指定して挙動を確認していく。
※今回はWindows11でやっています。Linuxを使う場合"(ダブルクォーテーション)など一部変える必要あるかも。
※下記を参考にaws vaultを使いaws cliのprofile設定を行ってます。profileは任意で変えてOK。
amegaeru.hatenablog.jp
検証項目
1.通常の変更時の挙動確認
2.通常の削除時の挙動
3.create_before_destroy検証
4.prevent_destroy検証
5.ignore_changes検証
6.replace_triggered_by検証
7.後始末
今回使うTerraformコード
provider "aws" { region = "ap-northeast-1" profile = "testvault" } variable "env" { default = { env_name = "test" vpc_cidr = "10.0.0.0/16" sb_az1a = "ap-northeast-1a" sb_az1a_cidr = "10.0.1.0/24" } } resource "aws_vpc" "vpc" { cidr_block = "${var.env.vpc_cidr}" tags = { Name = "${var.env.env_name}vpc" } } resource "aws_subnet" "public_1a" { vpc_id = "${aws_vpc.vpc.id}" availability_zone = "${var.env.sb_az1a}" cidr_block = "${var.env.sb_az1a_cidr}" tags = { Name = "${var.env.env_name}public_1a" } }
実践!
0.事前準備
0-1.Terraformコードのtfファイルを作成
今回は下記に作成。※どこでもよいです。
c:\test\main.tf
0-2.作業ディレクトリ移動
# cd c:\test
0-3.リソースを作成
# terraform plan # terraform apply
0-4.リソース作成状況確認
> aws ec2 describe-vpcs --profile testvault | jq ".Vpcs[] | .CidrBlock,.VpcId" "10.0.0.0/16" "vpc-08a330246600a9a35" > aws ec2 describe-subnets --profile testvault | jq ".Subnets[] | .Tags[].Value,.SubnetId" "testpublic_1a" "subnet-0edba038b4ea7a111"
下記のようなエラーが出る場合
jq: error (at <stdin>:102): Cannot iterate over null (null)
※vpcやsubnetが複数あり、jqで指定されたキーを持っていない場合エラーとなる。
取得順序によっては先に持っていないキーを参照し、エラーとなって以降が取得されない事態になる。今回でいうとSubnetのTagsを持っていない可能性あり。デフォルトで作成されているSubnetはTagsを持っていねぇ。。。
【回避策】
※エラーを回避したいキーの最後に?を入れる。
> aws ec2 describe-subnets --profile testvault | jq ".Subnets[] | .Tags[]?.Value,.SubnetId"
1.通常の変更時の挙動確認
1-1.Subnetを変更
variable "env" { default = { env_name = "test" vpc_cidr = "10.0.0.0/16" sb_az1a = "ap-northeast-1a" # 変更 sb_az1a_cidr = "10.0.2.0/24" # - } }
1-2.適用
# terraform plan Plan: 1 to add, 0 to change, 1 to destroy. ※ 作成、破棄ですね。 # terraform apply
1-3.動作確認
>aws ec2 describe-vpcs --profile testvault | jq ".Vpcs[] | .CidrBlock,.VpcId" "10.0.0.0/16" "vpc-08a330246600a9a35" >aws ec2 describe-subnets --profile testvault | jq ".Subnets[] | .Tags[]?.Value,.SubnetId" "testpublic_1a" "subnet-02ba12a759c99030a" ※Subnetが再作成されてますね。
2.通常の削除時の挙動
2-1.Subnetを削除
### コメントアウト # resource "aws_subnet" "public_1a" { # vpc_id = "${aws_vpc.vpc.id}" # availability_zone = "${var.env.sb_az1a}" # cidr_block = "${var.env.sb_az1a_cidr}" # tags = { # Name = "${var.env.env_name}public_1a" # } #}
2-2.適用
# terraform plan Plan: 0 to add, 0 to change, 1 to destroy. ※ 破棄ですね。 # terraform apply
2-3.動作確認
>aws ec2 describe-vpcs --profile testvault | jq ".Vpcs[] | .CidrBlock,.VpcId" "10.0.0.0/16" "vpc-08a330246600a9a35" >aws ec2 describe-subnets --profile testvault | jq ".Subnets[] | .Tags[]?.Value,.SubnetId" ※Subnet消えた
2-4.戻し作業(コメントアウトを消して適用)
3.create_befor_destroy検証
3-1.terraformファイル修正
resource "aws_subnet" "public_1a" { vpc_id = "${aws_vpc.vpc.id}" availability_zone = "${var.env.sb_az1a}" cidr_block = "${var.env.sb_az1a_cidr}" tags = { Name = "${var.env.env_name}public_1a" } ## 追加 lifecycle { create_before_destroy = true } ## - }
3-2.適用
# terraform plan Plan: 1 to add, 0 to change, 1 to destroy. ※ 変更と変わりなし。 # terraform apply ※ 作成後追加なのですばやくapply後素早く3-3を実行します。
3-3.動作確認
>aws ec2 describe-vpcs --profile testvault | jq ".Vpcs[] | .CidrBlock,.VpcId" "10.0.0.0/16" "vpc-08a330246600a9a35" >aws ec2 describe-subnets --profile testvault | jq ".Subnets[] | .Tags[]?.Value,.SubnetId" "testpublic_1a" "subnet-0347c08b19f390f5b" ※ 早すぎてCLIじゃ追えない、、、Webでも見てみたがだめでした。。。
4.prevent_destroy検証
4-1.terraformファイル修正
resource "aws_subnet" "public_1a" { vpc_id = "${aws_vpc.vpc.id}" availability_zone = "${var.env.sb_az1a}" cidr_block = "${var.env.sb_az1a_cidr}" tags = { Name = "${var.env.env_name}public_1a" } lifecycle { ## 変更 prevent_destroy = true ## - } }
4-2.適用
# terraform plan No changes. Your infrastructure matches the configuration. Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed. ※No changesと出るが適用されるっぽい。4-4参照 # terraform apply
4-3.terraformファイル修正
variable "env" { default = { env_name = "test" vpc_cidr = "10.0.0.0/16" sb_az1a = "ap-northeast-1a" ## 変更 sb_az1a_cidr = "10.0.3.0/24" ## - } }
4-4.適用
# terraform plan Planning failed. Terraform encountered an error while generating this plan. ╷ │ Error: Instance cannot be destroyed │ │ on main.tf line 23: │ 23: resource "aws_subnet" "public_1a" { │ │ Resource aws_subnet.public_1a has lifecycle.prevent_destroy set, but │ the plan calls for this resource to be destroyed. To avoid this error │ and continue with the plan, either disable lifecycle.prevent_destroy │ or reduce the scope of the plan using the -target flag. ※変更できない
4-5.元に戻す(LifeCycel削除)
resource "aws_subnet" "public_1a" { vpc_id = "${aws_vpc.vpc.id}" availability_zone = "${var.env.sb_az1a}" cidr_block = "${var.env.sb_az1a_cidr}" tags = { Name = "${var.env.env_name}public_1a" } ## 削除 lifecycle { prevent_destroy = true } ##- }
# terraform plan # terraform apply
5.ignore_changes検証
5-1.terraformファイル修正
resource "aws_subnet" "public_1a" { vpc_id = "${aws_vpc.vpc.id}" availability_zone = "${var.env.sb_az1a}" cidr_block = "${var.env.sb_az1a_cidr}" tags = { Name = "${var.env.env_name}public_1a" } lifecycle { ## 変更 ignore_changes = [tags] ## - } }
5-2.適用
# terraform plan # terraform apply
5-3.事前設定確認
>aws ec2 describe-vpcs --profile testvault | jq ".Vpcs[] | .CidrBlock,.VpcId" "10.0.0.0/16" "vpc-08a330246600a9a35" >aws ec2 describe-subnets --profile testvault | jq ".Subnets[] | .Tags[]?.Value,.SubnetId" "testpublic_1a" "subnet-078eeb25be1be6f4a"
5-4.Tags変更
resource "aws_subnet" "public_1a" { vpc_id = "${aws_vpc.vpc.id}" availability_zone = "${var.env.sb_az1a}" cidr_block = "${var.env.sb_az1a_cidr}" tags = { ## 変更 Name = "testtesttest" ## - } lifecycle { ignore_changes = [tags] } }
5-5.適用
# terraform plan No changes. Your infrastructure matches the configuration. Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed. ※No changesですね。 # terraform apply Apply complete! Resources: 0 added, 0 changed, 0 destroyed. ※ 念のためapply。変更なし。
5-6.lifecycleを外したらどうなるか
resource "aws_subnet" "public_1a" { vpc_id = "${aws_vpc.vpc.id}" availability_zone = "${var.env.sb_az1a}" cidr_block = "${var.env.sb_az1a_cidr}" tags = { Name = "testtesttest" } ## 削除 ## - }
5-7.適用
# terraform plan Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: ~ update in-place Terraform will perform the following actions: # aws_subnet.public_1a will be updated in-place ~ resource "aws_subnet" "public_1a" { id = "subnet-078eeb25be1be6f4a" ~ tags = { ~ "Name" = "testpublic_1a" -> "testtesttest" } ~ tags_all = { ~ "Name" = "testpublic_1a" -> "testtesttest" } # (15 unchanged attributes hidden) } Plan: 0 to add, 1 to change, 0 to destroy. ──────────────────────────────────────────────────────────────────────── Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now. ※変えてくれそう # terraform apply Apply complete! Resources: 0 added, 1 changed, 0 destroyed. ※ステータス上は変わった
5-8.設定確認
>aws ec2 describe-vpcs --profile testvault | jq ".Vpcs[] | .CidrBlock,.VpcId" "10.0.0.0/16" "vpc-08a330246600a9a35" >aws ec2 describe-subnets --profile testvault | jq ".Subnets[] | .Tags[]?.Value,.SubnetId" "testtesttest" "subnet-078eeb25be1be6f4a" ※変わった!
6.replace_triggered_by
vpcが作り直しされたらsubnetが作り直しがされないかを検証しようかと思ったけど、vpcが消えたらsubnetを消えるからこの環境では検証は無理と悟りました。甘かった。。。また今度。。。
7.後始末
# terraform destroy
※楽ちん!
感想
簡単に終わらせようと思ったけど以外と時間かかった。まぁこういうのの積み重ねが重要だよね。。。きっと( 一一)