Remote PowerShell / WinRM

There’s a lot of articles online how to setup remote PowerShell or how to configure remote PowerShell. I have found that all articles on how to setup remote PowerShell are not all complete. As in there’s some information there, different parts of information all over the place across different posts and not in the complete order and/or missing steps.

You need Remote PowerShell to administer Windows servers and these days with PowerShell, you can do everything and anything with PowerShell, so remote PowerShell is a must.

Remote PowerShell is a little hard to setup and comes in two flavours, HTTP (port 5985) and HTTPS (port 5986). In the theme of security, this post will focus on the most secure way of setting up Remote PowerShell, port 5986 HTTPS with SSL. Also too, I am not focusing on domain based machines, I am focusing on just stock standard machines, machines not connected to the domain aka ‘workgroup’ servers.

Two script below, one which assumes you already have a trusted cert setup, the other assumes you want to create a brand new self signed cert. Both Scripts take into consideration of delegated authentication – to set the stage for if you want to use CredSSP.


# This Sets up WinRM Remote PowerShell with delegated authentication for an existing trusted SSL cert
########################################################################
################ Do this on the Server
########################################################################
#Enable WinRM on a VM – with on-demand certificate creation
Function RemotePowerShell {
$process = 'cmd.exe'
$arguments = '/c winrm invoke restore winrm/config @{}'
start-process $process -ArgumentList $arguments -Wait
#enable Server Manager Remoting
Configure-SMRemoting.exe -enable
#Enable WinRM
Enable-PSRemoting -SkipNetworkProfileCheck -Force
<# Only if you really want to tighten up security, however this can break certain DSC functions which call various things locally on port HTTP
Get-ChildItem WSMan:\Localhost\listener | Where -Property Keys -eq "Transport=HTTP" | Remove-Item -Recurse
Remove-Item -Path WSMan:\Localhost\listener\listener* -Recurse
#>
$domain = 'domain.com'
$Hostname = "{0}$domain" -f '*.'
$CertificateThumbprint = (Get-ChildItem Cert:\LocalMachine\My | ? {$_.Subject -match $domain}).Thumbprint;
$CommandLine = "winrm create winrm/config/Listener?Address=*+Transport=HTTPS @{Hostname=`"$($Hostname)`";CertificateThumbprint=`"$($CertificateThumbprint)`"}";
CMD.EXE /C $CommandLine
Set-Item wsman:\localhost\client\trustedhosts * -Force
# Enable delegation of credentials
Enable-WSManCredSSP -Role Server -Force
Restart-Service winrm
}
Function FirewallRules {
################ Firewall Rules
New-NetFirewallRule -DisplayName "Windows Remote Management (HTTPS-In)" -Name "Windows Remote Management (HTTPS-In)" -Profile Any -LocalPort 5986 -Protocol TCP
New-NetFirewallRule -DisplayName "RemotePowerShell" -Direction Inbound –LocalPort 5985-5986 -Protocol TCP -Action Allow
}
RemotePowerShell
FirewallRules
########################################################################
################ Do this on the Client to setup delegated authentication
########################################################################
Enable-PSRemoting -Force
Enable-WSManCredSSP -Role Client -DelegateComputer * -Force
$allowed = @('WSMAN/*.domain.com')
$TopKey = 'hklm:\SOFTWARE\Policies\Microsoft\Windows\CredentialsDelegation'
if (!(Test-Path $TopKey)) {
md $TopKey
}
New-ItemProperty -Path $TopKey -Name AllowFreshCredentials -Value 1 -PropertyType Dword -Force
New-ItemProperty -Path $TopKey -Name AllowFreshCredentialsWhenNTLMOnly -Value 1 -PropertyType Dword -Force
New-ItemProperty -Path $TopKey -Name AllowSavedCredentialsWhenNTLMOnly -Value 1 -PropertyType Dword -Force
New-ItemProperty -Path $TopKey -Name ConcatenateDefaults_AllowFresh -Value 1 -PropertyType Dword -Force
New-ItemProperty -Path $TopKey -Name ConcatenateDefaults_AllowFreshNTLMOnly -Value 1 -PropertyType Dword -Force
New-ItemProperty -Path $TopKey -Name ConcatenateDefaults_AllowSavedNTLMOnly -Value 1 -PropertyType Dword -Force
$key = Join-Path $TopKey 'AllowFreshCredentialsWhenNTLMOnly'
if (!(Test-Path $key)) {
md $key
}
$i = 1
$allowed |% {
# Script does not take into account existing entries in this key
New-ItemProperty -Path $key -Name $i -Value $_ -PropertyType String -Force
$i++
}
$key = Join-Path $TopKey 'AllowSavedCredentialsWhenNTLMOnly'
if (!(Test-Path $key)) {
md $key
}
$i = 1
$allowed |% {
# Script does not take into account existing entries in this key
New-ItemProperty -Path $key -Name $i -Value $_ -PropertyType String -Force
$i++
}
$key = Join-Path $TopKey 'AllowFreshCredentials'
if (!(Test-Path $key)) {
md $key
}
# Script does not take into account existing entries in this key
$i = 1
New-ItemProperty -Path $key -Name $i -Value 'wsman/*' -PropertyType String -Force
Restart-Service WinRM

