Terraforming: 既存のインフラリソースを Terraform コードに落としこむ
インフラ界隈のみなさま、 Terraform 使ってますか? 導入したいけど、既存のリソースの管理をどうするか悩んでないですか?
少し前からこの問題を解決する Terraforming というツールを作っていて、今回 v0.1.0 を公開したので紹介します :tada:
https://github.com/dtan4/terraforming
https://rubygems.org/gems/terraforming
これはなに
Vagrant で有名な HashiCorp が開発している Terraform というインフラをコードで管理するためのツールがあります。 Infrastructure as Code というやつです。 Terraforming は、AWS の API を叩いていま動いている既存の AWS リソースから Terraform のコードを生成するツールです。
インストール
RubyGems として公開されているので、
$ gem install terraforming
Docker Image も用意しているので、そちらを使いたい方は
$ docker pull quay.io/dtan4/terraforming:latest
使い方
予め AWS のクレデンシャルを環境変数に入れておきます。 Mac ユーザなら envchain おすすめです。
export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXXXXXX export AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx export AWS_DEFAULT_REGION=xx-yyyy-0
リソース名を指定するだけだと、tf 形式で出力されます。 S3 bucket の場合だと:
$ terraforming s3
resource "aws_s3_bucket" "hoge" { bucket = "hoge" acl = "private" } resource "aws_s3_bucket" "fuga" { bucket = "fuga" acl = "private" }
これを s3.tf
とかに書き出せばよいです。
--tfstate
オプションをつけると、tfstate 形式で出力されます。
$ terraforming s3 --tfstate
{ "version": 1, "serial": 1, "modules": { "path": [ "root" ], "outputs": { }, "resources": { "aws_s3_bucket.hoge": { "type": "aws_s3_bucket", "primary": { "id": "hoge", "attributes": { "acl": "private", "bucket": "hoge", "id": "hoge" } } }, "aws_s3_bucket.fuga": { "type": "aws_s3_bucket", "primary": { "id": "fuga", "attributes": { "acl": "private", "bucket": "fuga", "id": "fuga" } } } } } }
また、tfstate は既存の terraform.tfstate
にマージした形で出力することもできます。ちゃんと serial
(terraform.tfstate
のバージョン番号) もインクリメントされます*1。
$ terraforming s3 --tfstate --merge=/path/to/tfstate
{ "version": 1, "serial": 89, "remote": { "type": "s3", "config": { "bucket": "terraforming-tfstate", "key": "tf" } }, "modules": { "path": [ "root" ], "outputs": { }, "resources": { "aws_iam_user.dtan4": { "type": "aws_iam_user", "primary": { "id": "dtan4", "attributes": { "arn": "arn:aws:iam::012345678901:user/dtan4", "id": "dtan4", "name": "dtan4", "path": "/", "unique_id": "ABCDEFGHIJKLMN1234567" } } }, "aws_s3_bucket.hoge": { "type": "aws_s3_bucket", "primary": { "id": "hoge", "attributes": { "acl": "private", "bucket": "hoge", "id": "hoge" } } }, "aws_s3_bucket.fuga": { "type": "aws_s3_bucket", "primary": { "id": "fuga", "attributes": { "acl": "private", "bucket": "fuga", "id": "fuga" } } } } } }
これを terraform.tfstate
に上書きすればよいです。
最後に terraform plan
を実行して、差分が出ないことを確認して下さい。
$ terraform plan No changes. Infrastructure is up-to-date. This means that Terraform could not detect any differences between your configuration and the real physical resources that exist. As a result, Terraform doesn't need to do anything.
対応している AWS リソース
v0.1.0 時点では以下のリソースに対応しています。
- Database Parameter Group
- Database Security Group
- Database Subnet Group
- EC2
- ELB
- IAM Group
- IAM Group Policy
- IAM Instance Profile
- IAM Policy
- IAM Role
- IAM Role Policy
- IAM User
- IAM User Policy
- Network ACL
- Route53 Record
- Route53 Hosted Zone
- RDS
- S3
- Security Group
- Subnet
- VPC
もちろん鋭意追加予定です。
兄弟分
DNSimple に対応した Terraforming::DNSimple があります*2。 一応プロバイダが違うのでツールを分割したけど、いつかは Terraforming に統合するかもしれません*3。
ここから余談
作ったきっかけ
インターン先*4でインフラをコードで管理しようという機運が盛り上がり、Terraform を導入することになりました。
公開されている中だと、KAIZEN Platform の Terraform 導入事例や CyberAgent の Terraform 導入事例があります。 どちらもインフラ刷新や新規構築のタイミングで Terraform を導入しています。 つまり一からコードを書いてそれを元に Terraform でインフラを構築するということで、 Terraform のユースケースには合っています。
しかし、自分たちは別に刷新のタイミングなんかではなかったので、今動いている既存のインフラをコードに起こす必要がありました。 そこで既存のインフラを Terraform で管理する術を調べていたところ、人間が手作業でやるのは相当厳しいとわかったので生成ツールを作ろうと思い立ったのでした。
Terraform がどうやってインフラを管理しているのか
Terraform は、状態管理用の terraform.tfstate
というファイルを用いて自分が管理しているインフラを把握しています。
terraform.tfstate
の実体は JSON で、Terraform が管理しているインフラの状態が記述されています。
{ "version": 1, "serial": 88, "remote": { "type": "s3", "config": { "bucket": "terraforming-tfstate", "key": "tf" } }, "modules": [ { "path": [ "root" ], "outputs": {}, "resources": { "aws_iam_user.dtan4": { "type": "aws_iam_user", "primary": { "id": "dtan4", "attributes": { "arn": "arn:aws:iam::012345678901:user/dtan4", "id": "dtan4", "name": "dtan4", "path": "/", "unique_id": "ABCDEFGHIJKLMN1234567" } } } } } ] }
terraform plan
や terraform apply
では、この terraform.tfstate
と開発者が書いた tf ファイルを突き合わせることで、どのリソースが増えた消えた変更されたを計算するのです。
既存のインフラリソースを追加する場合は、tf ファイルにそのリソースを書くだけではダメなのです。
terraform.tfstate
に記述されていないと、Terraform はそのリソースがないものとみなして新しく作成しようとします。
そのため、同時に terraform.tfstate
にも手を加えるする必要があるのです。
ほら、手で JSON を書くのは辛いでしょう? そのための Terraforming なのですよ。
既存のインフラを Terraform で管理する
かなり早い段階でこの問題に触れていたのは下の記事です。
Handling extant resources in Terraform « dan phrawzty's blog
自分もこの記事を見て terraform.tfstate
の更新がいることを知ったのでした。
Terraform のリポジトリでも半年前から issue は上がっているのですが、大して進展していません(どこから見つけたのか、Terraforming が登場していたりします)。
Import resources into Terraform · Issue #581 · hashicorp/terraform
ちなみに cookpad が作っている codenize.tools には、我々が求めていた既存インフラの export 機能がちゃんとあります。 もともとは cookpad のインフラをコード化するために作られたツールなので、この機能は必須だったのでしょう。
開発方法
tf はドキュメント化されているのでともかく、tfstate は出力形式がわからなかったので実際にリソースを Terraform で作ってその結果をもとに実装したりしていました。 結構ドキュメント化されていない暗黙の決まりみたいなのが多く(API レスポンスのどのパラメータを tfstate に反映するのだとか)、Terraform 本体のコードもそれなりに読みました。
おわりに
Terraforming という便利ツールを開発した話と、それにまつわるいろいろを書きました。 既存リソースの export 機能、それなりに需要ありそうですがどうなんでしょう…
Issue, Pull Request は大歓迎です!!!ぜひ使ってみてください :tada: