TerraformInventory で、TerrafromとAnsibleを2コマンドで連携させる【エンジニアブログ】
全国のTerrafrom愛好家の皆様こんにちは。
技術4課の岩本です。
Terraformのプロビジョナーには非常に残念ながらAnsibleがありません。
通常であればTerraformで環境を構築後に、別途Ansible用のInventoryファイルを作成してという流れになりますが、
Inventoryファイルの作成なしにTerraform Inventoryを使って、TerraformとAnsibleを2コマンドで連携させてみました。
Terraform Inventory とは?
AnsibleのDynamicInventoryをTerrafromのStateファイルから生成するプログラムです。
Terraform Inventory
また、AWS以外にも
- DigitalOcean
- CloudStack
- VMware
- OpenStack
- Google Compute Engine
- SoftLayer
- Exoscale
- Scaleway
に対応しています。
インストール
Mac環境では brew からインストール可能です。
brew install terraform-inventory
使い方
AnsibleのInventoryとして、terraform-inventoryを指定することで、
Terraformで作成したEC2インスタンスにAnsibleが実行されます。
ansible-playbook --inventory-file=/path/to/terraform-inventory deploy/playbook.yml
参照されるTerrafromのStateファイルは実行したコマンドを実行したディレクトリ内のものが参照されます。
別ディレクトリ内にあるStateファイルを参照する場合は、環境変数 [ TF_STATE ]で指定をします。
TF_STATE=deploy/terraform.tfstate ansible-playbook --inventory-file=/path/to/terraform-inventory deploy/playbook.yml
他にも、下記コマンドを実行することで、Stateファイル内からホスト/ホストグループを確認することができます。
terraform-inventory --list fixtures/example.tfstate
やってみた
とりあえず作成したTerraformを使ってAWS環境を構築します。
・main.t
provider "aws" {
access_key = "XXXXXXXXXXXXXXXXXX"
secret_key = "XXXXXXXXXXXXXXXXXX"
region = "ap-northeast-1"
}
resource "aws_vpc" "my-vpc" {
cidr_block = "10.1.0.0/16"
instance_tenancy = "default"
enable_dns_support = "true"
enable_dns_hostnames = "false"
tags {
Name = "my-vpc"
}
}
resource "aws_internet_gateway" "my-igw" {
vpc_id = "${aws_vpc.my-vpc.id}"
}
resource "aws_subnet" "public-a" {
vpc_id = "${aws_vpc.my-vpc.id}"
cidr_block = "10.1.1.0/24"
availability_zone = "ap-northeast-1a"
}
resource "aws_route_table" "public-route" {
vpc_id = "${aws_vpc.my-vpc.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.my-igw.id}"
}
}
resource "aws_route_table_association" "puclic-a" {
subnet_id = "${aws_subnet.public-a.id}"
route_table_id = "${aws_route_table.public-route.id}"
}
resource "aws_security_group" "admin" {
name = "admin"
description = "Allow SSH inbound traffic"
vpc_id = "${aws_vpc.my-vpc.id}"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_instance" "ansible-test" {
ami = "ami-ceafcba8"
instance_type = "t2.micro"
key_name = "XXXXXXXXXXXXX"
vpc_security_group_ids = [
"${aws_security_group.admin.id}"
]
subnet_id = "${aws_subnet.public-a.id}"
associate_public_ip_address = "true"
root_block_device = {
volume_type = "gp2"
volume_size = "10"
}
}
・plan
$ terraform plan
Plan: 7 to add, 0 to change, 0 to destroy.
・apply
$ terraform apply
Apply complete! Resources: 7 added, 0 changed, 0 destroyed.
次に上記で作成されたStateファイルを指定し、Ansibleを実行します。
(ホストにPINGをおこなうだけの簡単なプレイブックです。)
$ TF_STATE=../sample01-terraform/terraform.tfstate ansible-playbook --inventory-file=/usr/local/bin/terraform-inventory setup.yml
PLAY [all] *********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [xxx.xxx.xxx.xxx]
TASK [sample-roles : PING] *****************************************************
ok: [xxx.xxx.xxx.xxx]
PLAY RECAP *********************************************************************
xxx.xxx.xxx.xxx : ok=2 changed=0 unreachable=0 failed=0
タイトルには、2ステップとありましたが、実は[ terraform plan ]を入れると3ステップとなります。
ごめんなさい、じゃっかんの釣りタイトルでした・・・
ですが、ご覧いただく通りInventoryの修正なしで、Ansibleの実行が可能です。
ただし、2ステップ(Planを入れると3ステップ)で実行をを行うには、Ansible側で工夫が少し必要でした。
・setup.yml
- hosts: all
become: yes
remote_user: ec2-user
vars:
ansible_ssh_private_key_file: /PATH/TO/KEY/xxxxxx.pem
ansible_ssh_extra_args: '-o StrictHostKeyChecking=no'
roles:
- role: sample-roles
Ansibleを適用する場合、ssh_configを作成し、InventoryファイルおよびGroup_varsを環境に応じて設定することが可能でした。
同じ様にGroup_versを利用するには、下記の様にtagsを設定します。
なお、Terraform Inventoryでは配布元で説明があるとおり、Ansibleで指定するホスト名は、
プロバイダ上でのホスト名ではなくTerraformで定義したリソース名となります。
This will provide the resource names and IP addresses of any instances found in the state file to Ansible, which can then be used as hosts patterns in your playbooks.
resource "aws_instance" "ansible-test" {
ami = "ami-ceafcba8"
instance_type = "t2.micro"
tags = {
Env = "dev"
}
.....
}
上記、Terraform内にて設定したタグは、ホストグループ [ env_dev ]として定義されますので、
group_versディレクトリ内に、[ env_dev.yml ]ファイルを設置します。
また、同様に
tags = {
Env = "dev"
Role = "web"
}
と設定し、AnsibleのPlaybook内では
- hosts: role_web
と指定することで、役割毎に実行するPlaybookの変更も可能です。
上記2つを組み合わせることで、1つのTerraform・Playbookを元に開発・検証・本番などの様に
設定値だけを切り替える構成が、インフラ環境の構築からEC2内の設定まで可能となります。
夢が広がりますね!
では、また!!!