Using Powershell and Posh-SSH to GSD.

I’m a guy who thinks you should use the right tool for the job.

For instance, if you’re in a Windows environment, and you need to script something, installing Cygwin just so you can use BASH is probably not the right way to go.

That being said, I love BASH, but I find myself in Windows environments more and more, so I figured it was time to dust off my PowerShell skills- Especially when I found out about Posh-SSH.

Posh-SSH is one of the (for now) missing SSH libraries that Windows/PowerShell so desperately needs.

You can use Posh to create SSH sessions, send commands and teardown ssh sessions.

This script can log into several devices simultaneously, then run several different commands (of your choosing) on each of the devices, then either display the output on the console, or write to a log file for each device.

It is still a work in progress, and I will probably add a few things to it like:

  • Choosing the directory where the log files go
  • Doing various console cleanup based on the device you are logging into
  • Have all of the input via console (instead of pop-ups)

A few limitations:

  • The devices that this is run against must have the same user/pass
  • The devices that this run against need to support the same commands (or you won’t get any valid input
  • Commands that expect a break to stop (like ping in Linux), will keep running as we are unable to pass a break at this point in time

Find the script below, enjoy!

Param(
   [Parameter(Mandatory=$False,Position=1)]
   [string[]]$devices,
	
   [Parameter(Mandatory=$True)]
   [string[]]$commands,

   [Parameter(Mandatory=$false)]
   [switch] $enable,

   [Parameter(Mandatory=$false)]
   [switch] $logfile
)
#uncomment line below if you want to do a huge list of devices without having to pass them via the command line

#$devices =@("10.106.0.1")


If ($devices -eq $null){
    Write-Host "you need to have a device defined, try -devices"
    Exit
}

#if we don't set the terminal some remote terminals will whine

$TERM = "msys"

#all your creds are belong to script


$creds = Get-Credential

#if your device requires an enable password provide it

If (($enable -eq $true)){
   $enablepass = Read-Host please enter your enable password -AsSecureString
}

#we don't want your password visible as you type it, but we need to use that variable later, so convert the secure string to a usable variable

$clearenablepass = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
    [Runtime.InteropServices.Marshal]::SecureStringToBSTR($enablepass))


#this is the scriptblock that gets called by the job

