cloudpack の Sebastian です。
ansibleが何かはお勉強してね?
簡単に話すとsshを使ってremote serverの管理・構成を行うツールです。
Windowsでssh?
LinuxならSSH+shellでloginと操作ができるけれどWindowsにはsshdなんて入っていません。
じゃあ、どうやって繋ぐかと言うと公式documentには
Starting in version 1.7, Ansible also contains support for managing Windows machines. This uses native powershell remoting, rather than SSH.
(バージョン1.7からWindowsマシン管理のサポートを開始しました。これはSSHでは無くてWindows NativeのPowershellリモーティングを使います)
Ansible will still be run from a Linux control machine, and uses the “winrm” Python module to talk to remote hosts.
(Ansibleは未だにLinuxから実行され、リモートホストとの通信にwinrm python moduleを使用します。)
と書いてある
Windowsに対してはWinRMで繋いでPowershell 3.0を叩いて使うようだね
WinRMって?
Windows Remote Managementの事だね。
訳すとそのままWindows遠隔管理
読んで時の如く、Windowsを外から操作する為の管理システムだね
Windowsは2009年にWindows Vista / Windows Server 2003 R2以降にWindows Remote Management (WinRM) と Windows Remote Shell (WinRS) という機能が追加されて居ますね
どうやらAnsibleはこの機能に接続してWindowsを外から操作する事を試みているようです。
MicrosoftのWinRmの説明を見てみると
Windows リモート管理 (WinRM) サービスは、リモート管理用の WS-Management プロトコルを実装しています。WS-Management は、リモートのソフトウェアとハードウェアの管理に使用される標準の Web サービス プロトコルです。WinRM サービスは、ネットワーク上で WS-Management 要求をリッスンして処理します。WinRM サービスがネットワークをリッスンできるように、winrm.cmd コマンド ライン ツールを使用するかグループ ポリシーを介して、WinRM サービスにリスナを構成する必要があります。このサービスを使用することにより、WMI データへのアクセスとイベントの収集が可能になります。イベントの収集およびイベントに対するサブスクリプションを実行する場合、WinRM サービスが稼働している必要があります。WinRM メッセージは、HTTP および HTTPS をトランスポートとして使用します。WinRM サービスは IIS には依存しませんが、同じコンピュータ上で 1 つのポートを IIS と共有するよう事前に設定されています。このサービスでは URL のプレフィックスとして /wsman が予約されています。IIS との競合を避けるため、IIS 上のどの Web サイトでも /wsman が URL プレフィックスとして使用されていないことを確認する必要があります
HTTPとHTTPS上にてMessageの送受信を行うWS-Managementってプロトコルが在るわけですね。
先に書いておくとansibleからWinRMで幸せな通信を行うにはやることは6つ
- WinRMのserviceを起動せよ
- WinRM用にLisnerを作成せよ
- portを開ろ
- NetworkProfileのカテゴリをPrivateにしておこう
- HTTPからの受付の有効化
- Basic認証の許可
今更、Windows XPやWindows 2003は引っ張りだす事は少ないと思うけれど、もし古いマシンも管理に加えたいならここで更新してWindowsXPにWinRM入れておくと幸せになれるかも知れない。
↑
ごめん、WindowsXPやWindows2003はWinRM入ってもPowershell 3.0が入らないからansible使えないね。
WinRMの起動
Windows 2012 辺りだと最初から起動している事が多いようですね
さて、まずpowershellを立ち上げておもむろに
PS C:UsersAdministrator>winrm quickconfig
もしくは
PS C:UsersAdministrator>winrm qc
と打ち込んでみましょう
既にWinRMがserviceとして起動していると
WinRM service is already running on this machine. WinRM is already set up for remote management on this computer.
起動していないと以下の様に起動するか聞かれます
と聞かれますy
を入力すると下記のメッセージが出てWinRMが起動します。
WinRM is not set up to receive requests on this machine. The following changes must be mode; Start the winRM service. Make these changes [y/n]? WinRM has been updated to receive requests. WinRM service started. WinRM is already set up for remote management on this computer.
日本語だとこんな感じになりますね。
WinRM は、管理用にこのコンピュータへのリモート アクセスを許可するように設定されていません。 次の変更を行う必要があります: WinRM サービスの種類を遅延自動開始に設定します。 WinRM サービスを開始します。 このコンピュータ上のあらゆる IP への WS-Man 要求を受け付けるため、HTTP://* 上に WinRM リスナを作成します。 変更しますか [y/n]? y WinRM はリモート管理用に更新されました。 WinRM サービスの種類を正しく変更できました。 WinRM サービスが開始されました。 このコンピュータ上のあらゆる IP への WS-Man 要求を受け付けるため、HTTP://* 上に WinRM リスナを作成しました。
因みにコマンドラインで操作しなくてもコンピュータの管理からサービスを見ると当たり前にWindows Remote Management(WS-Management)
が居るのでこれを起動するだけで大丈夫です。
Listenerの作成
上記のwinrm qc
コマンドは名前通りQuick Configなので実はこの時点でLisnerが作成されててPortも空いてます。
手動でListenerを作る際は下記のコマンドで作成します。
PS C:UsersAdministrator> winrm create winrm/config/listener?Address=*+Transport=HTTP
既にQuickConfigにて作成されているので下の様なエラーメッセージが出て既に在るからもう作れないと言われて終わるだけですけれどね
English
WSManFault Message ProviderFault WSManFault Message = The WS-Management service cannot perform the configuration operation. A listener with Address=* and Transport=HTTP configuration already exists. You have to delete the existing listener first in order to be able to create it with the same Address and Transport values. Error number: -2144108493 0x80338033 The WS-Management service cannot create the resource because it already exists.
日本語
WSManFault Message ProviderFault WSManFault Message = WS-Management サービスは構成操作を実行できません。アドレス=* およびトランスポート=HTTP の構成のリスナは は既に存在します。 同じアドレスおよびトランスポート値で作成するには、まず既存のリスナを 削除する必要があります。 エラー番号: -2144108493 0x80338033 既に存在するため、WS-Management サービスはリソースを作成できません。
Portの解放
これもQuickConfigで開放されてるっぽいので特に必要無さそう
一応書いておくと以下のコマンドで通る
netsh firewall add portopening TCP 80 "Windows Remote Management"
実はここまで殆どcommand一つで出来るって書いてから気がついた
Enable-PSRemoting -Force
これだけで
- WinRMを起動する
- WinRMサービスのスタートアップの種類を自動に
- どのIPアドレスからでも受け付けるリスナ作成
- Windows FirewallにWS-Management traffic (httpのみ)の例外を作成
- WinRMで接続した時に管理者権限で実行の設定
が全部終わるらしい・・・..
しかもWindos2012 R2は初期状態で実行された状態になってるらしいよorz
尚、無効にする時は以下のcommandで行けます。
Disable-PSRemoting
NetworkProfileのカテゴリを変更する
はい、気を取り直して次!
最近のWindowsがnetwork繋ぐときに聞いてくるパブリックネットワークかプライベートネットワークか社内ネットワークかってやつね
取り敢えず今どうなってるか確認しておこう
PS C:UsersAdministrator> Get-NetConnectionProfile -IPv4Connectivity Internet
上のcommandを実行するとこんな感じに返ってくる
この中で確認するべきはNetworkCategoryの欄
Name : Network 2 InterfaceAlias : Ethernet InterfaceIndex : 12 NetworkCategory : Public IPv4Connectivity : Internet IPv6Connectivity : NoTraffic
Publicになっているので以下のcommandを入力してNetworkCategoryをPrivateに変更しておく
Set-NetConnectionProfile -InterfaceAlias (Get-NetConnectionProfile -IPv4Connectivity Internet).InterfaceAlias -NetworkCategory Private
Basic認証を許可しよう
Ansibleはパスワード投げて通信するので認証が許可されないと行けません。
そういう訳で認証の許可を行います。
WinRMのsetコマンドにて設定を書き換えます。
PS C:UsersAdministrator> winrm set winrm/config/service/auth '@{Basic="true"}'
以下の用に実行結果が返ります。
設定値auth以下の値が一覧で返ってきてBasic
がtrue
になっているのがわかります。
Auth Basic = true Kerberos = true Negotiate = true Certificate = false CredSSP = false CbtHardeningLevel = Relaxed
また、確認するだけならget
コマンドで同じ結果が取得できます。
PS C:UsersAdministrator> winrm get winrm/config/service/auth
HTTP通信からの接続を受け付ける
HTTPをトランスポートとして利用することを許可しないとAnsibleからの通信は通りません
そこで設定変えます
PS C:UsersAdministrator> winrm set winrm/config/service '@{AllowUnencrypted="true"}' Service RootSDDL = O:NSG:BAD:P(A;;GA;;;BA)(A;;GR;;;IU)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD) MaxConcurrentOperations = 4294967295 MaxConcurrentOperationsPerUser = 1500 EnumerationTimeoutms = 240000 MaxConnections = 300 MaxPacketRetrievalTimeSeconds = 120 AllowUnencrypted = true Auth Basic = true Kerberos = true Negotiate = true Certificate = false CredSSP = false CbtHardeningLevel = Relaxed DefaultPorts HTTP = 5985 HTTPS = 5986 IPv4Filter = * IPv6Filter = * EnableCompatibilityHttpListener = false EnableCompatibilityHttpsListener = false CertificateThumbprint AllowRemoteAccess = true PS C:UsersAdministrator> winrm get winrm/config/service
これでUndecryptedつまり暗号化されていないHTTPも受け付けると言う設定です。
最終的には以下の設定になればOKです
PS C:UsersAdministrator> winrm get winrm/config Config MaxEnvelopeSizekb = 500 MaxTimeoutms = 60000 MaxBatchItems = 32000 MaxProviderRequests = 4294967295 Client NetworkDelayms = 5000 URLPrefix = wsman AllowUnencrypted = false Auth Basic = true Digest = true Kerberos = true Negotiate = true Certificate = true CredSSP = false DefaultPorts HTTP = 5985 HTTPS = 5986 TrustedHosts = * Service RootSDDL = O:NSG:BAD:P(A;;GA;;;BA)(A;;GR;;;IU)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD) MaxConcurrentOperations = 4294967295 MaxConcurrentOperationsPerUser = 1500 EnumerationTimeoutms = 240000 MaxConnections = 300 MaxPacketRetrievalTimeSeconds = 120 AllowUnencrypted = true Auth Basic = true Kerberos = true Negotiate = true Certificate = false CredSSP = false CbtHardeningLevel = Relaxed DefaultPorts HTTP = 5985 HTTPS = 5986 IPv4Filter = * IPv6Filter = * EnableCompatibilityHttpListener = false EnableCompatibilityHttpsListener = false CertificateThumbprint AllowRemoteAccess = true Winrs AllowRemoteShellAccess = true IdleTimeout = 7200000 MaxConcurrentUsers = 10 MaxShellRunTime = 2147483647 MaxProcessesPerShell = 25 MaxMemoryPerShellMB = 1024 MaxShellsPerUser = 30
Powershell ?
公式Documentを見ると
Powershell 3.0 or higher is needed for most provided Ansible modules for Windows
(Windows用AnsibleモジュールはPowershell 3.0以上が必要です。)
現時点では最新環境は4.0でAnsibleが求めているのは3.0
WikipediaにあるWindowsとの対比表が以下の通り
「◎」標準搭載、「○」インストールして利用可能、「→」上位バージョン標準搭載、「×」利用不可
項目 | 1.0 | 2.0 | 3.0 | 4.0 |
---|---|---|---|---|
Windows Server 2003 | ○ | ○ | × | × |
Windows Server 2003 R2 | ○ | ○ | × | × |
Windows Server 2008 | ○ | ○ | × | |
Windows Server 2008 R2 | ◎ | ○ | ○ | |
Windows Server 2012 | ◎ | ○ | ||
Windows Server 2012 R2 | ◎ | |||
Windows XP x64 | ○ | × | × | × |
Windows XP | ○ | ○ | × | × |
Windows Vista | ○ | ○ | × | × |
Windows 7 | ◎ | ○ | ○ | |
Windows 8 | ◎ | × | ||
Windows 8.1 | ◎ |
この一覧からするとServerサイドはWindows 2008以上
一般的なWindowsだとWindows 7以上が必要条件になりそうである。
まぁ、XPはサポートの終了したOSなのでそもそも推奨するものではないので良いだろう
Windows2008などは標準でPowershell 2.0なのでバージョンアップが必要なのでMicrosoftからDownloadしてinstallしましょう
公式Documentに以下の様に記載しているのでここで指定されているscriptファイルを使っても良いかもしれない。
内容は単に、Powershellをdownloadして実行しているだけのようだし
Looking at an ansible checkout, copy the examples/scripts/upgrade_to_ps3.ps1 script onto the remote host and run a powershell console as an administrator. You will now be running Powershell 3 and can try connectivity again using the win_ping technique referenced above.
Ansible側の設定
WinRM対応
ansible側ではpython経由でWinRMに接続できるようにする必要があります。
PythonにはWinRM接続用のモジュールがリリースされておりこれを導入すればOKです。
以下のcommandを入力しpythonにpywinrmモジュールを追加して下さい。
pip install http://github.com/diyan/pywinrm/archive/master.zip#egg=pywinrm
BugFix
実はwinrmモジュールにはバグがあります
1行だけなのでLinuxの場合は以下のcommandで対処できます。
sed -i.bak '90s/exc.args[0]/exc/' /usr/lib/python2.7/site-packages/ansible/runner/connection_plugins/winrm.py
OSXでAnsibleを動作させている場合はLibraryのpathが異なるので以下の様になります。
sed -i.bak '90s/exc.args[0]/exc/' /Library/Python/2.7/site-packages/ansible/runner/connection_plugins/winrm.py
実際にやっていることはwinrm.pyの90行目にて.args[0]
と言う文字列を削除しているだけなので手で消しても問題は無い。
90c90 < err_msg = str(exc.args[0]) --- > err_msg = str(exc)
また、これらのcommandはpathがPythonのバージョンで異なるので任意のバージョンにて2.7の文字列を差し替えて考える。
Inventoryの指定
はい、じゃあAmazon ec2でWindows 2012 R2を起動して上の作業をサクサク進めます。
Windows2012 R2はPowershellは3.0以上でWinRMも最初から入っているのでNetwork Profileのcategory変えて認証を通して上げてHTTP通信許可すればそれで通ります。
ansible側のinventoryの設定は以下のように指定します。
[windows] ec2_windows ansible_ssh_host=54.64.xxx.xxx ansible_ssh_user=Administrator ansible_ssh_pass=XXXXXXXXX ansible_ssh_port=5985 ansible_connection=winrm
remote hostがLinuxの場合と異なる点は ansible_connection
の値がwinrm
である点とansible_ssh_port
の値が5985
となっている点です。
実行してみる
試しにWindows用のpingモジュールを使ってみる
一応、Debug出力も指定(-vvvv)して何が返るかを確認してみよう
ansible -m win_ping ec2_windows -vvvv
01 <54.64.xxx.xxx> ESTABLISH WINRM CONNECTION FOR USER: Administrator on PORT 5985 TO 54.64.xxx.xxx 02 <54.64.xxx.xxx> WINRM CONNECT: transport=plaintext endpoint=http://54.64.xxx.xxx:5985/wsman 03 <54.64.xxx.xxx> REMOTE_MODULE win_ping 04 <54.64.xxx.xxx> EXEC (New-Item -Type Directory -Path $env:temp -Name "ansible-tmp-1414376293.75-207546716604835").FullName | Write-Host -Separator ''; 05 <54.64.xxx.xxx> WINRM EXEC 'PowerShell' ['-NoProfile', '-NonInteractive', '-EncodedCommand','KABOAGUAdwAtAEkAdABlAG0AIAAtAFQAeQBwAGUAIABEAGkAcgBlAGMAdABvAHIAeQAgAC0AUABhAHQAaAAgACQAZQBuAHYAOgB0AGUAbQBwACAALQBOAGEAbQBlACAAIgBhAG4AcwBpAGIAbABlAC0AdABtAHAALQAxADQAMQA0ADMANwA2ADIAOQAzAC4ANwA1AC0AMgAwADcANQA0ADYANwAxADYANgAwADQAOAAzADUAIgApAC4ARgB1AGwAbABOAGEAbQBlACAAfAAgAFcAcgBpAHQAZQAtAEgAbwBzAHQAIAAtAFMAZQBwAGEAcgBhAHQAbwByACAAJwAnADsA'] 06 <54.64.xxx.xxx> WINRM RESULT07 <54.64.xxx.xxx> PUT /tmp/tmpKkQpAi TO C:UsersAdministratorAppDataLocalTempansible-tmp-1414376293.75-207546716604835\win_ping 08 <54.64.xxx.xxx> WINRM PUT /tmp/tmpKkQpAi to C:UsersAdministratorAppDataLocalTempansible-tmp-1414376293.75-207546716604835\win_ping.ps1 (offset=0 size=2030) 09 <54.64.xxx.xxx> WINRM PUT /tmp/tmpKkQpAi to C:UsersAdministratorAppDataLocalTempansible-tmp-1414376293.75-207546716604835\win_ping.ps1 (offset=2030 size=2030) 10 <54.64.xxx.xxx> WINRM PUT /tmp/tmpKkQpAi to C:UsersAdministratorAppDataLocalTempansible-tmp-1414376293.75-207546716604835\win_ping.ps1 (offset=4060 size=1425) 11 <54.64.xxx.xxx> PUT /tmp/tmpC6QNR7 TO C:UsersAdministratorAppDataLocalTempansible-tmp-1414376293.75-207546716604835\arguments 12 <54.64.xxx.xxx> WINRM PUT /tmp/tmpC6QNR7 to C:UsersAdministratorAppDataLocalTempansible-tmp-1414376293.75-207546716604835\arguments (offset=0 size=2) 13 <54.64.xxx.xxx> EXEC PowerShell -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -File "C:UsersAdministratorAppDataLocalTempansible-tmp-1414376293.75-207546716604835\win_ping.ps1" "C:UsersAdministratorAppDataLocalTempansible-tmp-1414376293.75-207546716604835\arguments"; Remove-Item "C:UsersAdministratorAppDataLocalTempansible-tmp-1414376293.75-207546716604835" -Force -Recurse; 14 <54.64.xxx.xxx> WINRM EXEC 'PowerShell' ['-NoProfile', '-NonInteractive', '-EncodedCommand','UABvAHcAZQByAFMAaABlAGwAbAAgAC0ATgBvAFAAcgBvAGYAaQBsAGUAIAAtAE4AbwBuAEkAbgB0AGUAcgBhAGMAdABpAHYAZQAgAC0ARQB4AGUAYwB1AHQAaQBvAG4AUABvAGwAaQBjAHkAIABVAG4AcgBlAHMAdAByAGkAYwB0AGUAZAAgAC0ARgBpAGwAZQAgACIAQwA6AFwAVQBzAGUAcgBzAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcgBcAEEAcABwAEQAYQB0AGEAXABMAG8AYwBhAGwAXABUAGUAbQBwAFwAYQBuAHMAaQBiAGwAZQAtAHQAbQBwAC0AMQA0ADEANAAzADcANgAyADkAMwAuADcANQAtADIAMAA3ADUANAA2ADcAMQA2ADYAMAA0ADgAMwA1AFwAXAB3AGkAbgBfAHAAaQBuAGcALgBwAHMAMQAiACAAIgBDADoAXABVAHMAZQByAHMAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByAFwAQQBwAHAARABhAHQAYQBcAEwAbwBjAGEAbABcAFQAZQBtAHAAXABhAG4AcwBpAGIAbABlAC0AdABtAHAALQAxADQAMQA0ADMANwA2ADIAOQAzAC4ANwA1AC0AMgAwADcANQA0ADYANwAxADYANgAwADQAOAAzADUAXABcAGEAcgBnAHUAbQBlAG4AdABzACIAOwAgAFIAZQBtAG8AdgBlAC0ASQB0AGUAbQAgACIAQwA6AFwAVQBzAGUAcgBzAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcgBcAEEAcABwAEQAYQB0AGEAXABMAG8AYwBhAGwAXABUAGUAbQBwAFwAYQBuAHMAaQBiAGwAZQAtAHQAbQBwAC0AMQA0ADEANAAzADcANgAyADkAMwAuADcANQAtADIAMAA3ADUANAA2ADcAMQA2ADYAMAA0ADgAMwA1AFwAIgAgAC0ARgBvAHIAYwBlACAALQBSAGUAYwB1AHIAcwBlADsA'] 15 <54.64.xxx.xxx> WINRM RESULT ec2_windows | success >> { "changed": false, "ping": "pong" }
見ていると、responseの4行目でremoteのtemporaryにansible-tmp-1414376293.75-207546716604835"
ディレクトリを作成しているようだ
どうやらlinux用の設定のためかansible.cfgのremote_tmpは無視されている。
5行目は暗号化されているcommandをPowershellにて実行して6行目はそのresult
7行目以降はansibleのresponse用の一時ファイルを転送しているのでここでまずC:\Users\ユーザー名\AppData\Local\Temp\
にansible用にresponerのPowershellを転送している。
14行目で実際にpingを実行して15行目がresponse結果である。
元記事はこちらです。
「ANSIBLEからWINDOWSを叩く」