TerraformとAnsibleを連携させたい

# TerraformとAnsibleを連携させたい

Terraformの[Provisoner](https://www.terraform.io/docs/provisioners/index.html)にAnsibleがない...でもTerraformとAnsibleを連携させたい...

その戦いの記録です。

## 前提

### AWS構成

![ec2-and-rds.png](https://qiita-image-store.s3.amazonaws.com/0/808/cda45dd5-d629-a615-9237-568295a2d056.png)

### ディレクトリ構成

```
server/
├ ansible
│     └── roles
└ terraform
       ├── production
       └── staging
```

- Ansibleのディレクトリ構成はベストプラクティスに準拠

## やりたいこと

- a. Terraformで作成したEC2インスタンスに対して（登録したKey Pairで） `ansible-playbook` を実行したい
- b. [mysql_db](http://docs.ansible.com/ansible/mysql_db_module.html)モジュールを利用して、EC2インスタンス経由でRDSインスタンスにデータベースを作成したい

### Provisioner的な連続実行は諦めました

本当は、VagrantのProvisionerでAnsibleを実行するような流れるような実行をしたかったのですが、諦めました。

理由は以下です。

- local-execがEC2インスタンスがSSH接続できるようになる前に実行されてしまう。タイムアウトでアウト。
- RDSの接続情報も欲しい => `terraform apply` が完全に終わってから実行したい。。

というわけで戦いの記録です。

## "a. Terraformで作成したEC2インスタンスに対して `ansible-playbook` を実行" の戦い

### 作戦1: Route53を利用して、生成したインスタンスに動的にドメインを割り当てる

- Inventoryファイルはドメイン固定で作成
- TerraformでRoute53にドメインのAレコードにEC2に割り当てたEIPを当てる

ドメインがRoute53経由であてるならアリかと。

### 作戦2: AnsibleのDynamic Inventory機能とterraform-inventoryを利用する

https://github.com/adammck/terraform-inventory

```bash
$ cd ansible/
$ TF_STATE=../terraform/staging/terraform.tfstate ansible-playbook -i $(which terraform-inventory) staging.yml --private-key=~/.ssh/b_chaoo_staging_rsa
```

個人的には好みです。

### 作戦3: null_resourceを利用してInventoryファイルを作成

```
# null
resource "null_resource" "for-ansible" {
  provisioner "local-exec" {
    command = "echo ここでがんばってInventoryファイルを作成する"
  }
}
```

がんばる...

## "b. RDSのエンドポイントを取得してmysql_dbモジュールでデータベース作成" の戦い

### 作戦1: null_resourceを利用してRDS情報のファイルを作成してLookupモジュールで読み込む

[null_resource](https://www.terraform.io/docs/provisioners/null_resource.html)を利用してRDSの情報を取得してファイルで取得します。

```
# null
resource "null_resource" "for-ansible" {
  provisioner "local-exec" {
    command = "echo ${var.db_username} > dbuser;echo ${var.db_password} > dbpass;echo ${aws_db_instance.nf-staging-rds.address} > dbhost"
  }
}
```

で、Lookupモジュールで読み込みます。以下はsite.ymlの例

```yaml
---
- name: example.com
  hosts: rds.example.com
  sudo: true
  user: centos
  vars:
    docroot: /var/www/html/
    dbuser: "{{ lookup('file', '../terraform/staging/dbuser') }}"
    dbpass: "{{ lookup('file', '../terraform/staging/dbpass') }}"
    dbhost: "{{ lookup('file', '../terraform/staging/dbhost') }}"
    dbname: my_dbname
  roles:
    - common
    - xxx1
    - xxx2
    - rds
```

CSVファイルを作って`csvfile` Lookupを利用するのもいいかも。
ファイルが3つに分かれるのがなんとも。。。

### 作戦2: jqを利用してterraform.tfstateから必要情報を取得する

terraform.tfstate がJSONなのを利用して[jq](https://stedolan.github.io/jq/)で情報を取得してみます。

```yaml
---
- name: データベースを作成
  mysql_db:
    login_host="{{ lookup('pipe', 'jq \'.modules[].resources[\"aws_db_instance.nf-staging-rds\"].primary.attributes.address\' ../../../../terraform/staging/terraform.tfstate') }}"
    login_user="{{ lookup('pipe', 'jq \'.modules[].resources[\"aws_db_instance.nf-staging-rds\"].primary.attributes.username\' ../../../../terraform/staging/terraform.tfstate') }}"
    login_password="{{ lookup('pipe', 'jq \'.modules[].resources[\"aws_db_instance.nf-staging-rds\"].primary.attributes.password\' ../../../../terraform/staging/terraform.tfstate') }}"
    name=my_dbname
    state=present
    encoding=utf8
```

## まとめ

TerraformとAnsibleをいろんな作戦で連携させてみました。

やっぱりTerraformにAnsible Provisionerが欲しいです。