$scriptblock =  {
   #we have to pass all these variables as arguments (from the start-job) because when launched from start-job each runs in its own space (that doesn't have the previously defined variables)

   $device = $args[0]
   $devices = $args[1]
   $commands = $args[2]
   $time = $args[3]
   $creds = $args[4]
   $enable = $args[5]
   $logfile = $args[6]
   $path = $args[7]
   
   #Create Sessions

   New-SSHSession -ComputerName $device -AcceptKey -Credential $creds | Out-Null

   #Run all the commands that were originally passed as arguments

   Foreach ($command in $commands){
       #Figure out what your SSH session is

       $session = Get-SSHSession  -Computername $device
       
       #Then build a stream for it (we only need to do this once per device

       If ($stream -eq $null){
            $stream = $session.Session.CreateShellStream("xterm", 1000, 1000, 1000, 1000, 1000)
       }

       #We have to give the stream a few seconds to build the terminal (otherwise all of the commands will get passed before the terminal has had a chance to finish creation)

       Start-Sleep -Seconds 2
       
       
      #If you pass the enable parameter, lets make sure to get into enable mode

      #also, we only want to enter enable mode once per device

       If (($enable -eq $true)){
            $stream.write("enable `n")
            $stream.write("$clearenablepass`n")
            $alreadyenabled = $true

   }

       #if the logfile parameter is passed write everything to a log file

    If (($logfile -eq $true)){
        
        #provide some nice formatting for the beginning of the block

        Write-Output "************************************************************************" >> "$($path)\$($device)__$($time).txt"
        Write-Output "Begin Output for command $command on device $device" >> "$($path)\$($device)__$($time).txt"
        Write-Output "************************************************************************" >> "$($path)\$($device)__$($time).txt"
        
        #lets read the stream, press enter and then clear that from the buffer

        #you're not really interested in the output of logging in, thats why you're running this script

        $streamoutput = $stream.read()
        $stream.Write("$command `n")
        Clear-Variable streamoutput
        Start-Sleep -Seconds 1
        
        #we did just nuke this object, so lets go ahead and redefine it

        $streamoutput = $stream.Read()
        
        #provide some nice formatting for the end of the block

        Write-Output $streamoutput >> "$($path)\$($device)__$($time).txt"
        Write-Output "************************************************************************" >> "$($path)\$($device)__$($time).txt"
        Write-Output "End Output for command $command on device $device" >> "$($path)\$($device)__$($time).txt"
        Write-Output "************************************************************************" >> "$($path)\$($device)__$($time).txt"
        Write-Output "" >> "$($path)\$($device)__$($time).txt"
        Write-Output "" >> "$($path)\$($device)__$($time).txt"
        Write-Output "" >> "$($path)\$($device)__$($time).txt"
        Write-Output "" >> "$($path)\$($device)__$($time).txt"
    }

    #otherwise write the output to the terminal

    Else{
        
       #provide some nice formatting for the beginning of the block

       Write-Host "************************************************************************"
       Write-Host "Begin Output for command $command on device $device"
       Write-Host "************************************************************************"
       
       #lets read the stream, press enter and then clear that from the buffer

       #you're not really interested in the output of logging in, thats why you're running this script

       $streamoutput = $stream.read()
       $stream.Write("$command `n")
       Clear-Variable streamoutput
       Start-Sleep -Seconds 1
       
       #we did just nuke this object, so lets go ahead and redefine it      

       $streamoutput=$stream.Read()
       
       #provide some nice formatting for the end of the block

       Write-Host $streamoutput
       Write-Host "************************************************************************"
       Write-Host "End Output for command $command on device $device"
       Write-Host "************************************************************************"
       Write-Host ""
       Write-Host ""
       Write-Host ""
       Write-Host ""
   }
}


   #well, we're done with this ssh session, might as well kill it

   Remove-SSHSession -SSHSession $session | Out-Null
}



#create an empty array for the job objects

$jobs = @()

Foreach ($device in $devices){    
   $time = Get-Date -Format "yyyy-MM-dd-HH-mm-ss"
   $path = Get-Location
   $job_run = Start-Job -Name "SSH-FORK-$device" -ScriptBlock $scriptblock -ArgumentList $device,$devices,$commands,$time,$creds,$enable,$logfile,$path
   
   
   $jobs += ,$job_run
}

#we only need to worry about grabbing the console output of the jobs if we aren't using a logfile

If ($logfile -ne $true){

    #we need to grab the console output for each of the jobs

    Foreach ($job in $jobs){
        
        #going to need the id of the job for that

        $jobid = $job.id
        
        #while the job state isn't complete, tell the user to wait

        DO
        {
            Write-Host "Waiting for jobs to complete"
            $jobstate = $job.state

            #lets put in a quick sleep here, no need to see how fast we can run this loop

            Start-Sleep -Seconds 1
            
        } While ($jobstate -ne "Completed")
        
        #wubalubadubdub!  view the result of your (not so)hard work!

        Receive-Job $job
    }
}

Tagged #A10, #Cisco, #Posh-SSH, #powershell, #script, #Scripting, #ssh

2021

Back to top ↑

2020

Self Service with Satellite

less than 1 minute read

Recently I had a customer who was running into some legacy infrastructure challenges with VM consistency, meeting (internal) customer expectations, and gener...

Winning with Soft Tokens

2 minute read

Well, I’d call that a sabbatical, but I think that would mean I was relaxing. Time to revive this thing (again).

Back to top ↑

2018

Back to top ↑

2017

Using Powershell and Posh-SSH to GSD.

5 minute read

I’m a guy who thinks you should use the right tool for the job. For instance, if you’re in a Windows environment, and you need to script something, installi...

iDrac, RACADM, sshpass, and BASH

8 minute read

If it were up to me, I suppose that the only thing that I’d really be responsible for would be core networking infrastructure (and consumption of craft brews...

Dynamic DNS and you

3 minute read

Okay, so I know the popular thing with network engineers is to remember the IP of EVERYTHING.  I’m pretty good at it too.  But having a lab at home and needi...

Back to top ↑