moved
ブロックはTerraform1.1で導入された新機能です。moduleのリファクタリングに使えるか検証しました。
実行環境
ubuntu-20.04 on Windows 10 (WSL2)
$ terraform --version Terraform v1.1.9 on linux_amd64 + provider registry.terraform.io/hashicorp/aws v3.55.0
コード
準備したコードです。事前にapplyまで済ませてあります。
modules/vpc/variables.tf
variable "vpc_name" { type = string } variable "vpc_cidr" { type = string } variable "subnet" { type = list(object({ name = string cidr = string })) }
modules/vpc/main.tf
resource "aws_vpc" "this" { cidr_block = var.vpc_cidr tags = { Name = var.vpc_name } } resource "aws_subnet" "pub" { for_each = { for i in var.subnet : i.name => i } vpc_id = aws_vpc.this.id cidr_block = each.value.cidr tags = { Name = each.value.name } } output "vpc_id" { value = aws_vpc.this.id }
main.tf
module "vpc" { source = "../modules/vpc" vpc_name = "vpc-sample" vpc_cidr = "172.16.0.0/16" subnet = [ { name = "subnet-sample1" cidr = "172.16.0.0/20" }, { name = "subnet-sample2" cidr = "172.16.16.0/20" } ] } // moduleを使わない resource "aws_security_group" "this" { name = "securitygroup-sample1" vpc_id = module.vpc.vpc_id }
moduleを使わないリソースのリネーム
まずmoduleを使わず定義したリソースで試します。
セキュリティグループのリソース名をdefault
に変更します。
diff --git a/vpc_sample/main.tf b/vpc_sample/main.tf index 6b09809..bfa4756 100755 --- a/vpc_sample/main.tf +++ b/vpc_sample/main.tf @@ -15,7 +15,7 @@ module "vpc" { ] } -resource "aws_security_group" "this" { +resource "aws_security_group" "default" { name = "securitygroup-sample1" vpc_id = module.vpc.vpc_id }
当然ですが、リソースを作り直すplanが出力されます。
$ terraform plan aws_security_group.this: Refreshing state... [id=sg-02a601a6be35932e4] module.vpc.aws_vpc.this: Refreshing state... [id=vpc-0c91c8c5673d89ee5] module.vpc.aws_subnet.pub["subnet-sample1"]: Refreshing state... [id=subnet-01629b6f8717a5523] module.vpc.aws_subnet.pub["subnet-sample2"]: Refreshing state... [id=subnet-0edf4b9618cf8f38f] Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create - destroy Terraform will perform the following actions: # aws_security_group.default will be created + resource "aws_security_group" "default" { + arn = (known after apply) + description = "Managed by Terraform" + egress = (known after apply) + id = (known after apply) + ingress = (known after apply) + name = "securitygroup-sample1" + name_prefix = (known after apply) + owner_id = (known after apply) + revoke_rules_on_delete = false + tags_all = (known after apply) + vpc_id = "vpc-0c91c8c5673d89ee5" } # aws_security_group.this will be destroyed # (because aws_security_group.this is not in configuration) - resource "aws_security_group" "this" { - arn = "arn:aws:ec2:ap-northeast-1:601660378746:security-group/sg-02a601a6be35932e4" -> null - description = "Managed by Terraform" -> null - egress = [] -> null - id = "sg-02a601a6be35932e4" -> null - ingress = [] -> null - name = "securitygroup-sample1" -> null - owner_id = "601660378746" -> null - revoke_rules_on_delete = false -> null - tags = {} -> null - tags_all = {} -> null - vpc_id = "vpc-0c91c8c5673d89ee5" -> null } Plan: 1 to add, 0 to change, 1 to destroy.
これを避けるには、従来はterraform state mv
でリソース名をリネームする必要がありました。Terraform 1.1からはmovedブロックが利用できます。
movedブロックを追加して、再度planを実行してみます。
$ git diff diff --git a/vpc_sample/main.tf b/vpc_sample/main.tf index 6b09809..2cb6d0b 100755 --- a/vpc_sample/main.tf +++ b/vpc_sample/main.tf @@ -15,7 +15,13 @@ module "vpc" { ] } -resource "aws_security_group" "this" { +resource "aws_security_group" "default" { name = "securitygroup-sample1" vpc_id = module.vpc.vpc_id -} \ No newline at end of file +} + +moved { + from = aws_security_group.this + to = aws_security_group.default +}
$ terraform plan module.vpc.aws_vpc.this: Refreshing state... [id=vpc-0c91c8c5673d89ee5] aws_security_group.default: Refreshing state... [id=sg-02a601a6be35932e4] module.vpc.aws_subnet.pub["subnet-sample1"]: Refreshing state... [id=subnet-01629b6f8717a5523] module.vpc.aws_subnet.pub["subnet-sample2"]: Refreshing state... [id=subnet-0edf4b9618cf8f38f] Terraform will perform the following actions: # aws_security_group.this has moved to aws_security_group.default resource "aws_security_group" "default" { id = "sg-02a601a6be35932e4" name = "securitygroup-sample1" tags = {} # (8 unchanged attributes hidden) } Plan: 0 to add, 0 to change, 0 to destroy.
今度はリソース名をリネームするplanが出力されました。add
/destroy
が0であることを確認して、applyします。
$ terraform apply ... Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
無事にリソースを再作成することなくapplyできました。念のため再度planします。
$ terraform plan module.vpc.aws_vpc.this: Refreshing state... [id=vpc-0c91c8c5673d89ee5] aws_security_group.default: Refreshing state... [id=sg-02a601a6be35932e4] module.vpc.aws_subnet.pub["subnet-sample2"]: Refreshing state... [id=subnet-0edf4b9618cf8f38f] module.vpc.aws_subnet.pub["subnet-sample1"]: Refreshing state... [id=subnet-01629b6f8717a5523] 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.
コードとstateが同期されていることを確認できました。
module内リソースのリネーム
ここからが本題のmoduleのリファクタリングです。
vpcのリソース名を変更するので、先に確認しておきます。
$ terraform state list | grep aws_vpc
module.vpc.aws_vpc.this
リソース名を変更し、movedを定義します。
$ git diff diff --git a/modules/vpc/main.tf b/modules/vpc/main.tf index 15d46e6..b6848a6 100755 --- a/modules/vpc/main.tf +++ b/modules/vpc/main.tf @@ -1,15 +1,20 @@ -resource "aws_vpc" "this" { +resource "aws_vpc" "default" cidr_block = var.vpc_cidr tags = { Name = var.vpc_name } } +moved { + from = aws_vpc.this + to = aws_vpc.default +} resource "aws_subnet" "pub" { for_each = { for i in var.subnet : i.name => i } - vpc_id = aws_vpc.this.id + vpc_id = aws_vpc.default.id cidr_block = each.value.cidr tags = { Name = each.value.name } } output "vpc_id" { - value = aws_vpc.this.id + value = aws_vpc.default.id }
planを実行します。
$ terraform plan module.vpc.aws_vpc.default: Refreshing state... [id=vpc-0c91c8c5673d89ee5] aws_security_group.default: Refreshing state... [id=sg-02a601a6be35932e4] module.vpc.aws_subnet.pub["subnet-sample1"]: Refreshing state... [id=subnet-01629b6f8717a5523] module.vpc.aws_subnet.pub["subnet-sample2"]: Refreshing state... [id=subnet-0edf4b9618cf8f38f] Terraform will perform the following actions: # module.vpc.aws_vpc.this has moved to module.vpc.aws_vpc.default resource "aws_vpc" "default" { id = "vpc-0c91c8c5673d89ee5" tags = { "Name" = "vpc-sample" } # (15 unchanged attributes hidden) } Plan: 0 to add, 0 to change, 0 to destroy.
期待通りに、リネームするplanが出力されました。applyします(ログは省略)。
foreachを使ったリソースのリネーム
最後にforeachを使って生成したリソースをリネームできるか検証します。
サブネットのリソース名を確認しておきます。
$ terraform state list | grep aws_subnet module.vpc.aws_subnet.pub["subnet-sample1"] module.vpc.aws_subnet.pub["subnet-sample2"]
サブネットのリソース名を変更し、movedを定義しました。
$ git diff diff --git a/modules/vpc/main.tf b/modules/vpc/main.tf index b6848a6..9fd0774 100755 --- a/modules/vpc/main.tf +++ b/modules/vpc/main.tf @@ -15,6 +15,15 @@ resource "aws_subnet" "pub" { tags = { Name = each.value.name } } +moved { + from = aws_subnet.pub["subnet-sample1"] + to = aws_subnet.pub["pri-subnet-sample1"] +} +moved { + from = aws_subnet.pub["subnet-sample2"] + to = aws_subnet.pub["pri-subnet-sample2"] +} + output "vpc_id" { value = aws_vpc.default.id } diff --git a/vpc_sample/main.tf b/vpc_sample/main.tf index 2cb6d0b..009f1d2 100755 --- a/vpc_sample/main.tf +++ b/vpc_sample/main.tf @@ -5,11 +5,11 @@ module "vpc" { vpc_cidr = "172.16.0.0/16" subnet = [ { - name = "subnet-sample1" + name = "pri-subnet-sample1" cidr = "172.16.0.0/20" }, { - name = "subnet-sample2" + name = "pri-subnet-sample2" cidr = "172.16.16.0/20" } ]
planを実行します。
$ terraform plan module.vpc.aws_vpc.default: Refreshing state... [id=vpc-0c91c8c5673d89ee5] module.vpc.aws_subnet.pub["pri-subnet-sample1"]: Refreshing state... [id=subnet-01629b6f8717a5523] aws_security_group.default: Refreshing state... [id=sg-02a601a6be35932e4] module.vpc.aws_subnet.pub["pri-subnet-sample2"]: Refreshing state... [id=subnet-0edf4b9618cf8f38f] 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: # module.vpc.aws_subnet.pub["pri-subnet-sample1"] will be updated in-place # (moved from module.vpc.aws_subnet.pub["subnet-sample1"]) ~ resource "aws_subnet" "pub" { id = "subnet-01629b6f8717a5523" ~ tags = { ~ "Name" = "subnet-sample1" -> "pri-subnet-sample1" } ~ tags_all = { ~ "Name" = "subnet-sample1" -> "pri-subnet-sample1" } # (9 unchanged attributes hidden) } # module.vpc.aws_subnet.pub["pri-subnet-sample2"] will be updated in-place # (moved from module.vpc.aws_subnet.pub["subnet-sample2"]) ~ resource "aws_subnet" "pub" { id = "subnet-0edf4b9618cf8f38f" ~ tags = { ~ "Name" = "subnet-sample2" -> "pri-subnet-sample2" } ~ tags_all = { ~ "Name" = "subnet-sample2" -> "pri-subnet-sample2" } # (9 unchanged attributes hidden) } Plan: 0 to add, 2 to change, 0 to destroy.
期待通りにリネームするplanが出力されました。change
が2件出力されているのはtags
を変更したためです。論点ではないので、このままapplyします。
$ terraform apply ... module.vpc.aws_subnet.pub["pri-subnet-sample1"]: Modifying... [id=subnet-01629b6f8717a5523] module.vpc.aws_subnet.pub["pri-subnet-sample2"]: Modifying... [id=subnet-0edf4b9618cf8f38f] module.vpc.aws_subnet.pub["pri-subnet-sample2"]: Modifications complete after 0s [id=subnet-0edf4b9618cf8f38f] module.vpc.aws_subnet.pub["pri-subnet-sample1"]: Modifications complete after 0s [id=subnet-01629b6f8717a5523] Apply complete! Resources: 0 added, 2 changed, 0 destroyed.
無事applyできました。
変更後のコード
modules/vpc/variables.tfは変更なし。
modules/vpc/main.tf
resource "aws_vpc" "default" { cidr_block = var.vpc_cidr tags = { Name = var.vpc_name } } moved { from = aws_vpc.this to = aws_vpc.default } resource "aws_subnet" "pub" { for_each = { for i in var.subnet : i.name => i } vpc_id = aws_vpc.default.id cidr_block = each.value.cidr tags = { Name = each.value.name } } moved { from = aws_subnet.pub["subnet-sample1"] to = aws_subnet.pub["pri-subnet-sample1"] } moved { from = aws_subnet.pub["subnet-sample2"] to = aws_subnet.pub["pri-subnet-sample2"] } output "vpc_id" { value = aws_vpc.default.id }
main.tf
module "vpc" { source = "../modules/vpc" vpc_name = "vpc-sample" vpc_cidr = "172.16.0.0/16" subnet = [ { name = "pri-subnet-sample1" cidr = "172.16.0.0/20" }, { name = "pri-subnet-sample2" cidr = "172.16.16.0/20" } ] } // moduleを使わない resource "aws_security_group" "default" { name = "securitygroup-sample1" vpc_id = module.vpc.vpc_id } moved { from = aws_security_group.this to = aws_security_group.default }
所感
moduleをリファクタリングするにはstateの移動がかなり面倒で、リファクタリングを後回しにする要因になっていました。movedブロックはリファクタリングのトイルを減らす待望の機能追加です。