This article demonstrates how to fetch information from network devices. The Powershell code will telnet to the network switches and fetch the device information. Also, it performs configuration backup.

I have posted another article to get network device configuration backup using python (Click here to view the article) which is in a sequential way. This post describes the use of parallel processing and Telnet.

SSH is another way to backup the network device. Please have a look at F5 backup article @ Automate-F5-Backup.

Automate Cisco Switch Configuration backup and Inventory

Use Case: Fetch Network Switch information using Powershell

Mail from CIO: There has been an incident where our inventory database is corrupted and the backup data is unreadable. We need to find all the network switch details which is required for auditing. Please make it ready ASAP!

Solution: As the management interface is configured for all the devices, we can telnet or ssh to the switch and fetch the information. As the number of switches is quite high, we will automate the task with PowerShell.

Apart from usual scripting technique, because the task can be executed independent of the devices. We have introduce parallel task which drastically reduce the execution time.

For parallel execution, we are using runspace. I have used the foreach-parallel code from https://powertoe.wordpress.com/2012/05/03/foreach-parallel/

To telnet to the switches, we are using the Get-Telnet function from https://community.spiceworks.com/scripts/show/1887-get-telnet-telnet-to-a-device-and-issue-commands. I have tested it with Cisco switches and it works perfectly.

#Creating folder where the configuration backup and inventory file will be saved
#Running the script will create a folder Script in C drive if it is not available
if(!(Get-Item C:\Script -ErrorAction SilentlyContinue)){
    New-Item -Path C:\ -Name Script -ItemType Directory
}
if(!(Get-Item C:\Script\SWBKP -ErrorAction SilentlyContinue)){
    New-Item -Path C:\Script -Name SWBKP -ItemType Directory
}


