PowerShell/Community-Blog

Request - How to get all the alive servers in the domain?

pawanadubey opened this issue ยท 14 comments

Summary of the request

As an admin, have to do a lot of reporting for the servers in the domain, however not all the servers are always running.
Maybe using the ActiveDirectory module, get all the servers, and then do the Test-Connection command on the server to check if it is reachable, and return the alive servers.

good idea...

If all the servers are in a specific OU, you can perform the following:

$servers = Get-ADComputer -Filter * -SearchBase 'OU=Severs, DC=company, DC=pri'
foreach ($server in $servers) {
Test-Connection -TargetName $server
}

What I did back in the day before foreach -Parallel {} was:

Invoke-Command -Name (Get-ADComputer -Filter *).Name -ea Silent {
  $ENV:COMPUTERNAME
}

If all the servers are in a specific OU, you can perform the following:

$servers = Get-ADComputer -Filter * -SearchBase 'OU=Severs, DC=company, DC=pri'
foreach ($server in $servers) {
Test-Connection -TargetName $server
}

@dr-raypc
I'm not sure that this is the place for this, but in case you're going to write something about this topic, then you may care to know that there's no reason to put a Test-Connection command inside of a Foreach language construct. The TargetName parameter can accept more than a single computer at a time, such as Test-Connection -TargetName 'server1','server2','server3'. Therefore, you issue the Get-ADComputer command and then do Test-Connection -TargetName $servers.name. Don't forget to use dotted notation, or you'll be sending the entire computer object to the Test-Connection command (even in your example), and that's probably not what you want. Anyway, you should write about this!

@tommymaynard that is great information, thank you Tommy!

One issue to remember is that Test-Connection takes a parameter called ComputerName. In AD, the AD Computer object holds the computer name in the name property. So you can't do this:

Get-ADComputer ... | Test-Connection ...

There a couple of ways to solve this. You can define some Type XML and import it so as to add an alias to the AD Compuer object. I would hope to get a blog article up that shows this.

Alternatively, you can code around it like this:

Get-ADComputer ... | Test-Connection -ComputerName $_.Name ...

@tommymaynard one small error in your code. When you create $Servers, you create an array of computer objects each of which has a Name property. When you pass $Server to Test-Connection the cmdlet fails with a 'cannot resolve the target name" error. This is because Test-Connection is looking for a string of the computer name, whereas your code passes it the full DN of the computer object. You can code around this as noted above.

Hi @doctordns. I appreciate the feedback and discussion and I can see how a little back a forth here might actually be helpful for folks, especially if someone opts to take this topic and write about it. I'm not seeing things the same way you have, however. First, Test-Connection only has a ComputerName parameter in Windows PowerShell. This changed to TargetName at some time after 5.1. Perhaps it's because we can use ICMP with more than just computers. I'm uncertain whether that change was during the 6.x timeframe or later. Either way, when dr-raypc commented, he did so using the TargetName parameter, which to me indicated he was using PowerShell (not Windows PowerShell).

Second, in my code example, I specifically wrote Test-Connection -TargetName $servers.name. I indicated to use dotted notation in order that the entire AD computer objects, with their multiple properties, weren't used by Test-Connection. This was to ensure that only the name property of each object was being used as the TargetName parameter value. Let me know if there's any confusion, but if there was an error in my code, then I'm not sure what it was. Thanks.

Edit: It just dawned on me. I think you were looking at the code I quoted from dr-raypc. That wasn't my code.

I was not referring to the change in the parameter name in Test-Connection. You are correct that PowerShell 7 now uses a -TargetName parameter, but there is an alias to ComputerName. For the curious, the code for the Test-Connection cmdlet describes the parameter as follows:

/// <summary>
        /// Gets or sets the destination hostname or IP address.
        /// </summary>
        [Parameter(
            Mandatory = true,
            Position = 0,
            ValueFromPipeline = true,
            ValueFromPipelineByPropertyName = true)]
        [ValidateNotNullOrEmpty]
        [Alias("ComputerName")]
        public string[]? TargetName { get; set; }

I was, instead, referring to the potential pipeline mismatch between the output of Get-ADComputer and the pipeline input to the Test-Connection cmdlet. The former produces objects with a property name of Name to hold the computer name (the 'NetBIos' name so to speak) of the AD computer. The Test-Connection cmdlet supports (as shown above) a parameter with a name (TargetName) and an alias (ComputerName). All of this means you can not directly pipe Get-ADComputer to Test-Connection. You get an error like this:

PS C:\Foo> Get-ADComputer -Identity Cookham1 | Test-Connection
Test-Connection: Cannot validate argument on parameter 'TargetName'. The argument is null, empty, or an element of the argument collection contains a null value. Supply a collection that does not contain any null values and then try the command again.

Instead, you would need to do this:

PS C:\Foo> Get-ADComputer -Identity Cookham1 | ForEach-Object {Test-Connection -TargetName $_.Name}
   Destination: COOKHAM1
Ping Source           Address                   Latency BufferSize Status
                                                   (ms)        (B)
---- ------           -------                   ------- ---------- ------
   1 cookham24        10.10.10.9                      0         32 Success
   2 cookham24        10.10.10.9                      0         32 Success
   3 cookham24        10.10.10.9                      0         32 Success
   4 cookham24        10.10.10.9                      0         32 Success

There is another way to 'fix' this. That is, you can create some PowerShell Type XML that adds an alias (i.e. TargetName) to the name property in any ADComputerObject by default. You then import that in your profile and from there on in, the initial pipeline works as you might want it too. You might also want to use the AD Computer's DnsHostName property as it has a FQDN that might be better to use with Test-Connection.

Another thing - if you are using PowerShell 7, then use ForeachObject -Parallel to run each test connection in parallel. This would really speed up any script that is waiting for a computer that is not online (and waiting and waiting).

Hope this clarifies things.

I don't like requiring Pwsh JUST to run this test in parallel. If I were stuck in a similar situation, I'd do something like

Test-Connection -ComputerName (Get-ADComputer -Identity MyComputerName).Name

(But with a search base instead of a single identity, of course).

If I really HAD to pipeline it, I'd probably use a calculated property:

Get-ADComputer -Identity MyCoputerName | Select-Object @{n='TargetName';e={$_.Name}} | Test-Connection

Also note that Test-Connection isn't quite the same as using Test-WSMan and I generally try to avoid using Test-Connection for AD Computers.

if you are only going to ping it, I take the point. But if you want to do stuff on each available host (eg check if an update has been applied, clean up old event logs, etc), being able to run things in parallel across a number of machines has real benefit.

There are, so far, 4 ways to bridge the gap between the AD's Name property and the -TargetName parameter for Test-Connection. :-)

Loved the way the community is discussing each point.
After going through the comments, what I would do is to create the below functions:

  1. Get-ComputerNameFromAD: It will have parameters to filter the result on the basis of OU, OperatingSystem, etc.
  2. Check-IsComputerAvailable: The parameters could be an array of strings as ComputerName. And on the basis of what Edition of PowerShell is running, it would use the appropriate parameter name for Test-Connection.

Thanks for the review comments here on GitHub. I love that the community, as @pawanadubey mentions, can discuss the post before it's created. Hopefully, such discussions can lead to a better post. I appreciate, in particular, the reminder over Target-Name.

I have taken the discussion here and created a post and have issued a PR - #42.

I intend to have this post posted next week - comments most welcome!

Post #42 is now ready to merge so I am closing this issue.
This blog post is meant to get posted 1st week in April.