Query AD for Computers and Use Ping to Determine Status

Query AD for Computers and Use Ping to Determine Status

  • Comments 18
  • Likes

Summary: Learn how to use Windows PowerShell to query Active Directory for computers, ping for status, and display in green/red.

 

Microsoft Scripting Guy Ed Wilson here. While the Scripting Wife and I were out in California speaking to the Microsoft Premier Field Engineers (PFE) about Windows PowerShell, a question arose. The PFE said he had a customer that needed to send a ping to a number of computers. The customer did not need a lot of information about the status, but more of a simple yes or no report on connectivity. Ping was the tool of choice due to simplicity. The problem is that Ping returns too much information. What the customer really wanted was an output that displayed the computer name in green (for up) and in red (for down). A simple status board to display the computer names. One problem, the PFE related, is that the collection of computers to monitor changes on nearly a daily basis. Therefore, the customer wants to query Active Directory for the computer names.

We left the reception Thursday night, and I went up to the room and put together this script. It could have been a “one-liner” but, it would have been difficult to read, so I spread it out over a couple of different lines. Here is the complete Query Active Directory and Ping Computers script in Windows PowerShell (for ease of use, I uploaded the script to the Scripting Guys Script Repository):

Query Active Directory and Ping Computers

Import-Module active*

$rtn = $null

Get-ADComputer -Filter * |

ForEach-Object {

  $rtn = Test-Connection -CN $_.dnshostname -Count 1 -BufferSize 16 -Quiet

  IF($rtn -match 'True') {write-host -ForegroundColor green $_.dnshostname}

  ELSE { Write-host -ForegroundColor red $_.dnshostname }

}

The first thing the Query Active Directory and Ping Computers script does is import the ActiveDirectory module. I have written about this module quite a bit. In the Install Active Directory Management Service for Easy PowerShell Access post, I go into detail about the requirements to set up the Active Directory Management gateway. Next, I set the value of the $rtn variable to $null. This helps to avoid problems if there is already a $rtn variable with a different value.

I then use the Get-ADComputer cmdlet from the ActiveDirectory module (I have used the Get-ADComputer cmdlet numerous times on the Hey, Scripting Guy! Blog) to return all computers. The following line of code returns an ADComputer object from the Microsoft.ActiveDirectory.Management namespace:

Get-ADComputer -Filter *

By default, the ADComputer object contains only a few properties. The default properties are shown here:

DistinguishedName                                                                   

DNSHostName                                                                         

Enabled                                                                             

Name                                                                                

ObjectClass                                                                         

ObjectGUID                                                                          

SamAccountName                                                                      

SID                                                                                 

UserPrincipalName  

Other properties exist on a computer object. For example, the following figure illustrates properties that contain a value for the W7Client computer. The figure from ADSI Edit is shown here.

Image of properties containing a value for W7Client computer

To obtain access to additional property values, I need to add them to the property parameter (the reason for the small subset of properties returned by default is because of performance concerns). Here is an example of returning the DNSHostname and the lastlogon attribute values.

Get-ADComputer -Filter * -Properties lastlogon | select dnshostname, lastlogon

Unfortunately, the Test-Connection cmdlet does not accept piped input, and I need to use the Foreach-Object cmdlet to walk through the collection of ADComputer objects and ping the DNSHostname of each computer. To speed things along, I send one ping and reduce the BufferSize. I store the results in a variable named $rtn:

