Configuring Network Devices Programmatically with PowerShell

๐Ÿงญ 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:

PowerShell
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:

PowerShell
$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:

PowerShell
$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.