I wrote a function to check if a host is online or offline and return $true or $false. This function works perfectly and I would like to improve it a bit by seeing
if it's possible to remove the script-wide variables like $Script:arrayCanPingResult and $script:tmpPingCheckServers.
Why do I want this?
When I call the function within a foreach loop I usually use the switch -Remember so it doesn't check the same host twice. To be able to use this properly I have to begin all my scripts where I use this function by declaring both variables empty ( $Script:arrayCanPingResult=$script:tmpPingCheckServers=@{}). And I can imagine people forgetting to put this first line in their script and when testing multiple times in the PowerShell ISE editor, it won't do the tests again on the second run when the host has already been checked once in ISE (F5).
Is there a way to avoid using the script-wide variables in this case? So we don't need to declare them empty in the beginning of new scripts? If this was possible, it would be great because then we can include this function in a custom module.
As always, thank you for your advice or help. I really learned a lot here with you guys.
# Function to check if $Server is online
Function Can-Ping ($Server,[switch]$Remember) {
$PingResult = {
# Return $true or $false based on the result from script block $PingCheck
foreach ($_ in $Script:arrayCanPingResult) {
# Write-Host "$(Get-TimeStamp) $Server > Function Can-Ping: $_ " -ForegroundColor Green
if ($Server -eq $($_.Split(",")[0])) {
#Write-Host "$(Get-TimeStamp) $Server > Function Can-Ping: We will return $($_.Split(",")[1])" -ForegroundColor Green
return $($_.Split(",")[1])
}
}
}
$PingCheck = {
$Error.Clear()
if (Test-Connection -ComputerName $Server -BufferSize 16 -Count 1 -ErrorAction 0 -quiet) { # ErrorAction 0 doesn't display error information when a ping is unsuccessful
Write-Host "$(Get-TimeStamp) $Server > Function Can-Ping: Ping test ok" -ForegroundColor Gray; $Script:arrayCanPingResult+=@("$Server,$true"); return
}
else {
$Error.Clear()
Write-Host "$(Get-TimeStamp) $Server > Function Can-Ping: Ping test FAILED" -ForegroundColor Gray
Write-Host "$(Get-TimeStamp) $Server > Function Can-Ping: Flushing DNS" -ForegroundColor Gray
ipconfig /flushdns | Out-Null
Write-Host "$(Get-TimeStamp) $Server > Function Can-Ping: Registering DNS" -ForegroundColor Gray
ipconfig /registerdns | Out-Null
Write-Host "$(Get-TimeStamp) $Server > Function Can-Ping: NSLookup" -ForegroundColor Gray
nslookup $Server | Out-Null # Suppressing error here is not possible unless using '2> $null', but if we do this, we don't get $true or $false for the function so '| Out-Null' is an obligation
if (!$?) {
Write-Host "$(Get-TimeStamp) $Server > Function Can-Ping: NSlookup can't find the host '$Server', DNS issues or hostname incorrect?" -ForegroundColor Yellow
# Write-Host $Error -ForegroundColor Red
if ($SendMail) {
Send-Mail $MailTo "FAILED Ping test" "$(Get-TimeStamp) NSlookup can't find the host '$Server', hostname incorrect or DNS issues?" "<font color=`"red`">$error</font>"
}
$script:arrayCanPingError += "ERROR | $(Get-TimeStamp) Ping test failed: NSlookup can't find the host '$Server', hostname incorrect or DNS issues?$error"
$script:HTMLarrayCanPingError += "ERROR | $(Get-TimeStamp) Ping test failed:<br>NSlookup can't find the host '$Server', hostname incorrect or DNS issues?<br><font color=`"red`">$error</font>"
$Script:arrayCanPingResult+=@("$Server,$false")
return
}
else {
Write-Host "$(Get-TimeStamp) $Server > Function Can-Ping: Re-pinging '$Server'" -ForegroundColor Gray
if (Test-Connection -ComputerName $Server -BufferSize 16 -Count 1 -ErrorAction 0 -Quiet) {
Write-Host "$(Get-TimeStamp) $Server > Function Can-Ping: Ping test ok, problem resolved" -ForegroundColor Gray
$Script:arrayCanPingResult+=@("$Server,$true")
return
}
else {
Write-Host "$(Get-TimeStamp) $Server > Function Can-Ping: DNS Resolving is ok but can't connect, server offline?" -ForegroundColor Yellow
if ($SendMail) {
Send-Mail $MailTo "FAILED Ping test" "$error" "DNS Resolving is ok but can't connect to $Server, server offline?"
}
$script:arrayCanPingError += "ERROR Ping test failed: DNS Resolving is ok but can't connect to $Server, server offline?$error"
$script:HTMLarrayCanPingError += "ERROR Ping test failed: DNS Resolving is ok but can't connect to $Server, server offline?<br><font color=`"red`">$error</font>"
$Script:arrayCanPingResult+=@("$Server,$false")
return
}
}
}
}
# Call the script block $PingAction every time, unless the switch $Remember is provided, than we only check each server once
if ($Remember) {
Write-Host "$(Get-TimeStamp) $Server > Function Can-Ping: Switch '-Remember' detected" -ForegroundColor Gray
While ($tmpPingCheckServers -notcontains $Server) {
&$PingCheck
$script:tmpPingCheckServers = @($tmpPingCheckServers+$Server) #Script wide variable, otherwise it stays empty when we leave the function / @ is used to store it as an Array (table) instead of a string
}
&$PingResult
}
else {
&$PingCheck
&$PingResult
}
}
Test-Connectiononce and the rest is logic (which is too convoluted to even read) andWrite-Hostcalls (Write-Host is evil, avoid it in production code). Could you provide a sample of input and expected output?Can-Ping -Remember SERVER1and returns$trueif SERVER1 is online and$falsewhen it's not. The second time I runCan-Ping -Remember SERVER1it won't do any tests at all and returns the last known result for that server. In my script I call this function each time within aforeachloop with a new$Servername. Depending on the result I then checkPSRemotingcapabilities and other stuff.