AnsibleでWindows Server 2012を構成する

# Ansible の Windows 対応について（ドキュメント抜粋）
2015/08/25に書いたものですが、部分的にアップデートをしています。

http://www.ansible.com/windows

* Windowsホスト情報をゲット
* MSIをインストール、アンインストール
* Windowの機能を有効化・無効化
* Windowsのサービスの管理（起動・停止）
* ローカルユーザ・グループの作成・管理
* ChocolateyパッケージマネージャによるWindowsパッケージの管理
* Windowsアップデートの管理・インストール
* リモートサイトからファイルを取得
* PowerShellでスクリプトを書いても実行可
* runas というWindowsのコマンドをサポートできるようになる

> Linux/Unix の sudo , su のようなやつ（以下リファレンス）
> [Windowsのrunasコマンドで、一時的に他のユーザー権限でプログラムを実行する](http://www.atmarkit.co.jp/ait/articles/0606/17/news015.html)

* PowerShell で Moduleを書くことができる

> Ansible ユーザは、ファイルシステムのACLを管理、Windowsファイアウォール、ホスト名とドメインのメンバシップを管理等、多くのモジュールを書ている

* Ansible Towerも対応

# 環境

- OpenStack Kilo 上インスタンス
- コントロールマシン
    - Ubuntu 14.04 x86_64
- リモートマシン
    - Windows Server 2012 R2 Standard

![2015-08-26 10-33-15.jpg](https://qiita-image-store.s3.amazonaws.com/0/75458/f1a93468-7a65-b795-7a5c-9ebd5dd5ae5c.jpeg)

# コントロールマシン 準備

## Ansible インストール
```shell-session
$ sudo apt-get install software-properties-common
$ sudo apt-add-repository ppa:ansible/ansible
$ sudo apt-get update
$ sudo apt-get install ansible

For Windows remote host
$ sudo apt-get install python-pip
$ sudo pip install https://github.com/diyan/pywinrm/archive/master.zip#egg=pywinrm
$ sudo apt-get install libkrb5-dev python-dev
$ sudo pip install kerberos
```

## インベントリファイル（～ 1.9.x）

```shell-session
$ more hosts
[windows]
10.1.1.6
test.example.com

[windows:vars]
ansible_ssh_user=Administrator
ansible_ssh_pass=XXXXXXXXXXXX
ansible_ssh_port=5986
ansible_connection=winrm
```

## インベントリファイル（2.x～）

```shell-session
$ more hosts
[windows]
10.1.1.6
test.example.com

[windows:vars]
ansible_user=Administrator
ansible_password=XXXXXXXXXXXX
ansible_port=5986
ansible_connection=winrm
# The following is necessary for Python 2.7.9+ when using default WinRM self-signed certificates:
ansible_winrm_server_cert_validation: ignore
```
<font color=red>パスワード指定のパラメータに注意！（ansible_ssh_pass => ansible_password）</font>

```
ansible_password を ansible_pass など他の文字列にすると、下記のようなエラーがでます。

192.168.1.6 | UNREACHABLE! => {
    "changed": false,
    "msg": "ssl: auth method ssl requires a password",
    "unreachable": true
}
```
# Windows Server 2012 準備

* <font color=red>特別手を加えていない</font>
* PowerShell 3.0以上が必要だが、既に4.0が含まれている

![2015-08-24 17-45-21.jpg](https://qiita-image-store.s3.amazonaws.com/0/75458/14876667-b9a8-40e6-7822-c4498a2e4ea6.jpeg)


# モジュール試行
上記環境で確認できたPlaybook例など示す。

## モジュール （Stable Ansible v1.9.2）

### copy

* ファイルのコピー（ディレクトリも可能だが、それのみでないとだめ）
* 1MBを越えると数分かかる
* 3MB以上だと500エラーでる
* 大きいファイルには向かないので、win_get_urlモジュールか、ファイルサーバを利用する

```shell-session:playbook
    - name: Copy a file
      win_copy: src=hogefile.zip dest={{ mydir }}
```

<font color="red">v2.0.0 からは改良されており、大きなファイルも送れるようになっています。</font>

### win_feature
telnet クライアントを入れた例

```shell-session:playbook
    - name: Install Telnet Client
      win_feature:
        name: "Telnet-Client"
        state: absent
        restart: no
        include_sub_features: no
        include_management_tools: no
```

### win_file

```shell-session:playbook
    - name: Create directory structure
      win_file: path={{ mydir }}\testfolder state=directory
```

### win_get_url
インターネット上のファイルをダウンロードする例

```shell-session:playbook
    - name: Get a file from web site
      win_get_url:
        url: 'http://sarah.secret.jp/worklog/wp-content/uploads/2015/03/pen8-300x209.jpg'
        dest: '{{ mydir }}\test.jpg'
```

- .Net Frameworkの **System.Net.WebClient** クラスで実装されている
- HTTP Proxy に注意（認証プロキシ未対応）
  - PRしてみた [Add HTTP Proxy options for win_get_url module #1979](https://github.com/ansible/ansible-modules-core/pull/1979)
  - マージされたので2.0から利用可能

### win_group

```shell-session:playbook
    - name: Create group
      win_group:
        name: hogefuga
        description: Deploy Group
        state: present
```

### win_user

```shell-session:playbook
    - name: Add a user
      win_user:
        name: hogefuga
        password: "eisaku@00"
        groups: ["Users"]
        state: present
```


### win_msi 
Vagrant をインストールした例

```shell-session:playbook
    - name: MSI Install
      win_msi: path={{ mydir }}\vagrant_1.7.2.msi state=present
```

### win_ping

```shell-session:playbook
    - name: Test ping
      action: win_ping
```

### win_service
```shell-session:playbook
    - name: Restart a service
      win_service:
        name: snmptrap
        state: started
```
### win_stat

以下の例では「win.ini」の情報を取得して、assert でチェック

```shell-session:playbook
    - name: Returns information about a Windows file
      tags: stat_file
      win_stat: path={{ mydir }}\win.ini
      register: stat_file

    - debug: var=stat_file

    - name: Check stat_file result
      tags: check_stat
      assert:
        that:
          - "stat_file.stat.exists"
          - "not stat_file.stat.isdir"
          - "stat_file.stat.size > 0"
          - "stat_file.stat.md5"
```

### win_template
```shell-session:playbook
  vars:
    mydir: C:\Users\Administrator\Desktop
    ntp_srv_addr: 100.100.100.100
  tasks:
    - name: template
      win_template: src=./ntp.conf.j2 dest={{ mydir }} backup=yes
```

### win_updates
```shell-session:playbook
    - name: Update
      win_updates:
        category: critical # critical or security
```

## "win_"が付かないモジュール
### setup
```shell-session
$ ansible 10.1.1.6 -m setup
10.1.1.6 | success >> {
    "ansible_facts": {
        "ansible_distribution": "Microsoft Windows NT 6.3.9600.0",
        "ansible_distribution_version": "6.3.9600.0",
        "ansible_fqdn": "ansible-target-",
        "ansible_hostname": "ANSIBLE-TARGET-",
        "ansible_interfaces": [
            {
                "default_gateway": "10.1.1.254",
                "dns_domain": "openstacklocal",
                "interface_index": 12,
                "interface_name": "Red Hat VirtIO Ethernet Adapter"
            }
        ],
        "ansible_ip_addresses": [
            "10.1.1.6",
            "fe80::69c3:3631:3bd:845a"
        ],
        "ansible_os_family": "Windows",
        "ansible_powershell_version": 4,
        "ansible_system": "Win32NT",
        "ansible_totalmem": 4294967296,
        "ansible_winrm_certificate_expires": "2025-08-24 06:52:04"
    },
```

### slup

```shell-session
$ ansible 10.1.1.6 -m slurp -a src='C:\Users\Administrator\Desktop\win.ini'
10.1.1.6 | success >> {
    "changed": false,
    "content": "OyBmb3IgMTYtYml0IGFwcCBzdXBwb3J0DQo=",
    "encoding": "base64"
}
```

```shell-session:playbook
    - name: slurp module
      slurp:
        src: '{{ mydir }}\win.ini'
```

### fetch
リモートホストからデータをフェッチする

以下の例ではリモートホスト（Windows Server）上の ntp.conf.txt ファイルをコントロールマシンのplaybook実行カレントに「test/\<windows server\>/C\:/Users/Administrator/Desktop/ntp.conf.txt」という形でフェッチされる

```shell-session:playbook
  vars:
    mydir: C:\Users\Administrator\Desktop

    - name: fetch module
      fetch:
        src: '{{ mydir }}\ntp.conf.txt'
        dest: ./test
```

### raw
* リモートホスト上の個別のコマンドや.exe、PowerShellスクリプトを実行したい時

```shell-session:playbook
    - name: raw module
      tags: raw
      raw: ipconfig
      register: ipconfig
    - debug: var=ipconfig
```

### script
* 実行プログラムを転送した後、PowerShell のスクリプトを実行したい時

```shell-session:playbook
    - name: run test script
      script: files/test_script.ps1
```

## モジュール（Dev Ansible v2.0）

### win_environment (v2.0)

```shell-session:playbook
- name: Set Environment
  tags: env
  win_environment:
    state: present
    name: TestVariable
    value: "Test value"
    level: machine
```

### win_regedit (v2.0)

```shell-session:playbook
- name: Set Registry
  tags: reg
  win_regedit:
    key: HKCU:\Software\MyCompany
    value: hello
    data: 1337
    datatype: dword
```
![2015-08-26 17-30-18.jpg](https://qiita-image-store.s3.amazonaws.com/0/75458/3228c1f6-faf4-2390-aad3-90cec8a99d91.jpeg)

### win_unzip (v2.0)
- PowerShell Community Extensions (PSCX) Module が必要
- zip 以外も展開可（bzip,gzなど）
- 再帰的にアーカイブを展開可
- 展開先のディレクトリがなければ作ってから展開
- 展開後、不要なソースファイルを削除可

以下例の「presen.zip」は展開すると「presen\test.zip」となり、更にtest.zipを展開するとテキストファイルに展開される。つまり、通常全て展開すると下記のようになる。

```
presen
      \__test.txt
      \__test.zip

```

これを下記のように指定してPlaybookを実行（再帰的に展開）してみる。

```shell-session:playbook
- name: Unzip file
  tags: unzip
  win_unzip:
    src: '{{ mydir }}\presen.zip'
    dest: '{{ mydir }}\archive'
    recurse: yes
    rm: true
    creates: '{{ mydir }}\archive'
```

結果は以下のようになった。再帰的に展開はされ、元のzipファイルは全て削除されているが、ちょっと直感的ではなかった。

```
archive
       \presen #（この下は空）
       \test.txt
```




## モジュール （Ansible Docs）
以下は未評価

* win_chocolatey (v1.9.2 extras) # Enterpriseとして使うか否か
* win_dotnet_ngen (v2.0)
* win_iis_virtualdirectory (v2.0)
* win_iis_webapplication (v2.0)
* win_iis_webapppool (v2.0)
* win_iis_webbinding (v2.0)
* win_iis_website (v2.0)
* win_package (v2.0)
* win_scheduled_task (v2.0)
* win_webpicmd (v2.0)

# おまけ
## コンフィグファイルを設定

```shell-session
guest@ahost:~$ cp /etc/ansible/ansible.cfg ~/
guest@ahost:~$ sed -i -e 's/\/etc\/ansible\/hosts/\/home\/guest\/hosts/g'
guest@ahost:~$ export ANSIBLE_CONFIG=~/ansible.cfg
```

## Chocolatey 対応

* 1.9.2 のモジュールでうまくいっていない

```shell-session
# cd /usr/share/pyshared/ansible/modules/extras/windows/
# cp win_chocolatey.py org_win_chocolatey.py
# cp win_chocolatey.ps1 org_win_chocolatey.ps1

# curl https://raw.githubusercontent.com/ansible/ansible-modules-extras/devel/windows/win_chocolatey.ps1 -o ./win_chocolatey.ps1
# curl https://raw.githubusercontent.com/ansible/ansible-modules-extras/devel/windows/win_chocolatey.py -o ./win_chocolatey.py
```

## Reminder: You Must Have a Linux Control Machine
Windows を管理するのはLinuxコントロールマシンであり、Windowsのコントロールマシンではない。
また、Cygwinはサポートされていない。

## テストにおいて、Windows Server 2012 留意点
Ansibleがどうこうという意味ではなく、Windows Server 2012 はこうだったというメモ。

* セキュリティが高い設定は相変わらずだが至極当然
* ブラウザからデフォルト状態ではファイルダウンロードX
* ファイアウォールも有効
* 隠しファイルはデフォルト見えない

# 参考
http://docs.ansible.com/ansible/intro_windows.html
http://docs.ansible.com/ansible/list_of_windows_modules.html