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 59855986 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 59855986 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 Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s