Terraformのリファクタリングツールのtfirmgを公開しました
目次
はじめに
色々と忙しい日々を過ごしているゲインです。
今回はterraformのimport blockとremoved block(と将来的にはmoved block)を自動で生成するツールを雑に公開したので紹介します。
モチベーション
terraformを日々追加していると別tfstateに分けたいということは多々発生するでしょう。
terraform v1.5.0以前であればひたすらリソースを terraform import hoge
&& terraform state rm hoge
実行するという辛いリファクタリングをしていた方もいるのではないでしょうか。
また複数人が絡むチーム開発ではtfmigrate を利用してCIフレンドリーにリファクタリングしている方もいらっしゃるでしょう。
しかし、シンプルに数が多くなってくるとコマンドを大量に実行するのも疲れますし自身が雑な人間なのでミスも増えてしまいます。
そのため実際のコードのリファクタリングに合わせてtfstateも移動してくれるツールが欲しいなと思っていました。
そんな中 terraformのアップデートによりv1.5.0からは Import Block1が、 v.1.7.0からは Removed Block2がサポートされました。
このアップデートによりterraformのstateを行う際には、import blockで移動させたいリソースをimportし、removed blockで移動させいたいリソースをコードベースで定義することが可能になりました。
そのため2つのstateにまたがってこれらのコードを生成すればstate跨ぎのリファクタリングが可能になるということです。
また、import blockの構文の id
はtfstateに値があることを発見しこれを利用することでコードベースのリソース移動によるリファクタリングが可能なのではないかということで今回作成してみました。
tfirmg
こちらで公開しています
https://github.com/gainings/tfirmg
tfstateの中身はまったくいじらないツールなのでお気軽に試していただければと思います。
使用例
以下の例だとネットワークとec2のリソースのみがあります。
これぐらいだったら手動でimportしても問題はありませんが、大量に書かれたリソースがあると想像してください。
#current_state/main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.47.0"
}
}
}
provider "aws" {
region = "ap-northeast-1"
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = false
enable_vpn_gateway = false
tags = {
Terraform = "true"
Environment = "dev"
}
}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"]
}
resource "aws_instance" "my_perfect_app" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
tags = {
Terraform = "true"
Name = "HelloWorld"
}
}
このterraformを以下のように分割したいと考えます。
#current_state/main.tf
...
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = false
enable_vpn_gateway = false
tags = {
Terraform = "true"
Environment = "dev"
}
}
#app_state/main.tf
...
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"]
}
resource "aws_instance" "my_perfect_app" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
tags = {
Terraform = "true"
Name = "HelloWorld"
}
}
そこで以下のコマンドを実行することで import blockとremoved blockを生成します。
tfirmg generate --src-dir ./current_state --dst-dir ./app_state --src-tfstate-path file://$(PWD)/current_state/terraform.tfstate
このコマンドの結果により、 current_state/removed.tf
と app_state/import.tf
を生成します。
リソースの移動によって参照先が無くなる場合があるのでそれは普通にリファクタリングを行い、通常通りterraform applyを行えば無事にstate跨ぎの移動が完了します。
この方法はCIに新規のツールを導入する必要もなく、Terraformの機能に乗っかることができる点が利点だと思っています。
課題
現状では僕の仕事のユースケースのためlocal backendとs3 backendのみにしか対応していません。
gcsぐらいは個人で検証できるとは思いますが、Azure、Terraform Cloudやその他はまったく見識がないのでPRお待ちしています。
またこちらはすべてのリソースに対応しているわけではありません。
というのも各種providerのリソースはtfstateに記載されているidで必ずimport出来るわけでもなく、そもそもimportに対応していないリソースもあるためです。
それを回避するために愚直にid生成のコードを書いていたりもしておりなんとかならないかなーと思っています。
https://github.com/gainings/tfirmg/tree/main/internal/rules/providers/aws
とはいえ対応してないリソースについては生成された後にimport blockを手動で削除したり修正したりすれば良いためまぁとりあえずはこのほうほうでもんだいないかな〜と思っています。
まとめ
terraformのリファクタリングを行うツールを公開しました。
現状ではimport / removed blockを生成する機能しかありませんが、暇なタイミングでmodule間のリソース移動などなど作成していきたいと思っています。
使ってみて感想などいただけたら幸いです。