none
PowerShell - Traversing two-dimensional array RRS feed

  • Question

  • Hi,

    I need to create a script that will search 6 domains for a computer name and output which have been found or not found to separate files. I have the domain information stored in an array that will be used with the Get-ADComputer cmdlet. The values stored in the array do not output what is actually stored. Here is a snippet:

    #Store domain info in array
    $domains = @(
    				("DC=Usa,DC=Int", "dc01.usa.int"),
    				("DC=Mexico,DC=Int", "dc02.mexico.int"),
    				("DC=Colombia,DC=Int", "dc03.colombia.int"),
    				("DC=Panama,DC=Int", "dc04.panama.int"),
    				("DC=Vietnam,DC=Int", "dc05.vietnam.int"),
    				("DC=usa,DC=ext", "dc06.usa.ext")
    	)
    		
        #Traverse array and output values to console
         for ($i = 0; $i -lt $domains.Count; $i++) {
    		Write-Host "Domain $i = " $domains[$i]
    			#$object = Get-ADComputer -filter {name -eq $computer} -SearchBase "$domains[$i][$i]" -server "$domains[$i][$i+1]"
    
    	}
    #}

    The output looks like this:

    Domain 0,0 = System.Object[] System.Object[] System.Object[] System.Object[] System.Object[] System.Object[][0][0]
    Domain 0,1 = System.Object[] System.Object[] System.Object[] System.Object[] System.Object[] System.Object[][0][1]
    Domain 1,0 = System.Object[] System.Object[] System.Object[] System.Object[] System.Object[] System.Object[][1][0]
    Domain 1,1 = System.Object[] System.Object[] System.Object[] System.Object[] System.Object[] System.Object[][1][1]
    Domain 2,0 = System.Object[] System.Object[] System.Object[] System.Object[] System.Object[] System.Object[][2][0]
    Domain 2,1 = System.Object[] System.Object[] System.Object[] System.Object[] System.Object[] System.Object[][2][1]
    Domain 3,0 = System.Object[] System.Object[] System.Object[] System.Object[] System.Object[] System.Object[][3][0]
    Domain 3,1 = System.Object[] System.Object[] System.Object[] System.Object[] System.Object[] System.Object[][3][1]
    Domain 4,0 = System.Object[] System.Object[] System.Object[] System.Object[] System.Object[] System.Object[][4][0]
    Domain 4,1 = System.Object[] System.Object[] System.Object[] System.Object[] System.Object[] System.Object[][4][1]
    Domain 5,0 = System.Object[] System.Object[] System.Object[] System.Object[] System.Object[] System.Object[][5][0]
    Domain 5,1 = System.Object[] System.Object[] System.Object[] System.Object[] System.Object[] System.Object[][5][1]

    Since these values are needed for command line switches, these values obviously don't work. How do I get the exact value that I stored in the array? I'm not even sure what this output symbolizes but it definitely is not what I need and doesn't work. Some lines are commented out as this snippet is a small portion of the full script. This is the only part of the script that isn't working.

    Thank you!

    Rob




    • Edited by robwm1 Monday, December 11, 2017 5:11 PM
    Monday, December 11, 2017 4:38 PM

Answers

  • The following is an example of how learning PowerShell will help you to create understandable and reliable code:

    #Store domain info in array
    $domains = @(
        "dc01.usa.int",
        "dc02.mexico.int",
        "dc03.colombia.int",
        "dc04.panama.int",
        "dc05.vietnam.int",
        "dc06.usa.ext"
    )
    
    #Check each domain to find a computer name from the list
    Foreach ($computer in $computers) {
        
        #Search for computer name in all domains
        for ($i = 0; $i -lt $domains.Count; $i++) {
            
            if($object = Get-ADComputer -Filter { Name -eq $computer } -Server $domains[$i]){
                #Output to 'found' file
                $publishedat = Get-ADDN -Object $object
                "$computer,$publishedat" | Out-File -FilePath "$reportfilename.csv" -Append
                Write-Host "Found: $computer" -ForegroundColor Green
            }else{
                #Output to 'not-found' file
                write-host "Not Found: $computer" -ForegroundColor Red
                "$computer" | Out-File -FilePath "$reportfilename-notfound.csv" -Append
            }
        }
    }

    Using "Stop" is useless without Try/Catch.  Please reefer to the documentation on this and all CmdLets to learn how these things work.


    \_(ツ)_/



    • Edited by jrv Monday, December 11, 2017 8:27 PM
    • Marked as answer by robwm1 Monday, December 11, 2017 9:13 PM
    Monday, December 11, 2017 8:25 PM