#Class SwitchSpec definition - To save the switch information
Class SwitchSpec {

    [string]$Serial
    [string]$Model
    [string]$DeviceIP
    #To sort the result based on 4th octet of IP address
	[int]$IPNo
	
    SwitchSpec() {
        $this.Serial = ""
        $this.Model = ""
        $this.DeviceIP = ""
        $this.IPNo = ""
    }

    readInfo($FileName) {
        $Contents = Get-Content $FileName
		
        foreach($content in $Contents) {
            if($content.ToUpper() -like "*SYSTEM SERIAL NUMBER*" -and $content.ToUpper() -notlike "*| INC SYSTEM SERIAL NUMBER*" ) {
                $this.Serial = $content.Split(':')[1].trim()
            }
            if($content.ToUpper() -like "*MODEL NUMBER*" -and $content.ToUpper() -notlike "*| INC MODEL NUMBER*" ) {
                $this.Model = $content.Split(":")[1].trim()
            }
            $this.DeviceIP = $FileName.Split('_')[0].split('\')[-1]
            $this.IPNo = [int]$this.DeviceIP.Split('.')[-1]
        }
    }
}


### ForEach-Parallel Code###
function ForEach-Parallel {
    param(
        [Parameter(Mandatory=$true,position=0)]
        [System.Management.Automation.ScriptBlock] $ScriptBlock,
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [PSObject]$InputObject,
        [Parameter(Mandatory=$false)]
        [int]$MaxThreads=10
    )
    BEGIN {
        $iss = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
        $pool = [Runspacefactory]::CreateRunspacePool(1, $maxthreads, $iss, $host)
        $pool.open()
        $threads = @()
        $ScriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock("param(`$_)`r`n" + $Scriptblock.ToString())
    }
    PROCESS {
        $powershell = [powershell]::Create().addscript($scriptblock).addargument($InputObject)
        $powershell.runspacepool=$pool
        $threads+= @{
            instance = $powershell
            handle = $powershell.begininvoke()
        }
    }
    END {
        $notdone = $true
        while ($notdone) {
            $notdone = $false
            for ($i=0; $i -lt $threads.count; $i++) {
                $thread = $threads[$i]
                if ($thread) {
                    if ($thread.handle.iscompleted) {
                        $thread.instance.endinvoke($thread.handle)
                        $thread.instance.dispose()
                        $threads[$i] = $null
                    }
                    else {
                        $notdone = $true
                    }
                }
            }
        }
    }
}
### End of ForEach-Parallel Code###


#Fetching the Switch IP, UserName and Password from CSV file - the file format is added at the end of this article
$SwitchIP = Get-Content "C:\Script\SwitchIPDetails.csv" | ConvertFrom-Csv

$SwitchIP | ForEach-Parallel { 

	#Assigning each devices IP, username and password to variable
	$IP = $_.deviceIp
	$U = $_.userName
	$P = $_.password


	$__DateTime = Get-Date
	$FilePath = "C:\Script\SWBKP\" + $IP + "_Backup_$($__DateTime.Day)_$($__DateTime.Month)_$($__DateTime.Year).txt"

	#As the telnet is initiated inside the parallel runspace, we have to define the Get-telnet function inside foreach-parallel script block
	#I have removed the help metadata to minimize the code. Please refer to the original post for full information
	Function Get-Telnet
	{   Param (
			[Parameter(ValueFromPipeline=$true)]
			[String[]]$Commands = @("username","password","disable clipaging","sh run"),
			[string]$RemoteHost = "HostnameOrIPAddress",
			[string]$Port = "23",
			[int]$WaitTime = 1000,
			[string]$OutputPath = "C:\Script\SwitchInfos.cs"
		)
		#Attach to the remote device, setup streaming requirements
		$Socket = New-Object System.Net.Sockets.TcpClient($RemoteHost, $Port)
		If ($Socket)
		{   $Stream = $Socket.GetStream()
			$Writer = New-Object System.IO.StreamWriter($Stream)
			$Buffer = New-Object System.Byte[] 1024 
			$Encoding = New-Object System.Text.AsciiEncoding

			#Now start issuing the commands
			ForEach ($Command in $Commands)
			{   $Writer.WriteLine($Command) 
				$Writer.Flush()
				Start-Sleep -Milliseconds $WaitTime
			}
			#All commands issued, but since the last command is usually going to be
			#the longest let's wait a little longer for it to finish
			Start-Sleep -Milliseconds ($WaitTime * 4)
			$Result = ""
			#Save all the results
			While($Stream.DataAvailable) 
			{   $Read = $Stream.Read($Buffer, 0, 1024) 
				$Result += ($Encoding.GetString($Buffer, 0, $Read))
			}
		}
		Else     
		{   
			$Result = "Unable to connect to host: $($RemoteHost):$Port"
		}
		#Done, now save the results to a file
		$Result | Out-File $OutputPath
	}



	#This will telnet to the switches and initiate the following switch commands
	#terminal length 0 								---- Set terminal length to zero
	#sh run | inc ip address							---- 
	#sh version | inc System [Ss]erial [Nn]umber
	#sh version | include Model [Nn]umber
	#sh vtp status
	#sh run
	
	Get-Telnet -RemoteHost $IP -Commands $U,$P,"terminal length 0","sh version | inc System [Ss]erial [Nn]umber","sh version | include Model [Nn]umber","sh vtp status","sh run" -OutputPath $FilePath -WaitTime 1000

}




#Defining a list of type class which is defined at the top of this code
$SwitchInfoList = New-Object Collections.Generic.List[SwitchSpec]

#Read all configuration files created by Get-Telnet function
$files = Get-childitem C:\Script\SWBKP\
$files | % {

$objSwitchInfo = New-Object ([SwitchSpec]::new())
$objSwitchInfo.readInfo($_.FullName)
$SwitchInfoList.Add($objSwitchInfo)
}

$SwitchInfoList | select DeviceIP, Serial, Model, IPNo | Sort-Object IPNo | Export-Csv C:\Script\SwitchInfos.csv -NoTypeInformation

Finally we have the device information @ C:\Script\SwitchInfos.csv and configurations @ C:\Script\SWBKP\192.168.1.1_Backup_27_2_2020.txt

The input file “C:\Script\SwitchIPDetails.csv” should have details such as IP Address, Username, and Password as shown below.

deviceIp,userName,password
10.0.0.1,admin,complexpassword
10.0.0.2,admin,complexpassword2

Thank you for reading my post. Hope it is helpful for you.