ForEach-Object {

  $rtn = Test-Connection -CN $_.dnshostname -Count 1 -BufferSize 16 –Quiet

The Test-Connection returns a True or a False depending upon whether the connection succeeds. But unfortunately, it is a string and not a true Boolean value. Therefore, in my decision portion of the script, I used the IF statement and did a match on the string True. If Test-Connection returns true, I display the DNSHostname of the computer in green. Otherwise, I display the DNSHostname of the computer in red. This portion of the script is shown here:

ForEach-Object {

  $rtn = Test-Connection -CN $_.dnshostname -Count 1 -BufferSize 16 -Quiet

  IF($rtn -match 'True') {write-host -ForegroundColor green $_.dnshostname}

  ELSE { Write-host -ForegroundColor red $_.dnshostname }

}

The script and associated output are shown in the following figure.

Image of script and associated output

 

Well, that is about all there is to querying Active Directory Domain Services for computer accounts, and pinging them to see if they are up and running or not. Join me tomorrow for more cool Windows PowerShell stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson, Microsoft Scripting Guy

 

 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hi Ed and Teresa,

    I hope you enjoyed life in sunny California a little bit apart from your official "program" and that you return safely!

    The Powershell-AD-Ping-Program ( what a word-creation :-) is very useful to every admin and should be part of the admin's toolbox! Great!!

    Just one little change that I would make for myself:

    You mentioned the $rtn variable here and that it might be a good idea to set it to $null because "This helps to avoid problems if there is already a $rtn variable with a different value."

    Right! But if it were used for different purposes, you might bring trouble to another script component that may rely on it having a reasonable value ... for its pupposes!

    BTW: That's why I would love to have namespaces and voted for it!!!

    In this case, you don't really need to remember the value of $rtn later on in your script if you code it a little bit different, something I personally would even prefer:

    IF((Test-Connection -CN $_.dnshostname -Count 1 -BufferSize 16 -Quiet) -match 'True') {

       write-host -ForegroundColor green $_.dnshostname

    } ELSE {

       Write-host -ForegroundColor red $_.dnshostname

    }

    Just evaluate the condition in the if clause and you don't have to steal a variable for that! It looks a bit more complicated, but to me it is perectly OK to code it this way.

    Klaus.

  • Hello Mr. Ed

    How come, in my Powershell the Test-Connection cmdlet with -quiet parameter produces a System.Boolean ?

    What version of PS do you use?

  • @Jacques

    same result here!

    I didn't check the type but we can see this:

    PS C:\Users\klaus> $rtn.gettype()

    IsPublic IsSerial Name                                     BaseType                                                                  

    -------- -------- ----                                     --------                                                                  

    True     True     Boolean                                  System.ValueType                                                          

    So you are right and we can get rid of the -match operator!

    Klaus

  • Very nice Ed. As my predominant use for this is in a production, I tend to do a .net call to the System.Net.DNS and lookup via hostname. For us this tends to afford us the opportunity to catch systems that may be offline and catch systems that have registered. We do have aging and scavenging turned on so it helps give us a time based stamp of whether a system has been seen on the network.

    foreach ($comp in $allcomputers){

       $dns = $([system.net.dns]::gethostbyname($comp.DnsName))

           if ($dns -ne $null){

    $computers += $comp

    $dns = $null}

           elseif ($dns -eq $null){

    $nodns += $comp

    $dns = $null}

    }

  • I gather the list of computers, ping each, if the computer is offline I stick it in an array list then run get-adcomputer again to find out the lastlogontime for the offline computer array.  Does it make sense to use an array list in this instance?  

  • @Klaus Schulte Yes, the Scripting Wife and I had a great time in sunny California. We had a bit of time to go to a couple of museums while we were there. I try to give the scripts a descriptive name as it makes them easier to find ... but it does lead to long names :-) You are completely right about possibly "messing" up the variable for another script ... generally for me that is not a problem. The problem instead arises when I am using the Windows PowerShell ISE, and I have it open all day while working on lots of different scripts. I do tend to use the same type of variable names, and therefore I sometimes get into problem with variables that have a left over value; or even when I am writing the script its previous variables linger with old values. It is a good practice in such situations to initialize the variables (such as setting to 0 or $null, or to an initial value) rather than hoping it has not been used. Variable Scoping can definately help with this, but generally for simple scripts I do not bother.

  • @Jacques @Klaus Schulte hmmmm.... I might have been using my laptop that has a different version of PowerShell installed on it ... they are both the same from the outside. I just tested this on my desktop computer that definately has PowerShell 2.0 installed on it, and it does return a Boolean ... So you are right, the string check is not required.

  • @Benjamin Ross I like using the .NET classes to do a DNS lookup ... this is a nice technique ... and a great idea.

  • @Tony there is nothing wrong with using an array in this circumstance. In fact, an array is a great data structure. You are pretty much limited by memory, but other than that it works fine.

  • All

    Test-Connnection has a switch called 'Quiet'.  It is defined as setting no output on the command and also forcing a boolean to be returned. Test-Connection is new for PowerShell 2.0.

    Type: help Test-Connection -para q*

  • @JV you are correct; Test-Connection is introduced in PowerShell 2.0, and the -quiet switch should force the cmdlet to return a boolean value that indicates return or no return.

  • Hi guys,

    I work on a rather large estate and I'm wondering if it would be possible to make the ping check in this script multi-threaded so that effectively it would be pinging 3-5 machines at once instead of 1, wait for a reply, next 1, wait for a reply... etc.

    If so how would I even start to go about that?!

    Thanks guys, love the blog.

  • Also, any feedback on this error?

    test-Connection : Cannot validate argument on parameter 'ComputerName'. The argument is null or empty. Supply an argume

    t that is not null or empty and then try the command again.

    t c:\scripts\find_online_AD_computers.ps1:12 char:29

      $rtn = Test-Connection -CN <<<<  $_.dnshostname -Count 2 -BufferSize 16 -Quiet

      + CategoryInfo          : InvalidData: (:) [Test-Connection], ParameterBindingValidationException

      + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.TestConnectionCommand

  • Figured it out

    Import-Module active*

    $rtn = $null

    $date = Get-Date -format -yyyMMdd

    Get-ADComputer -SearchBase "OU=, DC=, DC=" -Filter * |  

    ForEach-Object {  

     $rtn = Test-Connection -CN $_.dnshostname -Count 2 -BufferSize 16 -Quiet

     IF($rtn) {write-output $_.dnshostname | out-file -filepath c:\scripts\online_users\noshutdown_$date.txt -append}  

    }

  • Thanks for the helpful post Ed!

    A few modifications has made it real easy to find those XP systems still around and online before the 8th. Grabbing the IP and excluding those offline was helpful in my case too.

    $Computers = Get-ADComputer -Filter{OperatingSystem -Like "Windows XP*"} -Properties LastLogon | Select dnshostname, lastlogon
    ForEach ($Computer in $Computers) {
    $UP = Test-Connection -CN $Computer.dnshostname -Count 1 -BufferSize 16 -Quiet
    IF($UP -match 'True') {
    write-host -ForegroundColor green $Computer.dnshostname
    $IP = Test-Connection -CN $Computer.dnshostname -Count 1 -BufferSize 16
    write-host -ForegroundColor green $IP.ProtocolAddress
    $IP = $Null
    }
    }

    Scott