【Ansible】独自モジュール作成時の変数の呼び出し方について

*ansible歴半年の新米インフラエンジニアです*

# はじめに
当方、とある企業でインフラエンジニアとしてansibleのroleやplaybook作成に日々勤しんでいる者です。
まだansible歴が半年ですが、やっと開発・運用に慣れてきた今日この頃。
そんな最中に舞い込んできた業務が「独自モジュール」を作って欲しいとのことでした。

ansibleには、多くのモジュールが実装されています。
（公式ホームページ参照：http://docs.ansible.com/ansible/list_of_all_modules.html ）
しかし、今回の依頼は上記のような既存モジュールでは非対応の要件とのこと。
プログラミング経験は、1か月前からpythonを始めたばかりの新米。
そんな自分が本当に独自モジュール作成なんてカッコイイ仕事が出来るのか、当初は不安でした。

不安でした、が！

Ansibleのモジュールは、意外にも簡単に作成することが可能でした。
作るだけならハードルは高くありません。
しかし、独自モジュールはansibleとは分離しているものなので
ansible側で定義した変数を呼び出すやり方を知らないと、
呼び出せないストレスで苦しむ可能性があります。

長くなりましたが、ここからが本題です。

このページでは、そんなストレスに対抗すべく
初心者が躓くであろう変数の呼び出し方をご紹介します。

　※どの言語でも作成可能とのことですが、今回は当方が学んでいるpythonでの書き方でご説明します。

# 環境
・CentOS 7.2
・Python 2.7.5

# 下準備
まず、独自モジュール置き場を用意します。
playbook内に「library」ディレクトリを作成します。
　※ディレクトリ名は原則です、ここを違う名前にすると読み込んでくれません
今回は、実行するrole内に作成します。
role構成は以下の通りです。

```
ansible-test-playbook
  |_test-server.yml
  |_group_vars
  |_host_vars
  |_vars
  |_inventory
  |_roles
      |_role_test
          |_defaults
            |_main.yml
          |_tasks
            |_main.yml
          |_templates
          |_library  ⬅︎new!
            |_test.py  ⬅︎new!
```

下準備もできたので、次に独自モジュールを作成していきます。
簡単なシェルスリプトだけでも動きますが、
今回は「ansible側で宣言した変数をモジュール側で読み込んで実行」するやり方を中心に書いていきます。

ansibleでは一度記載した変数は呼び出せば自由に使用できますよね。
ですが、独自モジュール側ではそのまま呼び出そうとしてもうまくいきません。
そこでtaskで呼び出したい変数を指定し、独自モジュールに持っていくという工程が必要となります。
以下では、変数を独自モジュールに持っていき、実行結果をansible側に戻すまでを記載します。

# defaults/main.yml作成

今回呼び出したい変数は以下のようにdefaultsで宣言をします。
（変数の書き方は本ページでは割愛します）

```yaml:main.yml
un: "Hello"
deux: "World"
```

# tasks/main.yml作成
次にtaskの用意です。
taskでは以下のように書きます。

```yaml:main.yml
- name: get hello world
  test: # .pyを除いた実行する独自モジュール名
    un: "{{ un }}" # 独自モジュールで使用したい変数
    deux: "{{ deux }}"
  register: cinq # 独自モジュールの実行結果を格納する変数名
```

# 独自モジュール作成
では、モジュールを作成してみましょう。
libraryディレクトリに「test.py」というファイルを作成します。

```python:test.py
#!/usr/bin/env python
# coding: utf-8

# 必要なものをインポート
import json, urllib2
from ansible.module_utils.basic import *

def main():
    # varsから情報を取得するおまじないのようなものです
    # この時に変数名を、taskで宣言したものと一致するように書いてください
    module = AnsibleModule(
        argument_spec=dict(
            un=dict(required=True, type='str'),
            deux=dict(required=True, type='str')
        ),
        # no skip
        supports_check_mode=True,
    )

    # 上記で取得した情報を格納して、モジュール内で使用できるようにする
    un = module.params['un']
    deux = module.params['deux']

    # このモジュールで実行する内容
    trois = un + deux

    # 実行結果を辞書型で格納
    # 後述のansible_factsが辞書型の形式で作られているので、この形は必須です
    quatre = {"quatre": trois}

    # 実行結果をansible側に引き渡す
    module.exit_json(changed=True, ansible_facts=quatre)

if __name__ == '__main__':
    main()
```

# 実行
では、準備が整いましたので上記を実行してみます。

```
PLAYBOOK: test-server.yml ********************************************************
1 plays in test-server.yml

PLAY [all] *********************************************************************

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

TASK [role-test : get hello world] *******************************************
changed: [localhost] => {
    "ansible_facts": {
        "quatre": "HelloWorld"　⬅︎実行結果
    }, 
    "changed": true, 
    "invocation": {
        "module_args": {
            "deux": "World", 
            "un": "Hello"
        }, 
        "module_name": "test"
    }
}
```
独自モジュールの実行結果がansible_factsの中に格納されているのが分かります。
では、今度はansible側で実行結果を呼び出すにはどう書けば良いか。

```
"{{ cinq.ansible_facts.quatre }}"
```
「taskの中のregisterで指定した変数名.ansible_facts.独自モジュール内で宣言した辞書型のkey」
この書き方をすればansible内で実行結果を使用できます。

これで作業は終了です。

# あとがき
いかがだったでしょうか？
少しでもこのような悩みに陥った方のお役に立てれば幸いです。

今後もansibleやpythonにふれていく予定なので、
また新しい学びがあったら投稿したいと思います。

ご覧いただきありがとうございました。