Ansibleで自作Windowsモジュールを作ってみる

ここでは、Ansibleで簡単なWindowsの自作モジュールを作ってみます。

# 1. 前提条件

公式の [ConfigureRemotingForAnsible.ps1](https://github.com/ansible/ansible/blob/devel/examples/scripts/ConfigureRemotingForAnsible.ps1) をWindowsで実行してWinRMの設定がされていることを前提とします。

# 2. 開発環境

|      項目      | バージョン |
|----------------|------------|
| Ansible        | 2.5.5      |
| RHEL           | 7.4        |
| Windows Server | 2016       |

# 3. Windowsのモジュールについて

Windowsのモジュールは以下2ファイルから構成されます。

| ファイル |                                            説明                                           |
|----------|-------------------------------------------------------------------------------------------|
| .ps1     | Winowsで実行するPowreshellスクリプト、実際にこのスクリプトがWindows側へ送られて実行される |
| .py      | Ansibleのドキュメント用pythonファイル、無くても動作はする                                 |

# 4. Windowsのmodule_utils

Windowsモジュールを作る場合は以下のmodule_utilsを使用します。
詳細は以下リンク先のコードを参照ください。

[https://github.com/ansible/ansible/tree/devel/lib/ansible/module_utils/powershell](https://github.com/ansible/ansible/tree/devel/lib/ansible/module_utils/powershell)

`Exit-Json` `Fail-Json` `Parse-Args` `Get-AnsibleParam` を使うだけであれば `Ansible.ModuleUtils.Legacy` を読み込むだけで大丈夫です。

# 5. モジュールを作ってみる

ここでは、イメージがつきやすいように簡単なモジュールを作ってみます。
作るモジュールはファイルの存在チェックをするだけのものを作ってみようと思います。

## 5-1. Powershellソース

```powershell:win_file_check.ps1
#!powershell

# module_utilsから必要なモジュールを読み込む
#Requires -Module Ansible.ModuleUtils.Legacy

# 結果の連想配列(ハッシュ)を作る
$result = @{ changed = $false }

# モジュールのオプション処理
$params = Parse-Args $args -supports_check_mode $true
$path = Get-AnsibleParam -obj $params -name "path" -type "str"

# path変数に値がない場合は `Fail-Json` を実行
if(!$path) { Fail-Json -message "Absolute path is not specified for path" }

# path変数に指定された絶対パスでファイルの存在確認
$r = Test-Path $path

# Test-Pathの戻り値がFalseの場合は `Fail-Json` を実行
if(!$r) { Fail-Json -message "$path not found" }

# ファイルが存在すればresult変数(連想配列)を `Exit-Json` に渡す
Exit-Json $result
```

`Requires` で必要なモジュールを読み込みます。
`Parse-Args` と `Get-AnsibleParam` でモジュールに渡したオプションをパースして取得します。

## 5-2. Pythonソース例

```python:win_file_check.py
#!/usr/bin/python
# -*- coding: utf-8 -*-

ANSIBLE_METADATA = {'metadata_version': '1.1',
                    'status': ['preview'],
                    'supported_by': 'individual'}

DOCUMENTATION = '''
module: win_file_check
short_description: Module to check existence of windows files
description:
    - Confirm existence of file by using absolute path as an argument.
options:
    - path:
      description:
        - Absolute path of the file.
'''

EXAMPLE = '''
---
- name: win file check
  win_file_check:
    path: C:\Users\Administrator\Desktop\hoge.txt
'''
```

# 6. 動かしてみる

## 6-1. Playbook

```yaml:main.yml
---
- name: salf-made windows module test
  hosts: all
  gather_facts: no
  tasks:
    - win_file_check:
        path: C:\Users\Administrator\Desktop\hoge.txt
```

## 6-2. Inventory

```yaml:inventory
[windows]
192.168.0.135

[windows:vars]
ansible_user=administrator
ansible_password=secret
ansible_port=5986
ansible_connection=winrm
ansible_winrm_server_cert_validation=ignore
```

## 6-3. 実行

自作したモジュールは `module` ディレクトリに保存しています。

```bash
[root@localhost ~]# ansible-playbook main.yml -i inventory --module-path module/

PLAY [salf-made windows module test] ****************************************************************************************************

TASK [win_file_check] *******************************************************************************************************************
ok: [192.168.0.135]

PLAY RECAP ******************************************************************************************************************************
192.168.0.135              : ok=1    changed=0    unreachable=0    failed=0
```

# 7. 最後に

個人的にPythonでモジュールはいくつか作ってきましたが、Powershellも同じように

* 必要なモジュールを読み込んで
* オプションをパースして
* 必要な変数に格納して
* 処理を実行して
* 結果を返す(`Exit-Json` `Fail-Json`)

という風に作れて簡単ですね :)
Ansible便利 :)