This is a function (3 functions) I have been meaning to write for a while. It is a Ping function that will ping more then one target and report what is responding, what is not and the time.
To keep the display easy to read, if there is no state change then the last line is over written.
This is better shown by example then description.
- Line 1, I’m dot sourcing the script to load the functions.
- Line 2, I’m calling the function with a list of Ping targets.
- Line 3, At 2:44 the function started pinging, everything responded.
- Line 4, At 2:44 and 55 seconds PingTest02 went down.
- Line 5, At 2:46 and 25 seconds PingTest02 came back up.
- Line 6, the script kept running until 2:47 and 20 seconds when I closed the script.
Limitations
- The script will not display correctly in the Powershell ISE. The cursor location does not work in the ISE so you will get a new line every Ping sweep.
- I have not tested what will happen if you ping so many targets that the display has to wrap to a new line. It “should” work.
Usage
MPing -PingTargets <string[]> [-PingSweeps <int>] [-PingChanges <int>] [-PingDelay <int>]
-PingSweeps <int> The number of times to ping all the targets. 0 = unlimited and is the default.
-PingChanges <int> The script will exit after the number of Ping changes (ie Up to down or down to up) 0 = unlimited and is the default.
-PingDelay <int> The delay between each Ping sweep in seconds. 2 is the default.
Examples
MPing -PingTargets “1.1.1.1” -PingSweeps 100 -PingChanges 10 -PingDelay 1
MPing “1.1.1.1”,”2.2.2.2″,”3.3.3.3″ -PingSweeps 100 -PingChanges 10 -PingDelay 5
MPing DC01.test.local,1.2.3.4,www.webserver.com -PingChanges 10 -PingDelay 1
The script is a bit too long to go through it in a tutorial but I have put in plenty of comments so it should be easy to follow.
# multiple ping script
# v1.001
# Jason Street
# 29/03/2020
<#
basically a ping function. will return the time in ms if successful, or return a $Null if unsuccessful.
#>
function Do-FuncPing
{
Param(
[Parameter(Mandatory = $true)] $PTarget,
$NumPings = 1
)
$PResult = Test-Connection $PTarget -Count $NumPings -ErrorAction SilentlyContinue
if ($?)
{
return $PResult.ResponseTime
}else{
return $null
}
}
<#
The function will take a text string and pad the end with spaces to make it $MaxSpace in length
$testStringofLength20 = Do-FuncPadSpace $testStringOfLength10 20
#>
function Do-FuncPadSpace
{
Param(
[Parameter(Mandatory = $true)] $DisplayText,
[Parameter(Mandatory = $true)] $MaxSpace
)
if ($DisplayText.length -le $MaxSpace)
{
# add spaces to text to make it the correct length
# I should do it with a loop but its not very readable
$spacesToAdd = $MaxSpace - $DisplayText.length
if ($spacesToAdd -eq 0){}
if ($spacesToAdd -eq 1){$Spaces = " "}
if ($spacesToAdd -eq 2){$Spaces = " "}
if ($spacesToAdd -eq 3){$Spaces = " "}
if ($spacesToAdd -eq 4){$Spaces = " "}
if ($spacesToAdd -eq 5){$Spaces = " "}
if ($spacesToAdd -eq 6){$Spaces = " "}
if ($spacesToAdd -eq 7){$Spaces = " "}
if ($spacesToAdd -eq 8){$Spaces = " "}
if ($spacesToAdd -eq 9){$Spaces = " "}
if ($spacesToAdd -eq 10){$Spaces = " "}
if ($spacesToAdd -eq 11){$Spaces = " "}
if ($spacesToAdd -eq 12){$Spaces = " "}
if ($spacesToAdd -eq 13){$Spaces = " "}
if ($spacesToAdd -eq 14){$Spaces = " "}
if ($spacesToAdd -eq 15){$Spaces = " "}
if ($spacesToAdd -eq 16){$Spaces = " "}
if ($spacesToAdd -eq 17){$Spaces = " "}
if ($spacesToAdd -eq 18){$Spaces = " "}
if ($spacesToAdd -eq 19){$Spaces = " "}
if ($spacesToAdd -eq 20){$Spaces = " "}
if ($spacesToAdd -eq 21){$Spaces = " "}
$Result = $DisplayText + $Spaces
return $Result
}else{
# text too long error. With the way the columns are now sized this should never happen.
}
}
<#
The main function.
usage MPing -PingTargets "1.1.1.1" -PingSweeps 100 -PingChanges 10 -PingDelay 1
.SYNOPSIS
MultiPing function. will ping multiple targets
.DESCRIPTION
Will ping multiple targets and report on connectivity. success in green, failures in red.
the output will only go to a new line when there is a state change (a target connects or disconnects)
MPing -PingTargets "1.1.1.1","8.8.8.8","1.2.3.4"
MPing -PingTargets 1.1.1.1,2.2.2.2 -PingSweeps 100 -PingChanges 10
MPing -PingTargets "1.1.1.1","8.8.8.8" -PingSweeps 100 -PingChanges 10
.PARAMETER PingTargets
a list (array of IPs or UNCs) eg "1.1.1.1","2.2.2.2" or 1.1.1.1,2.2.2.2
.PARAMETER PingSweeps
Number of times to ping the list of targets. if not entered will default to 0 (unlimited)
.PARAMETER PingChanges
Number of times list the state changes (up to down or down to up). if not entered will default to 10. 0 = unlimited.
.PARAMETER PingDelay
Number of seconds between Ping sweeps. Will default to 2 seconds.
#>
function MPing
{
Param(
[Parameter(Mandatory = $true)] $PingTargets,
[Parameter(Mandatory = $false)] $PingSweeps = 0,
[Parameter(Mandatory = $false)] $PingChanges = 0,
[Parameter(Mandatory = $false)] $PingDelay = 2
)
# convert the inputted IP array in to an object array with Lastresult and ColumnLength attributes.
$PingObjs = @()
foreach ($PingTarget in $PingTargets)
{
$tmpPingObj = '' | Select IP,LastResult,ColLength
$tmpPingObj.IP = $PingTarget
$PingObjs += $tmpPingObj
}
$Windowwidth = (Get-Host).UI.RawUI.MaxWindowSize.Width
# the first run will not have a previous run to compare results to,
$FirstRun = $true
# get the cursur posistion
$OrigCursPos = $host.UI.RawUI.CursorPosition
do{
$StateChange = $false
if($FirstRun -eq $true)
{
# draw the time at the start of the line
$NiceDate = get-date -f 'HH:mm:ss dd/MM/yyyy'
write-host (Do-FuncPadSpace $NiceDate 22) -NoNewline
# loop through the IPs to be tested, get the number of the loop so we can update the main array (Lastresult)
$NumLoops = 0
foreach ($PingObj in $PingObjs)
{
if($PingObj.IP -ne $null)
{
#before doing the check, get the length (width) of the column and add 2 charictors , then write that to the ColLength attribute.
$LengthThisCol = $PingObj.IP.length + 2
$PingObjs[$NumLoops].ColLength = $LengthThisCol
$PingRes = Do-FuncPing $PingObj.IP
if ($PingRes -ne $null)
{
# Ping failed
write-host (Do-FuncPadSpace $PingObj.IP $LengthThisCol) -ForegroundColor green -NoNewline
# set lastResult to $True
$PingObjs[$NumLoops].LastResult = $True
}else{
# Ping Successfull
write-host (Do-FuncPadSpace $PingObj.IP $LengthThisCol) -ForegroundColor red -NoNewline
# set lastResult to $False
$PingObjs[$NumLoops].LastResult = $false
}
}
$NumLoops ++
}
# Write a carriage return
write-host " "
# set the save cursor Position to this Position. so the next result is on a new line
$OrigCursPos = $host.UI.RawUI.CursorPosition
$FirstRun = $false
}else{
# put the cursor back to the start of the last line
$host.UI.RawUI.CursorPosition = $OrigCursPos
# draw the time at the start of the line
$NiceDate = get-date -f 'HH:mm:ss dd/MM/yyyy'
write-host (Do-FuncPadSpace $NiceDate 22) -NoNewline
# loop through the IPs to be tested, get the number of the loop so we can update the main array (Lastresult)
$NumLoops = 0
foreach ($PingObj in $PingObjs)
{
if($PingObj.IP -ne $null)
{
# ping test
$PingRes = Do-FuncPing $PingObj.IP
if ($PingRes -ne $null)
{
# Ping failed
write-host (Do-FuncPadSpace $PingObj.IP $PingObj.ColLength) -ForegroundColor green -NoNewline
# if last ping was a success then StateChange is $True
if ($PingObj.LastResult -eq $false){$StateChange = $true}
# set lastResult to $True
$PingObjs[$NumLoops].LastResult = $True
}else{
# Ping Successful
write-host (Do-FuncPadSpace $PingObj.IP $PingObj.ColLength) -ForegroundColor red -NoNewline
# if last ping was a fail then StateChange is $True
if ($PingObj.LastResult -eq $true){$StateChange = $true}
# set lastResult to $false
$PingObjs[$NumLoops].LastResult = $false
}
}
$NumLoops ++
}
# Write a carrige return
write-host ""
}
# delay the next ping sweep
start-sleep -Seconds $PingDelay
if ($StateChange -eq $true)
{
# if there has been a StateChange then save the Cursor Position, so the next result is written to a new line.
$OrigCursPos = $host.UI.RawUI.CursorPosition
$PingChanges --
}
# decrease the number of PingSweepes if above 1 (script will end at 1, or will be infinit loop if 0 (so no point decreasing it))
if ($PingSweeps -gt 1){$PingSweeps --}
}until($PingSweeps -eq 1 -or $PingChanges -eq 1)
}
Installation
Quick way
- Copy everything on the code window into a Powershell window or the ISE.
- run MPing with a list of targets.
Slower (dot sourcing) way
- Copy the above code in to a txt file.
- Save it and rename the txt file to whatever.ps1
- In a Powershell (or ISE) window run . .\path\whatever.ps1
- Run MPing with a list of targets.
Slower (download and dot sourcing) way
- Download the txt file below.
- Save it and rename the file to whatever.ps1
- In a Powershell (or ISE) window run . .\path\whatever.ps1
- Run MPing with a list of targets.
This is very cool, works perfectly. Thanks for the script and the write-up.