この記事は、FUJITSU Advent Calendar 2017 の 13日目の記事です。
11日目の記事「ansibleでリモート側にある複数のzipファイルを展開する方法」 に引き続き、Ansible について書きます。
MS が今年リリースした機能の中で、個人的に素晴らしいと感じた 2 つの機能をベースに、Azure 上に Windows 開発環境を構築してみました。また、今年 9 月にリリースされた Ansible 2.4 で正式追加となったモジュール win_dsc を実際に触ってみました。
tl;dr
- Nested Virtualization in Azure は開発環境にやさしい
- Ansible on Windows Subsystem for Linux は開発環境の整備に使える
- Ansible v2.4 から導入された win_dsc モジュールは、Windows PowerShell Desired State Configuration の 組み込みリソース もしくは PowerShell Gallery にあるカスタムリソースを使いたい場合には有効
注意事項
The Windows Subsystem for Linux is not supported by Microsoft or Ansible and should not be used for production systems.
- Can Ansible run on Windows? - Ansible doc
WSL で Ansible を動作させることは、Production 環境ではサポートされていないため、ご注意ください。
Ansible と Desired State Configuration
Ansible
RedHat が提供するシンプルな運用自動化ツールです。オープンソースコミュニティでの開発も活発です。Playbook と呼ばれる YAML 形式の設定ファイルに、宣言的にリソースの状態を記述します。Chef や Puppet との違いは @IT の記事にまとめられています。Ansible のエンジンやモジュール (ライブラリ) は、Python で開発されていますが、Windows モジュールに関しては、対象マシンとの接続に使う WinRM プロトコルと親和性が高く、.Net フレームワークの恩恵が得られる Powershell で開発されています。
Ansible は、Ansibleの処理単位である Role の共有サービス Ansible Galaxy や、Ansible エンジンに加え GUI ダッシュボードやタスクのスケジューリングを可能にする Ansible Tower、そのアップストリーム版にあたる AWX などを提供しています。
また、コンテナイメージを Ansible の既存の Playbook を使って作成したり、実行したりできる Ansible Container なんかもあります。
Desired State Configuration
Microsoft が提供する Powershell をベースとした構成管理ツールです。こちらもオープンソースとして公開されていますが、開発コミュニティは活発とは言えません。DSC は対象マシンへ WinRM で接続したあと、現在の状態の取得、構成に変更が必要かチェック、構成の変更の3段階に分けてタスクを実行します。Ansible でいうモジュールは、DSC ではリソースと呼ばれます。
win_dsc モジュール
Ansible の win_dsc モジュールは、対象ホスト上に存在する DSC リソースを実行する wrapper モジュールです。もともと、非公式のモジュールとして公開されていましたが、v2.4 からめでたく公式モジュール入りしました。ただでさえ少ない Windows モジュールの救世主になるのではと、多くの人が待ち望んでいたであろうモジュールです。(遠い目)
Ansible on Windows Subsystem for Linux
今回は、Azure 上に Nested Virtualization に対応した Dv3 サイズ の VM を立て、そこでいろいろと遊びました。WSL を試したいので、Windows 10 Pro, Version 1709 の VM イメージを使っています。Windows Server 2016 もバージョン 1709 から Semi-Anual Channel リリースに対応していますが、デスクトップ搭載の Windows Server は未対応なため、今回は Windows 10 を選択しました。
また、Vagrant を使って VirtualBox 上にゲスト OS (検証環境) を構築し、win_dsc モジュールを試します。
ホスト OS の構成
ホスト OS の構成は以下の通りです。
Azure IaaS
- VM サイズ: Standard D2 v3 (2 vcpu 数、8 GB メモリ)
- OS: Windows 10, Version 1709 (OS Build 16299.64)
- VirtualBox: v5.1.30
- Vagrant: v2.0.1
Ansible 実行環境を整備
WSL で Ubuntu のパッケージを使用しました。Ubuntu の導入に関しては、Build Insider の記事に詳しく書かれているため割愛します。
$ cat /etc/os-release
NAME="Ubuntu"
VERSION="16.04.3 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.3 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial
Ansible のインストール手順は公式のドキュメントを見るのが正確ですが、参考までに記載しておきます。今回はドメインに参加しているアカウントでもクレデンシャルを委譲してよしなにやってくれる CredSSP 認証によって WinRM 接続を確立するよう設定します。
$ sudo apt-get update
$ sudo apt-get install python-pip git libffi-dev libssl-dev -y
$ sudo pip install --upgrade pip
$ sudo pip install ansible pywinrm
$ sudo pip install pywinrm[credssp]
$ ansible --version
ansible 2.4.1.0
config file = None
configured module search path = [u'/home/toversus/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /home/toversus/.local/lib/python2.7/site-packages/ansible
executable location = /home/toversus/.local/bin/ansible
python version = 2.7.12 (default, Nov 19 2016, 06:48:10) [GCC 5.4.0 20160609]
ホスト OS との接続確認
Ansible が公式に提供している Windows 環境用のセットアップスクリプト examples/scripts/ConfigureRemotingForAnsible.ps1 をホスト OS 上で実行します。
PS > Invoke-WebRequest `
-Uri https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1 `
-OutFile ConfigureRemotingForAnsible.ps1
PS > powershell -ExecutionPolicy RemoteSigned .\ConfigureRemotingForAnsible.ps1 -EnableCredSSP
インベントリファイルを作成し、接続先のマシンの情報を書いておきます。
[windows]
localhost
[windows:vars]
ansible_user=<your_account>
ansible_password=<your_password>
ansible_port=5986
ansible_connection=winrm
ansible_winrm_server_cert_validation=ignore
ホスト OS に対して win_ping モジュールで疎通確認します。
$ ansible windows -m win_ping -i development
localhost | SUCCESS => {
"changed": false,
"failed": false,
"ping": "pong"
}
無事ホスト OS との接続を確立できました。
SSL 絡みのエラーが出たら...
localhost | UNREACHABLE! => {
"changed": false,
"msg": "ssl: HTTPSConnectionPool(host='localhost', port=5986): Max retries exceeded with url: /wsman (Caused by SSLError(SSLError(\"bad handshake: SysCallError(-1, 'Unexpected EOF')\",),))",
"unreachable": true
}
ConfigureRemotingForAnsible.ps1 に「-ForceNewSSLCert」オプションを渡して、自己署名証明書を再度発行しましょう。
PS > powershell -ExecutionPolicy RemoteSigned .\ConfigureRemotingForAnsible.ps1 -ForceNewSSLCert
開発環境の整備
せっかくなので、Ansible on WSL でホスト OS を開発環境として整備してみます。
作業ディレクトリの構成
Ansible のベストプラクティスに書かれているディレクトリ構成を参考にしました。
$ tree
.
├── ansible.cfg
├── dev4win.yml
├── development
└── roles
└── dev4win
├── handlers
│ └── main.yml
└── tasks
└── main.yml
4 directories, 5 files
ansible.cfg の作成
Playbook の実行に失敗したときに、毎度 .retry ファイルを作成されると鬱陶しいので、.retry ファイルを作成しないよう設定します。また、Ansible の実行ログを保存するよう設定します。
[defaults]
retry_files_enabled = False
log_path=/var/tmp/ansible.log
handlers の定義
特定のタスクが実行され、状態が変化した (changed を返した) 場合に、それをトリガーとして起動するタスクを定義します。例えば、あるタスクが実行されたときだけマシンを再起動したい場合に使えます。その他 handlers の特徴は 「Ansible: notify と handlers の使い方について調べた」に詳しく書かれています。
- name: reboot a machine
win_reboot:
reboot_timeout_sec: 6000
test_command: whoami
- name: restart Explorer process
win_shell: Stop-Process -Name Explorer
tasks の定義
task で定義した内容は以下の通りです。
- システムロケールの変更
- ログインユーザーのパスワード無期限化
- 「自動的に現在のフォルダーまで展開する」を有効化
- 「ファイル名拡張子の表示」を有効化
- ファイアウォールの受信ルールで ping を受け付けるよう設定
- chocolatey 経由でパッケージをインストール
- chocolatey で入手したいパッケージはここで探せます。
- name: Get current system locale
win_shell: (Get-WinSystemLocale).Name
register: locale
changed_when: false
- name: Set system locale to ja-JP
win_shell: Set-WinSystemLocale ja-JP
when: locale.stdout.find('ja-JP') == -1
notify: reboot a machine
- name: Set individual user's password to never expire
win_user:
name: "{{ ansible_env.USERNAME }}"
password_expired: no
- name: Enable automatically expand to current folder option
win_regedit:
path: HKCU:\\Software\\Microsoft/Windows\\CurrentVersion\\Explorer\\Advanced
name: NavPaneExpandToCurrentFolder
data: 1
type: dword
notify: restart Explorer process
- name: Show file name extentions
win_regedit:
path: HKCU:\\Software\\Microsoft/Windows\\CurrentVersion\\Explorer\\Advanced
name: HideFileExt
data: 0
type: dword
notify: restart Explorer process
- name: Enable firewall rule for receiving echo request
win_firewall_rule:
name: Virtual Machine Monitoring (Echo Request - ICMPv4-In)
direction: in
action: allow
- name: Install latest packages via chocolatey
win_chocolatey:
name: "{{ item }}"
state: latest
with_items:
- chocolatey-core.extension
- 7zip.install
- visualstudiocode
- sourcetree
- teraterm
- git
- name: Install fixed verion of packages via chocolatey
win_chocolatey:
name: "{{ item.name }}"
version: "{{ item.version}}"
state: present
with_items:
- name: virtualbox
version: 5.1.30
- name: vagrant
version: 2.0.1
- name: nodejs.install
version: 6.12.1
- name: nvm
version: 1.1.5
- name: googlechrome
version: 62.0.3202.94
- name: selenium-chrome-driver
version: 2.33
Playbook の作成
Playbook を作成し、先ほど定義した task をインポートします。
- hosts: windows
gather_facts: yes
tasks:
- import_tasks: roles/dev4win/tasks/main.yml
handlers:
- import_tasks: roles/dev4win/handlers/main.yml
Playbook の実行
全てのタスクが実行されたあと、handlers で定義したホスト OS の再起動が走ります。
$ ansible-playbook dev4win.yml -i development
PLAY [windows] *************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************
ok: [localhost]
TASK [Get current system locale] ***************************************************************************************
ok: [localhost]
TASK [Set system locale to ja-JP] **************************************************************************************
changed: [localhost]
TASK [Set individual user's password to never expire] ******************************************************************
ok: [localhost]
TASK [Enable automatically expand to current folder option] ************************************************************
changed: [localhost]
TASK [Show file name extentions] ***************************************************************************************
changed: [localhost]
TASK [Enable firewall rule for receiving echo request] *****************************************************************
changed: [localhost]
TASK [Install latest packages via chocolatey] **************************************************************************
changed: [localhost] => (item=chocolatey-core.extension)
changed: [localhost] => (item=7zip.install)
changed: [localhost] => (item=visualstudiocode)
changed: [localhost] => (item=sourcetree)
changed: [localhost] => (item=teraterm)
changed: [localhost] => (item=git)
[WARNING]: Chocolatey was missing from this system, so it was installed during this task run.
TASK [Install fixed verion of packages via chocolatey] **************************************************************************
changed: [localhost] => (item={u'version': u'5.1.30', u'name': u'virtualbox'})
changed: [localhost] => (item={u'version': u'2.0.1', u'name': u'vagrant'})
changed: [localhost] => (item={u'version': u'6.12.1', u'name': u'nodejs.install'})
changed: [localhost] => (item={u'version': u'1.1.5', u'name': u'nvm'})
changed: [localhost] => (item={u'version': u'62.0.3202.94', u'name': u'googlechrome'})
changed: [localhost] => (item={u'version': u'2.33', u'name': u'selenium-chrome-driver'})
追加のタスクを定義
追加で以下のタスクを定義してみます。
- GO のインストール
- GO の作業フォルダの作成
- ユーザー環境変数に GOPATH を追加
全てのタスクを一から実行するのはだるいので、tags を設定して追加のタスクのみ実行します。
- name: Install Golang v1.9.2 via chocolatey
win_chocolatey:
name: golang
version: 1.9.2
state: present
tags: golang
- name: Create Go workplace
win_file:
path: "{{ ansible_env.USERPROFILE }}/go"
state: directory
tags: golang
- name: Set GOPATH environment variable for current user
win_path:
name: GOPATH
elements: "{{ ansible_env.USERPROFILE }}/go"
scope: user
state: present
tags: golang
追加のタスクの実行
tags オプションを指定することで、特定のタスクのみを実行できます。
$ ansible-playbook dev4win.yml -i development --tags golang
PLAY [windows] **************************************************************************************
TASK [Gathering Facts] ******************************************************************************
ok: [localhost]
TASK [Install Golang v1.9.2 via chocolatey] *********************************************************
changed: [localhost]
TASK [Create Go workplace] **************************************************************************
changed: [localhost]
TASK [Set GOPATH environment variable for current user] *********************************************
changed: [localhost]
PLAY RECAP ******************************************************************************************
localhost : ok=4 changed=3 unreachable=0 failed=0
以上で、Windows 10 開発環境の整備は終了です。
VirtualBox 上に Windows Server 2016 評価版を構築
ベースとなる Vagrant box として「gusztavvargadr/w16s Vagrant box」を使用しました。
作成した Vagrantfile は以下の通りです。「config.winrm.retry_limit」と「config.winrm.retry_delay」 は、VM の起動でタイムアウトが頻発したので付け足しました。
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
config.vm.box = "gusztavvargadr/w16s"
config.vm.network "private_network", ip: "192.168.56.10"
config.winrm.retry_limit = 30
config.winrm.retry_delay = 20
config.vm.provider "virtualbox" do |vb|
vb.gui = true
vb.name = "win2016-test01"
vb.memory = "2048"
vb.cpus = 1
vb.customize [ "modifyvm", :id, "--clipboard", "bidirectional", "--draganddrop", "bidirectional" ]
end
end
- Windows Server 2016 評価版 box を起動します。
PS> vagrant up
ゲスト OS で WinRM のセットアップ
ホスト OS のときと同じなので割愛します。
動作確認
hosts ファイルに接続先の情報を追記します。
[winservers]
192.168.56.10
[winservers:vars]
ansible_user=vagrant
ansible_password=vagrant
ansible_port=5986
ansible_connection=winrm
ansible_winrm_server_cert_validation=ignore
ansible_winrm_transport=credssp
win_ping モジュールで疎通確認します。
$ ansible winservers -m win_ping -i staging
192.168.56.10 | SUCCESS => {
"changed": false,
"failed": false,
"ping": "pong"
}
win_dsc モジュールを使う
ここからやっと本題です。
今回は、xCertificate リソースを操って、証明書ストアに .pfx ファイル証明書をインポートしてみます。
作業ディレクトリの構成
$ tree
.
├── ansible.cfg
├── dev4win.yml
├── development
├── roles
│ ├── common
│ │ ├── certs
│ │ │ └── myserver.pfx
│ │ └── tasks
│ │ └── main.yml
│ └── dev4win
│ ├── handlers
│ │ └── main.yml
│ └── tasks
│ └── main.yml
├── staging
└── winservers.yml
7 directories, 9 files
task の定義
xCertificate リソースに必要な引数はここで確認できます。証明書のインポートに必要なクレデンシャルを
PSCredential で渡しているのが分かります。Ansible で PSCredential を取り扱うときは、注意が必要です。クレデンシャルの情報をログに吐き出さないようにするためには、no_log:true を定義したタスクに追加する必要があります。
- name: install xCertificate DSC resource
win_psmodule:
name: xCertificate
state: present
- name: Copy certs file to tmp directory
win_copy:
src: roles/common/certs/myserver.pfx
dest: C:/tmp/
- name: import pfx certificate
win_dsc:
resource_name: xPfxImport
Thumbprint: 8b952114b7774787a2d7c7492eb8ca15473cfffd
Path: C:/tmp/myserver.pfx
Location: LocalMachine
Store: My
Credential_username: vagrant
Credential_password: XXXXXXXXXX
no_log: true
Playbook の作成
先ほど定義した task をインポートする Playbook を作成します。
- hosts: staging
gather_facts: no
tasks:
- import_tasks: roles/common/tasks/main.yml
Playbook の実行
$ ansible-playbook winservers.yml -i staging
PLAY [winservers] *********************************************************************************************************
TASK [install xCertificate DSC resource] *******************************************************************************
changed: [192.168.56.10]
TASK [Copy certs file to tmp directory] ********************************************************************************
changed: [192.168.56.10]
TASK [import pfx certificate] ******************************************************************************************
changed: [192.168.56.10]
PLAY RECAP *************************************************************************************************************
192.168.56.10 : ok=0 changed=3 unreachable=0 failed=0
素晴らしいですね!
2017/12/13 現在 Ansible の Windows モジュールに証明書を扱うモジュールはないので、Powershell のコマンドレットを組み合わたり、冪等性をどう担保するか考える必要がなくなり (証明書の場合は、証明書ストアに Test-Path して無かったら入れればよいだけですが)、楽になります。ただし、xCertificate は試験的なリソースとという扱いなので、何かあっても自己責任な部分は変わりません。
他にも多くの DSC リソースが Powershell Gallery で公開されているので、探してみるのも良いかもしれません。(ちなみに、私は他に使えそうなリソースを見つけることができませんでした。)
まとめ
Ansible on WSL で Windows 開発環境を整備し、win_dsc モジュールを試してみました。win_dsc モジュールに関しては、魅力的な only on DSC リソースが個人的に少ないと感じたので、今後に期待です。WSL 自体はどんどん機能拡張していっており、次期 Windows 10 メジャーアップデートからバックグランドタスクにも対応するかもしれません。Windows で開発 (笑) と笑われない時代が割と真面目に来る予感がしています。
注意: この記事の内容は、個人の見解であり、所属する会社・組織を代表するものではありません。