If you’re a cheap skate, you can have PowerShell create a brand new Self Signed certificate for you, which you can use.


# This Sets up WinRM Remote PowerShell with delegated authentication while creating a self signed certificate
########################################################################
################ Do this on the Server
########################################################################
#Enable WinRM on a VM – with on-demand certificate creation
Function RemotePowerShell {
$process = 'cmd.exe'
$arguments = '/c winrm invoke restore winrm/config @{}'
start-process $process -ArgumentList $arguments -Wait
#enable Server Manager Remoting
Configure-SMRemoting.exe -enable
#Enable WinRM
Enable-PSRemoting -SkipNetworkProfileCheck -Force
<# Only if you really want to tighten up security, however this can break certain DSC functions which call various things locally on port HTTP
Get-ChildItem WSMan:\Localhost\listener | Where -Property Keys -eq "Transport=HTTP" | Remove-Item -Recurse
Remove-Item -Path WSMan:\Localhost\listener\listener* -Recurse
#>
$Hostname = $env:COMPUTERNAME
$CertificateThumbprint = (New-SelfSignedCertificate -DnsName $Hostname -CertStoreLocation Cert:\LocalMachine\My).Thumbprint;
$CommandLine = "winrm create winrm/config/Listener?Address=*+Transport=HTTPS @{Hostname=`"$($Hostname)`";CertificateThumbprint=`"$($CertificateThumbprint)`"}";
CMD.EXE /C $CommandLine
Set-Item wsman:\localhost\client\trustedhosts * -Force
# Enable delegation of credentials
Enable-WSManCredSSP -Role Server -Force
Restart-Service winrm
}
Function FirewallRules {
################ Firewall Rules
New-NetFirewallRule -DisplayName "Windows Remote Management (HTTPS-In)" -Name "Windows Remote Management (HTTPS-In)" -Profile Any -LocalPort 5986 -Protocol TCP
New-NetFirewallRule -DisplayName "RemotePowerShell" -Direction Inbound –LocalPort 5985-5986 -Protocol TCP -Action Allow
}
RemotePowerShell
FirewallRules
########################################################################
################ Do this on the Client to setup delegated authentication
########################################################################
Enable-PSRemoting -Force
Enable-WSManCredSSP -Role Client -DelegateComputer * -Force
$allowed = @('WSMAN/*.domain.com')
$TopKey = 'hklm:\SOFTWARE\Policies\Microsoft\Windows\CredentialsDelegation'
if (!(Test-Path $TopKey)) {
md $TopKey
}
New-ItemProperty -Path $TopKey -Name AllowFreshCredentials -Value 1 -PropertyType Dword -Force
New-ItemProperty -Path $TopKey -Name AllowFreshCredentialsWhenNTLMOnly -Value 1 -PropertyType Dword -Force
New-ItemProperty -Path $TopKey -Name AllowSavedCredentialsWhenNTLMOnly -Value 1 -PropertyType Dword -Force
New-ItemProperty -Path $TopKey -Name ConcatenateDefaults_AllowFresh -Value 1 -PropertyType Dword -Force
New-ItemProperty -Path $TopKey -Name ConcatenateDefaults_AllowFreshNTLMOnly -Value 1 -PropertyType Dword -Force
New-ItemProperty -Path $TopKey -Name ConcatenateDefaults_AllowSavedNTLMOnly -Value 1 -PropertyType Dword -Force
$key = Join-Path $TopKey 'AllowFreshCredentialsWhenNTLMOnly'
if (!(Test-Path $key)) {
md $key
}
$i = 1
$allowed |% {
# Script does not take into account existing entries in this key
New-ItemProperty -Path $key -Name $i -Value $_ -PropertyType String -Force
$i++
}
$key = Join-Path $TopKey 'AllowSavedCredentialsWhenNTLMOnly'
if (!(Test-Path $key)) {
md $key
}
$i = 1
$allowed |% {
# Script does not take into account existing entries in this key
New-ItemProperty -Path $key -Name $i -Value $_ -PropertyType String -Force
$i++
}
$key = Join-Path $TopKey 'AllowFreshCredentials'
if (!(Test-Path $key)) {
md $key
}
# Script does not take into account existing entries in this key
$i = 1
New-ItemProperty -Path $key -Name $i -Value 'wsman/*' -PropertyType String -Force
Restart-Service WinRM

Then to connect using Remote Management, the below script is somethimng like what you would use.


$RemoteMachines = '1598.domain.com', '1599.domain.com'
$adminname = 'user.name'
$adminpassword = 'YourPassword'
$password = ConvertTo-SecureString $adminpassword -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential (".\$adminname", $password)
$SessionOptions = New-PSSessionOption –SkipCACheck –SkipCNCheck –SkipRevocationCheck
$PSSession = New-PSSession -ComputerName $RemoteMachines -Port 5986 -UseSSL -Authentication Credssp -Credential $cred -SessionOption $SessionOptions
# Do stuff
Invoke-Command -Session $PSSession -ScriptBlock {
gci C:
whoami
}
Get-PSSession | Remove-PSSession

Leave a comment