Windows Server 2008 R2をAnsibleからコントロールするには？

# Windows Server 2008 R2をAnsibleからコントロールするには？ #

- 対象のServer OS
Windows Server 2008 R2 Service Pack 1 / Windows Server 2012

## 事前にインストールしておくべきもの ##

- Windows Management Framework 4.0 
http://www.microsoft.com/en-us/download/details.aspx?id=42643

- Microsoft .NET Framework 4.5.2
http://www.microsoft.com/ja-jp/download/details.aspx?id=40855

## インストールできたか確認 ##

	PS C:\Users\Administrator> $PSVersionTable
	
	Name                           Value
	----                           -----
	PSVersion                      4.0
	WSManStackVersion              3.0
	SerializationVersion           1.1.0.1
	CLRVersion                     4.0.30319.34209
	BuildVersion                   6.3.9600.16406
	PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0}
	PSRemotingProtocolVersion      2.2
	
	PS C:\Users\Administrator>

## Basic認証を許可 ##

現在のAnsibleは、Basic認証を前提に開発されていますので、Basic認証を有効にします。

	PS C:\Users\Administrator>winrm set winrm/config/service/auth '@{Basic="true"}'
	Auth
	    Basic = true
	    Kerberos = true
	    Negotiate = true
	    Certificate = false
	    CredSSP = false
	    CbtHardeningLevel = Relaxed

## HTTPでの接続許可 ##

	PS C:\Users\Administrator> 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

## HTTPS 有効化 ##

HTTPでも暗号化して送信される為、プライベートなサブネットで特に問題はないかもしれませんが、通信路の暗号化を行うことにします。
証明書とか作るのも面倒なので、以下のスクリプトでザクッと作成してしまいます。

スクリプトは以下のものを少し修正しました。

