AnsibleでChocolateyを使ってWindowsアプリをインストールする

# はじめに

[前回](http://qiita.com/yunano/items/f9d5652a296931a09a70)でAnsibleを使ってWindowsを操作する準備はできた。
今回は実践として、Windows用のパッケージマネージャ[Chocolatey](https://chocolatey.org/)のインストールと、Chocolateyを使ったアプリケーションのインストールを行いながら、AnsibleがWindowsに対してできることを見ていくことにする。

環境は前回と同じ、Ansible 1.7.1でWindows 8.1 Updateを操作している。

使用しているインベントリファイルを再掲する。

```text:hosts
[windows]
10.0.2.172

[windows:vars]
ansible_ssh_user=<Windows側のユーザ名>
ansible_ssh_pass=<Windows側ユーザのパスワード>
ansible_ssh_port=5986
ansible_connection=winrm
```

## win_chocolateyモジュール（Ansible 1.9以降）

Ansible 1.9よりExtras Moduleとしてwin_chocolateyモジュールが追加された。
そのため、この文書の次項以降の作業は不要となった。

**2015-06-27追記**： Windows側にChocolatey 0.9.9以降がインストールされた場合、chocolateyの仕様変更にAnsible 1.9.2までに同梱されているwin_chocolateyモジュールが対応できておらず正常に終了しない。簡単な対処としてはwin_chocolateyモジュールをdevelにあるものに差し替えることである。

```
# curl https://raw.githubusercontent.com/ansible/ansible-modules-extras/devel/windows/win_chocolatey.ps1 -o /usr/lib/python2.7/site-packages/ansible/modules/extras/windows/win_chocolatey.ps1
# curl https://raw.githubusercontent.com/ansible/ansible-modules-extras/devel/windows/win_chocolatey.py -o /usr/lib/python2.7/site-packages/ansible/modules/extras/windows/win_chocolatey.py
```

以下のようなplaybookでWindowsアプリをインストールできる。
Chocolatey自体も最初にwin_chocolateyモジュールを使った時に自動的にインストールされる。

```yaml:site.yml
- hosts: windows
  tasks:
  - win_chocolatey: name={{ item }}
    with_items:
    - SourceTree
    - sysinternals
```

# Windowsに対して使用できるAnsibleモジュール

現状、Ansibleに用意されたモジュールのほとんどがWindowsに対して使用することができない。使用できるのは[公式ドキュメント](http://docs.ansible.com/intro_windows.html)によると、

* [raw](http://docs.ansible.com/raw_module.html)
* [script](http://docs.ansible.com/script_module.html)
* [fetch](http://docs.ansible.com/fetch_module.html)
* [slurp](http://docs.ansible.com/slurp_module.html)
* [setup](http://docs.ansible.com/setup_module.html)

および、Windows用に追加されたモジュール

* [win_feature](http://docs.ansible.com/win_feature_module.html)
* [win_get_url](http://docs.ansible.com/win_get_url_module.html)
* [win_group](http://docs.ansible.com/win_group_module.html)
* [win_msi](http://docs.ansible.com/win_msi_module.html)
* [win_ping](http://docs.ansible.com/win_ping_module.html)
* [win_service](http://docs.ansible.com/win_service_module.html)
* [win_stat](http://docs.ansible.com/win_stat_module.html)
* [win_user](http://docs.ansible.com/win_user_module.html)

**2016-02-27追記：** Ansible 2.0時点ではwin_*モジュールはもっと大量に増えている。

他に[debug](http://docs.ansible.com/debug_module.html)等、操作する側でしか処理が行われないモジュールは使用できることを確認している。

つまり現状、Windows環境にファイルを受け渡す方法が、どこか適当なWebサーバにアップロードしてからwin_get_urlでダウンロードする以外にないようだ。
（SSHサーバやrsyncサーバなどがWindows側に立っていたりはしない前提で）

アプリの設定ファイルなんかも作りたければ、templateやlineinfile、ini_fileで作った設定ファイルをWebサーバにアップロードしてからwin_get_urlでダウンロードしろ、あるいはこれらのモジュールで動的に作ったPowerShellスクリプトをscriptで流し込め、ということになるのだろう。

# chocolateyのインストール

## rawモジュールを使用する

使用できるモジュールが上記の通りだけ、ということはrawモジュールが大活躍するに違いない、というわけでまずrawモジュールを使用する。

まずChocolatey公式の、PowerShellを使ってインストールする時に実行するコマンドを流してみる。

（見やすいようにコマンドと出力を1行開けている）

```
# ansible windows -i hosts -m raw -a 'iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))'

10.0.2.172 | FAILED | rc=1 >>
'iex' is not recognized as an internal or external command,
operable program or batch file.
```

エラーになった。
結局試した限りどんなPowerShellコマンド（「コマンドレット」と呼ぶ）もrawモジュールで流すとエラーになる、という結論になった。

**2016-02-27追記：** いつからなのかは調べていないが、Ansible 2.0.1で試す限りPowerShellコマンドがエラーにならず実行されるようだ。上のコマンドも-aの囲みを""にすれば通る。

今度はコマンドプロンプトからChocolateyをインストールする時に実行するコマンドを流してみよう。

```
# ansible windows -i hosts -m raw -a '@powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex ((new-object net.webclient).DownloadString(\"https://chocolatey.org/install.ps1\"))" && SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin'

10.0.2.172 | success | rc=0 >>
（以下省略）
```

というわけでこちらでは問題なくChocolateyをインストールできた。

## scriptモジュールを使用する

rawモジュールでできないPowerShellコマンドを実行したければscriptモジュールを使えば良い。
上記のPowerShell用のインストールコマンドを書いたps1ファイルを作る。

```powershell:chocolatey.ps1
iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))
```

そしてscriptモジュールで流し込む。

```
# ansible windows -i hosts -m script -a 'chocolatey.ps1'

10.0.2.172 | success >> {
    "changed": true,
    "rc": 0,
    "stderr": "",
    "stdout": （以下省略）
```

問題ない。

scriptモジュールには、ファイルがある時には実行しないcreatesオプションがあるので試してみる。

```
# ansible windows -i hosts -m script -a 'chocolatey.ps1 creates=C:\ProgramData\chocolatey\bin\chocolatey.exe'
10.0.2.172 | success >> {
    "changed": true,
    "rc": 0,
    "stderr": "",
    "stdout": （以下省略）
```

「"changed": true」になっている、ということは効いていない。
ファイルがない時には実行しないオプションremovesも同様であった。

ここで期待した通りのことをしたければwin_statモジュールを使用する。
Playbookを作って実行する。

```yaml:site.yml
- hosts: windows
  tasks:
    - win_stat: path=C:\ProgramData\chocolatey\bin\chocolatey.exe
      register: file_info
    - script: chocolatey.ps1
      when: file_info.stat.exists == false
```

```
# ansible-playbook -i hosts site.yml

PLAY [windows] ****************************************************************

GATHERING FACTS ***************************************************************
ok: [10.0.2.172]

TASK: [win_stat path=C:\ProgramData\chocolatey\bin\chocolatey.exe] ************
ok: [10.0.2.172]

TASK: [script chocolatey.ps1] *************************************************
skipping: [10.0.2.172]

PLAY RECAP ********************************************************************
10.0.2.172                 : ok=2    changed=0    unreachable=0    failed=0
```

今度は期待通り「skipping」になった。

# Windowsアプリのインストール

Chocolateyのパッケージインストール用のコマンドであるcinstはコマンドプロンプトでも実行できるのでrawモジュールを使うことができる。
先程のPlaybookに追加して実行することにする。

**2015-06-27追記**： Chocolatey 0.9.9以降、cinstに-yオプションを付けないとすぐにインストールしてくれなくなった。それより前のバージョンでは-yは必要ない。

```yaml:site.yml
- hosts: windows
  tasks:
    - win_stat: path=C:\ProgramData\chocolatey\bin\chocolatey.exe
      register: file_info
    - script: chocolatey.ps1
      when: file_info.stat.exists == false
    - raw: cinst {{ item }} -y
      with_items:
        - SourceTree
        - sysinternals
```

```
# ansible-playbook -i hosts site.yml

PLAY [windows] ****************************************************************

GATHERING FACTS ***************************************************************
ok: [10.0.2.172]

TASK: [win_stat path=C:\ProgramData\chocolatey\bin\chocolatey.exe] ************
ok: [10.0.2.172]

TASK: [script chocolatey.ps1] *************************************************
skipping: [10.0.2.172]

TASK: [raw cinst {{ item }}] **************************************************
ok: [10.0.2.172] => (item=SourceTree)
ok: [10.0.2.172] => (item=sysinternals)

PLAY RECAP ********************************************************************
10.0.2.172                 : ok=3    changed=0    unreachable=0    failed=0
```

問題なくアプリケーションをインストールできた。

# まとめ

Ansible 1.7.1時点での、今回使用したモジュールのまとめ。

* rawモジュールではPowerShellコマンドを流すとエラーになり、コマンドプロンプトで実行できるものだけが実行できる。PowerShellコマンドを流すにはscriptモジュールを使用する。
* scriptモジュールのcreatesオプション、removesオプションはWindowsに対しては利用できない。win_statモジュールを利用して同等のことはできる。