<# SSH Manager Interactive SSH launcher for Windows PowerShell. This portfolio-safe version uses mock server names, documentation-friendly domains, and example certificate paths. Replace the entries in $servers with real hosts before using it in an internal environment. #> param ( [string]$SshUser = "", [string]$IdentityFile = "", [string]$CertificateFile = "" ) $ErrorActionPreference = "Stop" $defaultIdentityFile = Join-Path $PSScriptRoot "..\..\ssh\id_ed25519" $defaultCertificatePath = Join-Path $PSScriptRoot "certificates" if ([string]::IsNullOrWhiteSpace($IdentityFile) -and (Test-Path -LiteralPath $defaultIdentityFile -PathType Leaf)) { $IdentityFile = (Resolve-Path -LiteralPath $defaultIdentityFile).Path } $servers = @( @{ Environment = "DEV" Name = "Development - arlapi-dev-01.example.com" Domain = "api-dev-01.example.com" Host = "192.0.2.11" Hostname = "arlapi-dev-01" CertificatePath = $defaultCertificatePath Port = 22 }, @{ Environment = "DEV" Name = "Development - arlworker-dev-01.example.com" Domain = "worker-dev-01.example.com" Host = "192.0.2.12" Hostname = "arlworker-dev-01" CertificatePath = $defaultCertificatePath Port = 22 }, @{ Environment = "QAS" Name = "Quality - arlapi-qas-01.example.com" Domain = "api-qas-01.example.com" Host = "198.51.100.21" Hostname = "arlapi-qas-01" CertificatePath = $defaultCertificatePath Port = 22 }, @{ Environment = "QAS" Name = "Quality - arldb-qas-01.example.com" Domain = "db-qas-01.example.com" Host = "198.51.100.22" Hostname = "arldb-qas-01" CertificatePath = $defaultCertificatePath Port = 22 }, @{ Environment = "QAS" Name = "Quality - arlworker-qas-01.example.com" Domain = "worker-qas-01.example.com" Host = "198.51.100.23" Hostname = "arlworker-qas-01" CertificatePath = $defaultCertificatePath Port = 22 }, @{ Environment = "PROD" Name = "Production - arlapi-prd-01.example.com" Domain = "api-prd-01.example.com" Host = "203.0.113.31" Hostname = "arlapi-prd-01" CertificatePath = $defaultCertificatePath Port = 22 }, @{ Environment = "PROD" Name = "Production - arlapi-prd-02.example.com" Domain = "api-prd-02.example.com" Host = "203.0.113.32" Hostname = "arlapi-prd-02" CertificatePath = $defaultCertificatePath Port = 22 }, @{ Environment = "PROD" Name = "Production - arldb-prd-01.example.com" Domain = "db-prd-01.example.com" Host = "203.0.113.33" Hostname = "arldb-prd-01" CertificatePath = $defaultCertificatePath Port = 22 }, @{ Environment = "PROD" Name = "Production - arlmonitor-prd-01.example.com" Domain = "monitor-prd-01.example.com" Host = "203.0.113.34" Hostname = "arlmonitor-prd-01" CertificatePath = $defaultCertificatePath Port = 22 }, @{ Environment = "PROD" Name = "Production - arlworker-prd-01.example.com" Domain = "worker-prd-01.example.com" Host = "203.0.113.35" Hostname = "arlworker-prd-01" CertificatePath = $defaultCertificatePath Port = 22 } ) $environments = @( @{ Label = "DEV" Value = "DEV" }, @{ Label = "QAS" Value = "QAS" }, @{ Label = "PRD" Value = "PROD" } ) function Show-EnvironmentMenu { Clear-Host Write-Host "Select environment:" -ForegroundColor Cyan Write-Host "" for ($i = 0; $i -lt $environments.Count; $i++) { Write-Host ("[{0:D2}] {1}" -f ($i + 1), $environments[$i].Label) } Write-Host "" Write-Host "[0] Exit" Write-Host "" } function Get-EnvironmentLabel { param ( [hashtable]$Server ) if ($Server.Name -match "^(?.+?)\s+-\s+") { return $Matches.EnvironmentLabel } return $Server.Environment } function Get-ShortServerName { param ( [hashtable]$Server ) $shortName = $Server.Hostname if ([string]::IsNullOrWhiteSpace($shortName)) { $shortName = $Server.Host } $shortName = $shortName.ToLowerInvariant() $shortName = $shortName -replace "^arl", "" $shortName = $shortName -replace "\.(e?corp|lrd)\.cat\.com$", "" $shortName = $shortName -replace "\.example\.com$", "" return $shortName } function Show-ServerMenu { param ( [string]$Environment ) Clear-Host Write-Host ("Select server from {0}:" -f $Environment) -ForegroundColor Cyan Write-Host "" $environmentServers = @( $servers | Where-Object { $_.Environment -eq $Environment } | Sort-Object { Get-ShortServerName -Server $_ } ) $shortNameWidth = 0 foreach ($server in $environmentServers) { $shortNameLength = (Get-ShortServerName -Server $server).Length if ($shortNameLength -gt $shortNameWidth) { $shortNameWidth = $shortNameLength } } for ($i = 0; $i -lt $environmentServers.Count; $i++) { $server = $environmentServers[$i] $environmentLabel = Get-EnvironmentLabel -Server $server $shortName = Get-ShortServerName -Server $server Write-Host ("[{0:D2}] {1,-11} {2,-$shortNameWidth} ({3} / {4}:{5})" -f ($i + 1), $environmentLabel, $shortName, $server.Domain, $server.Host, $server.Port) } Write-Host "" Write-Host "[0] Back" Write-Host "" } function Resolve-CertificateFile { param ( [string]$Path ) if ([string]::IsNullOrWhiteSpace($Path)) { return "" } if (Test-Path -LiteralPath $Path -PathType Leaf) { return $Path } if (Test-Path -LiteralPath $Path -PathType Container) { $certificateFile = Get-ChildItem -LiteralPath $Path -File | Where-Object { $_.Name -match "(-cert\.pub|\.pem|\.crt|\.cer)$" } | Sort-Object Name | Select-Object -First 1 if ($null -ne $certificateFile) { return $certificateFile.FullName } } return "" } function Get-SshArguments { param ( [hashtable]$Server, [string]$User ) $sshArguments = @() if (-not [string]::IsNullOrWhiteSpace($IdentityFile)) { if (-not (Test-Path -LiteralPath $IdentityFile)) { throw "Identity file not found: $IdentityFile" } Write-Host ("Using identity file: {0}" -f $IdentityFile) -ForegroundColor DarkGray $sshArguments += @("-i", $IdentityFile) } if (-not [string]::IsNullOrWhiteSpace($CertificateFile)) { $resolvedCertificateFile = Resolve-CertificateFile -Path $CertificateFile if ([string]::IsNullOrWhiteSpace($resolvedCertificateFile)) { throw "Certificate file not found: $CertificateFile" } Write-Host ("Using certificate file: {0}" -f $resolvedCertificateFile) -ForegroundColor DarkGray $sshArguments += @("-o", "CertificateFile=$resolvedCertificateFile") } if ([string]::IsNullOrWhiteSpace($CertificateFile) -and $Server.ContainsKey("CertificatePath")) { $serverCertificatePath = $Server.CertificatePath $resolvedServerCertificateFile = Resolve-CertificateFile -Path $serverCertificatePath if (-not [string]::IsNullOrWhiteSpace($resolvedServerCertificateFile)) { Write-Host ("Using certificate file: {0}" -f $resolvedServerCertificateFile) -ForegroundColor DarkGray $sshArguments += @("-o", "CertificateFile=$resolvedServerCertificateFile") } elseif (-not [string]::IsNullOrWhiteSpace($serverCertificatePath)) { Write-Host ("Certificate not found or not a file: {0}" -f $serverCertificatePath) -ForegroundColor Yellow } } if ([string]::IsNullOrWhiteSpace($User)) { throw "SSH user not defined." } $sshArguments += @("-p", $Server.Port, "$User@$($Server.Host)") return $sshArguments } if (-not (Get-Command ssh.exe -ErrorAction SilentlyContinue)) { Write-Host "ssh.exe was not found on Windows." -ForegroundColor Red Write-Host "Install or enable the OpenSSH Client optional feature." -ForegroundColor Yellow exit 1 } while ($true) { Show-EnvironmentMenu $environmentChoice = Read-Host "Your choice" if ($environmentChoice -eq "0") { Write-Host "Closing..." exit 0 } $parsedEnvironmentChoice = 0 if (-not [int]::TryParse($environmentChoice, [ref]$parsedEnvironmentChoice)) { Write-Host "Invalid choice." -ForegroundColor Red Start-Sleep -Seconds 2 continue } $environmentIndex = $parsedEnvironmentChoice - 1 if ($environmentIndex -lt 0 -or $environmentIndex -ge $environments.Count) { Write-Host "Invalid choice." -ForegroundColor Red Start-Sleep -Seconds 2 continue } $selectedEnvironment = $environments[$environmentIndex] $selectedEnvironmentValue = $selectedEnvironment.Value $environmentServers = @( $servers | Where-Object { $_.Environment -eq $selectedEnvironmentValue } | Sort-Object { Get-ShortServerName -Server $_ } ) while ($true) { Show-ServerMenu -Environment $selectedEnvironmentValue $serverChoice = Read-Host "Your choice" if ($serverChoice -eq "0") { break } $parsedServerChoice = 0 if (-not [int]::TryParse($serverChoice, [ref]$parsedServerChoice)) { Write-Host "Invalid choice." -ForegroundColor Red Start-Sleep -Seconds 2 continue } $serverIndex = $parsedServerChoice - 1 if ($serverIndex -lt 0 -or $serverIndex -ge $environmentServers.Count) { Write-Host "Invalid choice." -ForegroundColor Red Start-Sleep -Seconds 2 continue } $selectedServer = $environmentServers[$serverIndex] $sshUserForConnection = $SshUser if ([string]::IsNullOrWhiteSpace($sshUserForConnection)) { $sshUserForConnection = Read-Host "SSH user" } if ([string]::IsNullOrWhiteSpace($sshUserForConnection)) { Write-Host "SSH user is required." -ForegroundColor Red Start-Sleep -Seconds 2 continue } $sshArguments = Get-SshArguments -Server $selectedServer -User $sshUserForConnection Write-Host "" Write-Host ("Connecting to {0} as {1}..." -f $selectedServer.Name, $sshUserForConnection) -ForegroundColor Green & ssh.exe @sshArguments exit $LASTEXITCODE } }