Nested ForEach Loops

Using Powershell to pull data from eDirectory, the data I’m trying to grab looks like:

cn primary secondary
123joe ABCD 5678
123joe ABCD 8998
123joe ABCD 1648
123janis DEFG null
123janis GHEX null
Where a user ID might have one or more primary values, and zero or more secondary values.

The code I’m working with allows me to entirely grab a user like 123joe in three rows with all three of his secondary identifiers, but this code won’t grab 123janis at all, who has no secondary identifiers. I’d like to fix the query to pull all IDs, including the ones with no secondary identifiers.

$eDirPath = 'LDAP://0.0.0.0'
$eDirUser = 'userID'
$eDirPWD = 'password'
$eDirAuthType = 'None'

#Establish eDirectory Connection and Enumerate
$Root = New-Object System.DirectoryServices.DirectoryEntry -argumentlist $eDirPath,$eDirUser,$eDirPWD,$eDirAuthType
$Query = New-Object System.DirectoryServices.DirectorySearcher
$Query.SearchRoot = $Root
$Query.Filter = "(cn=123*)"
$SearchResults = $Query.FindAll()
$ldapIDs = @()
ForEach ($Result in $SearchResults) `
  {
   $CCGroupALL = [PSCustomObject]$Result.Properties
        ForEach ($Item in $CCGroupALL)
            {
                $primary = $Item.primary
                $secondary = $Item.secondary
                
                ForEach ($cn in $Item.cn)
                    {ForEach ($primary in $Item.primary)
                        {ForEach ($secondary in $Item.secondary)
                            {
                            $ldapIDs += "$cn,$primary,$secondary"
                            }
                        }
                     $CSVoutput = "cn,primary,secondary" , $ldapIDs
                     }
            }
                    
   }
$CSVoutput | Out-File C:\ldapFile.csv

This will never output data if there are no secondary items because of the ForEach logic on line 23. If $Item.secondary is null then the ForEach will operate 0 times, resulting in line 25 being skipped because that loop doesn’t do anything and therefore producing no data for $CSVoutput on line 28.

Separating the primary and secondary foreach loops should solve the problem:

$eDirPath = 'LDAP://0.0.0.0'
$eDirUser = 'userID'
$eDirPWD = 'password'
$eDirAuthType = 'None'

#Establish eDirectory Connection and Enumerate
$Root = New-Object System.DirectoryServices.DirectoryEntry -argumentlist $eDirPath,$eDirUser,$eDirPWD,$eDirAuthType
$Query = New-Object System.DirectoryServices.DirectorySearcher
$Query.SearchRoot = $Root
$Query.Filter = "(cn=123*)"
$SearchResults = $Query.FindAll()

$ldapIDs = @()
ForEach ($Result in $SearchResults) {

    $CCGroupALL = [PSCustomObject]$Result.Properties
    ForEach ($Item in $CCGroupALL) {

        $primary = $Item.primary
        $secondary = $Item.secondary
                
        ForEach ($cn in $Item.cn) {

            $ldapIDs += "$cn"                           #store the cn value

            ForEach ($primary in $Item.primary) {
                $ldapIDS += "$primary"                  #store the primary value
            } #ForEach $primary
                
            ForEach ($secondary in $Item.secondary) {
                $ldapIDs += "$secondary"                #store the secondary value
            } #ForEach $secondary

        } #ForEach $cn

        $CSVoutput = "cn,primary,secondary", $ldapIDs   #store values in CSVoutput

    } #ForEach $Item

} #ForEach $Result

$CSVoutput | Out-File C:\ldapFile.csv

(untested)

Of course, this isn’t the best methodology.

What exactly are you trying to end up with?

Trying to end up with three columns in a csv like:

cn primary secondary
123joe ABCD 5678
123joe ABCD 8998
123joe ABCD 1648
123janis DEFG
123janis GHEX
Basically, for each user ID, grab all the primary values, and if there are any secondary values grab those too.

But if the user ID doesn’t have a secondary value like 123janis, then just grab the user ID and primary value(s).

I tried grokkit’s code and it looks like all the data is being dropped into the first column.

Getting closer…

The following code uses If and ElseIf to run a separate query based on whether Item.secondary is null or not null.

When I use the Write-Host output, it looks correct on screen, but the CSV file ends up with all the data sitting in row 2 of the file, like:

cn primary secondary
123joe,ABCD,5678,123joe,ABCD,8998,123joe,ABCD,1648,123janis,DEFG,123janis,GHEX
Any ideas how to get the $CSVOutput formatted correctly?

 

ForEach ($Result in $SearchResults) `
  {
   $CCGroupALL = [PSCustomObject]$Result.Properties
        ForEach ($Item in $CCGroupALL)
            {
                $primary = $Item.primary
                $secondary = $Item.secondary
                ForEach ($cn in $Item.cn)
                    {
                    If ($null -ne $Item.secondary)                
                        {ForEach ($primary in $Item.primary)
                            {ForEach ($secondary in $Item.secondary)
                                {
                                #Write-Host "$cn",",","$primary",",","$secondary"
                                $ldapIDs += "$cn,$primary,$secondary"
                                }
                            }
                     
                        }
                    ElseIf ($null -eq $Item.secondary)
                        {ForEach ($primary in $Item.primary)
                            {
                            #Write-Host "$cn",",","$primary",",","$secondary"
                            $ldapIDs += "$cn,$primary,$secondary"
                            }
                        }
                    }
            $CSVoutput = "cn,primary,secondary" , $ldapIDs
            }        
   }
