ConfigureRemotingForAnsible.ps1 6.1 KB
Newer Older
1 2 3 4 5 6 7 8 9
# 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.
10 11
#
# Written by Trond Hindenes <trond@hindenes.com>
12
# Updated by Chris Church <cchurch@ansible.com>
13 14
#
# Version 1.0 - July 6th, 2014
15
# Version 1.1 - November 11th, 2014
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

Param (
    [string]$SubjectName = $env:COMPUTERNAME,
    [int]$CertValidityDays = 365,
    $CreateSelfSignedCert = $true
)


Function New-LegacySelfSignedCert
{
    Param (
        [string]$SubjectName,
        [int]$ValidDays = 365
    )
    
31
    $name = New-Object -COM "X509Enrollment.CX500DistinguishedName.1"
32 33
    $name.Encode("CN=$SubjectName", 0)

34
    $key = New-Object -COM "X509Enrollment.CX509PrivateKey.1"
35 36 37 38 39 40 41
    $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()

42
    $serverauthoid = New-Object -COM "X509Enrollment.CObjectId.1"
43
    $serverauthoid.InitializeFromValue("1.3.6.1.5.5.7.3.1")
44 45 46
    $ekuoids = New-Object -COM "X509Enrollment.CObjectIds.1"
    $ekuoids.Add($serverauthoid)
    $ekuext = New-Object -COM "X509Enrollment.CX509ExtensionEnhancedKeyUsage.1"
47 48
    $ekuext.InitializeEncode($ekuoids)

49
    $cert = New-Object -COM "X509Enrollment.CX509CertificateRequestCertificate.1"
50 51 52
    $cert.InitializeFromPrivateKey(2, $key, "")
    $cert.Subject = $name
    $cert.Issuer = $cert.Subject
53
    $cert.NotBefore = (Get-Date).AddDays(-1)
54 55 56 57
    $cert.NotAfter = $cert.NotBefore.AddDays($ValidDays)
    $cert.X509Extensions.Add($ekuext)
    $cert.Encode()

58
    $enrollment = New-Object -COM "X509Enrollment.CX509Enrollment.1"
59 60 61 62
    $enrollment.InitializeFromRequest($cert)
    $certdata = $enrollment.CreateRequest(0)
    $enrollment.InstallResponse(2, $certdata, 0, "")

63 64
    # Return the thumbprint of the last installed cert.
    Get-ChildItem "Cert:\LocalMachine\my"| Sort-Object NotBefore -Descending | Select -First 1 | Select -Expand Thumbprint
65 66 67
}


68 69 70 71 72 73
# Setup error handling.
Trap
{
    $_
    Exit 1
}
74 75
$ErrorActionPreference = "Stop"

76 77 78

# Detect PowerShell version.
If ($PSVersionTable.PSVersion.Major -lt 3)
79
{
80
    Throw "PowerShell version 3 or higher is required."
81 82 83
}


84 85 86 87 88 89 90 91 92 93 94
# 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
}
95 96


97 98 99 100
# 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."
101
    Enable-PSRemoting -Force -ErrorAction Stop
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
}
Else
{
    Write-Verbose "PS Remoting is already enabled."
}


# Test a remoting connection to localhost, which should work.
$httpResult = Invoke-Command -ComputerName "localhost" -ScriptBlock {$env:COMPUTERNAME} -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)
139 140
    {
        $cert = New-SelfSignedCertificate -DnsName $env:COMPUTERNAME -CertStoreLocation "Cert:\LocalMachine\My"
141
        $thumbprint = $cert.Thumbprint
142
    }
143 144 145 146 147
    Else
    {
        $thumbprint = New-LegacySelfSignedCert -SubjectName $env:COMPUTERNAME
    }

148 149
    # Create the hashtables of settings to be used.
    $valueset = @{}
150 151
    $valueset.Add('Hostname', $env:COMPUTERNAME)
    $valueset.Add('CertificateThumbprint', $thumbprint)
152 153

    $selectorset = @{}
154 155
    $selectorset.Add('Transport', 'HTTPS')
    $selectorset.Add('Address', '*')
156

157 158 159 160 161 162 163
    Write-Verbose "Enabling SSL listener."
    New-WSManInstance -ResourceURI 'winrm/config/Listener' -SelectorSet $selectorset -ValueSet $valueset
}
Else
{
    Write-Verbose "SSL listener is already active."
}
164 165


166 167 168 169 170
# 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."
171
    Set-Item -Path "WSMan:\localhost\Service\Auth\Basic" -Value $true
172 173 174 175 176 177
}
Else
{
    Write-Verbose "Basic auth is already enabled."
}

178

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
# 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."
}
196 197


198
Write-Verbose "PS Remoting has been successfully configured for Ansible."