Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Powershell - looping through an array

Tags:

powershell

I'm looking to search the C and E drives of all Windows servers in Active Directory for any existing copies of putty.exe and their version. The output needs to have the server name, full path to the executable, and the file version. So far I have the following code (which right now is only using two servers for testing:

$ComputerName = Get-ADComputer -filter "name -like 'computer01' -or name `
-like 'server01'" | select -ExpandProperty name

$OutputArr = @()

$findFiles = foreach($computer in $computername){

    $file = Invoke-Command -computername $computer { Get-ChildItem -Path `
    c:\, e:\ -Recurse | where-object{(!$_.psiscontainer -eq $true) -and `
    ($_.name -like "putty.exe")} | ForEach-Object -process {$_.fullname} }


    $output = $OutputObj = New-Object -TypeName PSobject  
    $OutputObj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $computer
    $OutputObj | Add-Member -MemberType NoteProperty -Name FilePath -Value $file

    $OutputArr += $OutputObj
    Write-Verbose $OutputObj
}

$OutputArr | fl

The above code outputs the following array:

ComputerName : COMPUTER01
FilePath     : {C:\Program Files\PuTTY\putty.exe, C:\Program Files (x86)\PuTTY\PUTTY.EXE}

ComputerName : SERVER01
FilePath     : {C:\Program Files (x86)\putty\putty.exe, C:\Users\testuser\Desktop\Public Desktop\putty.exe}

This produces the correct data, but now I need to run another snippet of code against each separate filepath under computername, but am not sure how to accomplish this, as it is taking the full filepath with multiple entries.

Essentially, I need to separate each ComputerName in the array into multiple lines:

COMPUTER01,C:\Program Files\PuTTY\putty.exe
COMPUTER01,C:\Program Files (x86)\PuTTY\PUTTY.EXE
SERVER01,C:\Program Files (x86)\putty\putty.exe

Etc...

Is an array not the correct way to do it?


1 Answers

If you are working strictly with what you already have stored in $OutputArr, the following will work:

$out = foreach ($line in $OutputArr) {
   if ($line.filepath.count -gt 1) {
     foreach ($fp in $line.FilePath) {
       [pscustomobject][ordered]@{ComputerName = $line.ComputerName; FilePath = $fp}
     }
   } 
   else {
     $line
   }
 } 

$out | ConvertTo-Csv -NoTypeInformation

The foreach loop creates new objects with properties ComputerName and FilePath and stores them in $out as an array of objects.

If you do not care about properties and just want a comma-delimited list, you can use the following:

foreach ($line in $OutputArr) {
  if ($line.filepath.count -gt 1) {
    foreach ($fp in $line.FilePath) {
      "{0},{1}" -f $line.ComputerName,$fp 
    }
  }
  else {
      "{0},{1}" -f $line.ComputerName,$line.FilePath
  }
 } 

This does the same looping as the first solution but instead uses the format operator (-f) to format the output. Piping to ConvertTo-Csv formats the output to be comma-delimited with the properties as headers.

You could move your desired functionality into your code before you even store anything in $OutputArr. I feel like doing all this after all of the other looping to create $OutputArr is just adding inefficiency.

like image 87
AdminOfThings Avatar answered Oct 18 '25 12:10

AdminOfThings



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!