既存の環境からterraformのファイルを出力するterraformerを使ってみた

2019.07.02

既存のAWS環境をエクスポートしてTerraformにまとめたい。
そんなことありませんか。

既存のAWS環境を元にtfファイルとtfstateファイルを出力してくれるterraformerというツールが最近出ました。
GoogleCloudPlatformが中心となって開発しており、GCPのみならず、既存のAWS、GitHub、Datadog、GitHub、Kubernetesの環境にも対応しています。

インストール方法

macOSでは、homebrew経由でのインストールが既に可能です。
また、goで書かれているので直接ビルドすることも可能です。 下記にビルドする方法を記載しておきます。

$ git clone https://github.com/GoogleCloudPlatform/terraformer.git
$ GO111MODULE=on go mod vendor
$ go build -v

使用方法

AWSではこのように使用します。
使用可能なサービス一覧はREADMEに記載があります。
VPCやEC2、ALB、RDSなどといった主要なサービスは既にサポートされています。

$ echo 'provider "aws" {}' > init.tf
$ terraform init
$ terraformer import aws --resources=vpc,subnet --connect=true --regions=eu-west-1
$ terraformer import aws --resources=vpc,subnet --filter=aws_vpc=vpc_id1:vpc_id2:vpc_id3 --regions=eu-west-1

# https://github.com/GoogleCloudPlatform/terraformer#use-with-aws

GCPではこのように使用します。
使用可能なサービス一覧はREADMEに記載があります。

$ echo 'provider "gcp" {}' > init.tf
$ terraform init
$ terraformer import google --resources=gcs,forwardingRules,httpHealthChecks --connect=true --zone=europe-west1-a --projects=aaa,fff
$ terraformer import google --resources=gcs,forwardingRules,httpHealthChecks --filter=google_compute_firewall=rule1:rule2:rule3 --zone=europe-west1-a --projects=aaa,fff

# https://github.com/GoogleCloudPlatform/terraformer#use-with-gcp

AWS環境での検証

v0.7.5段階での内容ですが注意点があります。
現在terraformerはAWS CLIのプロファイルに対応していません
なので認証情報を下記のようにして渡す必要があります。

  • export AWS_ACCESS_KEY_ID=xxxexport AWS_SECRET_ACCESS_KEY=xxxといった形で環境変数を通じて渡す
  • assume-roleを使用して下記のように実行する
    $ assume-role your_profile terraformer import aws --resources=vpc,subnet --connect=true --regions=eu-west-1
      

実行環境

  • terraform : v0.12.2
  • terraformer : v0.7.5
  • OS : macOS

やってみた

バージニア北部にEC2とALBを構築してその内容をエクスポートしてみました。
terraformerの使用前にterraformにproviderの情報を渡す必要があるのでinit.tfというファイルを作成して初期化します。

$ echo 'provider "aws" {}' > init.tf
$ terraform init

ここまでできたらterraformerを実行します。

$ export AWS_ACCESS_KEY_ID=xxx
$ export AWS_SECRET_ACCESS_KEY=xxx
$ terraformer import aws \
  --resources=vpc,subnet,igw,sg,alb,ec2_instance \
  --filter=aws_vpc=id1:vpc-xxxxxxxxxxxxxxxxx \
  --regions=us-east-1

2019/07/02 10:31:47 aws importing region us-east-1
2019/07/02 10:31:47 aws importing... vpc
2019/07/02 10:31:57 Refreshing state... aws_vpc.vpc-xxxxxxxxxxxxxxxxx
2019/07/02 10:32:04 aws importing... subnet
2019/07/02 10:32:11 Refreshing state... aws_subnet.subnet-xxxxxxxx
2019/07/02 10:32:11 Refreshing state... aws_subnet.subnet-xxxxxxxxxxxxxxxxx
2019/07/02 10:32:11 Refreshing state... aws_subnet.subnet-xxxxxxxxxxxxxxxxx
2019/07/02 10:32:11 Refreshing state... aws_subnet.subnet-xxxxxxxx
2019/07/02 10:32:11 Refreshing state... aws_subnet.subnet-xxxxxxxx
2019/07/02 10:32:11 Refreshing state... aws_subnet.subnet-xxxxxxxx
2019/07/02 10:32:11 Refreshing state... aws_subnet.subnet-xxxxxxxxxxxxxxxxx
2019/07/02 10:32:11 Refreshing state... aws_subnet.subnet-xxxxxxxxxxxxxxxxx
2019/07/02 10:32:11 Refreshing state... aws_subnet.subnet-xxxxxxxx
2019/07/02 10:32:11 Refreshing state... aws_subnet.subnet-xxxxxxxx
...
...

terraformer aws importを実行するとターミナルへの出力の後にgeneratedというディレクトリが生成されます。
その中にterraform関連のファイルが出来上がっており、terraform用のファイルが入っています。
terraform.tfstateや、*.tfといったファイルが確認できます。
ディレクトリ構造はこのような形になっています。

