Terraformの新機能movedを試してみた
この記事は??
従来のTerraformではリファクタリングした時やリソースの命名を変更した際は、
terrafrom state mv
を実行または直接 tfstate
を直接編集する必要があり、
それをバージョン管理外で実施しなければいけないのが課題でした。
(リソースの変更手順はこちらにまとめてあります。)
この記事ではTerraform version1.1系の新機能 moved
を使って、既存のインフラの構造を
変更することなくリファクタリングとリソースの命名変更が出来ることを確認します。
この記事の目的
-
moved
ブロックの使い方と挙動を理解すること
手順
ソースコードはこちら
resourceを作る
resource "aws_vpc" "example" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "example" {
vpc_id = aws_vpc.example.id
availability_zone = "ap-northeast-1a"
cidr_block = "10.0.1.0/24"
}
resource "aws_security_group" "example" {
name = "http_sg"
vpc_id = aws_vpc.example.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
output "vpc_id" {
value = aws_vpc.example.id
}
moved
を試すためにVPC、サブネット、セキュリティグループをresourceを使って作成します。
apply後、次はmoduleを使ってリファクタリングしてみます。
moduleでリファクタリング
// modules/network/main.tf
variable "cidr_block" {}
variable "availability_zone" {}
resource "aws_vpc" "example" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "example" {
vpc_id = aws_vpc.example.id
availability_zone = var.availability_zone
cidr_block = var.cidr_block
}
output "vpc_id" {
value = aws_vpc.example.id
}
// modules/security_group/main.tf
resource "aws_security_group" "example" {
name = "http_sg"
vpc_id = var.vpc_id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
// main.tf
module "modified_network" {
source = "./modules/network"
availability_zone = "ap-northeast-1a"
cidr_block = "10.0.1.0/24"
}
module "sg" {
source = "./modules/security_group"
vpc_id = module.modified_network.vpc_id
}
module作成後、実行計画を確認します。
❯❯❯ terraform plan
aws_vpc.example: Refreshing state... [id=vpc-0c1962cc15d60eef0]
aws_subnet.example: Refreshing state... [id=subnet-018f681f7549f1134]
aws_security_group.example: Refreshing state... [id=sg-07f7564a362f56901]
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.example will be destroyed
# (because aws_security_group.example is not in configuration)
- resource "aws_security_group" "example" {
- arn = "arn:aws:ec2:ap-northeast-1:762742784206:security-group/sg-07f7564a362f56901" -> null
- description = "Managed by Terraform" -> null
- egress = [
- {
- cidr_blocks = [
- "0.0.0.0/0",
]
- description = ""
- from_port = 0
- ipv6_cidr_blocks = []
- prefix_list_ids = []
- protocol = "-1"
- security_groups = []
- self = false
- to_port = 0
},
] -> null
- id = "sg-07f7564a362f56901" -> null
- ingress = [
- {
- cidr_blocks = [
- "0.0.0.0/0",
]
- description = ""
- from_port = 80
- ipv6_cidr_blocks = []
- prefix_list_ids = []
- protocol = "tcp"
- security_groups = []
- self = false
- to_port = 80
},
] -> null
- name = "http_sg" -> null
- owner_id = "762742784206" -> null
- revoke_rules_on_delete = false -> null
- tags = {} -> null
- tags_all = {} -> null
- vpc_id = "vpc-0c1962cc15d60eef0" -> null
}
# aws_subnet.example will be destroyed
# (because aws_subnet.example is not in configuration)
- resource "aws_subnet" "example" {
- arn = "arn:aws:ec2:ap-northeast-1:762742784206:subnet/subnet-018f681f7549f1134" -> null
- assign_ipv6_address_on_creation = false -> null
- availability_zone = "ap-northeast-1a" -> null
- availability_zone_id = "apne1-az4" -> null
- cidr_block = "10.0.1.0/24" -> null
- id = "subnet-018f681f7549f1134" -> null
- map_customer_owned_ip_on_launch = false -> null
- map_public_ip_on_launch = false -> null
- owner_id = "762742784206" -> null
- tags = {} -> null
- tags_all = {} -> null
- vpc_id = "vpc-0c1962cc15d60eef0" -> null
}
# aws_vpc.example will be destroyed
# (because aws_vpc.example is not in configuration)
- resource "aws_vpc" "example" {
- arn = "arn:aws:ec2:ap-northeast-1:762742784206:vpc/vpc-0c1962cc15d60eef0" -> null
- assign_generated_ipv6_cidr_block = false -> null
- cidr_block = "10.0.0.0/16" -> null
- default_network_acl_id = "acl-02735963094a4b45c" -> null
- default_route_table_id = "rtb-0fda05ae0d556db67" -> null
- default_security_group_id = "sg-08cd847daea28946d" -> null
- dhcp_options_id = "dopt-3bb6fa5c" -> null
- enable_classiclink = false -> null
- enable_classiclink_dns_support = false -> null
- enable_dns_hostnames = false -> null
- enable_dns_support = true -> null
- id = "vpc-0c1962cc15d60eef0" -> null
- instance_tenancy = "default" -> null
- main_route_table_id = "rtb-0fda05ae0d556db67" -> null
- owner_id = "762742784206" -> null
- tags = {} -> null
- tags_all = {} -> null
}
# module.http_sg.aws_security_group.example will be created
+ resource "aws_security_group" "example" {
+ arn = (known after apply)
+ description = "Managed by Terraform"
+ egress = [
+ {
+ cidr_blocks = [
+ "0.0.0.0/0",
]
+ description = ""
+ from_port = 0
+ ipv6_cidr_blocks = []
+ prefix_list_ids = []
+ protocol = "-1"
+ security_groups = []
+ self = false
+ to_port = 0
},
]
+ id = (known after apply)
+ ingress = [
+ {
+ cidr_blocks = [
+ "0.0.0.0/0",
]
+ description = ""
+ from_port = 80
+ ipv6_cidr_blocks = []
+ prefix_list_ids = []
+ protocol = "tcp"
+ security_groups = []
+ self = false
+ to_port = 80
},
]
+ name = "http_sg"
+ name_prefix = (known after apply)
+ owner_id = (known after apply)
+ revoke_rules_on_delete = false
+ tags_all = (known after apply)
+ vpc_id = (known after apply)
}
# module.network.aws_subnet.example will be created
+ resource "aws_subnet" "example" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "ap-northeast-1a"
+ availability_zone_id = (known after apply)
+ cidr_block = "10.0.1.0/24"
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ map_public_ip_on_launch = false
+ owner_id = (known after apply)
+ tags_all = (known after apply)
+ vpc_id = (known after apply)
}
# module.network.aws_vpc.example will be created
+ resource "aws_vpc" "example" {
+ arn = (known after apply)
+ cidr_block = "10.0.0.0/16"
+ default_network_acl_id = (known after apply)
+ default_route_table_id = (known after apply)
+ default_security_group_id = (known after apply)
+ dhcp_options_id = (known after apply)
+ enable_classiclink = (known after apply)
+ enable_classiclink_dns_support = (known after apply)
+ enable_dns_hostnames = (known after apply)
+ enable_dns_support = true
+ id = (known after apply)
+ instance_tenancy = "default"
+ ipv6_association_id = (known after apply)
+ ipv6_cidr_block = (known after apply)
+ main_route_table_id = (known after apply)
+ owner_id = (known after apply)
+ tags_all = (known after apply)
}
Plan: 3 to add, 0 to change, 3 to destroy.
Changes to Outputs:
- vpc_id = "vpc-0c1962cc15d60eef0" -> null
すると、リソースは削除および再作成されることがわかります。
movedを使う
既存のインフラの構造に変更を加えることなく、リファクタリングするために今までは、
terraform state mv
を使うか tfstate
を直接変更していましたが、今後は
moved
を使って次の様に宣言的に記述することが出来ます。
// main.tf
moved {
from = aws_vpc.example
to = module.network.aws_vpc.example
}
moved {
from = aws_subnet.example
to = module.network.aws_subnet.example
}
moved {
from = aws_security_group.example
to = module.sg.aws_security_group.example
}
実行計画を確認してみます。
❯❯❯ terraform plan
module.network.aws_vpc.example: Refreshing state... [id=vpc-0c1962cc15d60eef0]
module.network.aws_subnet.example: Refreshing state... [id=subnet-018f681f7549f1134]
module.sg.aws_security_group.example: Refreshing state... [id=sg-07f7564a362f56901]
Terraform will perform the following actions:
# aws_subnet.example has moved to module.network.aws_subnet.example
resource "aws_subnet" "example" {
id = "subnet-018f681f7549f1134"
tags = {}
# (10 unchanged attributes hidden)
}
# aws_vpc.example has moved to module.network.aws_vpc.example
resource "aws_vpc" "example" {
id = "vpc-0c1962cc15d60eef0"
tags = {}
# (15 unchanged attributes hidden)
}
# aws_security_group.example has moved to module.sg.aws_security_group.example
resource "aws_security_group" "example" {
id = "sg-07f7564a362f56901"
name = "http_sg"
tags = {}
# (8 unchanged attributes hidden)
}
Plan: 0 to add, 0 to change, 0 to destroy.
Changes to Outputs:
- vpc_id = "vpc-0c1962cc15d60eef0" -> null
You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure.
既存のインフラの構造に変更が発生しないことが確認出来ました!
movedでリソースの命名を変更する
さらに moved
ブロックはリソースの命名の変更にも対応しています。
// main.tf
module "modified_network" {
source = "./modules/network"
availability_zone = "ap-northeast-1a"
cidr_block = "10.0.1.0/24"
}
module "sg" {
source = "./modules/security_group"
vpc_id = module.modified_network.vpc_id
}
moved {
from = module.network
to = module.modified_network
}
命名の変更はこの様に記述することが出来ます。
さいごに
terraform state mv
や tfstate
を手動で変更するよりも直感的で分かりやすいですね。
今後リファクタリングする際は moved
ブロックで構成を変更 -> PRを送ってレビューしてもらう
-> 変更を適用 -> moved
ブロックは不要なので削除という流れで進めるのが良さそうです。
Discussion