OpenSSH for Windows

OpenSSH provides a consistent connection method across Linux and Windows environments.

Administrative account access permissions

Windows OpenSSH handles credential lookup based on user privileges.

  • Standard Users: Lookups occur within the user profile path: C:\Users\<UserName>\.ssh\authorized_keys.
  • Administrative Users: Lookups bypass user directories and check a global configuration file: C:\ProgramData\ssh\administrators_authorized_key..

Critical Security Warning: The administrators_authorized_keys file must be restricted to explicit Access Control Lists (ACLs). Only the local SYSTEM and Administrators groups may possess permissions. If permissions are too broad, the OpenSSH service ignores the keys for security compliance.

Set the Default Shell

By default, OpenSSH on Windows may drop you into a Command Prompt (cmd.exe). Ansible requires a PowerShell environment. You must set the default shell on the Windows target using the following registry command (run as Admin):

 New-ItemProperty `
      -Path "HKLM:\SOFTWARE\OpenSSH" `
      -Name "DefaultShell" `
      -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" `
      -PropertyType String `
      -Force

OpenSSH inventory configuration

[windows_ssh]
winserver.example.com

[windows_ssh:vars]
ansible_connection=ssh
ansible_shell_type=cmd
ansible_user=Administrator

SSH authentication methods

Option local accounts Active directory accounts Credential delegation
Key Yes Yes No
GSSAPI No Yes Yes
Password Yes Yes Yes

Provision OpenSSH on target hosts

Use the following PowerShell script to automate installation, enable the firewall rules, enforce the correct default shell, and harden administrative access permissions:

<#
.SYNOPSIS
    Production-Hardened OpenSSH Provisioning Script for Windows Targets.
    Compatible with Windows Server 2019, 2022, and 2025.
#>
$ErrorActionPreference = "Stop"

# ==============================================================================
# 1. Enforce Administrator Elevation Access Layer
# ==============================================================================
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal($identity)
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
    Write-Error "Execution Halted: This script must be executed within an Elevated Administrator context."
    Exit 1
}

# ==============================================================================
# 2. Unified Windows Capability Onboarding (Server 2019 - 2025)
# ==============================================================================
Write-Host "Evaluating Component-Based Servicing state for OpenSSH..." -ForegroundColor Cyan
$capability = Get-WindowsCapability -Online -Name "OpenSSH.Server*"

if ($capability.State -ne 'Installed') {
    Write-Host "Target missing capability. Provisioning via Active OS Deployment Engine..." -ForegroundColor Yellow
    Add-WindowsCapability -Online -Name $capability.Name
} else {
    Write-Host "OpenSSH Server capability confirmed active." -ForegroundColor Green
}

# ==============================================================================
# 3. Configure Daemon Service State Machine
# ==============================================================================
Write-Host "Configuring service operational thresholds..." -ForegroundColor Cyan
Set-Service -Name sshd -StartupType 'Automatic'
if ((Get-Service -Name sshd).Status -ne 'Running') {
    Start-Service sshd
}

# ==============================================================================
# 4. Assertive Firewall Enforcement
# ==============================================================================
Write-Host "Validating Security Edge Boundaries..." -ForegroundColor Cyan
$fwRule = Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue

if (-not $fwRule) {
    Write-Host "Inbound Rule missing. Applying clean Port 22 Profile..." -ForegroundColor Yellow
    New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True `
        -Direction Inbound -Protocol TCP -LocalPort 22 -Action Allow
} elseif ($fwRule.Enabled -ne 'True') {
    Write-Host "Inbound Rule exists but is suppressed. Overriding to Enabled status..." -ForegroundColor Yellow
    Set-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -Enabled True
} else {
    Write-Host "Firewall verification passed." -ForegroundColor Green
}

# ==============================================================================
# 5. Native 64-Bit Registry Overrides (Bypassing WoW64 Translation)
# ==============================================================================
Write-Host "Locking default command interpretation environment..." -ForegroundColor Cyan
$shellPath = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"

$hive = [Microsoft.Win32.RegistryHive]::LocalMachine
$view = [Microsoft.Win32.RegistryView]::Registry64
$baseKey = [Microsoft.Win32.RegistryKey]::OpenBaseKey($hive, $view)

$sshKey = $baseKey.CreateSubKey("SOFTWARE\OpenSSH")
$sshKey.SetValue("DefaultShell", $shellPath, [Microsoft.Win32.RegistryValueKind]::String)

$sshKey.Close()
$baseKey.Close()
Write-Host "Default shell environment set to native 64-bit PowerShell process architecture." -ForegroundColor Green

# ==============================================================================
# 6. Cryptographic ACL Normalization via Well-Known SIDs
# ==============================================================================
Write-Host "Hardening key storage access schemas..." -ForegroundColor Cyan
$adminKeyPath = "$env:ProgramData\ssh\administrators_authorized_keys"

if (-not (Test-Path $adminKeyPath)) {
    # Ensure parent directory is structured if service hasn't initialized disk footprints yet
    $null = New-Item -ItemType File -Path $adminKeyPath -Force
}

# Using Explicit SIDs (*S-1-5-32-544 = Builtin Administrators, *S-1-5-18 = Local System) 
# This preserves execution integrity across global internationalized operating systems.
icacls.exe $adminKeyPath /inheritance:r /grant "*S-1-5-32-544:F" /grant "*S-1-5-18:F"

Write-Host "OpenSSH configuration normalized. Target node is fully prepped for Ansible orchestration." -ForegroundColor Green

An Ansible role for setting up and enabling sshd on Windows Server 2019+ using Features On Demand (FODs) is available at Windows Ansible roles