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.
I love this! Except I do not see an explanation of how to format the input file “Book1.csv”. I am guessing it is 3 columns with the stated info. I am going to try that, but look forward to any input from you.
Hi, Thank you for pointing it out. I have added the CSV format to the last section of the article. Check it out.
If you are not able to view it, please clear the browser cache and try again 🙂
OK, I’ve got that now and found a couple of issues running it. Your switchbackup.txt destination is D: which will error for most. Also your out.csv in the sentence below the script is D:. Also, I am not great with PS, but is it possible to have the script make the SWBKP folder if it does not exist? I made it myself, but that would streamline the prep.
Thanks so much! You’re saving me so much effort and it means a ton.
Glad that the script helped you 🙂
I have put the folder creation part also. It is better to put all the file/folder path declarations to the top of the script and use that variable in the script. This makes it more readable and easy to change the path. I will make this change soon.
I hope this script is still useful to you and I am not being a pain! I noticed, after examining the info more, that it only outputs the last model and SN of a stack when the switches are stacked.
We have a bunch of Integrated Services Routers that I am working on getting it to work with as well. Sh Ver is not useful on them, but Sh Inventory is! However the SN: and PID: are on one line. The PID is the model and SN is obvious.