変数1つごとに暗号化できるようになったAnsible Vault

（2017-03-25: ドキュメントが追加されていた。 http://docs.ansible.com/ansible/playbooks_vault.html#single-encrypted-variable これによると2.3から、ということになっている。また、公式に名称は「Single Encrypted Variable」となった）

# per variable vault

Ansible Vaultはパスフレーズ等の機密情報をAnsibleで扱える状態で暗号化する機能であるが、通常暗号化したいのは値だけで変数名は暗号化する必要がないにもかかわらず、従来、一連の変数名とその値を定義した1ファイルまるごと暗号化しないとならなかった。
結果として、利用者はどのような変数が定義されているのかを、暗号化されたファイルを一見して知ることができなかった。

そこでAnsible 2.2より変数の値を1つごとに暗号化することができるようになった。
この機能をper variable vaultなどと呼ぶようだ。

# 使い方

* 値をまず「!vault」で始める（「!vault-encrypted」でも良い）。
* この後改行を含む文字列を記述するので、改行を保存することを表す"|"を記述する。
* ansible-vault encryptなどのコマンドで暗号化した値をその後に記述する。ただし、YAMLなのでインデントが必要になる。

つまり、vault_passというAnsible Vaultのパスワードが書かれたファイルを使用して、role_test1/var/main.ymlの"test_password"という変数に"PASSWORD"という値をセットする場合、以下のようなコマンドを実行すれば良い。

```bash
$ echo -n 'PASSWORD' | ansible-vault encrypt --vault-password-file vault_pass | awk 'BEGIN { printf "%s: !vault |\n", "test_password" } { printf "%2s%s\n", "", $0 }' >> role_test1/vars/main.yml
```

ここまでのことをAnsible 2.3で追加されたansible-vault encrypt_stringを使っても行える。
`echo -n 'PASSWORD' | ansible-vault encrypt_string --vault-password-file vault_pass`の標準出力を変数のところに貼り付けてインデントを調整する。

これを実行するとrole_test1/vars/main.ymlの中身は以下のようになる。
（ロールrole_test1はあらかじめ`ansible-galaxy init --offline role_test1`で作成されているものとする）

```yaml:role_test1/vars/main.yml
---
# vars file for role_test1
test_password: !vault |
  $ANSIBLE_VAULT;1.1;AES256
  36633235636233353730356634363634323665656431646337393138383335346434636336386234
  3136326634306139343639636433623764643530376231610a333761383265343238653534663161
  38653332336437336663343437353831663363303464313061663564343435623762343963333635
  3030613963326537350a353333636533326166353234366539613730363864626331633565333966
  3035
```

これが使えることを確認してみる。
role_test1/tasks/main.ymlを以下のようにする。

```yaml:role_test1/tasks/main.yml
---
# tasks file for role_test1
- debug:
    var: test_password
```

role_test1ロールを呼び出すbase_test1.ymlを作る。

```yaml:base_test1.yml
- hosts: localhost
  roles:
  - role_test1
```

`ansible-playbook --vault-password-file vault_pass base_test1.yml`を実行してみる。

```bash
$ ansible-playbook --vault-password-file vault_pass base_test1.yml
 [WARNING]: provided hosts list is empty, only localhost is available


PLAY [localhost] ***************************************************************

TASK [setup] *******************************************************************
ok: [localhost]

TASK [role_test1 : debug] ******************************************************
ok: [localhost] => {
    "test_password": "PASSWORD"
}

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0

```

暗号化される前の文字列が見えた。

# 注意点

* 今のところansible_passwordを暗号化すると接続に失敗する。同様にansible_become_passを暗号化するとsudoに失敗する。報告したら修正されたので2.2.1では直っているはず。
https://github.com/ansible/ansible/issues/19795
**2017-01-17追記**：2.2.1で修正されているのを確認した。
**2017-06-03追記**：このissueには続きがあってwinrm接続が未だに失敗する。

* この方法で暗号化されたものを含むファイルに対してansible-vaultコマンドを使うことはできない。なので例えば暗号化前の文字列を確認したければ上のように適当なplaybookを作るとか、適当なファイルを作ってコピペしてからansible-vault viewを使うとか。

# 帰結

[YAMLでAnsibleのインベントリファイルを書く](http://qiita.com/yunano/items/258b00402b6b46da71de)の時点でhost_varsディレクトリやgroup_varsディレクトリに置くのはAnsible Vaultで暗号化されたものだけになるかな、と思っていた。
しかしper variable vaultと合わせて考えると、もはやhost_varsディレクトリやgroup_varsディレクトリが必要なくなり、それらに定義していたような変数をインベントリファイルに定義することができるようになる。
（バグが解消されたのが前提になるが）