Add SSH Manager/ssh-manager.ps1

This commit is contained in:
2026-04-19 11:10:04 -03:00
parent ed1b5ff7a9
commit cc90f180a1
+378
View File
@@ -0,0 +1,378 @@
<#
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 "^(?<EnvironmentLabel>.+?)\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
}
}