Create ssh-copy-id script for Windows

21 Feb 2023 | Create ssh-copy-id script for Windows |

Those of us who use linux with keyless logins and ssh , usually find the utility ssh-copy-id as a convenient way to copy the ssh ke into the authorized keys files of the destination server. This works great on Linux systems, but windows (and also Mac) don’t have a native version fo ssh-copy-id, of course you could find scripts online that do this.. but many are overkill basically re-creating the entire ssh client in code just for the purpose of copying over a single key. Also since this is a securtity realted operation , you want to be confident those scripts are only doing what they say they’re doing.

With that said I hope to address these issues in this post and show you a quick, short and most importantly easy to modify powershell script that you can use in any modern version of Windows.

Security and Concerns

Finally there’s security concerns about using these scripts, unless you go over everly line of of code you’re never really sure what they’re doing with your key. Because of this the code velow is short, in native powershell and very easy to use, you litterally can read the code in this post and res-assured it’s only doing what it’s supposed to.

SSH-COPY-ID in Powershell

Windows Powershell is the defacto Widnows command line programming language replacing the traditional DOS batch files, and has been in available since around Windows 7 (circa 2007) . You can find a useful Powershell cheat sheet here:

param (
    [Parameter(Mandatory=$true)]
    [string]$connectionString,

    [Parameter(Mandatory=$true)]
    [string]$publicKeyFile
)

$connectionParts = $connectionString.Split('@')
if ($connectionParts.Length -ne 2) {
    throw "Invalid connection string format. Must be in the format 'user@hostname:port'"
}
$username = $connectionParts[0]
$hostParts = $connectionParts[1].Split(':')
$hostname = $hostParts[0]
if ($hostParts.Length -gt 1) {
    $port = $hostParts[1]
} else {
    $port = 22
}

$sshDir = "$env:USERPROFILE\.ssh"
if (!(Test-Path -Path $sshDir)) {
    New-Item -ItemType Directory -Path $sshDir | Out-Null
}

$sshPubKey = Get-Content $publicKeyFile
$sshPubKeyFileName = Split-Path $publicKeyFile -Leaf
$sshAuthorizedKeysFile = "$sshDir\authorized_keys"

$sshConnection = New-SSHSession -ComputerName $hostname -Credential $username -Port $port
$sshAuthorizedKeys = Invoke-SSHCommand -SessionId $sshConnection.SessionId -Command "cat $sshAuthorizedKeysFile" | Select-Object -ExpandProperty Output
if ($sshAuthorizedKeys -notcontains $sshPubKey) {
    Invoke-SSHCommand -SessionId $sshConnection.SessionId -Command "echo `"$sshPubKey`" >> $sshAuthorizedKeysFile"
}

Write-Output "Public key added to $hostname authorized_keys file"
?

This script requires the New-SSHSession and Invoke-SSHCommand cmdlets, which are available in the PowershellSSH module. You can install this module by running Install-Module -Name PowershellSSH.

How it works

The script takes three parameters: the username on the remote host, the hostname or IP address of the remote host, and the path to the public key file. The script then creates the ~/.ssh directory on the remote host if it doesn’t exist, reads the public key from the specified file, and adds it to the authorized_keys file on the remote host if it’s not already there. Finally, the script outputs a message indicating that the public key was added to the authorized_keys file.

To use this script, save it to a file with a .ps1 extension, and then run it with the required parameters. For example:

.\ssh-copy-id.ps1 user@remotehost:port -publicKeyFile C:\Users\myuser\.ssh\id_rsa.pub

Leave a Reply