terraformのnull_resourceが便利だよ！という話

# はじめに
この記事は、[dwango advent calender](http://qiita.com/advent-calendar/2016/dwango)の12日目の記事です！
ギリギリ間に合わない感じになってしまいましたが、気にせずやっていきます(☝◞‸◟)☝

# terraformとは？
もう説明不要ですよね！
awsやGCPやopenstackなどなどの環境をコード化できる構成管理ツールです。

# terraformのnull_resourceとは？
その名の通りですが、何もしないresourceです。
他のresourceができあがったことをトリガーとして、プロビジョニングを行うことができます。
ドキュメントは[こちら](https://www.terraform.io/docs/provisioners/null_resource.html)です。


# 何が便利なのか？
例えばの話ですが、EC2インスタンスが起動するまで、EC2のpublic_ipやprivate_ipはわからなかったりします。
そのため、IPがわかっていることを前提とした、クラスタ系の操作などはインスタンスが作成された後でしか実行することができません。
ですが、null_resourceを利用すると、別のresource(EC2インスタンスやRDSインスタンスなど)をトリガーとして、プロビジョニングを行えます。
これは、terraformをapplyするだけで、プロビジョニング済で利用できる、ということになります。

# ということで、自分の担当プロダクトで行っている実例
今回は、自分が担当しているプロダクトで実際に利用している例をもとに説明していこうと思います。
コードは[こちら](https://github.com/eigo-s/terraform-example/tree/master/null_resource_rds)にあげました。

## どうしてnull_resourceが必要だったのか？
オンプレ→AWSという移行案件だったのですが、フロントで利用しているmysqlと、管理ツールで利用しているmysqlと2系統ありました。
フロントの方で利用しているmysqlは、アクセスもデータ量も少なく、管理ツール用とあわせた方がmysqlの管理コストも低くなり良いだろう、と思った次第でした。
元々は、chefでmysqlをインストールしdatabaseを作成していましたが、RDSになったことで、chefでdatabaseの作成ができなくなったため、利用することにしました。
※[mysql provider](https://www.terraform.io/docs/providers/mysql/index.html)も当時存在していたのですが、うまいこと動作せず、リリースまで時間もなかったためこのようにしました(◞‸◟)

## 実際の肝となるコード部分
コードとしては下記のような感じです。

```provision.tf
resource "null_resource" "create-second-db" {
  triggers {
    endpoint = "${aws_db_instance.rds-master.endpoint}"
  }

  provisioner "remote-exec" {
    connection {
      type        = "ssh"
      host        = "${aws_instance.bastion.public_dns}"
      user        = "${var.bastion_user}"
      private_key = "${file("${path.module}/bastion.pem")}"
    }

    inline = [
      "sudo yum -y install mysql",
      "mysql -u${var.rds_username} -p${var.rds_password} -h${aws_db_instance.rds-master.address} -e\"create database ${var.rds_second_db};\"",
    ]
  }
}
```

1. null_resourceを利用し、RDSのmasterが作成されたことをトリガーとする
2. 踏み台サーバーにsshで接続し、mysql cliをインストールする
3. terraformで利用したRDSのuser/password変数を元に、mysql cliでRDSのmasterに接続し、2つ目のdatabaseを作成する

という流れとなります。

実行結果としては下記のような感じです。

```console
null_resource.create-second-db: Creating...
  triggers.%:        "" => "1"
  triggers.endpoint: "" => "rds-master.cffuvhvco6ag.ap-northeast-1.rds.amazonaws.com:3306"
null_resource.create-second-db: Provisioning with 'remote-exec'...
null_resource.create-second-db (remote-exec): Connecting to remote host via SSH...
null_resource.create-second-db (remote-exec):   Host: ec2-52-68-75-114.ap-northeast-1.compute.amazonaws.com
null_resource.create-second-db (remote-exec):   User: ec2-user
null_resource.create-second-db (remote-exec):   Password: false
null_resource.create-second-db (remote-exec):   Private key: true
null_resource.create-second-db (remote-exec):   SSH Agent: true
null_resource.create-second-db (remote-exec): Connected!
null_resource.create-second-db (remote-exec): Loaded plugins: priorities, update-motd,
null_resource.create-second-db (remote-exec):               : upgrade-helper
null_resource.create-second-db (remote-exec): amzn-main/latest | 2.1 kB     00:00
null_resource.create-second-db (remote-exec): amzn-updates/lat | 2.3 kB     00:00
null_resource.create-second-db (remote-exec): Resolving Dependencies
null_resource.create-second-db (remote-exec): --> Running transaction check
null_resource.create-second-db (remote-exec): ---> Package mysql.noarch 0:5.5-1.6.amzn1 will be installed
null_resource.create-second-db (remote-exec): --> Processing Dependency: mysql55 >= 5.5 for package: mysql-5.5-1.6.amzn1.noarch
null_resource.create-second-db (remote-exec): --> Running transaction check
null_resource.create-second-db (remote-exec): ---> Package mysql55.x86_64 0:5.5.52-1.13.amzn1 will be installed
null_resource.create-second-db (remote-exec): --> Processing Dependency: real-mysql55-libs(x86-64) = 5.5.52-1.13.amzn1 for package: mysql55-5.5.52-1.13.amzn1.x86_64
null_resource.create-second-db (remote-exec): --> Processing Dependency: mysql-config for package: mysql55-5.5.52-1.13.amzn1.x86_64
null_resource.create-second-db (remote-exec): --> Running transaction check
null_resource.create-second-db (remote-exec): ---> Package mysql-config.x86_64 0:5.5.52-1.13.amzn1 will be installed
null_resource.create-second-db (remote-exec): ---> Package mysql55-libs.x86_64 0:5.5.52-1.13.amzn1 will be installed
null_resource.create-second-db (remote-exec): --> Finished Dependency Resolution

null_resource.create-second-db (remote-exec): Dependencies Resolved

null_resource.create-second-db (remote-exec): ========================================
null_resource.create-second-db (remote-exec):  Package  Arch   Version
null_resource.create-second-db (remote-exec):                      Repository    Size
null_resource.create-second-db (remote-exec): ========================================
null_resource.create-second-db (remote-exec): Installing:
null_resource.create-second-db (remote-exec):  mysql    noarch 5.5-1.6.amzn1
null_resource.create-second-db (remote-exec):                      amzn-main    2.7 k
null_resource.create-second-db (remote-exec): Installing for dependencies:
null_resource.create-second-db (remote-exec):  mysql-config
null_resource.create-second-db (remote-exec):           x86_64 5.5.52-1.13.amzn1
null_resource.create-second-db (remote-exec):                      amzn-updates  49 k
null_resource.create-second-db (remote-exec):  mysql55  x86_64 5.5.52-1.13.amzn1
null_resource.create-second-db (remote-exec):                      amzn-updates 7.5 M
null_resource.create-second-db (remote-exec):  mysql55-libs
null_resource.create-second-db (remote-exec):           x86_64 5.5.52-1.13.amzn1
null_resource.create-second-db (remote-exec):                      amzn-updates 815 k

null_resource.create-second-db (remote-exec): Transaction Summary
null_resource.create-second-db (remote-exec): ========================================
null_resource.create-second-db (remote-exec): Install  1 Package (+3 Dependent packages)

null_resource.create-second-db (remote-exec): Total download size: 8.4 M
null_resource.create-second-db (remote-exec): Installed size: 31 M
null_resource.create-second-db (remote-exec): Downloading packages:
null_resource.create-second-db (remote-exec): (1/4): mysql-5.5 | 2.7 kB     00:00
null_resource.create-second-db (remote-exec): (2/4): mysql-con |  49 kB     00:00
null_resource.create-second-db (remote-exec): (3/4): mysql55-5 | 7.5 MB     00:00
null_resource.create-second-db (remote-exec): (4/4): mysql55-l | 815 kB     00:00
null_resource.create-second-db (remote-exec): ----------------------------------------
null_resource.create-second-db (remote-exec): Total       36 MB/s | 8.4 MB  00:00
null_resource.create-second-db (remote-exec): Running transaction check
null_resource.create-second-db (remote-exec): Running transaction test
null_resource.create-second-db (remote-exec): Transaction test succeeded
null_resource.create-second-db (remote-exec): Running transaction
null_resource.create-second-db (remote-exec):   Installing : mysql-co [         ] 1/4
null_resource.create-second-db (remote-exec):   Installing : mysql-co [#######  ] 1/4
null_resource.create-second-db (remote-exec):   Installing : mysql-config-5.5.5   1/4
null_resource.create-second-db (remote-exec):   Installing : mysql55- [         ] 2/4
null_resource.create-second-db (remote-exec):   Installing : mysql55- [#        ] 2/4
null_resource.create-second-db (remote-exec):   Installing : mysql55- [##       ] 2/4
null_resource.create-second-db (remote-exec):   Installing : mysql55- [###      ] 2/4
null_resource.create-second-db (remote-exec):   Installing : mysql55- [####     ] 2/4
null_resource.create-second-db (remote-exec):   Installing : mysql55- [#####    ] 2/4
null_resource.create-second-db (remote-exec):   Installing : mysql55- [######   ] 2/4
null_resource.create-second-db (remote-exec):   Installing : mysql55- [#######  ] 2/4
null_resource.create-second-db (remote-exec):   Installing : mysql55- [######## ] 2/4
null_resource.create-second-db (remote-exec):   Installing : mysql55-libs-5.5.5   2/4
null_resource.create-second-db (remote-exec):   Installing : mysql55- [         ] 3/4
null_resource.create-second-db (remote-exec):   Installing : mysql55- [#        ] 3/4
null_resource.create-second-db (remote-exec):   Installing : mysql55- [##       ] 3/4
null_resource.create-second-db (remote-exec):   Installing : mysql55- [###      ] 3/4
null_resource.create-second-db (remote-exec):   Installing : mysql55- [####     ] 3/4
null_resource.create-second-db (remote-exec):   Installing : mysql55- [#####    ] 3/4
null_resource.create-second-db (remote-exec):   Installing : mysql55- [######   ] 3/4
null_resource.create-second-db (remote-exec):   Installing : mysql55- [#######  ] 3/4
null_resource.create-second-db (remote-exec):   Installing : mysql55- [######## ] 3/4
null_resource.create-second-db (remote-exec):   Installing : mysql55-5.5.52-1.1   3/4
null_resource.create-second-db (remote-exec):   Installing : mysql-5. [         ] 4/4
null_resource.create-second-db (remote-exec):   Installing : mysql-5.5-1.6.amzn   4/4
null_resource.create-second-db (remote-exec):   Verifying  : mysql-5.5-1.6.amzn   1/4
null_resource.create-second-db (remote-exec):   Verifying  : mysql55-libs-5.5.5   2/4
null_resource.create-second-db (remote-exec):   Verifying  : mysql-config-5.5.5   3/4
null_resource.create-second-db (remote-exec):   Verifying  : mysql55-5.5.52-1.1   4/4

null_resource.create-second-db (remote-exec): Installed:
null_resource.create-second-db (remote-exec):   mysql.noarch 0:5.5-1.6.amzn1

null_resource.create-second-db (remote-exec): Dependency Installed:
null_resource.create-second-db (remote-exec):   mysql-config.x86_64 0:5.5.52-1.13.amzn1
null_resource.create-second-db (remote-exec):   mysql55.x86_64 0:5.5.52-1.13.amzn1
null_resource.create-second-db (remote-exec):   mysql55-libs.x86_64 0:5.5.52-1.13.amzn1

null_resource.create-second-db (remote-exec): Complete!
null_resource.create-second-db: Creation complete
```


実際は、他にもたくさんコンポーネントがあったりしますが、terraformのapplyを行った後でアプリケーションが乗る以前に状態を全て作ることができるのは、terraformの便利さを感じることができる瞬間でした。
そんなこんなで、個人的にはnull_resourceをもっと有効活用し、手動で何かを実施する、みたいなことをガンガン減らし、terraform applyだけで0からそのシステムの環境ができる状態を作っていきたいな、と思うのでした。


# 余談1
terraform 0.7.8以降では、aws_instanse + aws_eipの組み合わせの場合に、applyの度に、インスタンスが再作成されるというバグ？が存在します。
[issue](https://github.com/hashicorp/terraform/issues/9811)もあがっており、修正されてる[pull request](https://github.com/hashicorp/terraform/pull/10078)も出ていますが、まだmergeされていないため未リリースとなるので、気をつけて下さい。
自分の場合は、元々0.7.8で利用できるようになった、pagerduty providerやAWS WAF resourceを利用したかったため、0.7.8の検証を行っていたところ、ハマってしまった次第でした(◞‸◟)
先日のaws re:invent2016でhashicorpブースにお邪魔した際に、terraformのコミッターの方がいらしていて、「このissueで困ってるよ！」と伝えることができたので、早いところ治らないかなー、と心待ちにしていたりします。

# 余談2
terraformの[CHANGELOG.md](https://github.com/hashicorp/terraform/blob/master/CHANGELOG.md)をだいたい一日一回見る、みたいなことを日課にしているのですが、次メジャーバージョンアップ(0.8)で取り込まれるproviderで興味深い物がありました。
それは、 external providerというものです。
詳しくは、[こちらのpull request](https://github.com/hashicorp/terraform/pull/8768)を見ていただくと良いのですが、簡単にいうと、「実行したスクリプトのプログラムの実行結果を渡すことができる」というもののようです。
pull requestのように、実行結果の値を利用して、何かを行う、みたいなことの自由度があがりそうですね！
追記：クラメソさんの[記事](http://dev.classmethod.jp/devops/terraform-v-0-8-0-rc3-introduces-external-functionality/)でも紹介されてましたのでこちらもどうぞ！