When managing infrastructure across different operating systems, establishing secure communication between Linux and Windows servers is often a key requirement.
During a recent Oracle Cloud Infrastructure (OCI) implementation, I needed to establish SSH connectivity from a Linux-based management server to a Windows Server 2022 instance.
Although Windows Server 2022 supports OpenSSH natively, the configuration presented a few unexpected challenges - particularly around Windows Firewall configuration, SSH authentication, and PowerShell executable paths.
After resolving these issues, I created a complete PowerShell script to automate the entire configuration. This article covers the architecture, installation, configuration, troubleshooting, and testing process, including the full PowerShell automation script that incorporates the fixes used during testing.
Difficulty: Intermediate · Reading time: ~15 minutes · Focus: Windows Server 2022 OpenSSH in OCI
1. The Environment
The environment consisted of two instances hosted within Oracle Cloud Infrastructure.
| Component | Operating system | Private IP |
|---|---|---|
| Management server | Oracle Linux | 172.12.13.38 |
| Target server | Windows Server 2022 | 172.12.13.2 |
| Communication | SSH | TCP port 22 |
The objective was straightforward: enable the Linux management server to connect to Windows Server 2022 over SSH using its private IP address.
The network communication path was:
Oracle Linux Management Server → OCI Private Network → Windows Firewall → OpenSSH Server
This required configuration at three levels:
- Windows Server - installing and configuring OpenSSH.
- Windows Firewall - permitting inbound TCP port 22.
- OCI Networking - ensuring the relevant NSGs, security lists and routing permitted communication.
2. Prerequisites
Before proceeding, ensure the following requirements are met:
- Windows Server 2022 running in Oracle Cloud Infrastructure.
- Administrator access to Windows PowerShell.
- Network connectivity between the Windows and Linux instances.
- TCP port 22 permitted through OCI networking.
- A valid Windows account for SSH authentication.
- Access to the Linux management server for testing.
No third-party SSH software is required. Windows Server 2022 includes OpenSSH Server as an optional Windows capability.
3. Complete PowerShell Automation Script
Rather than manually installing OpenSSH, modifying firewall rules, editing authentication settings and restarting services, the entire process can be automated. The following script performs all the necessary configuration steps:
- Checks Administrator privileges.
- Installs OpenSSH Server if it is not already installed.
- Configures the SSH service for automatic startup.
- Starts the SSH service.
- Creates a Windows Firewall rule permitting SSH on TCP port 22 from any IP address.
- Locates the OpenSSH executable, including handling
System32andSysnativeredirection. - Creates a timestamped backup of the SSH configuration.
- Enables password authentication.
- Preserves existing SSH
Matchconfiguration blocks. - Validates the SSH configuration before restarting the service.
- Restarts OpenSSH and verifies service status and TCP port 22.
- Performs a local connectivity test and displays IPv4 addresses.
- Confirms the configured Windows Firewall rule.
Full script - Install-Configure-OpenSSH.ps1
Copy the entire script below into Notepad or your preferred editor and save it as Install-Configure-OpenSSH.ps1. You can also download the script from this post.
# ============================================================
# WINDOWS SERVER 2022 - COMPLETE OPENSSH CONFIGURATION
# Oracle Cloud Infrastructure (OCI)
#
# Installs OpenSSH Server
# Enables automatic startup
# Allows SSH from ANY IP on TCP 22
# Enables password authentication
# Handles System32 / Sysnative redirection
# Backs up existing SSH configuration
# Validates SSH configuration
# Restarts SSH and verifies connectivity
#
# Run as Administrator
# ============================================================
$ErrorActionPreference = "Stop"
Write-Host "========================================="
Write-Host " Windows Server 2022 - OpenSSH Setup"
Write-Host "========================================="
# STEP 1 - Check Administrator privileges
Write-Host "`n[1] Checking Administrator privileges..."
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal($identity)
if (-not $principal.IsInRole(
[Security.Principal.WindowsBuiltInRole]::Administrator)) {
throw "Run PowerShell as Administrator."
}
Write-Host "Administrator privileges confirmed." -ForegroundColor Green
# STEP 2 - Install OpenSSH Server
Write-Host "`n[2] Checking OpenSSH Server..."
$ssh = Get-WindowsCapability -Online -Name "OpenSSH.Server~~~~0.0.1.0"
if ($ssh.State -ne "Installed") {
Write-Host "Installing OpenSSH Server..."
Add-WindowsCapability -Online `
-Name "OpenSSH.Server~~~~0.0.1.0" | Out-Null
$ssh = Get-WindowsCapability -Online -Name "OpenSSH.Server~~~~0.0.1.0"
if ($ssh.State -ne "Installed") {
throw "OpenSSH installation incomplete. Check reboot requirements."
}
}
else {
Write-Host "OpenSSH Server already installed."
}
# STEP 3 - Locate OpenSSH executable
Write-Host "`n[3] Locating OpenSSH executable..."
$sshd = "$env:WINDIR\System32\OpenSSH\sshd.exe"
if (-not (Test-Path $sshd)) {
$sshd = "$env:WINDIR\Sysnative\OpenSSH\sshd.exe"
}
if (-not (Test-Path $sshd)) {
throw "Cannot locate sshd.exe"
}
Write-Host "SSH executable: $sshd" -ForegroundColor Green
# STEP 4 - Configure SSH service
Write-Host "`n[4] Configuring SSH service..."
Set-Service sshd -StartupType Automatic
if ((Get-Service sshd).Status -ne "Running") {
Start-Service sshd
}
Write-Host "SSH service running." -ForegroundColor Green
# STEP 5 - Configure Windows Firewall
Write-Host "`n[5] Configuring Windows Firewall..."
Get-NetFirewallRule `
-Name "OpenSSH-Server-In-TCP" `
-ErrorAction SilentlyContinue |
Remove-NetFirewallRule
New-NetFirewallRule `
-Name "OpenSSH-Server-In-TCP" `
-DisplayName "OpenSSH Server TCP 22 - Any IP" `
-Enabled True `
-Direction Inbound `
-Protocol TCP `
-Action Allow `
-LocalPort 22 `
-RemoteAddress Any `
-Profile Any |
Out-Null
Write-Host "Firewall configured: TCP 22 allowed from ANY IP." `
-ForegroundColor Green
# STEP 6 - Locate SSH configuration
Write-Host "`n[6] Locating SSH configuration..."
$config = "C:\ProgramData\ssh\sshd_config"
if (-not (Test-Path $config)) {
throw "SSH configuration file not found."
}
# STEP 7 - Back up SSH configuration
Write-Host "`n[7] Backing up SSH configuration..."
$backup = "$config.bak.$(Get-Date -Format 'yyyyMMdd-HHmmss')"
Copy-Item $config $backup -Force
Write-Host "Backup created: $backup" -ForegroundColor Green
# STEP 8 - Read configuration
Write-Host "`n[8] Reading SSH configuration..."
$content = @(Get-Content $config)
# Find first Match block
$matchIndex = $content.Count
for ($i = 0; $i -lt $content.Count; $i++) {
if ($content[$i] -match '^\s*Match\s+') {
$matchIndex = $i
break
}
}
# Extract global configuration
$globalConfig = @()
if ($matchIndex -gt 0) {
$globalConfig = @($content[0..($matchIndex - 1)])
}
# Remove existing global PasswordAuthentication directives
$globalConfig = @($globalConfig | Where-Object {
$_ -notmatch '^\s*#?\s*PasswordAuthentication\s+'
})
# Preserve Match blocks
$remaining = @()
if ($matchIndex -lt $content.Count) {
$remaining = @($content[$matchIndex..($content.Count - 1)])
}
# STEP 9 - Enable password authentication
Write-Host "`n[9] Enabling password authentication..."
$newConfig = @("PasswordAuthentication yes") +
$globalConfig +
$remaining
Set-Content -Path $config -Value $newConfig -Encoding ascii
Write-Host "PasswordAuthentication yes configured." `
-ForegroundColor Green
# STEP 10 - Validate configuration
Write-Host "`n[10] Validating SSH configuration..."
& $sshd -t
if ($LASTEXITCODE -ne 0) {
Copy-Item $backup $config -Force
throw "SSH configuration invalid. Original configuration restored."
}
Write-Host "SSH configuration valid." -ForegroundColor Green
# STEP 11 - Restart SSH
Write-Host "`n[11] Restarting SSH service..."
Restart-Service sshd -ErrorAction Stop
# STEP 12 - Verify SSH service
Write-Host "`n[12] Checking SSH service..."
Get-Service sshd |
Format-Table Name, Status, StartType -AutoSize
# STEP 13 - Verify TCP port 22
Write-Host "`n[13] Checking TCP port 22..."
$listening = Get-NetTCPConnection `
-LocalPort 22 `
-State Listen `
-ErrorAction SilentlyContinue
if ($listening) {
Write-Host "SSH listening on TCP 22." -ForegroundColor Green
$listening |
Format-Table LocalAddress, LocalPort, State -AutoSize
}
else {
Write-Host "WARNING: SSH not listening on port 22." `
-ForegroundColor Red
}
# STEP 14 - Test local connectivity
Write-Host "`n[14] Testing SSH connectivity..."
$test = Test-NetConnection `
-ComputerName 127.0.0.1 `
-Port 22 `
-WarningAction SilentlyContinue
if ($test.TcpTestSucceeded) {
Write-Host "SUCCESS: SSH connectivity test passed." `
-ForegroundColor Green
}
else {
Write-Host "FAILED: SSH connectivity test failed." `
-ForegroundColor Red
}
# STEP 15 - Display Windows IP addresses
Write-Host "`n[15] Windows Server IP addresses..."
Get-NetIPAddress -AddressFamily IPv4 |
Where-Object {
$_.IPAddress -notlike "169.254*" -and
$_.IPAddress -ne "127.0.0.1"
} |
Format-Table InterfaceAlias, IPAddress -AutoSize
# STEP 16 - Verify Windows Firewall
Write-Host "`n[16] Verifying SSH firewall rule..."
Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" |
Format-Table DisplayName, Enabled, Direction, Action -AutoSize
# STEP 17 - Final result
Write-Host "`n========================================="
Write-Host " OpenSSH Server Setup Completed"
Write-Host "========================================="
if ($test.TcpTestSucceeded) {
Write-Host "STATUS: SSH READY" -ForegroundColor Green
Write-Host "PORT: TCP 22"
Write-Host "FIREWALL: ANY IP ALLOWED"
Write-Host "PASSWORD AUTHENTICATION: ENABLED"
Write-Host "STARTUP: AUTOMATIC"
}
else {
Write-Host "STATUS: SSH REQUIRES TROUBLESHOOTING" `
-ForegroundColor Red
}
Write-Host "========================================="
Running the script
Open PowerShell as Administrator and execute:
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\Users\opc\Desktop\Install-Configure-OpenSSH.ps1"
Change the file path if you saved the script elsewhere. Once completed, the script should confirm that the SSH service is running and that TCP port 22 is accessible locally.
The script intentionally permits SSH from any IP address through Windows Firewall. This was useful for initial connectivity testing, but production environments should restrict -RemoteAddress Any to an authorised management IP or subnet.
4. Configuring Windows User Authentication
With OpenSSH installed and password authentication enabled, the next requirement is a valid Windows account. In this example, the Windows account was named opc.
To check whether the account exists:
Get-LocalUser -Name opc
If the account exists but is disabled:
Enable-LocalUser -Name opc
To configure a password for an authorised local account:
Set-LocalUser -Name opc -Password (
Read-Host "Enter SSH password" -AsSecureString
)
Changing an existing account password can affect services or automation that depend on those credentials. For ongoing automated management, a dedicated least-privilege service account and SSH public-key authentication are generally preferable.
5. Configuring OCI Network Security
Even with OpenSSH running successfully, connectivity will fail if Oracle Cloud Infrastructure blocks the traffic. OCI uses Network Security Groups (NSGs) and subnet security lists to control network access.
For this environment, the Linux management server needed to reach the Windows instance over TCP port 22. The recommended ingress configuration was:
| Setting | Value |
|---|---|
| Direction | Ingress |
| Source CIDR | 172.12.13.38/32 |
| Protocol | TCP |
| Destination port | 22 |
| Stateful | Yes |
This restricts SSH access to the management server. For temporary testing, 0.0.0.0/0 can be used to permit traffic from any IPv4 source that has a valid network path to the instance - but that should not be retained unnecessarily in production.
Where servers reside in different VCNs, routing may need to be configured through a Dynamic Routing Gateway (DRG), Local Peering Gateway (LPG), or another supported network path. Both the OCI network rules and Windows Firewall must permit the traffic.
6. Testing SSH Connectivity from Oracle Linux
Once the Windows configuration is complete, connect to the Linux management server. First, test whether TCP port 22 is accessible:
nc -zv 172.12.13.2 22
If connectivity is working, the command should indicate that the connection succeeded. Next, attempt an SSH connection:
ssh opc@172.12.13.2
On the first connection, SSH may ask you to verify the remote server's host key fingerprint. Verify the fingerprint using a trusted source before accepting it.
To explicitly request password authentication:
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no opc@172.12.13.2
If password authentication is enabled and the account has valid credentials, you should receive a password prompt. After successful authentication, the connection opens the configured Windows shell.
7. Troubleshooting Real-World Issues
During the implementation, several problems were encountered that are worth documenting.
Error 1 - Unsupported Windows Firewall Parameter
The initial firewall configuration failed with:
Set-NetFirewallAddressFilter:
A parameter cannot be found that matches parameter name
'AssociatedNetFirewallRule'.
The issue was caused by using an unsupported parameter with Set-NetFirewallAddressFilter. The resolution was to remove the existing named firewall rule and recreate it using New-NetFirewallRule, specifying the remote address directly. The full automation script above includes this corrected approach.
Error 2 - OpenSSH Executable Not Found
When validating the SSH configuration, PowerShell returned:
The term 'C:\Windows\System32\OpenSSH\sshd.exe'
is not recognized as the name of a cmdlet.
Interestingly, the SSH service was already running. To investigate, I checked the registered Windows service:
Get-CimInstance Win32_Service -Filter "Name='sshd'" |
Format-List Name,State,PathName
The output confirmed:
Name : sshd
State : Running
PathName : C:\Windows\System32\OpenSSH\sshd.exe
This suggested a possible PowerShell process architecture or file-system redirection issue. On 64-bit Windows, a 32-bit PowerShell process may be redirected when accessing the System32 directory.
The solution was to check the normal executable path and fall back to Sysnative when required:
$sshd = "$env:WINDIR\System32\OpenSSH\sshd.exe"
if (-not (Test-Path $sshd)) {
$sshd = "$env:WINDIR\Sysnative\OpenSSH\sshd.exe"
}
& $sshd -t
This allowed the SSH configuration to be validated successfully.
Error 3 - Permission Denied During SSH Login
After network connectivity was established, the SSH client returned:
Permission denied (publickey,keyboard-interactive).
At this stage the network configuration was functioning correctly - the SSH server was reachable, but authentication was unsuccessful. Troubleshooting focused on the Windows OpenSSH configuration and available authentication methods.
The relevant configuration file was C:\ProgramData\ssh\sshd_config. The required global directive was:
PasswordAuthentication yes
After updating the configuration, validating it and restarting sshd, password authentication could be tested again. The combined PowerShell script automates this process.
Remember that Windows account restrictions or SSH Match blocks may still prevent authentication even when password authentication is globally enabled.
Error 4 - SSH Service Running but Connections Failing
If SSH is running but remote connections fail, verify the following:
Check the SSH service:
Get-Service sshd
Check TCP port 22:
Get-NetTCPConnection -LocalPort 22 -State Listen
Test local connectivity:
Test-NetConnection 127.0.0.1 -Port 22
Check the Windows Firewall rule:
Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP"
Enable verbose SSH troubleshooting from Linux:
ssh -vvv opc@172.12.13.2
If the connection times out, investigate OCI routing, security rules and firewalls. If the connection reaches the server but returns Permission denied, investigate authentication.
8. Security Recommendations
Although the automation script is designed to simplify deployment and troubleshooting, production environments should apply additional security controls:
- Restrict SSH access to authorised management IP addresses.
- Use SSH public-key authentication where possible.
- Create dedicated least-privilege management accounts.
- Apply strong password policies when password authentication is required.
- Keep Windows Server and OpenSSH updated.
- Review OpenSSH and Windows security event logs.
- Verify SSH host key fingerprints.
- Avoid unnecessary public exposure of TCP port 22.
- Follow change-management procedures when modifying SSH configuration.
In particular, the -RemoteAddress Any setting in the script should be replaced with the required source address or CIDR range for production deployments.
9. Final Verification Checklist
Before completing the implementation, verify:
- OpenSSH Server is installed.
- The
sshdservice is running. - The SSH service starts automatically.
- TCP port 22 is listening.
- Windows Firewall permits the required traffic.
- OCI NSGs or security lists allow the connection.
- Routing exists between the Linux and Windows instances.
- SSH authentication is correctly configured.
- The Windows account is valid and enabled.
- The Linux server can establish an SSH session.
- Production security restrictions have been reviewed.
Conclusion
Installing OpenSSH Server on Windows Server 2022 within Oracle Cloud Infrastructure is relatively straightforward, but getting the entire configuration working correctly requires attention to several areas.
Windows Firewall configuration, SSH authentication, PowerShell executable paths and OCI networking can all introduce unexpected issues. In this implementation, the initial network connection was successful, but authentication and configuration validation required additional troubleshooting.
By combining the installation, firewall configuration, authentication settings and verification steps into a single PowerShell script, the process becomes easier to repeat across Windows Server instances.
The result is a reusable approach for enabling SSH connectivity between Windows Server 2022 and Linux-based management systems hosted within OCI. For production use, the final configuration should always be reviewed against organisational security requirements, with network access restricted and appropriate authentication controls applied.
Frequently Asked Questions
Does Windows Server 2022 support OpenSSH?
Yes. OpenSSH Server is available as an optional Windows capability and can be installed using PowerShell.
What port does OpenSSH use?
OpenSSH uses TCP port 22 by default.
Can Oracle Linux connect to Windows Server using SSH?
Yes. Once OpenSSH Server is installed and configured, Oracle Linux can connect using a standard SSH client and valid Windows credentials.
Why does SSH return Permission denied?
This can occur when password authentication is disabled, credentials are incorrect, the Windows account is restricted, or an acceptable SSH key is unavailable.
Why is sshd.exe not recognised even though the SSH service is running?
One possible cause is Windows file-system redirection when running 32-bit PowerShell on a 64-bit operating system.
Official Documentation
- Microsoft: Get Started with OpenSSH for Windows
- Microsoft: OpenSSH Server Configuration for Windows
- Oracle: OCI Network Security Groups