https://github.com/ansible/ansible/blob/devel/examples/scripts/ConfigureRemotingForAnsible.ps1

	# Configure a Windows host for remote management with Ansible
	# -----------------------------------------------------------
	#
	# This script checks the current WinRM/PSRemoting configuration and makes the
	# necessary changes to allow Ansible to connect, authenticate and execute
	# PowerShell commands.
	# 
	# Set $VerbosePreference = "Continue" before running the script in order to
	# see the output messages.
	#
	# Written by Trond Hindenes <trond@hindenes.com>
	# Updated by Chris Church <cchurch@ansible.com>
	#
	# Version 1.0 - July 6th, 2014
	# Version 1.1 - November 11th, 2014
	
	Param (
	    [string]$SubjectName = $env:COMPUTERNAME,
	    [int]$CertValidityDays = 3650,
	    $CreateSelfSignedCert = $true
	)
	
	
	Function New-LegacySelfSignedCert
	{
	    Param (
	        [string]$SubjectName,
	        [int]$ValidDays = 3650
	    )
	    
	    $name = New-Object -COM "X509Enrollment.CX500DistinguishedName.1"
	    $name.Encode("CN=$SubjectName", 0)
	
	    $key = New-Object -COM "X509Enrollment.CX509PrivateKey.1"
	    $key.ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
	    $key.KeySpec = 1
	    $key.Length = 1024
	    $key.SecurityDescriptor = "D:PAI(A;;0xd01f01ff;;;SY)(A;;0xd01f01ff;;;BA)(A;;0x80120089;;;NS)"
	    $key.MachineContext = 1
	    $key.Create()
	
	    $serverauthoid = New-Object -COM "X509Enrollment.CObjectId.1"
	    $serverauthoid.InitializeFromValue("1.3.6.1.5.5.7.3.1")
	    $ekuoids = New-Object -COM "X509Enrollment.CObjectIds.1"
	    $ekuoids.Add($serverauthoid)
	    $ekuext = New-Object -COM "X509Enrollment.CX509ExtensionEnhancedKeyUsage.1"
	    $ekuext.InitializeEncode($ekuoids)
	
	    $cert = New-Object -COM "X509Enrollment.CX509CertificateRequestCertificate.1"
	    $cert.InitializeFromPrivateKey(2, $key, "")
	    $cert.Subject = $name
	    $cert.Issuer = $cert.Subject
	    $cert.NotBefore = (Get-Date).AddDays(-1)
	    $cert.NotAfter = $cert.NotBefore.AddDays($ValidDays)
	    $cert.X509Extensions.Add($ekuext)
	    $cert.Encode()
	
	    $enrollment = New-Object -COM "X509Enrollment.CX509Enrollment.1"
	    $enrollment.InitializeFromRequest($cert)
	    $certdata = $enrollment.CreateRequest(0)
	    $enrollment.InstallResponse(2, $certdata, 0, "")
	
	    # Return the thumbprint of the last installed cert.
	    Get-ChildItem "Cert:\LocalMachine\my"| Sort-Object NotBefore -Descending | Select -First 1 | Select -Expand Thumbprint
	}
		
	# Setup error handling.
	Trap
	{
	    $_
	    Exit 1
	}
	$ErrorActionPreference = "Stop"
	
	# Detect PowerShell version.
	If ($PSVersionTable.PSVersion.Major -lt 3)
	{
	    Throw "PowerShell version 3 or higher is required."
	}
	
	
	# Find and start the WinRM service.
	Write-Verbose "Verifying WinRM service."
	If (!(Get-Service "WinRM"))
	{
	    Throw "Unable to find the WinRM service."
	}
	ElseIf ((Get-Service "WinRM").Status -ne "Running")
	{
	    Write-Verbose "Starting WinRM service."
	    Start-Service -Name "WinRM" -ErrorAction Stop
	}
	
	
	# WinRM should be running; check that we have a PS session config.
	If (!(Get-PSSessionConfiguration -Verbose:$false) -or (!(Get-ChildItem WSMan:\localhost\Listener)))
	{
	    Write-Verbose "Enabling PS Remoting."
	    Enable-PSRemoting -Force -ErrorAction Stop
	}
	Else
	{
	    Write-Verbose "PS Remoting is already enabled."
	}
	

	# Test a remoting connection to localhost, which should work.
	$httpResult = New-PSSession -ComputerName "localhost" -ErrorVariable httpError -ErrorAction SilentlyContinue
	$httpsOptions = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
	$httpsResult = New-PSSession -UseSSL -ComputerName "localhost" -SessionOption $httpsOptions -ErrorVariable httpsError -ErrorAction SilentlyContinue
	
	If ($httpResult -and $httpsResult)
	{
	    Write-Verbose "HTTP and HTTPS sessions are enabled."
	}
	ElseIf ($httpsResult -and !$httpResult)
	{
	    Write-Verbose "HTTP sessions are disabled, HTTPS session are enabled."
	}
	ElseIf ($httpResult -and !$httpsResult)
	{
	    Write-Verbose "HTTPS sessions are disabled, HTTP session are enabled."
	}
	Else
	{
	    Throw "Unable to establish an HTTP or HTTPS remoting session."
	}
		
	# Make sure there is a SSL listener.
	$listeners = Get-ChildItem WSMan:\localhost\Listener
	If (!($listeners | Where {$_.Keys -like "TRANSPORT=HTTPS"}))
	{
	    # HTTPS-based endpoint does not exist.
	    If (Get-Command "New-SelfSignedCertificate" -ErrorAction SilentlyContinue)
	    {
	        $cert = New-SelfSignedCertificate -DnsName $env:COMPUTERNAME -CertStoreLocation "Cert:\LocalMachine\My"
	        $thumbprint = $cert.Thumbprint
	    }
	    Else
	    {
	        $thumbprint = New-LegacySelfSignedCert -SubjectName $env:COMPUTERNAME
	    }
	
	    # Create the hashtables of settings to be used.
	    $valueset = @{}
	    $valueset.Add('Hostname', $env:COMPUTERNAME)
	    $valueset.Add('CertificateThumbprint', $thumbprint)
	
	    $selectorset = @{}
	    $selectorset.Add('Transport', 'HTTPS')
	    $selectorset.Add('Address', '*')
	
	    Write-Verbose "Enabling SSL listener."
	    New-WSManInstance -ResourceURI 'winrm/config/Listener' -SelectorSet $selectorset -ValueSet $valueset
	}
	Else
	{
	    Write-Verbose "SSL listener is already active."
	}
	
	
	# Check for basic authentication.
	$basicAuthSetting = Get-ChildItem WSMan:\localhost\Service\Auth | Where {$_.Name -eq "Basic"}
	If (($basicAuthSetting.Value) -eq $false)
	{
	    Write-Verbose "Enabling basic auth support."
	    Set-Item -Path "WSMan:\localhost\Service\Auth\Basic" -Value $true
	}
	Else
	{
	    Write-Verbose "Basic auth is already enabled."
	}
	
	
	# Configure firewall to allow WinRM HTTPS connections.
	$fwtest1 = netsh advfirewall firewall show rule name="Allow WinRM HTTPS"
	$fwtest2 = netsh advfirewall firewall show rule name="Allow WinRM HTTPS" profile=any
	If ($fwtest1.count -lt 5)
	{
	    Write-Verbose "Adding firewall rule to allow WinRM HTTPS."
	    netsh advfirewall firewall add rule profile=any name="Allow WinRM HTTPS" dir=in localport=5986 protocol=TCP action=allow
	}
	ElseIf (($fwtest1.count -ge 5) -and ($fwtest2.count -lt 5))
	{
	    Write-Verbose "Updating firewall rule to allow WinRM HTTPS for any profile."
	    netsh advfirewall firewall set rule name="Allow WinRM HTTPS" new profile=any
	}
	Else
	{
	    Write-Verbose "Firewall rule already exists to allow WinRM HTTPS."
	}
		
	Write-Verbose "PS Remoting has been successfully configured for Ansible."