Ansible用のWindowsモジュールを作る

# はじめに
AnsibleにおけるWindows用標準モジュールは充実してきたもののまだ以下の問題があります(と思っています)。

- 標準モジュールでは対応できない設定がある
- ごくまれに冪等性が担保されていないことがある

そこで、Windowsの自作モジュールを作ることにしました。
ところが調べてみてもWindowsのモジュール作成に関してはあまり見つけられなかったのでまとめることにしました。
とりあえず作れるようになる、程度の知識なので間違い等あれば是非ご指摘いただければ・・・。


# Windowsモジュールとして必要な記述(おまじない的な)
標準モジュールを見てみると大体書いてあるのが次の3行です。

```ps1:
#!powershell
# WANT_JSON
# POWERSHELL_COMMON
```

こいつがあるとどうやらWindowsモジュールとして動くらしい。
これについてはここに書いてあります。
[Ansible - Windows Support - Developers: Supported modules and how it works](http://docs.ansible.com/ansible/intro_windows.html#developers-supported-modules-and-how-it-works)
pythonで書く場合のモジュール読み込みのようなものと思っています。

# 引数を受け取る
以下のようにすることでPlaybookに記述したパラメータをPowershellで受け取ることができます。

```ps1:
$params = Parse-Args $args

$result = @{
    "changed" = $false
}

$state = Get-AnsibleParam `
         -obj $params `
         -name "state" `
         -default "enable" `
         -validateset "enable","disable" `
         -resultobj $result `
         -failifempty $false
```

Parse-ArgsやGet-AnsibleParamは `ansible/module_utils/powershell.ps1` で定義されています。
ざっくりと紹介します。

```yaml:
Parse-Args:
- 受け取ったJSONをオブジェクトに変換する
- 引数は以下の2つ
  - arguments
  - supports_check_mode: default = $false なので明示的に指定しないとcheck_mode時にコケる

Get-AnsibleParam:
- オブジェクトの中から変数を引っ張り出す
- 引数は以下の8つ
  - obj
  - name: 抜き出す変数名
  - default: パラメータが空の場合に変数に格納する値
  - resultobj: モジュール終了時に返すためのJSONオブジェクト
               failifempty = $true のときは必須
  - failifempty: パラメータが空の場合にFAILするかどうか
                 defaultを指定しているときは false にしておかないと意味がない
                 pythonのAnsibleModuleにおける "required"
  - emptyattributefailmessage: パラメータが空の場合にエラーとして返すメッセージ
                               あえて定義したい場合に利用する
  - ValidateSet: 利用可能な値を固定したい場合に利用
                 複数指定する場合は , で区切る
                 pythonのAnsibleModuleにおける "choices"
  - ValidateSetErrorMessage: ValidateSetで指定されていないパラメータを受け取った場合に
                             エラーとして返すメッセージ
                             あえて定義したい場合に利用する
```

# モジュールを終了させる
Ansibleにおけるモジュールの終了パターンは次の3つ。
Exit-Json、Fail-Jsonのどちらも `ansible/module_utils/powershell.ps1` で定義されています。

## 1. OK

```ps1:
$result = @{
    "changed" = $false
}

Exit-Json $result
```

OKの場合は `"changed" = $false` のままExit-Jsonに放り込みます。

## 2. Changed

```ps1:
$result = @{
    "changed" = $false
}

$result.changed = $true
Exit-Json $result
```

Changedの場合は `"changed" = $true` でExit-Jsonに放り込みます。

## 3. Failed

```ps1:
$result = @{
    "changed" = $false
}

Fail-Json $result "Error!!"
```

Failedの場合はFail-Jsonに放り込みます。
引数はどちらも無くても動作しますが、メッセージ(第二引数)は表示したほうが良いかと思われます。

# 動作確認モジュールを作ってみよう
これまでの内容を踏まえて3パターンの終了が確認できるモジュールを作ってみます。

```ps1:test.ps1
#!powershell
# WANT_JSON
# POWERSHELL_COMMON

$params = Parse-Args $args

$result = @{
    "changed" = $false
}

$state = Get-AnsibleParam `
         -obj $params `
         -name "state" `
         -default "ok" `
         -validateset "ok","changed","failed" `
         -resultobj $result `
         -failifempty $false

switch ($state) {
    "ok" {
        Exit-Json $result
    }
    "changed" {
        $result.changed = $true
        Exit-Json $result
    }
    "failed" {
        Fail-Json $result "[Error] state is failed!!"
    }
}
```

実行結果はこんな感じです。

```
# ansible -i inventory -m test win-test
win-test | SUCCESS => {
    "changed": false
}

# ansible -i inventory -m test -a "state=ok" win-test
win-test | SUCCESS => {
    "changed": false
}

# ansible -i inventory -m test -a "state=changed" win-test
win-test | SUCCESS => {
    "changed": true
}

# ansible -i inventory -m test -a "state=failed" win-test
win-test | FAILED! => {
    "changed": false,
    "failed": true,
    "msg": "[Error] state is failed!!"
}
```

# おわりに
この記事が初投稿です。
指摘・ツッコミお待ちしております。
初めて投稿する内容としてはニッチなところな気がしますが、これが誰かの役に立てれば幸いです。