Test-connection intermittently fails with a lack of resources error:
test-connection : Testing connection to computer 'SOMESERVER' failed: Error due to lack of resources
At line:1 char:45
+ ... ($server in $ServersNonProd.Name) { test-connection $server -Count 1}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ResourceUnavailable: (SOMESERVER:String) [Test-Connection], PingException
+ FullyQualifiedErrorId : TestConnectionException,Microsoft.PowerShell.Commands.TestConnectionCommand
As a result, it's not reliable and fairly useless when you need to test a list of computers in a loop. Is there a fix, alternative, or workaround to get this functionality reliably?
This is my current solution, but it's still not sufficiently reliable (sometimes they still fail 5 times in a row) and it takes forever because of all the delays and retries.
$Servers = Import-CSV -Path C:\Temp\Servers.csv
$result = foreach ($Name in $Servers.FQDN) {
$IP = $null
if ( Resolve-DNSName $Name -ErrorAction SilentlyContinue ) {
$IP = (Test-Connection -Count 1 -ComputerName $Name -ErrorAction SilentlyContinue).IPv4Address
if ( $IP -eq $null ) {
Start-Sleep -Milliseconds 100
$IP = (Test-Connection -Count 1 -ComputerName $Name -ErrorAction SilentlyContinue).IPv4Address
}
if ( $IP -eq $null ) {
Start-Sleep -Milliseconds 200
$IP = (Test-Connection -Count 1 -ComputerName $Name -ErrorAction SilentlyContinue).IPv4Address
}
if ( $IP -eq $null ) {
Start-Sleep -Milliseconds 300
$IP = (Test-Connection -Count 1 -ComputerName $Name -ErrorAction SilentlyContinue).IPv4Address
}
if ( $IP -eq $null ) {
Start-Sleep -Milliseconds 400
$IP = (Test-Connection -Count 1 -ComputerName $Name -ErrorAction SilentlyContinue).IPv4Address
}
}
new-object psobject -Property #{FQDN = $Name; "IP Address" = $IP}
}
A normal ping (ping.exe) works every time, so if there's a good way to parse that with powershell (host up or down, what IP is responding), that seems like the ideal solution, but I just need something that works, so I'm open to ideas.
In newer versions of PowerShell, the -Quiet parameter on Test-Connection does seem to always return either True or False. It didn't seem to work consistently on older versions, but either I'm doing something differently now or they've improved it:
$Ping = Test-Connection -ComputerName $ComputerName -Count 1 -Quiet
I haven't tested it recently when the network is simply unavailable, however.
Older answer:
Test-Connection doesn't respond well when DNS doesn't respond with an address or when the network is unavailable. That is, if the cmdlet decides it can't send the ping at all, it errors in unpleasant ways that are difficult to trap or ignore. Test-Connection is only useful, then, when you can guarantee that DNS will resolve the name to an address, and that the network will always be present.
I tend to use CIM Pings (Powershell v3+):
$Ping2 = Get-CimInstance -ClassName Win32_PingStatus -Filter "Address='$ComputerName' AND Timeout=1000";
Or WMI pings (Powershell v1 or v2):
$Ping = Get-WmiObject -Class Win32_PingStatus -Filter "Address='$ComputerName' AND Timeout=1000";
Either of which are basically the same, but return slightly different formats for things. Note that Get-WmiObject is not available at all beginning in Powershell v6 because Get-CimInstance was designed to supersede it.
The main disadvantage here is that you have to resolve the status code yourself:
$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'
};
$StatusCodes[$Ping.StatusCode];
$StatusCodes[$Ping2.StatusCode];
Alternately, I've used .Net Pings like #BenH described, too, which does a lot of that work for you. There was a reason I stopped using them in favor of WMI and CIM, but I can no longer remember what that reason was.
I am partial to using the .Net Ping class rather than Test-Connection
$Timeout = 100
$Ping = New-Object System.Net.NetworkInformation.Ping
$Response = $Ping.Send($Name,$Timeout)
$Response.Status
Note that the Send method can take additional parameters if you need to set TTL/Fragmentation. Also timeout is in milliseconds, with just $name the timeout I think is 5 seconds, which is usually too long.
Windows IP Helper defines IP_REQ_TIMED_OUT error to value 11010 wich is the same as Windows system error WSA_QOS_ADMISSION_FAILURE 11010 'Error due to lack of resources.'
so it is likely that what actually received in questioned case was time out error and simply misinterpreted as 'lack of resources'.
powershell v7 does not suffer from this issue when using test-connection
I'm going to cheat and use powershell 7. Microsoft is always unpingable.
test-connection -count 1 yahoo.com,microsoft.com | select destination,status
Destination Status
----------- ------
yahoo.com Success
microsoft.com TimedOut
Or multi-threaded:
echo yahoo.com microsoft.com | % -parallel { test-connection -count 1 $_ } |
select destination,status
Destination Status
----------- ------
yahoo.com Success
microsoft.com TimedOut
,'microsoft.com' * 10 | % -parallel { test-connection -count 1 $_ } |
select destination,status
Destination Status
----------- ------
microsoft.com TimedOut
microsoft.com TimedOut
microsoft.com TimedOut
microsoft.com TimedOut
microsoft.com TimedOut
microsoft.com TimedOut
microsoft.com TimedOut
microsoft.com TimedOut
microsoft.com TimedOut
microsoft.com TimedOut
# one time takes like 4 seconds
measure-command { ,'microsoft.com' * 10 | % -parallel { test-connection -count 1 $_ } |
select destination,status } | % seconds
9
I see this error when (in 5.x anyway) i include -computername in test-connection.
remove that and it works. The other thing about using ping vs test-connection, ICMP is blocked by default with the windows firewall where test-connection does the equivalent of the win32_pingstatus command. WMI is not blocked by default. However, if the WMI repository on the system is not healthy (or blocked by fw) it will of course fail.
Related
Test-Connection -ComputerName (Get-Content 'C:\xxxxx\xxxxx.txt') -ErrorAction Continue -Count 1
I currently use the above command to test connections on sevreal remote PCs. It's basic and does the job, however i am looking to automate this and where i get stuck is how to filter only the failed connections from the result and output them as an email?
Thanks
really unsure how to go from here.
You could do something like this:
$unreachables = Get-Content 'C:\xxxxx\xxxxx.txt' | ForEach-Object {
if (-not (Test-Connection -ComputerName $_ -Count 1 -Quiet)) {
# output the failed machines
$_
}
}
if (#($unreachables).Count) {
# create a Hashtable with parameters used for splatting to Send-MailMessage
# something like this
$mailParams = #{
From = 'me#contoso.com'
To = 'mycolleague#contoso.com'
Subject = "unreachable computers"
SmtpServer = 'mail.contoso.com'
Body = "The following computers are off-line ({0}):`r`n`r`n{1}" -f (Get-Date), ($unreachables -join "`r`n")
}
Send-MailMessage #mailParams
}
else {
Write-Host 'All tested computers are online'
}
Need some help adjusting this code so that it will only provide IP addresses that are not pinging and will be exported to a CSV. Currently, it outputs both up and down IP's and I want to clean up some of the clutter.
$ping = (Test-Connection -ComputerName $comp.IPAddress -Count 1 -ErrorAction SilentlyContinue)
if($ping)
{
$status = "Up"
}
else
{
$status = "Down"
}
[pscustomobject]#{
Location = $comp.Location
IP = $comp.IPAddress
Status = $status
}
I have tried manipulating the $status variable but haven't had any luck. I am sure it's something very simple, I am just missing it.
# -Quiet means we just return a boolean true / false value
$ping = Test-Connection -ComputerName ($comp.IPAddress) -Count 1 -ErrorAction SilentlyContinue -Quiet
if(-not $ping)
{
# we only care about when it's down, so move the logic to return our object here
[pscustomobject]#{
Location = $comp.Location
IP = $comp.IPAddress
Status = 'Down'
}
}
Aramus, Welcome to SO!
Test-Connection returns a Null if the connection is down so...
$ping = (Test-Connection -ComputerName $($comp.IPAddress) -Count 1 -ErrorAction SilentlyContinue)
if($Null -ne $ping)
{
$status = "Up"
}
else
{
$status = "Down"
}
Also note the $() around $Comp.IPAddress, this is to insure the parser fetches the IPAddress before running the rest of the command.
I need to be able to ping a range of IP addresses and when I get a reply on one, then capture that IP to put in a variable which I can use to update the local computers host file.
The purpose of this script is to be used for a Point-to-Site Azure VPN service. A remote server is connecting as a client and is given a second IP address which changes on each connection. The other server can only communicate to the remote server on this IP address, but the application it uses only uses DNS Names, so I will need to update the hosts file with the connecting servers IP each time it dials in.
$subnet = "172.16.201.0"
$start = 1
$end = 10
$ping = 1
while ($start -le $end) {
$IP = "172.16.201.$start"
Write-Host "Pinging $IP" -ForegroundColor Cyan
Test-Connection -ComputerName $IP -count 1 -Quiet
$start++
}
So far the above code only does a IP Sweep and outputs the success/failure of each IP. I need to capture the success IP and put that in a variable
i presume you want only the 1st responding IP address, not all the addresses in the range. [grin] you can turn this into a function fairly easily. you can also turn OFF the verbose output by commenting out the 2nd line.
$Old_VPref = $VerbosePreference
$VerbosePreference = 'Continue'
$Subnet = '192.168.1'
$RangeStart = 60
$RangeEnd = 69
$1stRespondingIPv4 = foreach ($Octet in $RangeStart..$RangeEnd)
{
$IP = $Subnet, $Octet -join '.'
$WV_Msg = 'Testing {0} ...' -f $IP
Write-Verbose -Message $WV_Msg
$Pingable = Test-Connection -ComputerName $IP -Count 1 -Quiet
if ($Pingable)
{
$IP
# the next line stops the foreach loop
break
}
}
''
$1stRespondingIPv4
$VerbosePreference = $Old_VPref
output ...
VERBOSE: Testing 192.168.1.60 ...
VERBOSE: Testing 192.168.1.61 ...
VERBOSE: Testing 192.168.1.62 ...
VERBOSE: Testing 192.168.1.63 ...
VERBOSE: Testing 192.168.1.64 ...
192.168.1.64
$dns = "domain.com"
$ipAddresses = #(
"172.16.201.0"
"172.16.201.1"
"172.16.201.2"
"172.16.201.3"
"172.16.201.4"
"172.16.201.5"
"172.16.201.6"
"172.16.201.7"
"172.16.201.8"
"172.16.201.9"
"172.16.201.10"
)
foreach($ip in $ipAddresses) {
Write-Host "Pinging $ip" -ForegroundColor Cyan
$ping = Test-Connection -ComputerName "$ip" -count 1 -Quiet
if ($ping) {
Add-Content -Path "C:\Windows\System32\drivers\etc\hosts" -Value "$ip $dns"
Write-Host "The host file was updated with the successful IP and hostname: $ip $dns" -ForegroundColor Cyan
}
}
I have a work machine(A) and a different Hyper-V server (B) with virtual machines running.
If I connect to the Hyper-V server B with RDP and run Get-VM Where-Object {$_.State -eq 'Running'} I get a valid answer:
Name State CPUUsage(%) MemoryAssigned(M) Uptime Status
---- ----- ----------- ----------------- ------ ------
vm1 Running 2 2048 20:07:05 Operating normally
vm2 Running 0 1024 3.00:49:30 Operating normally
Next, I wrote a PS script to run on my work machine A:
$sess = New-PSSession $SETTING_SESSION_HOST
$commandStr = "Get-VM | Where-Object { `$_.State -eq 'Running' }"
#or a simple version: $commandStr = "Get-VM"
[console]::writeline("Executing: [{0}]", $commandStr)
$commandBlock = [scriptblock]::Create($commandStr)
$job = Invoke-Command -Session $sess -ScriptBlock $commandBlock -AsJob
Wait-Job $job
$vml = Receive-Job $job
foreach($m in $vml)
{
[console]::writeline("Executing: [{0}]", $m.Name)
}
$g = Format-List -InputObject $vml
[console]::writeline("format list: [{0}]", $g)
Here I would expect to see 2 lines containing "vm1" and "vm2" respectively. But I get an empty response:
Executing: [Get-VM | Where-Object { $_.State -eq 'Running' }]
format list: []
Any idea on how to get remote response from remote job?
Also, the execution time of the script is ~6 seconds (all spent in Wait-Job), while on the server it runs instantaneously.
EDIT: added -AsJob parameter
EDIT: fixed variables
may I suggest the following:
$sess = New-PSSession $SETTING_SESSION_HOST
$wml = Invoke-Command -Session $sess -ScriptBlock {Get-VM | where State -eq 'Running'}
$wml.Name
PS: while writing this i noticed you set $wml to Receive-Job $job but then you iterate through $vml so it might just be a typo v for w
After doing some debugging, I found the answer: when there's only 1 machine running, then the return result is an object of type Microsoft.HyperV.PowerShell.VirtualMachine, so this code works nicely:
$vml = Invoke-Command -Session $sess -ScriptBlock {Get-VM | where State -eq 'Running'}
$vml.Name
However, if there are more than 1 machine running, then the result is Object[], and the code I found out working is like this:
$vml = Invoke-Command -Session $sess -ScriptBlock {Get-VM | where State -eq 'Running'}
$len = $vml.length
if ($len)
{
[console]::writeline("Machine count: [{0}]", $len)
for ($i=0; $i -lt $len; $i++)
{
$m = $vml[$i]
[console]::writeline("machine: [{0}]", $m.Name)
}
}
else
{
[console]::writeline("Machine: [{0}]", $vml.Name)
}
Note that doing a foreach($m in $vml) doesn't seem to work for some reason.
The problem in my opinion is that the method is either returning an object or an array.
So I have written a simple pinger that ping all that a network. I'm using test-connection, but I what to also get the devises name as well.
So I'm using cmd's nbtstat to get the name but (lets say it's not clean)
Is there a way to do this a bit cleaner?
this is what i have got
$a = New-Object System.Collections.ArrayList
for ($i =0;$i -le 225; $i++){
if (Test-Connection 192.168.1.$i -Count 1 -Quiet){
echo "live 192.168.1.$i"
$a.add("192.168.1.$i")
}else {
echo "dead 192.168.1.$i"
}
}
echo $a
foreach ($i in $a){
Test-Connection $i -Count 1
}
foreach ($i in $a){
nbtstat -a $i
}
There are always many ways to do something, one such example:
$Pinger = New-Object system.Net.NetworkInformation.Ping
$PC = "192.168.1.$i"
try{
$ErrorActionPreference = "Stop"
$PingResult = $Pinger.send($PC)
$ResultAddress = $PingResult.Address
$PingStatus = $PingResult.Status
$DNSObject = [System.Net.Dns]::GetHostEntry($PC).HostName
}
catch
{
}
finally
{
Write-Host "$PC $PingStatus $PingHost $? $DNSObject"
}
This gives more than you requested, but also I think it might also give you some ideas:
.NET is at your disposal, not just native cmdlets and Windows Commands
Do use built in operators like $?
I'd do it a bit differently. Every time you contact a host, an ARP entry is created. Why not leverage that?
Get-WmiObject Win32_PingStatus -Filter "Address='192.168.1.2' AND ResolveAddressNames='true'" |
Select IPV4Address, ProtocolAddressResolved, #{
Name="PhysicalAddress";Expression={
([regex]::Matches($(arp -a $_.IPV4Address),"[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}",#('MultiLine', 'Ignorecase'))).value
}
}
Test-Connection and Test-NetConnection now support returning more information, including names. The latter requires Power Shell 5.
Test-Connection
EXAMPLES
Example 1: Send echo requests to a remote computer
PS C:\> Test-Connection "Server01"
Source Destination IPV4Address IPV6Address Bytes Time(ms)
------ ----------- ----------- ----------- ----- --------
ADMIN1 Server01 10.59.137.44 32 0
ADMIN1 Server01 10.59.137.44 32 0
ADMIN1 Server01 10.59.137.44 32 0
ADMIN1 Server01 10.59.137.44 32 1
Source
Test-NetConnection
Test ping connectivity with detailed results
PS C:\> Test-NetConnection -InformationLevel "Detailed"
ComputerName : internetbeacon.msedge.net
RemoteAddress : 2a01:111:2003::52
NameResolutionResults : 2a01:111:2003::52
13.107.4.52
InterfaceAlias : Ethernet
SourceAddress : 2001:4898:d8:33:81e8:7b49:8bf5:8710
NetRoute (NextHop) : fe80::200:5eff:fe00:203
PingSucceeded : True
PingReplyDetails (RTT) : 6 ms
Source
Also, you mention that you are pinging a whole network. That's how found your question (among others), because I was trying to do a ping sweep too. I settles on this script. The guy calls it Fast Ping Sweep Asynchronous.
Even being a Power Shell n00b, I was able to pipe its output, and then modify its output to only include what I wanted. I came across other scripts, but could not decipher their scripts to modify them for my purposes.
I'm not sure what version Power Shell this requires, but it works on v4 and v5.
I have seen most of the Powershell IP scanner, ping sweeping scripts
but none of them uses the PingASync method.The "problem" with
synchronous scripts is that they have to wait until a node replies or
times out before continuing to the next address.Using this approach
may take s
function Global:Ping-IPRange {
<#
.SYNOPSIS
Sends ICMP echo request packets to a range of IPv4 addresses between two given addresses.
.DESCRIPTION
This function lets you sends ICMP echo request packets ("pings") to
a range of IPv4 addresses using an asynchronous method.
Therefore this technique is very fast but comes with a warning.
Ping sweeping a large subnet or network with many swithes may result in
a peak of broadcast traffic.
Use the -Interval parameter to adjust the time between each ping request.
For example, an interval of 60 milliseconds is suitable for wireless networks.
The RawOutput parameter switches the output to an unformated
[System.Net.NetworkInformation.PingReply[]].
.INPUTS
None
You cannot pipe input to this funcion.
.OUTPUTS
The function only returns output from successful pings.
Type: System.Net.NetworkInformation.PingReply
The RawOutput parameter switches the output to an unformated
[System.Net.NetworkInformation.PingReply[]].
.NOTES
Author : G.A.F.F. Jakobs
Created : August 30, 2014
Version : 6
.EXAMPLE
Ping-IPRange -StartAddress 192.168.1.1 -EndAddress 192.168.1.254 -Interval 20
IPAddress Bytes Ttl ResponseTime
--------- ----- --- ------------
192.168.1.41 32 64 371
192.168.1.57 32 128 0
192.168.1.64 32 128 1
192.168.1.63 32 64 88
192.168.1.254 32 64 0
In this example all the ip addresses between 192.168.1.1 and 192.168.1.254 are pinged using
a 20 millisecond interval between each request.
All the addresses that reply the ping request are listed.
.LINK
http://gallery.technet.microsoft.com/Fast-asynchronous-ping-IP-d0a5cf0e
#>
[CmdletBinding(ConfirmImpact='Low')]
Param(
[parameter(Mandatory = $true, Position = 0)]
[System.Net.IPAddress]$StartAddress,
[parameter(Mandatory = $true, Position = 1)]
[System.Net.IPAddress]$EndAddress,
[int]$Interval = 30,
[Switch]$RawOutput = $false
)
$timeout = 2000
function New-Range ($start, $end) {
[byte[]]$BySt = $start.GetAddressBytes()
[Array]::Reverse($BySt)
[byte[]]$ByEn = $end.GetAddressBytes()
[Array]::Reverse($ByEn)
$i1 = [System.BitConverter]::ToUInt32($BySt,0)
$i2 = [System.BitConverter]::ToUInt32($ByEn,0)
for($x = $i1;$x -le $i2;$x++){
$ip = ([System.Net.IPAddress]$x).GetAddressBytes()
[Array]::Reverse($ip)
[System.Net.IPAddress]::Parse($($ip -join '.'))
}
}
$IPrange = New-Range $StartAddress $EndAddress
$IpTotal = $IPrange.Count
Get-Event -SourceIdentifier "ID-Ping*" | Remove-Event
Get-EventSubscriber -SourceIdentifier "ID-Ping*" | Unregister-Event
$IPrange | foreach{
[string]$VarName = "Ping_" + $_.Address
New-Variable -Name $VarName -Value (New-Object System.Net.NetworkInformation.Ping)
Register-ObjectEvent -InputObject (Get-Variable $VarName -ValueOnly) -EventName PingCompleted -SourceIdentifier "ID-$VarName"
(Get-Variable $VarName -ValueOnly).SendAsync($_,$timeout,$VarName)
Remove-Variable $VarName
try{
$pending = (Get-Event -SourceIdentifier "ID-Ping*").Count
}catch [System.InvalidOperationException]{}
$index = [array]::indexof($IPrange,$_)
Write-Progress -Activity "Sending ping to" -Id 1 -status $_.IPAddressToString -PercentComplete (($index / $IpTotal) * 100)
Write-Progress -Activity "ICMP requests pending" -Id 2 -ParentId 1 -Status ($index - $pending) -PercentComplete (($index - $pending)/$IpTotal * 100)
Start-Sleep -Milliseconds $Interval
}
Write-Progress -Activity "Done sending ping requests" -Id 1 -Status 'Waiting' -PercentComplete 100
While($pending -lt $IpTotal){
Wait-Event -SourceIdentifier "ID-Ping*" | Out-Null
Start-Sleep -Milliseconds 10
$pending = (Get-Event -SourceIdentifier "ID-Ping*").Count
Write-Progress -Activity "ICMP requests pending" -Id 2 -ParentId 1 -Status ($IpTotal - $pending) -PercentComplete (($IpTotal - $pending)/$IpTotal * 100)
}
if($RawOutput){
$Reply = Get-Event -SourceIdentifier "ID-Ping*" | ForEach {
If($_.SourceEventArgs.Reply.Status -eq "Success"){
$_.SourceEventArgs.Reply
}
Unregister-Event $_.SourceIdentifier
Remove-Event $_.SourceIdentifier
}
}else{
$Reply = Get-Event -SourceIdentifier "ID-Ping*" | ForEach {
If($_.SourceEventArgs.Reply.Status -eq "Success"){
$_.SourceEventArgs.Reply | select #{
Name="IPAddress" ; Expression={$_.Address}},
#{Name="Bytes" ; Expression={$_.Buffer.Length}},
#{Name="Ttl" ; Expression={$_.Options.Ttl}},
#{Name="ResponseTime"; Expression={$_.RoundtripTime}}
}
Unregister-Event $_.SourceIdentifier
Remove-Event $_.SourceIdentifier
}
}
if($Reply -eq $Null){
Write-Verbose "Ping-IPrange : No ip address responded" -Verbose
}
return $Reply
}