$ tree generated/ | head -n 20
generated/
└── aws
    ├── alb
    │   └── us-east-1
    │       ├── lb.tf
    │       ├── lb_listener.tf
    │       ├── lb_listener_rule.tf
    │       ├── lb_target_group.tf
    │       ├── lb_target_group_attachment.tf
    │       ├── outputs.tf
    │       ├── provider.tf
    │       ├── terraform.tfstate
    │       └── variables.tf
    ├── ec2_instance
    │   └── us-east-1
    │       ├── instance.tf
    │       ├── outputs.tf
    │       ├── provider.tf
    │       └── terraform.tfstate
    ├── igw

tfファイルの中身はこのようになっています。
terraformerでエクスポートしたVPCに紐づくsubnetに関してはハードコーディングされずに出力されています。
逆にエクスポートしなかったデフォルトのVPCに紐づくsubnetに関してはハードコーディングされてしまっています。

generated/aws/subnet/us-east-1/subnet.tf

resource "aws_subnet" "subnet-xxxxxxxxxxxxxxxxx" {
  assign_ipv6_address_on_creation = false
  cidr_block                      = "192.168.1.0/24"
  map_public_ip_on_launch         = false

  tags {
    Name = "terraformer-test-pub-2"
  }

  vpc_id = "${data.terraform_remote_state.vpc.aws_vpc_vpc-xxxxxxxxxxxxxxxxx_id}"
}

resource "aws_subnet" "subnet-xxxxxxxxxxxxxxxxx" {
  assign_ipv6_address_on_creation = false
  cidr_block                      = "192.168.3.0/24"
  map_public_ip_on_launch         = false

  tags {
    Name = "terraformer-test-private-2"
  }

  vpc_id = "${data.terraform_remote_state.vpc.aws_vpc_vpc-xxxxxxxxxxxxxxxxx_id}"
}

resource "aws_subnet" "subnet-xxxxxxxxxxxxxxxxx" {
  assign_ipv6_address_on_creation = false
  cidr_block                      = "192.168.0.0/24"
  map_public_ip_on_launch         = false

  tags {
    Name = "terraformer-test-pub-1"
  }

  vpc_id = "${data.terraform_remote_state.vpc.aws_vpc_vpc-xxxxxxxxxxxxxxxxx_id}"
}

resource "aws_subnet" "subnet-xxxxxxxxxxxxxxxxx" {
  assign_ipv6_address_on_creation = false
  cidr_block                      = "192.168.2.0/24"
  map_public_ip_on_launch         = false

  tags {
    Name = "terraformer-test-private-1"
  }

  vpc_id = "${data.terraform_remote_state.vpc.aws_vpc_vpc-xxxxxxxxxxxxxxxxx_id}"
}

resource "aws_subnet" "subnet-xxxxxxxx" {
  assign_ipv6_address_on_creation = false
  cidr_block                      = "172.31.16.0/20"
  map_public_ip_on_launch         = true
  tags                            {}
  vpc_id                          = "vpc-xxxxxxxx"
}

resource "aws_subnet" "subnet-xxxxxxxx" {
  assign_ipv6_address_on_creation = false
  cidr_block                      = "172.31.48.0/20"
  map_public_ip_on_launch         = true
  tags                            {}
  vpc_id                          = "vpc-xxxxxxxx"
}

~~~

また、EC2に関してはVPC、SGなどがハードコーディングされてしまっています。

generated/aws/ec2_instance/us-east-1/instance.tf

resource "aws_instance" "i-xxxxxxxxxxxxxxxxx_erraformer-test-1" {
  ami                         = "ami-0b898040803850657"
  associate_public_ip_address = false
  availability_zone           = "us-east-1a"
  cpu_core_count              = "1"
  cpu_threads_per_core        = "1"

  credit_specification {
    cpu_credits = "standard"
  }

  disable_api_termination = false
  ebs_optimized           = false
  get_password_data       = false
  iam_instance_profile    = "SSM_for_EC2"
  instance_type           = "t2.micro"
  ipv6_address_count      = "0"
  key_name                = "terraformer-test"
  monitoring              = false
  private_ip              = "192.168.2.230"

  root_block_device {
    delete_on_termination = true
    iops                  = "100"
    volume_size           = "8"
    volume_type           = "gp2"
  }

  source_dest_check = true
  subnet_id         = "subnet-xxxxxxxxxxxxxxxxx"

  tags {
    Name = "erraformer-test-1"
  }

  tenancy = "default"

  volume_tags {
    name = "erraformer-test-1"
  }

  vpc_security_group_ids = ["sg-xxxxxxxxxxxxxxxxx"]
}

さいごに

既存の環境をエクスポートできるのでIaCへの移行が少し容易になるかもしれません。
まだベータ版ということもあり機能面ではまだまだ改良されていきそうなので今後が楽しみですね。