Search code examples
powershellforeachpowershell-3.0

Parts of ForEach loop in PowerShell being skipped over?


I have a Powershell script that seems to be functioning fine except for the fact that part of my output is skipped over and I can't figure out why. Here is the script being run:

#Server list provided for the script.
$ServerList = Get-Content $env:USERPROFILE\Documents\Servers.txt
#Counter for first If loop.
$Counter = 0
#Counter for second If loop.
$Counter2 = 0

#ForEach loop going through the server list, picking out OS, Drives, and CPU info and dumping it into an array.
ForEach($Server in ($ServerList))
    {
        "Collecting server information on $Server, please wait..."
        "Collecting Operating System..."
        $OS = gwmi Win32_OperatingSystem -ComputerName $Server | select Caption
        "Collecting Storage..."
        $Drives = gwmi Win32_LogicalDisk -ComputerName $Server | Format-Table DeviceId, @{n="Size in GB";e={[math]::Round($_.Size/1GB,2)}},@{n="Free Space in GB";e={[math]::Round($_.FreeSpace/1GB,2)}}
        "Collecting CPU..."
        $CPU = gwmi Win32_Processor -ComputerName $Server | select Name, Manufacturer
        $ServerInfo = @($OS,$Drives,$CPU)
        #$ServerInfo
        #Do loop that posts the info stored in the array and ups the first counter. Runs while counter is equal to 0.
        Do
            {
                "All done. Here's all the info we got on $($Server):"
                 $ServerInfo
                 $Counter++
            }While ($Counter -eq 0)
        #If loop that checks if the Counter has been bumped by the Do loop. Sets Counter to 0 and increases Counter2 by 1.
        If ($Counter -eq 1)
            {
                $Counter = 0
                $Counter2++
            }
        #If loop that checks if Coutner2 is equal to a certain number. This is the hard stop to the loop.
        If ($Counter2 -eq 2)
            {
                "Max number of runs met. Stopping."
                break
            }
    }

I know the script is messy and needs a lot of work, but for some reason after the second pass of the ForEach loop, the OS variable is completely skipped over, and doesn't show up in the console. After the first run, it leaves it out entirely and just posts the Drive and CPU information. I thought maybe it was something weird with the Do and If loops, so I commented them out to test but it's the same result.

I've tried posting the variables OS, Drives, and CPU as the loop runs to make sure it's actually saving something to the variable, and it is, and I'm also calling the variables themselves after the loop breaks to see if something weird is happening in the loop/script. I found out that calling the OS variable and the ServerInfo array after the loop finishes causes it to output with the OS information.

Any idea as to why that is?


Solution

  • I think the reason for the strange output is in the way you are using the counters.

    If I understand the question correctly, you want (console) output for each server in the Servers.txt file, BUT maximized to a certain number. There could be over one hundred servers in the text file, but you want to limit the output to just a certain number of servers and then break out of the main ForEach loop.

    Not only that, but you want to capture and later combine the different pieces of information you obtained using the various Get-WmiObject calls in a way that PowerShell uses to format them.

    The trick there is to use | Out-String.

    Below is my version of the script.

    $ServerList = Get-Content $env:USERPROFILE\Documents\Servers.txt
    $Counter  = 0  # the running counter; gets incremented for each server
    $MaxCount = 2  # the maximum number of servers you want to process
    
    #ForEach loop going through the server list, picking out OS, Drives, and CPU info and dumping it into an array.
    ForEach($Server in $ServerList) {
        Write-Host "Collecting server information on $Server, please wait..."
        Write-Host  "Collecting Operating System..." -ForegroundColor Yellow
    
        $OS = (Get-WmiObject Win32_OperatingSystem -ComputerName $Server | 
               Select-Object Caption |
               Out-String).Trim()
    
        Write-Host "Collecting Storage..." -ForegroundColor Yellow
        $Drives = (Get-WmiObject Win32_LogicalDisk -ComputerName $Server | 
                   Select-Object DeviceId, 
                                 @{n="Size in GB";e={[math]::Round($_.Size/1GB,2)}},
                                 @{n="Free Space in GB";e={[math]::Round($_.FreeSpace/1GB,2)}} |
                                 Out-String).Trim()
    
        Write-Host "Collecting CPU..." -ForegroundColor Yellow
        $CPU = (Get-WmiObject Win32_Processor -ComputerName $Server | 
                Select-Object Name, Manufacturer |
                Out-String).Trim()
    
        Write-Host
        # join these strings together with newlines so the output will be readable
        $ServerInfo = @($OS,$Drives,$CPU) -join "`r`n`r`n"
        # output the serverInfo for this server. 
        $ServerInfo
        Write-Host
    
        # instead of the -join above which looks more like the original code you gave,
        # you could also do:
        #  $ServerInfo = "{0}`r`n`r`n{1}`r`n`r`n{2}`r`n" -f $OS, $Drives, $CPU
        # or even:
        #  Write-Host $OS
        #  Write-Host
        #  Write-Host $Drives
        #  Write-Host
        #  Write-Host $CPU
        #  Write-Host
    
        # if $Counter has reached the maximum number of servers to process, break out of the ForEach loop
        If (++$Counter -ge $MaxCount) {
            Write-Host "Max number of runs ($MaxCount) met. Stopping." -ForegroundColor Green
            return
        }
        Write-Host
    }