๐งญ Introduction
Manually configuring network switches and routers can be time-consuming, error-prone, and inefficient; especially when managing multiple devices across different locations. As a developer or network engineer, automating these tasks can save hours of repetitive work and ensure consistency across your infrastructure.
In this post, Iโll walk you through how to use PowerShell and the SSH.NET library to build a lightweight SSH session manager that allows you to programmatically configure network devices. Whether you’re setting up VLANs, enabling SNMP, or pushing firmware updates, this approach gives you full control from a script.
โ๏ธ Why PowerShell + SSH.NET?
PowerShell is a powerful scripting language with deep integration into Windows environments, making it ideal for automation tasks. SSH.NET is a .NET library that provides SSH capabilities without requiring external dependencies like PuTTY or OpenSSH.
Together, they allow you to:
- Establish secure SSH connections to network devices.
- Send CLI commands and parse responses.
- Manage multiple sessions simultaneously.
- Clean up terminal output for readable logs.
๐งฑ Building the SSH Session Manager
At the heart of this solution is a custom PowerShell class called SSHSession
. It wraps around the SSH.NET client and shell stream, allowing you to manage connections and send commands cleanly.
Key Features:
- Session tracking via a static list.
- VT100 cleanup using regex to remove escape sequences.
- Shell stream support for interactive command execution.
Hereโs the meat and potatoes of the script:
class SSHSession {
static $VT100RegEx = "\u001B\[(1M|2K|2J|6n|\?(25h|6l|7h)|[0-9]*;[0-9]*[Hr])"
static $VT100NewLn = "\u001B\[1L"
static $Sessions = [System.Collections.Generic.List[SSHSession]]::new()
[int]$ID
[object]$Client
[object]$ShellStream
SSHSession($Client) {
$this.Client = $Client
$this.ID = [SSHSession]::Sessions.Count + 1
}
[bool]IsConnected() {
if ($null -eq $this.Client) { return $false }
return $this.Client.IsConnected
}
}
function Global:Import-SSHModule() {
$modPath = "C:\Windows\Temp\SSH.NET\lib\net40\Renci.SshNet.dll"
if ((Test-Path -Path $modPath) -eq $false) {
Write-Host "Installing SSH.NET..."
Invoke-WebRequest -UseBasicParsing -Uri "https://www.nuget.org/api/v2/package/SSH.NET" -OutFile "C:\Windows\Temp\SSH.NET.zip"
Expand-Archive -Path "C:\Windows\Temp\SSH.NET.zip" -DestinationPath "C:\Windows\Temp\SSH.NET\"
}
$dll = $modPath
Import-Module $dll
}
function Global:Get-SSHSession([int]$ID = 0) {
if ($ID -ne 0) {
return [SSHSession]::Sessions | Where-Object -Property ID -EQ -Value $ID
}
return [SSHSession]::Sessions
}
function Global:New-SSHSession(
[Parameter(Mandatory)][string]$Hostname,
[Parameter(Mandatory)][PSCredential]$Credential,
[int]$Port = 22
) {
Import-SSHModule
$Session = [SSHSession]::new([Renci.SshNet.SshClient]::new($Hostname, $Port, $Credential.UserName, $Credential.GetNetworkCredential().Password))
[SSHSession]::Sessions.Add($Session)
return $Session
}
function Global:Start-SSHSession(
[Parameter(Mandatory, ValueFromPipeline)][SSHSession]$Session
) {
$Client = ([Renci.SshNet.SshClient]$Session.Client)
Write-Host -ForegroundColor Yellow "Connecting to: $($Client.ConnectionInfo.Host)..."
$Client.Connect()
$Session.ShellStream = $Client.CreateShellStream("VT100", 100, 100, 1024, 1024, 1024)
return $Session
}
function Global:Stop-SSHSession(
[Parameter(Mandatory, ValueFromPipeline)][SSHSession]$Session
) {
$Client = ([Renci.SshNet.SshClient]$Session.Client)
Write-Host -ForegroundColor Yellow "Disconnecting from: $($Client.ConnectionInfo.Host)..."
$Client.Disconnect()
return $Session
}
function Global:Send-SSHCommand(
[Parameter(Mandatory, ValueFromPipeline)][SSHSession]$Session,
[Parameter(Mandatory)][string[]]$Commands,
[Regex]$Expect = [Regex]::new("# "),
[TimeSpan]$Timeout = [TimeSpan]::new(0, 0, 30),
[switch]$RawOutput = $false
) {
$Client = ([Renci.SshNet.SshClient]$Session.Client)
$Stream = ([Renci.SshNet.ShellStream]$Session.ShellStream)
$Guid = New-Guid
foreach($cmd in $Commands) {
Write-Host -ForegroundColor Green "Sending: $cmd"
$Stream.WriteLine($cmd)
}
Write-Host -ForegroundColor Red "Waiting for: $Expect"
$rawOut = $Stream.Expect($Expect, $Timeout)
if (-not $RawOutput) {
$clnOut = $rawOut -replace [SSHSession]::VT100RegEx, ""
$clnOut = $clnOut -replace [SSHSession]::VT100NewLn, "`r`n"
return $clnOut
}
return $rawOut
}
๐ ๏ธ Core Functions
The script includes several global functions to manage sessions:
Import-SSHModule
: Downloads and loads the SSH.NET DLL.New-SSHSession
: Creates a new session object.Start-SSHSession
/Stop-SSHSession
: Connects or disconnects the SSH client.Send-SSHCommand
: Sends commands and waits for expected output.
Hereโs how you might use them:
$cred = Get-Credential
$session = New-SSHSession -Hostname "192.168.1.1" -Credential $cred
Start-SSHSession -Session $session
Send-SSHCommand -Session $session -Commands @("configure terminal", "interface vlan 10", "ip address 192.168.10.1 255.255.255.0") -Expect "Switch#"
Stop-SSHSession -Session $session
๐งช Real-World Use Case: VLAN Configuration
Letโs say you need to configure VLANs on a batch of switches. With this script, you can loop through IPs and send the necessary commands:
$switches = @("192.168.1.1", "192.168.1.2", "192.168.1.3")
foreach ($ip in $switches) {
$session = New-SSHSession -Hostname $ip -Credential $cred
Start-SSHSession -Session $session
Send-SSHCommand -Session $session -Commands @(
"configure terminal",
"vlan 10",
"name Guest",
"exit"
) -Expect "Switch#"
Stop-SSHSession -Session $session
}
This approach ensures consistent configuration across all devices with minimal effort.
๐ Conclusion
Automating network device configuration with PowerShell and SSH.NET is a game-changer for infrastructure management. It reduces manual effort, improves consistency, and gives you the flexibility to scale your operations.
Whether you’re managing a few switches or an entire data center, this approach can streamline your workflow and empower you to focus on more strategic tasks.