Foreach looping question


So, first time poster here, so I’m sure I’ll screw up the formatting. :slight_smile: I looked around, but didn’t find anything here that seemed to match my situation, but I certainly could have missed it.

I’m trying to cobble together a simple script to query a list of computers for a particular Event ID. I have sections of this working, and I’m a little lost as to why this isn’t working correctly.

I can get my list of domain controllers, no problem:

$computername = get-adgroupmember "Domain Controllers" | select name

I can get a filtered event log if I put an actual computer name into the statement:

get-eventlog -logname "Directory Service" -ComputerName DC1 | ?{$_.eventid -eq "1864"} | select MachineName,EventID,TimeGenerated

I can have the script get a list of each DC and echo it back to me:

$computername = get-adgroupmember "Domain Controllers" |select name
foreach ($computer in $computername) {$computer}

And it spits the list back out at me, so I know the loop is iterating as I expect.

So why does this not work?:

$computername = getadgroupmember "Domain Controllers" |select name
foreach ($computer in $computername)
get-eventlog -logname "Directory Service" -ComputerName $computer | ?{$_.eventid -eq "1864"} | select MachineName,EventID,TimeGenerated

-ComputerName wants a string. I’m not sure if I’m passing a string or an object down the pipeline, honestly. I tried adding “out-string” after the “select name.” I’ve tried dumping the list to a file and using

$computername = get-content "C:\folder\computername.txt"

And have verified the txt file contains the proper output. I tried removing the “name” and dash line from the top of the text file. And the “get-content” method works the same when put into the “foreach” proof loop.

But, when trying to loop this, I get:

get-eventlog : The network path was not found.
At C:\eventid\1864-3.ps1:4 char:10
+          get-eventlog -logname "Directory Service" -ComputerName $computer | ?{$ ...
+          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Get-EventLog], IOException
    + FullyQualifiedErrorId : System.IO.IOException,Microsoft.PowerShell.Commands.GetEventLogCommand

This has to be something small and stupid I’m missing… any help is greatly appreciated.


Little tinny mistake :slight_smile:

Here is fixed script:

$computername = getadgroupmember "Domain Controllers" 
foreach ($computer in $computername)
      get-eventlog -logname "Directory Service" -ComputerName $ | ?{$_.eventid -eq "1864"} | select MachineName,EventID,TimeGenerated

You are welcomed :wink:

You are awesome. Now that the underlying mistake is corrected, I can expand on this to create some flexibility.

I SO appreciate the help. I’m still getting used to how to use the properties of objects.


I was thinking about your approach and I guess you wanted do it this way:

$computername = (get-adgroupmember "Domain Controllers").Name
foreach ($computer in $computername)
    get-eventlog -logname "Directory Service" -ComputerName $computer | ?{$_.eventid -eq "1864"} | select MachineName,EventID,TimeGenerated

First, I appreciate you coming back to this with another approach. The first one did work for what I needed it to do, but this second method helps clarify some of the ideas of powershell.

I come from doing a lot of Linux work where everything is text, so this idea of is still new to me. Thank you for bearing with me. :slight_smile:


Just some extra info that helped me a lot when I first learned this:

get-adgroupmember "Domain Controllers" | select -ExpandProperty name


(get-adgroupmember "Domain Controllers").Name

do the same thing. Both extracts or expands the name property and make it a string object.
Most or maybe all of the time when we mean text, PowerShell actually means a string object.

You can always check the object type by piping the cmdlet into “Get-Member” ot “gm”:

Get-Service winrm | select name | gm
# returns the object TypeName: Selected.System.ServiceProcess.ServiceController
# if you want to pipe it into another cmdlet whose property accepts string, it will break, because the selected property is a ServiceController object, not a String

Get-Service winrm | select -ExpandProperty name | gm
(Get-Service winrm).Name | gm
# returnes the object TypeName: System.String

I wouldn’t depend on using the Domain Controllers group to be a sure-fire way to get them. I’d use this:


Also, I tend to never use Get-EventLog. It’s pretty slow. Get-WinEvent is much faster and you have more filtering capabilities. Try something like this:

$DomainControllers = [Get-AdDomain].ReplicaDirectoryServers
foreach [$Dc in $DomainControllers] {
    Get-WinEvent -Computername $Dc -FilterHashTable @{'LogName' = 'Directory Service'; 'Id' = '1864'} | Select-Object MachineName,Id,TimeCreated

That should be much more reliable and quicker.


Thank you for those additional methods. As a noob I’m still trying to figure out what advantages or disadvantages each method has.


Thank you for this suggestion; I will be trying it later today. I’m scanning ~150 domain controllers, many across slow WAN connections, and my first pass at this took like 9 hours… Also, I see your point on the DC group.

For 150 DCs, Get-WinEvent is the way to go, for sure.

Sorry for being late to the party, but if you were using the AD Module, for gathering domain controllers this should also work.

(pre)(Get-ADDomainController -Filter *).Name(/pre)

And I believe all the options for this discussed so far are domain specific not forest wide just for some awareness on that.