$CSVoutput | Out-File C:\ldapFile.csv

Solved. A working version of the full script is pasted below. Made a modification based on this article (no longer using +=).

This version includes grabbing two additional columns from eDirectory: firstname $fn and lastname $ln.

$eDirPath = 'LDAP://0.0.0.0'
$eDirUser = 'userID'
$eDirPWD = 'password'
$eDirAuthType = 'None'

#Establish eDirectory Connection and Enumerate
$Root = New-Object System.DirectoryServices.DirectoryEntry -argumentlist $eDirPath,$eDirUser,$eDirPWD,$eDirAuthType
$Query = New-Object System.DirectoryServices.DirectorySearcher
$Query.SearchRoot = $Root
$Query.Filter = "(cn=123*)"
$SearchResults = $Query.FindAll()
$ldapIDs = New-Object System.Collections.Generic.List[System.String]
ForEach ($Result in $SearchResults) `
  {
   $CCGroupALL = [PSCustomObject]$Result.Properties
        ForEach ($Item in $CCGroupALL)
            {
                $fn = $Item.givenname
                $ln = $Item.sn
                $primary = $Item.primary
                $secondary = $Item.secondary
                ForEach ($cn in $Item.cn)
                    {
                    If ($null -ne $Item.secondary)  # use this loop if secondary is not null                    
                        {ForEach ($primary in $Item.primary)
                           {ForEach ($secondary in $Item.secondary)
                              {
                              $ldapIDs.Add("$cn,$fn,$ln,$primary,$secondary")
                              }
                           }
                        }
                     ElseIf ($null -eq $Item.secondary) # otherwise use this loop if secondary is null 
                        {ForEach ($primary in $Item.primary)
                            {
                            $ldapIDs.Add("$cn,$fn,$ln,$primary,$secondary")
                            }
                        }
                     
                     }
                $CSVoutput = "cn,fn,ln,primary,secondary" , $ldapIDs #create the CSVOutput with header row
            }
  }
$CSVoutput | Out-File C:\Ldap.csv

Note: if you want to test in your environment, input your eDirectory ldap path, userID, and password. Also the “primary” and “secondary” attributes probably don’t exist in your eDirectory environment, so you’ll want to modify these accordingly. Also the $Query.Filter above will only grab user IDs beginning with “123” so modify accordingly.

Your $eDirPath might look something like: ‘LDAP://your IP address/o=something/ou=somethingElse’

Whereas your $eDirUser credentials might look something like: ‘cn=yourID,ou=something,o=somethingElse’