Terraformerを使ってTerraformに既存インフラのリソースをインポートする

目次
インフラエンジニアの寺岡です。
前回Terraformに既存リソースをインポートする方法として
以下の記事でterraform importコマンドをご紹介しました。
この記事のまとめにも記載していますが
importコマンドで書き換えてくれるのはtfstateのみであり
tfファイルはtfstateとの差分を見ながら自力で書いていく必要があります。
数が多ければ途方もない時間がかかりそうです、これは困った。
そんな皆さんに朗報です。
terraformerというツールがOSSとして公開されています。
https://github.com/GoogleCloudPlatform/terraformer
CLI tool to generate terraform files from existing infrastructure (reverse Terraform). Infrastructure to Code
このように書かれている通り既存のインフラからTerraformのファイルを自動生成するCLIツールのようです。
使い方もGithubに記載されているので実際に使ってみましょう。
インストール
Macの場合はbrewコマンドでインストールできます。
$ brew install terraformer $ terraformer version Terraformer v0.8.7
ここまでは簡単ですね。
今回はv0.8.7を使っていきます。
インフラ構成
terraformerでインポートする対象のインフラを事前に作成しておきました。
https://github.com/beyond-teraoka/terraform-aws-multi-environment-sample
構成図

同一のAWSアカウントに3つの環境があります。
- develop
- production
- manage
さらに各環境ごとに以下のリソースがあります。
- VPC
- Subnet
- Route Table
- Internet Gateway
- NAT Gateway
- Security Group
- VPC Peering
- EIP
- EC2
- ALB
- RDS
図には書いていないですが各環境のリソースにはEnvironmentタグを付与しており
それぞれdev,prod,mngが値として設定されています。
認証情報の用意
AWSの認証情報を用意しておきます。
こちらは皆さんの環境に合わせて用意してください。
$ cat /Users/yuki.teraoka/.aws/credentials [beyond-poc] aws_access_key_id = XXXXXXXXXXXXXXXXXXXX aws_secret_access_key = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [beyond-poc-admin] role_arn = arn:aws:iam::XXXXXXXXXXXX:role/XXXXXXXXXXXXXXXXXXXXX source_profile = beyond-poc
terraformerの実行
まずはGitHubに書いている例の通りに実行してみます。
$ terraformer import aws --resources=alb,ec2_instance,eip,ebs,igw,nat,rds,route_table,sg,subnet,vpc,vpc_peering --regions=ap-northeast-1 --profile=beyond-poc-admin 2020/05/05 18:29:14 aws importing region ap-northeast-1 2020/05/05 18:29:14 aws importing... vpc 2020/05/05 18:29:15 open /Users/yuki.teraoka/.terraform.d/plugins/darwin_amd64: no such file or directory
terraform initした時のプラグインのディレクトリが必要みたいですね。
init.tfを用意してinitします。
$ echo 'provider "aws" {}' > init.tf
$ terraform init
もう一度実行してみます。
$ terraformer import aws --resources=alb,ec2_instance,eip,ebs,igw,nat,rds,route_table,sg,subnet,vpc,vpc_peering --regions=ap-northeast-1 --profile=beyond-poc-admin
インポートできたようです。
先ほどまではなかったgeneratedというディレクトリが出来上がっています。
ディレクトリ構造
$ tree
.
└── aws
├── alb
│ ├── lb.tf
│ ├── lb_listener.tf
│ ├── lb_target_group.tf
│ ├── lb_target_group_attachment.tf
│ ├── outputs.tf
│ ├── provider.tf
│ ├── terraform.tfstate
│ └── variables.tf
├── ebs
│ ├── ebs_volume.tf
│ ├── outputs.tf
│ ├── provider.tf
│ └── terraform.tfstate
├── ec2_instance
│ ├── instance.tf
│ ├── outputs.tf
│ ├── provider.tf
│ ├── terraform.tfstate
│ └── variables.tf
├── eip
│ ├── eip.tf
│ ├── outputs.tf
│ ├── provider.tf
│ └── terraform.tfstate
├── igw
│ ├── internet_gateway.tf
│ ├── outputs.tf
│ ├── provider.tf
│ ├── terraform.tfstate
│ └── variables.tf
├── nat
│ ├── nat_gateway.tf
│ ├── outputs.tf
│ ├── provider.tf
│ └── terraform.tfstate
├── rds
│ ├── db_instance.tf
│ ├── db_parameter_group.tf
│ ├── db_subnet_group.tf
│ ├── outputs.tf
│ ├── provider.tf
│ ├── terraform.tfstate
│ └── variables.tf
├── route_table
│ ├── main_route_table_association.tf
│ ├── outputs.tf
│ ├── provider.tf
│ ├── route_table.tf
│ ├── route_table_association.tf
│ ├── terraform.tfstate
│ └── variables.tf
├── sg
│ ├── outputs.tf
│ ├── provider.tf
│ ├── security_group.tf
│ ├── security_group_rule.tf
│ ├── terraform.tfstate
│ └── variables.tf
├── subnet
│ ├── outputs.tf
│ ├── provider.tf
│ ├── subnet.tf
│ ├── terraform.tfstate
│ └── variables.tf
├── vpc
│ ├── outputs.tf
│ ├── provider.tf
│ ├── terraform.tfstate
│ └── vpc.tf
└── vpc_peering
├── outputs.tf
├── provider.tf
├── terraform.tfstate
└── vpc_peering_connection.tf
13 directories, 63 files
ディレクトリの構造に注目してほしいのですが
terraformerはデフォルトで「{output}/{provider}/{service}/{resource}.tf」という構造でインポートするようです。
これはGitHubにも記載されています。
Terraformer by default separates each resource into a file, which is put into a given service directory.
The default path for resource files is {output}/{provider}/{service}/{resource}.tf and can vary for each provider.
この構造では以下の問題が出てきます。
- Terraformのリソースごとにtfstateが分割されているため、小規模な変更でも複数回のApplyが必要になる
- 全ての環境のリソースが同一のtfstateに記録されているため、1つの環境での変更が全環境に影響を及ぼす可能性がある
できればtfstateの分割基準はdevelop,production,manageなどの環境ごとにして
各環境のリソースは全て同一のtfstateに記録されるようにしたいですね。
これができないか調べてみると以下のことがわかりました。
- --path-patternオプションで階層構造を明示的に指定できる
- --filterオプションで指定したタグが付与されているリソースのみをインポートできる
この2つを組み合わせると実現できそうです、やってみましょう。
$ terraformer import aws --resources=alb,ec2_instance,eip,ebs,igw,nat,rds,route_table,sg,subnet,vpc,vpc_peering --regions=ap-northeast-1 --profile=beyond-poc-admin --path-pattern {output}/{provider}/develop/ --filter="Name=tags.Environment;Value=dev"
$ terraformer import aws --resources=alb,ec2_instance,eip,ebs,igw,nat,rds,route_table,sg,subnet,vpc,vpc_peering --regions=ap-northeast-1 --profile=beyond-poc-admin --path-pattern {output}/{provider}/production/ --filter="Name=tags.Environment;Value=prod"
$ terraformer import aws --resources=ec2_instance,eip,ebs,igw,route_table,sg,subnet,vpc,vpc_peering --regions=ap-northeast-1 --profile=beyond-poc-admin --path-pattern {output}/{provider}/manage/ --filter="Name=tags.Environment;Value=mng"
インポートが終わった後のディレクトリ構造は以下です。
ディレクトリ構造
$ tree
.
└── aws
├── develop
│ ├── db_instance.tf
│ ├── db_parameter_group.tf
│ ├── db_subnet_group.tf
│ ├── eip.tf
│ ├── instance.tf
│ ├── internet_gateway.tf
│ ├── lb.tf
│ ├── lb_target_group.tf
│ ├── nat_gateway.tf
│ ├── outputs.tf
│ ├── provider.tf
│ ├── route_table.tf
│ ├── security_group.tf
│ ├── subnet.tf
│ ├── terraform.tfstate
│ ├── variables.tf
│ └── vpc.tf
├── manage
│ ├── instance.tf
│ ├── internet_gateway.tf
│ ├── outputs.tf
│ ├── provider.tf
│ ├── route_table.tf
│ ├── security_group.tf
│ ├── subnet.tf
│ ├── terraform.tfstate
│ ├── variables.tf
│ ├── vpc.tf
│ └── vpc_peering_connection.tf
└── production
├── db_instance.tf
├── db_parameter_group.tf
├── db_subnet_group.tf
├── eip.tf
├── instance.tf
├── internet_gateway.tf
├── lb.tf
├── lb_target_group.tf
├── nat_gateway.tf
├── outputs.tf
├── provider.tf
├── route_table.tf
├── security_group.tf
├── subnet.tf
├── terraform.tfstate
├── variables.tf
└── vpc.tf
4 directories, 45 files
リソースが環境ごとに分割されていますね。
試しにdevelop以下のvpc.tfを見てみると対応する環境のVPCのみがインポートされています。
develop/vpc.tf
resource "aws_vpc" "tfer--vpc-002D-0eea2bc99da0550a6" {
assign_generated_ipv6_cidr_block = "false"
cidr_block = "10.1.0.0/16"
enable_classiclink = "false"
enable_classiclink_dns_support = "false"
enable_dns_hostnames = "true"
enable_dns_support = "true"
instance_tenancy = "default"
tags = {
Environment = "dev"
Name = "vpc-terraformer-dev"
}
}
tfstateに関しては各環境ごとのリソースが1ファイルに全て記録されているのでこちらも問題なさそうです。
中身は長いので省きます。
懸念点
リソースのID値がハードコーディングされてしまっている箇所がある
以下のaws_security_groupのvpc_idのように
リソースのID値がハードコーディングされてしまっている箇所が複数存在します。
resource "aws_security_group" "tfer--alb-002D-dev-002D-sg_sg-002D-00d3679a2f3309565" {
description = "for ALB"
egress {
cidr_blocks = ["0.0.0.0/0"]
description = "Outbound ALL"
from_port = "0"
protocol = "-1"
self = "false"
to_port = "0"
}
ingress {
cidr_blocks = ["0.0.0.0/0"]
description = "allow_http_for_alb"
from_port = "80"
protocol = "tcp"
self = "false"
to_port = "80"
}
name = "alb-dev-sg"
tags = {
Environment = "dev"
Name = "alb-dev-sg"
}
vpc_id = "vpc-0eea2bc99da0550a6"
}
新しくHCLを書く場合は「vpc_id = aws_vpc.vpc.id」のように動的参照するのですが
インポート時にそこまで補完するのはまだまだ難しいようです。
この部分はtfstateには既にVPCのIDが記録されているのでtfファイルをのみを修正すれば良いです。
terraform_remote_stateの記述が0.12に対応していない
以下のaws_subnetのvpc_idのようにHCLの記述がTerraformの0.11系のものになっている箇所があります。
resource "aws_subnet" "tfer--subnet-002D-02f90c599d4c887d3" {
assign_ipv6_address_on_creation = "false"
cidr_block = "10.1.2.0/24"
map_public_ip_on_launch = "true"
tags = {
Environment = "dev"
Name = "subnet-terraformer-dev-public-1c"
}
vpc_id = "${data.terraform_remote_state.local.outputs.aws_vpc_tfer--vpc-002D-0eea2bc99da0550a6_id}"
}
この状態で0.12系でApplyすると実行はできますが警告が発生します。
また、--path-patternオプションで環境ごとにディレクトリを分けるように変更すると
tfstate自体は1ファイルにまとめて出力されますが
tfファイル内でリソースを参照する時は相変わらずterraform_remote_stateで参照されています。
GitHubを見ると以下の記載があるので仕様ですね。
Connect between resources with terraform_remote_state (local and bucket).
上記のvpc_idの場合はaws_vpcとaws_subnetが同じtfstateに記録されているので
単純に「vpc_id = aws_vpc.tfer--vpc-002D-0eea2bc99da0550a6.id」のみで参照可能です。
この部分も自力で修正する必要がありそうです。
まとめ
いかがでしたでしょうか。
terraformerを使えば既存インフラのインポートも捗りそうですね。
いくつか懸念点はありますが、さほど修正は難しくないので許容できる範囲ではないでしょうか。
私も以前からterraform import辛いなと思っていたので
それをいい具合に解決してくれるツールを作成した「Waze SRE」の方々は素直に尊敬します。
皆さんも是非利用してみてください。
1