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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$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 |