Ansible Dynamic Inventoryを使ってAWSのEC2インスタンスに付与されたタグでプロビジョニング対象を決める

http://docs.ansible.com/ansible/intro_dynamic_inventory.html#example-aws-ec2-external-inventory-script

これの話です。

## 何がやれるのか

![](https://i.gyazo.com/52655ed88a111a456388f5f5be98c505.png)

こんな感じにタグ付けした時に、インベントリファイルに対象ホストのIPとかを直接書かずにタグ名でホストを扱うことができます。

インベントリファイルにこんな風に書いたのと同じ動作になります。

```ini:インベントリファイル
[tag_role_api]
xxx.xxx.xxx.xxx
[tag_role_db]
yyy.yyy.yyy.yyy
```

ec2を使うとサーバーのIPがコロコロ変わるのでインベントリファイルにIPを直で書くとインベントリファイルを都度都度更新することになってしまうので有用な方法です。

## やり方

### AWSのAPIを内部的に使用するのでアクセスキーが必要

EC2のread/writeが可能な権限を持ったIAMアカウントのAPIキーが必要です。AWSコンソールで事前に作っておく必要があります。
そして、環境変数にセットします。

```shell-session
export AWS_ACCESS_KEY_ID='AK123'
export AWS_SECRET_ACCESS_KEY='abc123'
```

### ec2.py, ec2.iniを取得する

https://github.com/ansible/ansible/tree/devel/contrib/inventory このへんにいっぱいあるので取得します。見たらわかると思いますが、EC2以外にもいろんなクラウド用があります。

### インベントリのディレクトリ構造

```
playbook
├── site.yml
├── hosts
│   ├── prod
│   │   ├── ec2.ini
│   │   └── ec2.py
│   └── stg
│       ├── ec2.ini
│       └── ec2.py
└── roles
```

### ec2.iniをカスタマイズ

#### 使わない種類のものを対象にしない

なんかエラーが出たのでroute53,RDS,elasticacheはFalseに設定しました。

```ini
# To tag instances on EC2 with the resource records that point to them from
# Route53, uncomment and set 'route53' to True.
route53 = False

# To exclude RDS instances from the inventory, uncomment and set to False.
rds = False

# To exclude ElastiCache instances from the inventory, uncomment and set to False.
elasticache = False
```

#### stageを絞るためにinstance_filtersを指定

```ini:hosts/stg/ec2.ini
# Filters are key/value pairs separated by '=', to list multiple filters use
instance_filters = tag:stage=stg
```

```ini:hosts/prod/ec2.ini
# Filters are key/value pairs separated by '=', to list multiple filters use
instance_filters = tag:stage=prod
```

### 試してみる

#### まずはec2.pyが何を返すか見てみよう

```shell-session
$ k$ ./host/stg/ec2.py
{
  "_meta": {
    "hostvars": {
      "52.69.149.28": {
        "ec2__in_monitoring_element": false, 
                  :
```

JSON形式で色々とれてきます。重要なのはこの辺りです。

```json
  "tag_role_api": [
    "52.69.236.80"
  ], 
  "tag_role_db": [
    "52.69.149.28"
  ], 
  "tag_stage_stg": [
    "52.69.236.80", 
    "52.69.149.28"
  ], 
```

tag_```タグ名```_```タグの値```という名前が付けられています。
これがAnsbleで言う[グループ名](http://docs.ansible.com/ansible/intro_inventory.html#hosts-and-groups)になります。

#### -m ping してみる

```shell-session
$ ansible -i host/stg all -m ping
52.69.149.28 | success >> {
    "changed": false, 
    "ping": "pong"
}

52.69.236.80 | success >> {
    "changed": false, 
    "ping": "pong"
}
```

```shell-session
$ ansible -i host/stg tag_role_api -m ping
52.69.236.80 | success >> {
    "changed": false, 
    "ping": "pong"
}
```

## 応用編

### roleの兼任

dev環境とかはコスト削減の名のもとに、一個のサーバーにいろいろ入れて誤魔化せと言われます。

|タグ名|     値    |
|-----|-----------|
|stage|dev|
|role|api,db|

こんな感じでroleの値をカンマ区切りにすると・・・```tag_role_api_db```というグループでくくられます。

### group of group

```yaml:site.yml
---
- hosts: tag_role_api
  roles:
    - nginx
    - tomcat
- hosts: tag_role_db
  roles:
    - mysql
```

こんなsite.ymlだったとすると、

```
$ ansible-playbook -i hosts/dev site.yml
```

とした時には空振ってしまいます。devのインベントリには```tag_role_api_db```というグループがあるだけで、```tag_role_api```も```tag_role_db```も無いからです。

[ここ](http://docs.ansible.com/ansible/intro_dynamic_inventory.html#static-groups-of-dynamic-groups)にちょろっと書いてありますが、

```ini:hosts/dev/groupDefine
[tag_role_api_db]

[tag_role_api:children]
tag_role_api_db

[tag_role_db:children]
tag_role_api_db
```

こんなファイルを書くと```tag_role_api```と```tag_role_db```が定義されるので期待通り動くと思います。