TerraformとAnsibleを連携させたい

  • 15
    Like
  • 1
    Comment
More than 1 year has 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が欲しいです。