All replies

  • Any reason you're not using a hashtable?

    -- Bill Stewart [Bill_Stewart]

    Monday, December 11, 2017 4:51 PM
    Moderator
  • I don't have a reason for not using a hashtable other than inexperience. I'm open to whatever works best for this solution. I guess my main challenge is to determine why System.Object[] is returned instead of the actual string value. I do see that the snippet is flawed as posted but I haven't stumbled upon what works so far.

    What I see happening is that System.Object is returned but System.String is what the switches are expecting.

    Domain 0 =  DC=Usa,DC=Int lwusadc1.usa.int
    ERROR: Get-ADComputer : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'SearchBase'. Specified method is not supported.
    ERROR: At D:\Sources\_Scripts\Get-PubllishedAt.ps1:147 char:74
    ERROR: + ... omputer -filter {name -eq $computer} -SearchBase $domains[$i] -server ...
    ERROR: +                                                      ~~~~~~~~~~~~
    ERROR:     + CategoryInfo          : InvalidArgument: (:) [Get-ADComputer], ParameterBindingException
    ERROR:     + FullyQualifiedErrorId : CannotConvertArgument,Microsoft.ActiveDirectory.Management.Commands.GetADComputer

    • Edited by robwm1 Monday, December 11, 2017 5:08 PM
    Monday, December 11, 2017 5:05 PM
  • If I understand your question:


    $domainNames = "DC=domain1,DC=fabrikam,DC=com","DC=domain2,DC=fabrikam,DC=com"
    $computerNames = "computer1","computer2"
    foreach ( $domainName in $domainNames ) {
      foreach ( $computerName in $computerNames ) {
        try {
          Get-ADComputer -LDAPFilter "(name=$computerName)" -SearchBase $domainName
        }
        catch [ArgumentException] {
        }
      }
    }
    


    -- Bill Stewart [Bill_Stewart]

    Monday, December 11, 2017 5:26 PM
    Moderator
  • This is working fine when a computer name is found but throws a bunch of error output in the console when the computer is not found. An error is thrown for each domain that is searched. I thought catch might suppress these errors.

    #Store domain info in array
    $domains = @(
    				("DC=Usa,DC=Int", "dc01.usa.int"),
    				("DC=Mexico,DC=Int", "dc02.mexico.int"),
    				("DC=Colombia,DC=Int", "dc03.colombia.int"),
    				("DC=Panama,DC=Int", "dc04.panama.int"),
    				("DC=Vietnam,DC=Int", "dc05.vietnam.int"),
    				("DC=usa,DC=ext", "dc06.usa.ext")
    			)
    #Check each domain to find a computer name from the list
    Foreach($computer in $computers){
        $computer = $computer.Trim()
        $object = $null
    
        #Traverse array to locate computer name
    	for ($i=0;$i -lt $domains.Count;$i++) {
    		
    		try {
    			$object = Get-ADComputer -Filter {Name -eq $computer} -SearchBase $domains[$i][$i] -Server $domains[$i][$i+1]
    			#$object = Get-ADComputer -LDAPfilter "(Name -eq $computer)" -SearchBase $domains[$i][$i]
    		}
    		catch [ArgumentException] {
    		
    		}
    
    		if($object) {
    			Break
    		}
    	}

    Here is a sample of the error this snippet produces:

    Get-ADComputer : Cannot validate argument on parameter 'Server'. The argument is null or empty. Provide an argument that is not null or empty, and then
    try the command again.
    At D:\sources\_Scripts\Get-PubllishedAt.ps1:142 char:95
    + ... eq $computer} -SearchBase $domains[$i][$i] -Server $domains[$i][$i+1]
    +                                                        ~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidData: (:) [Get-ADComputer], ParameterBindingValidationException
        + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.ActiveDirectory.Management.Commands.GetADComputer
     

    NOTE: The output to my two files, found and not-found are getting the correct values so the script is working but I would like to suppress the error output in the console when a computer is not found. Catch doesn't seem to take care of this.
    • Edited by robwm1 Monday, December 11, 2017 6:05 PM
    Monday, December 11, 2017 6:03 PM
  • Get-ADComputer -Filter {Name -eq $computer} -SearchBase $domains[$i][$i] -Server $domains[$i][$i+1] -ErrorAcation Stop

    You must use this in a Try/Catch block.



    \_(ツ)_/

    Monday, December 11, 2017 6:11 PM
  • I tried the following:

    -ErrorAction Stop and -ErrorAction SilentlyContinue but neither of these suppress the error output.

    Monday, December 11, 2017 6:13 PM
  • Errors on servers cannot be trapped,. YOU must test the server first if using it as the "Server" parameter


    \_(ツ)_/

    Monday, December 11, 2017 6:20 PM
  • I guess I don't understand what the error messages are telling me. The SearchBase or Server arguments shouldn't be null or empty because it is provided in the array. This works fine for computers that are found. Could it be that the Exception used in the Catch statement is the wrong one? Maybe ArgumentException doesn't catch the actual error being thrown?

    Get-ADComputer : Cannot validate argument on parameter 'Server'. The argument is null or empty. Provide an argument that is not null or empty, and then
    try the command again.
    At D:\sources\_Scripts\Get-PubllishedAt.ps1:142 char:95
    + ... eq $computer} -SearchBase $domains[$i][$i] -Server $domains[$i][$i+1]
    +                                                        ~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidData: (:) [Get-ADComputer], ParameterBindingValidationException
        + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.ActiveDirectory.Management.Commands.GetADComputer
     
    Get-ADComputer : Cannot validate argument on parameter 'SearchBase'. The argument is null. Provide a valid value for the argument, and then try running
    the command again.
    At D:\sources\_Scripts\Get-PubllishedAt.ps1:142 char:70
    + ... ter -Filter {Name -eq $computer} -SearchBase $domains[$i][$i] -Server ...
    +                                                  ~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidData: (:) [Get-ADComputer], ParameterBindingValidationException
        + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.ActiveDirectory.Management.Commands.GetADComputer

    Monday, December 11, 2017 6:30 PM
  • You're not running the code I posted.

    -- Bill Stewart [Bill_Stewart]

    Monday, December 11, 2017 6:35 PM
    Moderator
  • SearchBase is not needed in your script.  You are supplying the default searchbase which is not needed.


    \_(ツ)_/

    Monday, December 11, 2017 6:36 PM
  • I added this line right after the domains array is created and now all output is suppressed. My two result files are getting the correct computer names written in them as well:

    $ErrorActionPreference = "SilentlyContinue"

    Monday, December 11, 2017 6:38 PM
  • As for array subscripting:

    $domains = @(
        ("DC=Usa,DC=Int", "dc01.usa.int"),
        ("DC=Mexico,DC=Int", "dc02.mexico.int"),
        ("DC=Colombia,DC=Int", "dc03.colombia.int"),
        ("DC=Panama,DC=Int", "dc04.panama.int"),
        ("DC=Vietnam,DC=Int", "dc05.vietnam.int"),
        ("DC=usa,DC=ext", "dc06.usa.ext")
    )
    
    for ($i=0; $i -lt $domains.Count; $i++) {
        Write-Host SearchBase: $domains[$i][0] Server: $domains[$i][1]
    }
    


    \_(ツ)_/

    Monday, December 11, 2017 6:38 PM
  • As Bill is trying to point out.  Either use "SearchBase" or "Server".  Both are not needed.

    \_(ツ)_/

    Monday, December 11, 2017 6:41 PM
  • This is the working code after bug fixes and following your suggestions. This is working the way I intended it to work:

    #Store domain info in array
    $domains = @(
    				("DC=Usa,DC=Int", "dc01.usa.int"),
    				("DC=Mexico,DC=Int", "dc02.mexico.int"),
    				("DC=Colombia,DC=Int", "dc03.colombia.int"),
    				("DC=Panama,DC=Int", "dc04.panama.int"),
    				("DC=Vietnam,DC=Int", "dc05.vietnam.int"),
    				("DC=usa,DC=ext", "dc06.usa.ext")
    			)
    
    $ErrorActionPreference = "SilentlyContinue"			
    
    #Check each domain to find a computer name from the list
    Foreach($computer in $computers){
        $computer = $computer.Trim()
        $object = $null
    
        #Traverse array to locate computer name
    	for ($i=0;$i -lt $domains.Count;$i++) {
    		#Write-Host "Domain $i = " $domains[$i]
    		
    		try {
    			$object = Get-ADComputer -Filter {Name -eq $computer} -SearchBase $domains[$i][0] -Server $domains[$i][1]
    		}
    		catch [ArgumentException] { }
    
    		if($object) {
    			Break
    		}
    		
    	}

    Monday, December 11, 2017 6:53 PM
  • Try//Catch and SilentlyContinue are bot pointless.  Nether will work of provide anything.  As noted before.  You do not need both SearchBase and Server.  DNS will provide the correct server from the searchbase.

    $domains = @( "DC=Usa,DC=Int", "DC=Mexico,DC=Int", "DC=Colombia,DC=Int", "DC=Panama,DC=Int", "DC=Vietnam,DC=Int", "DC=usa,DC=ext" ) $resolversomputers = foreach($computer in $computers){ for ($i = 0; $i -lt $domains.Count; $i++) { if($object = Get-ADComputer -Filter { Name -eq $computer } -SearchBase $domains[$i]){
    $object break } } }


    You need to take some time to learn AD and PowerShell and this will become much easier.  You cannot guess your way through any complex technology.


    \_(ツ)_/



    • Edited by jrv Monday, December 11, 2017 7:12 PM
    Monday, December 11, 2017 7:05 PM
  • This may not seem like a good way of accomplishing my task but it does work. If I use SearchBase (with LDAPFilter switch) only, every search fails. If I use Server only (with Filter switch), it seems to be getting the same results as including both.

    The overall objective was to check AD for a computer name, if it's found, get the Distinguished Name and convert it to the format of PublishedAt as seen on any computer object in AD. This script does exactly what I need.

    Monday, December 11, 2017 7:13 PM
  • This may not seem like a good way of accomplishing my task but it does work. If I use SearchBase (with LDAPFilter switch) only, every search fails. If I use Server only (with Filter switch), it seems to be getting the same results as including both.

    The overall objective was to check AD for a computer name, if it's found, get the Distinguished Name and convert it to the format of PublishedAt as seen on any computer object in AD. This script does exactly what I need.

    The point of using technicians is to have people who actually understand what they are doing.  Just because you have accidently made something work does not make you a tech.

    Also we try to avoid adding pointless and confusing code to a script as it makes things harder for others and makes room for uncaught errors.  Globally setting SilentlyContinue will mask errors that occur for other reasons and will not cause Try/Catch to work.  Try/Catch requires "Stop" Please read the help and documentation to understand why what you are doing is a bad idea.


    \_(ツ)_/

    Monday, December 11, 2017 7:17 PM
  • So this seems to be working. Would you suggest any other changes?

    #Store domain info in array
    $domains = @(
    				"dc01.usa.int",
    				"dc02.mexico.int",
    				"dc03.colombia.int",
    				"dc04.panama.int",
    				"dc05.vietnam.int",
    				"dc06.usa.ext"
    			)
    
    
    #Check each domain to find a computer name from the list
    Foreach($computer in $computers){
        $computer = $computer.Trim()
        $object = $null
    
        #Search for computer name in all domains
    	for ($i=0;$i -lt $domains.Count;$i++) {
    		
    		try {
    			$object = Get-ADComputer -Filter {Name -eq $computer} -Server $domains[$i] -ErrorAction Stop
    		}
    		catch [ArgumentException] { }
    
    	    if($object){
    	        #Output to 'found' file
    	        $publishedat = Get-ADDN -Object $object
    	        "$computer,$publishedat" | Out-File -FilePath "$reportfilename.csv" -Append
    	        Write-Host "Found: $computer" -ForegroundColor Green
    	    }
    		
    	}
    
    	#Output results
    	if (-not $object) {
            #Output to 'not-found' file
            write-host "Not Found: $computer" -ForegroundColor Red
            "$computer" | Out-File -FilePath "$reportfilename-notfound.csv" -Append
        }
    }

    Monday, December 11, 2017 7:46 PM
  • Yes.  Try/Catch is pointless here and will lead to errors.

    First learn AD then learn PowerShell and how to use it with AD.

    What is Get-ADDN? To get the DN of any object just get it:

    $object.DistinguishedName

    or

    "$object"

    forcing to a string returns the DN.


    \_(ツ)_/


    • Edited by jrv Monday, December 11, 2017 8:05 PM
    Monday, December 11, 2017 8:03 PM
  • Get-ADDN gets the Distinguished Name and calls another function that converts it to PublishedAt format as found in Active Directory computer objects.

    I added Try-Catch per Bill's suggestion above. The snippet below seems to get me the same results with Try-Catch removed:

    #Store domain info in array
    $domains = @(
    				"dc01.usa.int",
    				"dc02.mexico.int",
    				"dc03.colombia.int",
    				"dc04.panama.int",
    				"dc05.vietnam.int",
    				"dc06.usa.ext"
    			)
    
    
    #Check each domain to find a computer name from the list
    Foreach($computer in $computers){
        $computer = $computer.Trim()
        $object = $null
    
        #Search for computer name in all domains
    	for ($i=0;$i -lt $domains.Count;$i++) {
    		
    	    $object = Get-ADComputer -Filter {Name -eq $computer} -Server $domains[$i] -ErrorAction Stop
    
    	    if($object){
    	        #Output to 'found' file
    	        $publishedat = Get-ADDN -Object $object
    	        "$computer,$publishedat" | Out-File -FilePath "$reportfilename.csv" -Append
    	        Write-Host "Found: $computer" -ForegroundColor Green
    	    }
    		
    	}
    
    	#Output results
    	if (-not $object) {
            #Output to 'not-found' file
            write-host "Not Found: $computer" -ForegroundColor Red
            "$computer" | Out-File -FilePath "$reportfilename-notfound.csv" -Append
        }
    }

    Monday, December 11, 2017 8:12 PM
  • The following is an example of how learning PowerShell will help you to create understandable and reliable code:

    #Store domain info in array
    $domains = @(
        "dc01.usa.int",
        "dc02.mexico.int",
        "dc03.colombia.int",
        "dc04.panama.int",
        "dc05.vietnam.int",
        "dc06.usa.ext"
    )
    
    #Check each domain to find a computer name from the list
    Foreach ($computer in $computers) {
        
        #Search for computer name in all domains
        for ($i = 0; $i -lt $domains.Count; $i++) {
            
            if($object = Get-ADComputer -Filter { Name -eq $computer } -Server $domains[$i]){
                #Output to 'found' file
                $publishedat = Get-ADDN -Object $object
                "$computer,$publishedat" | Out-File -FilePath "$reportfilename.csv" -Append
                Write-Host "Found: $computer" -ForegroundColor Green
            }else{
                #Output to 'not-found' file
                write-host "Not Found: $computer" -ForegroundColor Red
                "$computer" | Out-File -FilePath "$reportfilename-notfound.csv" -Append
            }
        }
    }

    Using "Stop" is useless without Try/Catch.  Please reefer to the documentation on this and all CmdLets to learn how these things work.


    \_(ツ)_/



    • Edited by jrv Monday, December 11, 2017 8:27 PM
    • Marked as answer by robwm1 Monday, December 11, 2017 9:13 PM
    Monday, December 11, 2017 8:25 PM
  • Your version works for me as well but when a computer is not found, I only need a computer name that is not found in all domains to be reported only once. Your version is reporting when it doesn't occur for each domain which results in the same computer name being output several times. Not necessarily a bad thing that it is reported for each domain but I only need to know when it is not found in all domains. By leaving the not-found condition outside of the For loop, I get the result that I need.

    Scripting is not the primary function of my role and I rarely have time to work with scripting so I'm learning as much as I can when working on a task. I'm not trying to cut corners or intentionally write bad code but sometimes I can only invest so much time in some of these scripting efforts. Since I have a lot of growing to do with PowerShell, I end up taking too much time on one task to find all of my answers due to my lack of experience. I do know with more practice, I will be able to write these better and faster than I do today. I keep at is as much as I can...

    Thank you to everyone for your help! I really appreciate it!



    • Edited by robwm1 Monday, December 11, 2017 9:20 PM
    Monday, December 11, 2017 9:12 PM
  • Then this would be easier and more understandable:

    #Store domain info in array
    $domains = @(
        "dc01.usa.int",
        "dc02.mexico.int",
        "dc03.colombia.int",
        "dc04.panama.int",
        "dc05.vietnam.int",
        "dc06.usa.ext"
    )
    
    #Check each domain to find a computer name from the list
    Foreach ($computer in $computers) {
        
        #Search for computer name in all domains
        $found = for ($i = 0; $i -lt $domains.Count; $i++) {
            Get-ADComputer -Filter { Name -eq $computer } -Server $domains[$i]
        }
        if($found){
                #Output to 'found' file
                $publishedat = Get-ADDN -Object $object
                "$computer,$publishedat" | Out-File -FilePath "$reportfilename.csv" -Append
                Write-Host "Found: $computer" -ForegroundColor Green
            } else {
                #Output to 'not-found' file
                write-host "Not Found: $computer" -ForegroundColor Red
                "$computer" | Out-File -FilePath "$reportfilename-notfound.csv" -Append
            }
        }
    }
    Learn PowerShell before it is too late.


    \_(ツ)_/

    Monday, December 11, 2017 9:26 PM