Install the latest OpenSSH Server on Windows using a batch file
Leave it to Microsoft to screw up installing a single package....
I'd had a pair of scripts that would install SSH server (https://www.nodinrogers.com/post/2024-02-10-installing-ssh-in-windows/), but that took about 20 minutes.
Compare that to ~10 seconds on a Linux install...
Grabbing/installing the latest OpenSSH server version from Github bypasses this lengthy install, and takes under a minute.
Bonus, there is only one .bat file script!
The batch file below does all of that. It checks for administrator rights, detects the Windows architecture, downloads the matching MSI from the latest GitHub release, verifies its SHA-256 digest and Authenticode signature, installs it silently, and configures the sshd service.
Note: At the time of writing, the release marked Latest in the PowerShell/Win32-OpenSSH GitHub repository is also marked as a preview release and non-production ready. This script deliberately follows GitHub's
releases/latestendpoint, so review the selected release before using it on a production system.
What the script does
The script completes the following steps:
- Confirms that it is running with administrator rights.
- Detects whether Windows is x64, ARM64, or 32-bit.
- Queries the GitHub API for the latest PowerShell/Win32-OpenSSH release.
- Downloads the MSI matching the computer's architecture.
- Compares the downloaded file with GitHub's SHA-256 digest.
- Requires a valid Windows Authenticode signature.
- Installs OpenSSH silently using
msiexec.exe. - Configures the
sshdservice to start automatically. - Starts the SSH server.
- Creates a Windows Firewall rule allowing inbound TCP traffic on port 22.
- Deletes the downloaded installer and displays the service status.
Microsoft's OpenSSH documentation also configures sshd for automatic startup and permits inbound connections on TCP port 22. The difference here is that the script installs the MSI published in the Win32-OpenSSH GitHub repository instead of installing the Windows optional capability.
OpenSSH installation batch file
Save the following as install-openssh-server.bat:
@echo off
setlocal EnableExtensions
fltmc >nul 2>&1
if errorlevel 1 (
echo ERROR: Right-click this file and select "Run as administrator".
pause
exit /b 1
)
if /i "%PROCESSOR_ARCHITECTURE%"=="AMD64" (
set "MSI_PATTERN=OpenSSH-Win64*.msi"
) else if /i "%PROCESSOR_ARCHITEW6432%"=="AMD64" (
set "MSI_PATTERN=OpenSSH-Win64*.msi"
) else if /i "%PROCESSOR_ARCHITECTURE%"=="ARM64" (
set "MSI_PATTERN=OpenSSH-ARM64*.msi"
) else (
set "MSI_PATTERN=OpenSSH-Win32*.msi"
)
set "INSTALLER=%TEMP%\OpenSSH-Installer.msi"
echo Finding and downloading the latest OpenSSH MSI from GitHub...
powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command "$ErrorActionPreference='Stop'; $headers=@{'User-Agent'='OpenSSH-Windows-Installer';'Accept'='application/vnd.github+json';'X-GitHub-Api-Version'='2022-11-28'}; $release=Invoke-RestMethod -Headers $headers -Uri 'https://api.github.com/repos/PowerShell/Win32-OpenSSH/releases/latest'; $asset=@($release.assets) | Where-Object { $_.name -like '%MSI_PATTERN%' } | Select-Object -First 1; if (-not $asset) { throw 'A matching OpenSSH MSI was not found in the latest GitHub release.' }; if ($asset.digest -notmatch '^sha256:[0-9a-fA-F]{64}$') { throw 'GitHub did not provide a valid SHA-256 digest for this asset.' }; Write-Host ('Downloading ' + $asset.name); Invoke-WebRequest -UseBasicParsing -Uri $asset.browser_download_url -OutFile '%INSTALLER%'; $expected=$asset.digest.Substring(7); $actual=(Get-FileHash -LiteralPath '%INSTALLER%' -Algorithm SHA256).Hash; if ($actual -ine $expected) { throw ('SHA-256 verification failed. Expected ' + $expected + ', received ' + $actual + '.'); }; Write-Host ('SHA-256 verified: ' + $actual); $signature=Get-AuthenticodeSignature -LiteralPath '%INSTALLER%'; if ($signature.Status -ne 'Valid') { throw ('Authenticode verification failed: ' + $signature.Status + '. ' + $signature.StatusMessage); }; if (-not $signature.SignerCertificate) { throw 'The installer does not contain a signer certificate.' }; Write-Host ('Valid Authenticode signer: ' + $signature.SignerCertificate.Subject)" <nul
if errorlevel 1 (
echo ERROR: The OpenSSH installer could not be downloaded or verified.
del "%INSTALLER%" >nul 2>&1
pause
exit /b 1
)
if not exist "%INSTALLER%" (
echo ERROR: The installer file was not created.
pause
exit /b 1
)
echo Installing OpenSSH...
msiexec.exe /i "%INSTALLER%" /quiet /norestart
set "MSI_RESULT=%ERRORLEVEL%"
if not "%MSI_RESULT%"=="0" if not "%MSI_RESULT%"=="3010" (
echo ERROR: Installation failed with MSI exit code %MSI_RESULT%.
del "%INSTALLER%" >nul 2>&1
pause
exit /b %MSI_RESULT%
)
echo Configuring the SSH server...
sc.exe config sshd start= auto >nul
if errorlevel 1 (
echo ERROR: The sshd service could not be configured.
del "%INSTALLER%" >nul 2>&1
pause
exit /b 1
)
sc.exe start sshd >nul 2>&1
netsh advfirewall firewall delete rule name="OpenSSH SSH Server" >nul 2>&1
netsh advfirewall firewall add rule name="OpenSSH SSH Server" dir=in action=allow protocol=TCP localport=22 >nul
if errorlevel 1 (
echo ERROR: The Windows Firewall rule could not be created.
del "%INSTALLER%" >nul 2>&1
pause
exit /b 1
)
del "%INSTALLER%" >nul 2>&1
echo.
echo OpenSSH was installed successfully.
echo SSH connections are permitted on TCP port 22.
echo.
sc.exe query sshd
echo Press any key to continue...
pause
endlocal
Run the installer
Right-click install-openssh-server.bat, then select Run as administrator.
The script uses fltmc as a simple elevation check. If the command cannot run with the required privileges, the script stops before downloading or installing anything.
The architecture checks deserve a little explanation. %PROCESSOR_ARCHITECTURE% normally identifies the architecture of the current process. %PROCESSOR_ARCHITEW6432% also catches a 32-bit command process running on 64-bit Windows. The resulting MSI pattern will be one of:
OpenSSH-Win64*.msiOpenSSH-ARM64*.msiOpenSSH-Win32*.msi
PowerShell then calls the GitHub Releases API and selects the first asset matching that pattern.
Installation results
The MSI is installed quietly and does not force a restart:
msiexec.exe /i "%INSTALLER%" /quiet /norestart
The script accepts MSI result 0, meaning success, and 3010, meaning the installation succeeded but Windows must be restarted before all changes take effect. Any other result is treated as an error.
After installation, the following command configures the OpenSSH server to start with Windows:
sc.exe config sshd start= auto
The space after
start=is required bysc.exe.
The script then starts sshd and replaces its own firewall rule with one allowing inbound TCP connections on port 22:
netsh advfirewall firewall delete rule name="OpenSSH SSH Server"
netsh advfirewall firewall add rule name="OpenSSH SSH Server" dir=in action=allow protocol=TCP localport=22
This rule allows port 22 from any source permitted by the active Windows Firewall profiles. If the server should only accept SSH from a management network, restrict the rule's remote addresses instead of exposing the port more broadly.
Test the SSH server
At the end of the installation, the script runs:
sc.exe query sshd
Look for STATE showing RUNNING.
From another computer with an SSH client, connect using the Windows account name and the hostname or IP address of the server:
ssh username@windows-hostname
On a domain-joined computer, Microsoft documents the username format as:
ssh domain\username@windows-hostname
The first connection should display the server's host-key fingerprint. Verify that fingerprint before accepting it, especially when connecting across an untrusted network.
A few security considerations
This is a convenience script, not a complete OpenSSH hardening guide.
- It downloads whichever release GitHub currently marks as latest. That may be a preview release.
- It requires the downloaded MSI to match the SHA-256 digest published in the GitHub release-asset metadata and to have a valid Authenticode signature before installation.
- It opens TCP port 22 without restricting the source IP address.
- It does not configure SSH keys, allowed users, authentication methods, or other options in
sshd_config. - It deletes and recreates any firewall rule named
OpenSSH SSH Server, so do not reuse that exact name for a separately customized rule.
The SHA-256 digest and MSI come from the same GitHub release, so this detects a damaged or altered download but is not an independent defense against a compromised publisher account. Authenticode adds a separate certificate-chain and file-integrity check. For a production server, also pin a reviewed release, confirm the displayed signer, limit the firewall rule to trusted networks, and configure key-based authentication. The Windows OpenSSH configuration file is normally located at %ProgramData%\ssh\sshd_config.