Edited at
HashiCorpDay 13

TerraformとAnsibleを連携させたい

More than 3 years have passed since last update.


TerraformとAnsibleを連携させたい

TerraformのProvisonerにAnsibleがない...でもTerraformとAnsibleを連携させたい...

その戦いの記録です。


前提


AWS構成

ec2-and-rds.png


ディレクトリ構成

server/

├ ansible
│   └── roles
└ terraform
├── production
└── staging


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


やりたいこと


  • a. Terraformで作成したEC2インスタンスに対して(登録したKey Pairで) ansible-playbook を実行したい

  • b. mysql_dbモジュールを利用して、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

$ 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を利用して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の例

---

- 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で情報を取得してみます。

---

- 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が欲しいです。