terraform planで消耗しないためにTFLintを作った話

f:id:watass:20161127204632p:plain

以前から、インフラそのものの構成管理にはTerraformを利用しています。主にAWSのリソース管理に使っていて、Terraform自体はaws-sdkのラッパーみたいな感じなのですが、宣言的に構築できるので、複製が容易であったり、変更に必要な操作(modify系のAPI実行)を意識せずに、こうなってほしい、を記述することで、現状から必要な操作を計算してAPIを叩いてくれたりというメリットがあります。
で、実際に変更したい状態にするために、Terraformが何を行なうか、については「terraform plan」というコマンドで確認できるようになっています。まぁdry-runみたいなものなのですが、あくまでもTerraformの実行計画上のdry-runであって、aws-sdkのdry-runではないというのが結構困りポイントです。
具体的には、Terraform上で別のリソースの値を参照する場合とかに、存在しないリソースとか指定すると、当然エラーになるのですが、例えば、AWSで利用できないようなインスタンスタイプとかを指定していても、terraform planでは教えてくれないわけです。これが原因でplanしてエラーじゃなかったからapplyしたら、すごいくだらないエラーで実行に失敗した、みたいなことになると結構辛い気持ちになります。そのため、terraform planしてから各値をよーく確かめて...みたいなことをしていました。

さすがにこれはどうなのよと思ったのと、最近仕事柄、Lintツールと戯れることが多かったので、これこそLintで解決するべき問題なのではと思いたち、TerraformのLinterとしてTFLintというツールを作りました。

使い方

大体READMEにも書いてますが、拙い英語で誰も理解できないと困るので簡単に日本語でも書いておきます。
インストールはビルド済みのバイナリをGitHubからダウンロードしてきて、Terraform同様にパスの通ったディレクトリに置くだけです。

$ wget https://github.com/wata727/tflint/releases/download/v0.1.0/tflint_darwin_amd64.zip
$ unzip tflint_darwin_amd64.zip
Archive:  tflint_darwin_amd64.zip
  inflating: tflint
$ mkdir -p /usr/local/tflint/bin
$ export PATH=/usr/local/tflint/bin:$PATH
$ install tflint /usr/local/tflint/bin
$ tflint -v

あとはTerraformを実行するディレクトリ下でコマンドを実行すれば結果が出ます。

$ tflint
template.tf
        NOTICE:1 "iam_instance_profile" is not specified. If you want to change it, you need to recreate it
        ERROR:3 "t2.2xlarge" is invalid instance type.

Result: 2 issues  (1 errors , 0 warnings , 1 notices)

上の例だと、EC2のインスタンスにIAMロールが指定されていないことと、「t2.2xlarge」なんてインスタンスタイプは存在しないよ、ということを指摘しています。他の細かいオプションとか諸々はREADMEを読んで下さい...

なんでLinterにしたのか

そもそも、こんな話はTerraform本家でやるべきでしょというのは、その通りなんですが、残念ながら現状のTerraformの仕組みを考えると、都度aws-sdkでdry-runするのは難しいのかなと思います。Terraformは別リソースの値を参照するような書き方ができるのですが、そのリソースがまだ存在していないと、実行時まで値が確定しないためですね。

あと、個人的な理由ですが、HCLを使った何かを作りたいと思っていたのも背景にあります。以前からJSONとかYAMLみたいな設定用言語には、いまいち不満があって、どちらも人間にとって優しくないなーと感じていました。HJSONなんてものもありましたし、この辺はみんなそう思っているんじゃないでしょうか。
対して、HCLはコメントや変数の仕組みがあって、YAMLのようにネストが深くなっても(比較的)読みやすいです。HCLはTerraformの設定言語という印象が強いですが、実際にはオープンソースとして公開されているので、Terraform以外でも使えるんですよ。
ちなみに、TFLintの設定ファイルもHCLで書くようにしたんですが、これはちょっとやりすぎたかもしれないですね...

AWSのクレデンシャルを読ませると、実際のリソースをdescribeして照らし合わせるような仕組みも作っています。なので、CI上にTerraformを乗っけている人は、その過程にTFLintを挟んでもらえると、ちょっとは消耗する要素を減らせるはずです。最近になってインフラ界隈にもInfrastructure as Codeをきっかけに、いろいろなアプリケーション開発における良い仕組みが流入してきているのを感じているので、テストや自動レビューができるようになっていく未来もそう遠くない...のではないでしょうか。

今後について

まだ全然ルールが少なくて、たぶんろくに役に立たないと思うんですが、飽きなければ徐々にルールを増やしていく予定です。AWS以外のリソースも今の仕組みで検証することはできるのですが、今のところ対応は考えていないです...
Terraformは初めて使うと、結構ハマるポイントが多いにも関わらず、ノウハウがあまり共有されにくくて、GitHubのIssueとか覗きに行くことが多いので、その辺のベストプラクティスとかもTFLintにまとめて、みんなで共有して自動検出できるような仕組みが作れたら素敵だなと思っています。
もしもTerraformで消耗しているポイントがあったら、ガンガン要望出してくれ!