0

I was setting up a monitoring script that would ping IPs and send an email when a packet fails. I decided to use Test-Connection. Below is a sample code:

Code1

$IPList = @("192.168.0.1","172.217.161.15")
Test-Connection -ComputerName $IPList -BufferSize 4 -Count 1 -AsJob -ErrorAction Stop
Get-Job | Wait-Job | Receive-Job

So the result I get is:

Source        Destination     IPV4Address      IPV6Address                              Bytes    Time(ms) 
------        -----------     -----------      -----------                              -----    -------- 
BLR-AA200906  172.217.161.15                                                            4        55       
BLR-AA200906  192.168.0.1                                                               4                 

The 192 series IP is supposed to throw an error. You can see that the Time(ms) column is empty.

Code2

$IPList = @("192.168.0.1","172.217.161.15")
foreach ($IP in $IPList)
{
    Start-job -ScriptBlock {Test-Connection -ComputerName $Args[0] -BufferSize 4 -Count 1 -ErrorAction Stop} -ArgumentList $IP
}
Get-Job | Wait-Job | Receive-Job

If I do this, it will throw an error that I can easily catch

Testing connection to computer '192.168.0.1' failed: Error due to lack of resources
    + CategoryInfo          : ResourceUnavailable: (192.168.0.1:String) [Test-Connection], PingException
    + FullyQualifiedErrorId : TestConnectionException,Microsoft.PowerShell.Commands.TestConnectionCommand
    + PSComputerName        : localhost

The Question

This brings me to my question. How can I receive/catch error messages thrown in Code1? The difference is I am using -AsJob which is much more efficient than spinning up a job in a foreach loop.

PS Version is 5.1

Sid
  • 2,586
  • 1
  • 11
  • 22
  • Is there a reason to use a job here if you're not even going to do things asynchronously? – Jacob Colvin Aug 07 '18 at 15:07
  • Pinging the machines in parallel is the one thing I cannot compromise on. `Start-Job` spins up a process for every machine and if the machinelist grows, `Start-Job` makes it worse. However, `-AsJob` does this with a single process and I have observed to be really fast but apparently, receiving error messages is causing me undue overhead. `Runspaces` is another option but it also causes overhead for processing results. Comparitively, `-AsJob` seem to be the better option here. – Sid Aug 08 '18 at 06:07
  • I'm saying that -AsJob creates a single job, right? So how is that faster than just running it. It's not parallel if it's a single job. – Jacob Colvin Aug 09 '18 at 13:23

2 Answers2

2

I don't have a particular solution, but this is the cause of your issue:

The -AsJob parameter creates a WMI job, whereas Start-Job creates a CIM job.

WMI jobs will each have their own instance, where as CIM jobs run in your current Powershell instance. Thus, WMI jobs do not take additional time to create and destroy instances using your current session, which can be very costly.

WMI jobs are a bit more detached from Powershell in general. They don't seem to have a "host" shell to transcribe to. This is where the duality of Powershell can get a bit confusing.

Unfortunately, in this case, that means you completely rely on the Test-Connection cmdlet implementing Powershell's event handlers, and it appears that is not the case, since job states always record as 'Completed'. You are no longer able to fall back to the "compatibility" that CIM jobs bring by simply dumping their own shell to the output stream.

Start-Job -ScriptBlock {Write-Host "This is not real output."} | Wait-Job | Receive-Job
PS> This is not real output.

Thus, in your case, if you must use -AsJob, the best you can do is monitor $error. (As far as I can tell.)

Jacob Colvin
  • 2,625
  • 1
  • 17
  • 36
1

It's important to differentiate that your code examples are doing different things, Code1 is spinning up a single job and running x number of test connections. Code2 is spinning up x number of jobs each testing 1 connection.

I'm not aware of a way to get the errors out of test-connection with -asjob, however a different approach would be to use the statuscode attribute. 11010 is the statuscode for Error due to lack of resources which occurrs when test-connection doesn't receive a response from the host, aka Request Timed Out.

The downside to using the statuscode, is that you need to translate the status codes.

$IPList = @("192.168.254.1","172.217.161.15")

$StatusCodes = @{
    [uint32]0     = 'Success'
    [uint32]11001 = 'Buffer Too Small'
    [uint32]11002 = 'Destination Net Unreachable'
    [uint32]11003 = 'Destination Host Unreachable'
    [uint32]11004 = 'Destination Protocol Unreachable'
    [uint32]11005 = 'Destination Port Unreachable'
    [uint32]11006 = 'No Resources'
    [uint32]11007 = 'Bad Option'
    [uint32]11008 = 'Hardware Error'
    [uint32]11009 = 'Packet Too Big'
    [uint32]11010 = 'Request Timed Out'
    [uint32]11011 = 'Bad Request'
    [uint32]11012 = 'Bad Route'
    [uint32]11013 = 'TimeToLive Expired Transit'
    [uint32]11014 = 'TimeToLive Expired Reassembly'
    [uint32]11015 = 'Parameter Problem'
    [uint32]11016 = 'Source Quench'
    [uint32]11017 = 'Option Too Big'
    [uint32]11018 = 'Bad Destination'
    [uint32]11032 = 'Negotiating IPSEC'
    [uint32]11050 = 'General Failure'
}

Test-Connection -ComputerName $IPList -BufferSize 4 -Count 1 -AsJob
Get-Job | Wait-Job | Receive-Job | ft Address,IPV4Address,IPV6Address,Buffersize,ResponseTime,@{n="Status";e={$StatusCodes[$_.statuscode]}}

This gives the following output

Address        IPV4Address    IPV6Address Buffersize ResponseTime Status           
-------        -----------    ----------- ---------- ------------ ------           
172.217.161.15 172.217.161.15                      4          381 Success          
192.168.254.1                                      4              Request Timed Out

I appreciate this doesn't give you the error messages, but you can check the status of each request and use that to do the business logic you would have done by catching the errors.

References

IcmpSendEcho2 fails with fails with WSA_QOS_ADMISSION_FAILURE and ERROR_NOACCESS

https://learn.microsoft.com/en-gb/windows/desktop/api/ipexport/ns-ipexport-icmp_echo_reply

Powershell - Test-Connection failed due to lack of resources

Jacob
  • 1,182
  • 12
  • 18
  • I like this one. Seems to be doing what I would like with acceptable overhead. Thank you. `Hashtable` is a nice touch, improves efficiency. – Sid Aug 08 '18 at 06:12