Ansibleでグループ毎に設定内容や処理を分岐する

要は備忘録です。
環境やタイトル毎に設定変えたいけどどうしたらいいかという質問を後輩の方からもらったのでその返答を軽くまとめたものです。

たとえば以下のような感じにインベントリに[groupname]のしたにホストを1行ずつかいて定義する

```config:inventory/host
[web:children]
test-web
dev-web

[test:children]
test-web

[dev:children]
dev-web

[test-web]
test-web01 ansible_host=10.0.0.3

[dev-web]
dev-web01 ansible_host=10.0.0.4
```

そして、hostvarsを出す以下のようなプレイブックを用意し、

```yaml:test/debug_hostvars.yaml
---
- hosts: all
  gather_facts: no
  tasks:
   - name: "dump variables hostvars"
     debug: var=hostvars[inventory_hostname] 

   - name: "dump variables vars"
     debug: var=vars
```
こんな感じでhostvarsをファイルに出すと
`ansible-playbook test/debug_hostvars.yaml > test.log --vault-password-file=.vaultpass`

結果ファイルに出たhostvarsの中のgroup_namesというキーにインベントリで設定したグループ名が親子ともに全部入る。

```text:test.log抜粋
        "group_names": [
            "test",
            "test-web",
            "web"
        ],
```

これでgroup_vars/{test,test-web,web}.ymlに指定した変数が全部読まれることが分かる。ちなみに優先度は上から下に順に読まれるようで下のやつが強いがchildrenを指定していれば階層が深いほど優先度高くなるので高いグループに書いた変数で上書きされる動きになる模様だった。

このホストが所属しているグループの情報を用いてtaskやtemplateで設定内容を制御したり処理を分岐することが可能。

templateの場合の例

```config:chronyの設定抜粋
{% macro hosts(host) -%}
{{ hostvars[host].ansible_host }}
{%- endmacro %}

{% if 'admin' in group_names %}   #★ここでif分岐
{# adminの場合 #}
{% for ntpsrv in min_stratum_ntppools %}
server {{ ntpsrv }} iburst
{% endfor %}

{# 要pip install netaddr at ansible host(clientには不要) #}
# Allow NTP client access from local network.
allow {{ net_mask | ipaddr('net') }}
{% else %}#★ここでそうでない場合
{%   if groups.admin is defined and groups.admin != [] %}#★ここでさらに分岐
{# adminでなく、adminが存在する場合 #}
{%     for h in groups.admin %}
server {{ hosts(h) }} iburst
port 0

# Allow NTP client access from local network.
#allow 192.168.0.0/16
{%     endfor %}
{%   else %}#★ここで入れ子のそうでない場合
{# adminでなく、adminが存在しない場合 #}
{% for ntpsrv in min_stratum_ntppools %}
server {{ ntpsrv }} iburst
{% endfor %}

# Allow NTP client access from local network.
#allow 192.168.0.0/16
{%   endif %}
{% endif %}
```
という、管理サーバを内部の時刻同期の代表にする感じの設定とか、
あと↓こんな感じでバーチャルホスト設定を環境グループ毎にログ飛ばす先変えるのとか。

```jinja:vhost.conf.j2
# {{ ansible_managed }}
<VirtualHost *:80>
    ServerName {{ contents_title }}.{{ env_subdomain }}.com
{% if 'production' in group_names %}
    ErrorLog "|/usr/bin/logger -i -p local7.info -t err_"
    CustomLog "|/usr/bin/logger -i -p local7.info -t acc_" combined env=!nolog
{% else %}
    ErrorLog "|/usr/sbin/rotatelogs  /var/log/httpd/{{ contents_title }}_{{ env_initial }}_error_log.%Y%m%d 86400 540" 
    CustomLog "|/usr/sbin/rotatelogs /var/log/httpd/{{ contents_title }}_{{ env_initial }}_access_log.%Y%m%d 86400 540" combined env=!nolog
{% endif %}
```

taskで分岐したい場合の例

```
## ALL
- include: tasks/ipmitool.yml
  when: ansible_virtualization_role != "guest"

## ADMIN
- include: tasks/admin.yml
  when: ('admin' in group_names)
```
※Ansible2.4以降だとincludeではなくimport_tasksのほうが警告がでない。

以下のように--limit指定で同じホストにプレイブック流して処理を分けたそれぞれテストするとたぶんわけたとこの挙動が確認しやすい。（ただし同じホストを違うグループに登録するとその分のグループが増えてしまうので片方コメントinしとかないと最後によまれたもので上書きされておかしなことに）

```
ansible-playbook site.yml --limit test-web -D --vault-password-file=.filename -e "contents_title=mytitle1"
ansible-playbook site.yml --limit dev-web -D -C --vault-password-file=.filename -e "contents_title=mytitle1"
```

あと、inventoryのグループにホストが登録されてたら、つまり居ない子の設定をしないようにというようなのは以下のようにlengthフィルタでリストの要素数が取れるのでそれを使うといけるようでした。
`groups.db|length > 0`

特定のまとまりで別のことするようなのは結構あると思うので使いこなすとたぶん便利。

以上

参考：
https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html
http://jinja.pocoo.org/docs/2.10/templates/#builtin-filters
https://docs.ansible.com/ansible/2.5/user_guide/playbooks_filters.html
https://showa-yojyo.github.io/notebook/python-jinja2.html