Test for connection before running Invoke-command

by hammer5 at 2013-03-20 14:14:42

Hello I was wondering if anyone had an example of how to test if a group of computers are available before running the Invoke-command cmdlet?
Here is an example command
Invoke-Command -ComputerName $computer -ScriptBlock{Get-itemProperty ‘HKLM:\SYSTEM\CurrentControlSet\Control\Class{4D36E965-E325-11CE-BFC1-08002BE10318}’}

I would like to test that each computer in $computer is available before the command is ran, or at least catch the error and report that the computer is offline.

I have tried this
$computers = Get-Content C:\scripts\computers1.txt
$computerstatus = @()

foreach ($computer in $computers) {
if(Test-Connection $computer -count 1 -Quiet){
#Invoke-Command -ComputerName $Computer {$env:COMPUTERNAME +" - "+(Test-Path ‘c:\program files (x86)\Microsoft Office\office14\winword.exe’)}
#$CheckReg = Invoke-Command -ComputerName $computer -ScriptBlock{Get-itemProperty ‘HKLM:\SYSTEM\CurrentControlSet\Control\Class{4D36E965-E325-11CE-BFC1-08002BE10318}’}
$objStatus = New-Object System.Object
$objStatus | Add-Member -type NoteProperty -name ComputerName -value $computer
$objStatus | Add-Member -type NoteProperty -name Status -Value "Online"
#$objStatus | Add-Member -type NoteProperty -name Lowerfilters -Value $CheckReg.Lowerfilters
#$objstatus | Add-Member -type NoteProperty -name Upperfilters -Value $CheckReg.Upperfilters
$computerstatus += $objStatus
}#Close if test-connection
else {
$objStatus = New-Object System.Object
$objStatus | Add-Member -type NoteProperty -name ComputerName -value $computer
$objStatus | Add-Member -type NoteProperty -name Status -Value "Offline"
$computerstatus += $objStatus
} #Close Else
} #Close Foreach
# build list of computers to check
$computerstocheck = $computerstatus | where {$_.Status -eq "Online"} | Select -ExpandProperty computername | out-string
$CheckReg = Invoke-Command -ComputerName $computerstocheck -ScriptBlock{Get-itemProperty ‘HKLM:\SYSTEM\CurrentControlSet\Control\Class{4D36E965-E325-11CE-BFC1-08002BE10318}’}

When I run the script it states One or more computer names is not valid. and I do not think I’m going about it correctly any way. My thought was that I would build a custom object to hold the status and the run the invoke-command against that list, but It’s not working very good.
Any idea’s will be greatly appreciated.
by DonJ at 2013-03-20 15:39:25
Welcome! Consider using the CODE button to format your script - makes it a ton easier to read.

You can do a couple of things. You could ping the machine using Test-Host… but that doesn’t tell you if Remoting will work. It’s just a ping. There’s also a Test-WSMan* cmdlet that actually "pings" the WinRM service on the remote machine. That’s probably best. Or, just trap the error from Invoke-Command - which is the fastest, since if it works you won’t have wasted time pinging.

What approach do you like?

It’d also help to see your input file with the computer names.
by hammer5 at 2013-03-20 17:27:08
Sorry for not using the code option. I would like to be able to trap the errors. As for the input file its just a text file with computer names one on each line. As you might notice I tried to use test-connection in an if statement, but you are correct it was real slow. The output I’m looking for would be a list of computer with the read registry keys. Ideally in a object.

V/r
John
by DonJ at 2013-03-21 06:46:10
Ok, trapping is probably easier than all that checking you’re doing.


Try {
Invoke-Command -ErrorAction Stop (add the rest of your parameters here)
} Catch {
# this will run if an error occurs
}


That’s the basic form. Rather than creating some internal status object, I’d suggest just enumerating your computers and making a new array of ones you CAN reach. For example:


$newcomputers = @()
foreach ($computer in $computers) {
Try {
$reachable = $true
Invoke-Command -ErrorAction Stop -ComputerName $computer -scriptblock { #whatever }
} Catch {
# this will run if an error occurs
$reachable = $false
}
if ($reachable) {
$newcomputers += $computer
}
}


Then you’ll know what computers ($newcomputers) worked. Note that this technique is going to Invoke-Command in sequential order, rather than in parallel. If the command you’re running is BIG, and you want them in parallel, then in the Invoke-Command I suggest, just do something short (like run Get-Service). Then after the ForEach loop you can Invoke-Command your REAL command against $newcomputers. In other words, I’m using Invoke-Command as a kind of "ping," since it’s testing the full service and not just ICMP connectivity. Not the only approach you could take - but I don’t think you need to build a big status object in your code; just build a list of comuters that worked.
by hammer5 at 2013-03-21 13:35:55
Thanks Mr. Jones, That really helped.
Here is what I have so far:


$computers = Get-Content C:\script\testing.txt
$Onlinecomputer = @()
$RegResult = @()
Foreach ($computer in $computers) {
Try {
$reachable = $true
$Result = Invoke-Command -ErrorAction Stop -computername $computer -scriptblock {
Get-itemProperty ‘HKLM:\SYSTEM\CurrentControlSet\Control\Class{4D36E965-E325-11CE-BFC1-08002BE10318}’

} #Close Results
} Catch {

#This will run if an error occurs
$reachable = $false
} #close catch
If ($reachable) {
$Onlinecomputer += $computer
$RegResult += $Result
}# Clost reachable if

If (!$reachable){
$Result = "" | Select PSComputerName, Status
$Result.Status = "Not Online"
$Result.pscomputername = $computer
$RegResult += $result

} #Close notreachable if
}

$RegResult | select pscomputerName, LowerFilters, UpperFilters, Status | Format-Table *

I did have another question? It’s a little off topic, but pretty simple in the first line [quote]$Computer = Get-content C:\script\testing.txt [/quote]
If I wanted to replace the Get-content command with my own list for testing purposes how would I do that? I tried $computers = "localhost, NotOnline" but as you know that failed. How could I get a shorter list of computers into the $computers variable without creating a short txt file?

Thank you very much for your help and willingness share your knowledge!
by DexterPOSH at 2013-03-27 16:49:26
[quote="hammer5"]If I wanted to replace the Get-content command with my own list for testing purposes how would I do that? I tried
CODE: SELECT ALL
$computers = "localhost, NotOnline"
but as you know that failed. How could I get a shorter list of computers into the $computers variable without creating a short txt file?[/quote]

You will have to use the following
$computers = "localhost","NotOnline"
Why ? If you use $computers = "localhost, NotOnline" then it is a string not a collection, this would be evident if you do this in the console:
$computers = "localhost, NotOnline"
$computers.GetType().fullname

and later do this$computers = "localhost","NotOnline"
$computers.GetType().fullname

P.S. - learned the neat trick to use try {} catch{} with Invoke-Command to test for connection :slight_smile:
Hope this helps