Skip to content

Instantly share code, notes, and snippets.

@cyrex562
Created September 14, 2026 13:09
Show Gist options
  • Select an option

  • Save cyrex562/87890710fa3fe95bfd5b5193c43b782f to your computer and use it in GitHub Desktop.

Select an option

Save cyrex562/87890710fa3fe95bfd5b5193c43b782f to your computer and use it in GitHub Desktop.
Powershell ssh-copy-id and ssh config tab complete add-ons to the `$PROFILE`
<#
.SYNOPSIS
Linux-like SSH utilities and autocomplete configuration for Windows PowerShell.
.DESCRIPTION
Adds tab completion for 'ssh', 'scp', 'sftp', and 'ssh-copy-id' by reading your ~/.ssh/config.
Also implements a native PowerShell wrapper function for 'ssh-copy-id'.
.USAGE
Paste this entire script block into your PowerShell profile.
To open your profile, run: notepad $PROFILE
#>
using namespace System.Management.Automation
# Helper function to parse Host entries from ssh config
function Get-Hosts($configFile) {
if (Test-Path $configFile) {
Get-Content $configFile `
| Select-String -Pattern "^Host " `
| ForEach-Object { $_ -replace "(?i)^host\s+", "" } `
| ForEach-Object { $_ -split "\s+" } `
| Where-Object { $_ -notmatch '[*?]' } `
| Sort-Object -Unique
}
}
# Native Windows port of the Linux ssh-copy-id script
function ssh-copy-id {
param(
[Parameter(Mandatory=$true, Position=0)]
[string]$UserAtMachine,
[Parameter(Mandatory=$false)]
[string]$KeyPath = "$env:USERPROFILE\.ssh\id_rsa.pub"
)
# Fall back to ed25519 if default RSA doesn't exist
if (-not (Test-Path $KeyPath)) {
$KeyPath = "$env:USERPROFILE\.ssh\id_ed25519.pub"
}
if (-not (Test-Path $KeyPath)) {
Write-Error "ERROR: Could not find a public key file at $env:USERPROFILE\.ssh\ (checked id_rsa.pub and id_ed25519.pub)"
return
}
Write-Host "Copying key $KeyPath to $UserAtMachine..." -ForegroundColor Cyan
# Securely append public key to remote host
Get-Content $KeyPath -Raw | ssh $UserAtMachine "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
}
# Register the interactive Tab-Completer for all SSH commands
Register-ArgumentCompleter -CommandName ssh, scp, sftp, ssh-copy-id -Native -ScriptBlock {
param($wordToComplete, $commandAst, $cursorPosition)
$sshDir = "${Env:USERPROFILE}\.ssh"
$hosts = @()
# Parse master config
if (Test-Path "$sshDir\config") {
$hosts += Get-Hosts "$sshDir\config"
# Traverse any external 'Include' directives
Get-Content "$sshDir\config" `
| Select-String -Pattern "^Include " `
| ForEach-Object { $_ -replace "(?i)^include\s+", "" } `
| ForEach-Object { Get-Hosts "$sshDir/$_" }
}
$hosts = $hosts | Sort-Object -Unique
$textToComplete = $wordToComplete
$generateCompletionText = { param($x) $x }
# Retain custom username prefix if typed (e.g., admin@ho[TAB])
if ($wordToComplete -match "^(?<user>[-\w/\\\\]+)@(?<host>[-.\w]*)$") {
$textToComplete = $Matches["host"]
$generateCompletionText = { param($hostname) $Matches["user"] + "@" + $hostname }
}
$hosts `
| Where-Object { $_ -like "${textToComplete}*" } `
| ForEach-Object { [CompletionResult]::new((&$generateCompletionText($_)), $_, [CompletionResultType]::ParameterValue, $_) }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment