Yesterday I shared some PowerShell ideas for remotely setting up the server component of ssh on a remote Windows 10 system. This would allow you to establish an ssh session to the desktop or even use PowerShell remoting over ssh. Next, we need to look at doing the same thing for Windows Servers.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
Windows Server 2019
If you are using Windows Server 2019, you can use the same code I used with Windows 10, at least based on my experiences. Windows Server 2019 includes the OpenSSH.Server capability that you can install with Add-WindowsCapability. I was able to use the same deployment script I used with Windows 10 to install PowerShell 7 and sshd. The only change I made was to the Write-Progress activity.
$prog = @{
Activity = "Deploy SSH Server to Windows 10/Windows Server 2019"
Status = $Computername.toUpper()
CurrentOperation = ""
PercentComplete = 0
}
Otherwise, everything ran the same and in minutes PowerShell 7 and ssh remoting were ready to go.
Windows Server 2016
Installing for Windows Server 2016 is a bit different. According to the Microsoft documentation that I could find, you need to install the latest bits from GitHub. You can do that with Invoke-Webrequest. Although before you do anything, you will most likely need to configure network security.
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
If you don't do this on Windows Server 2016, you'll get errors trying to connect to GitHub and the PowerShell Gallery. Once the zip file is downloaded, you can extract it and then run the setup script. Here's the server-focused PowerShell script.
#requires -version 5.1
<#
setup SSHD on Windows Server 2016
https://github.com/PowerShell/Win32-OpenSSH/wiki/Install-Win32-OpenSSH
This script is designed to be run remotely using Invoke-Command
Sample usage:
Verbose and Whatif
invoke-command -FilePath .\Setup-SSHServer2.ps1 -ComputerName srv1 -cred $admin -ArgumentList @("Continue",$True)
Verbose
invoke-command -FilePath .\Setup-SSHServer2.ps1 -ComputerName srv1 -cred $admin -ArgumentList @("Continue")
#>
[cmdletbinding(SupportsShouldProcess)]
Param([string]$VerboseOption = "SilentlyContinue", [bool]$WhatIfOption = $False)
$VerbosePreference = $VerboseOption
$WhatIfPreference = $WhatIfOption
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$url = 'https://github.com/PowerShell/Win32-OpenSSH/releases/latest/'
$request = [System.Net.WebRequest]::Create($url)
$request.AllowAutoRedirect = $false
$response = $request.GetResponse()
$dl = $([String]$response.GetResponseHeader("Location")).Replace('tag', 'download') + '/OpenSSH-Win64.zip'
$zip = "C:\OpenSSH64.zip"
Write-Verbose "download SSH from $dl to $zip"
if ($pscmdlet.ShouldProcess($dl,"Downloading OpenSSH-Win64 Package")) {
Try {
Invoke-WebRequest -Uri $dl -OutFile $zip -DisableKeepAlive -ErrorAction Stop
}
Catch {
Throw $_
}
if (Test-Path $zip) {
Write-Verbose "Extract zip file"
Expand-Archive $zip -DestinationPath "c:\Program Files" -force
}
else {
Return "zip file failed to download"
}
}
Write-Verbose "Install SSH"
if ($pscmdlet.ShouldProcess("install-sshd.ps1", "Execute")) {
Push-Location
Set-Location -Path 'C:\Program Files\OpenSSH-Win64\'
. .\install-sshd
Pop-Location
}
Write-Verbose "Set sshd to auto start"
Try {
if ($pscmdlet.ShouldProcess("sshd", "Configure service")) {
Set-Service -Name sshd -StartupType Automatic -ErrorAction stop
Write-Verbose "Start the sshd service"
Start-Service -Name sshd -ErrorAction Stop
Get-Service sshd | Select-Object -Property * | Out-String | Write-Verbose
}
}
Catch {
Write-Warning "There was a problem configuring the sshd service."
}
Write-Verbose "Add the firewall rule"
if ($pscmdlet.ShouldProcess("sshd", "Add firewall rule")) {
Try {
$rule = New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 -ErrorAction Stop
Write-Verbose ($rule | Out-String)
}
Catch {
Throw $_
}
}
if (-Not $WhatIfOption) {
#only display the summary if not using -WhatIf
$msg = @"
SSH setup complete. Edit $ENV:ProgramData\ssh\sshd_config for additional configurations options
or to enable remoting under PowerShell 7.
You will need to restart the sshd service for changes to take effect.
"@
Write-Host $msg -ForegroundColor green
}
Write-Verbose "Ending SSHD setup process."
As with the Windows 10 version of the script, this is designed to be run remotely using Invoke-Command. And of course, I have a control script.
#requires -version 5.1
#Deployment control script to setup SSH server on a remote Windows Server 2016
#It is assumed PowerShell 7 is, or will be, installed.
[cmdletbinding(SupportsShouldProcess)]
Param(
[Parameter(Position = 0, Mandatory)]
[string]$Computername,
[pscredential]$Credential,
[switch]$InstallPowerShell
)
#remove parameters from PSBoundparameter that don't apply to New-PSSession
if ($PSBoundParameters.ContainsKey("InstallPowerShell")) {
[void]($PSBoundParameters.remove("InstallPowerShell"))
}
if ($PSBoundParameters.ContainsKey("WhatIf")) {
[void]($PSBoundParameters.remove("WhatIf"))
}
#parameters for Write-Progress
$prog = @{
Activity = "Deploy SSH Server to Windows Server 2016"
Status = $Computername.toUpper()
CurrentOperation = ""
PercentComplete = 0
}
#create the PSSession
Try {
Write-Verbose "Creating a PSSession to $Computername"
$prog.CurrentOperation = "Creating a temporary PSSession"
$prog.PercentComplete = 10
Write-Progress @prog
$sess = New-PSSession @PSBoundParameters -ErrorAction Stop
}
Catch {
Throw $_
}
if ($sess) {
if ($InstallPowerShell) {
Write-Verbose "Installing PowerShell 7"
$prog.currentOperation = "Installing PowerShell 7"
$prog.percentComplete = 25
Write-Progress @prog
#install PowerShell
if ($pscmdlet.ShouldProcess($Computername.toUpper(), "Install PowerShell 7")) {
Invoke-Command -ScriptBlock {
#need to set security protocol to Tls12 for Windows Server 2016 otherwise the installs will fail
Write-Verbose "Setting network security to Tls12"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Write-Verbose "Using Security Protocol $([Net.ServicePointManager]::SecurityProtocol)"
[void](Install-PackageProvider -Name nuget -force -forcebootstrap -minimumversion '2.8.5.201')
Install-Module PSReleaseTools -Force -Scope AllUsers -Repository PSGallery
Install-PowerShell -EnableRemoting -EnableContextMenu -Mode Quiet
} -Session $sess
#Installing PowerShell may break the session so rebuild it if necessary
if ($sess.state -eq 'Broken') {
Write-Verbose "Rebuilding the temporary PSSession"
$sess = New-PSSession @PSBoundParameters -ErrorAction Stop
}
} #whatif
} #if install PowerShell 7
#setup SSH
$prog.currentOperation = "Installing OpenSSH Server"
$prog.percentComplete = 50
Write-Progress @prog
Invoke-Command -FilePath .\Setup-SSHServer2.ps1 -Session $sess -ArgumentList @($VerbosePreference, $WhatIfPreference)
#copy the sshd_config file. This assumes you've installed PowerShell 7 on the remote computer
Write-Verbose "Copying sshd_config to target"
$prog.currentOperation = "Copying default sshd_config to target"
$prog.percentcomplete = 60
Write-Progress @prog
Copy-Item -Path .\sshd_config_default -Destination $env:ProgramData\ssh\sshd_config -ToSession $sess
#restart the service
Write-Verbose "Restarting the sshd service on the target"
$prog.currentOperation = "Restarting the ssh service target"
$prog.percentComplete = 75
Write-Progress @prog
if ($pscmdlet.ShouldProcess("sshd", "Restart service")) {
Invoke-Command { Restart-Service -Name sshd } -Session $sess
}
Write-Verbose "Removing the temporary PSSession"
$prog.currentOperation = "Removing temporary PSSession"
$prog.percentComplete = 90
Write-Progress @prog
$sess | Remove-PSSession
Write-Progress @prog -Completed
$msg = @"
SSH Server deployment complete for $($Computername.toUpper()).
If PowerShell 7 is installed remotely, you should be able to test
with an expression like:
Enter-PSSession -hostname $Computername -username <user> -sshtransport
"@
Write-Host $msg -ForegroundColor yellow
} #if $sess
else {
#this should never get called
Write-Warning "Failed to create a temporary session to $Computername."
}
The script supports -Verbose and -WhatIf.
The script will copy the sshd_config_default file which you can grab from the previous post. Once in place, remoting with ssh in PowerShell 7 is a breeze.
Next Steps
You should test all of this code in a non-production environment and adjust as necessary. Anytime you play with remoting, you run the risk of leaving yourself vulnerable. Part of this process should include learning more about ssh. All I've done is get it installed and able to work with PowerShell. There is much more to ssh
Comments and questions welcome.