Simple method to cycle through a list of computers

Often we need to query a group of computers (using WinRM for example) to obtain some data or run certain commands. This can be tricky as some devices may be offline, so a simple for loop usually isn’t enough. Storing the computer list in an array and removing a device once it has been successfully processed is one way, but this means we cannot stop/pause our script, and we lose track of all progress if the device we’re running the script on goes down for whatever reason.

Below is a simple boilerplate function for storing the computer list in a file, and cycling though the file for as long as there are unprocessed computers remaining. Once a device has been processed, it is removed from the file immediately.

It a assumes a file is present with a list of hostnames (one per line) called PCList.txt.

function RunLoop {
    [CmdletBinding()]
    Param ()

    [System.Collections.ArrayList]$PCList = Get-Content PCList.txt
    
    while ($PCList.Count -gt 0) {
    
        Write-Host "$((Get-date).toString()): Starting loop, $($PCList.Count) PC(s) remaining"
        $TempList = $PCList.Clone()
        $ProgressCounter = 1

        foreach ($PC in $PCList) {
            $Data = $null

            Write-Host "Connecting to $PC ($ProgressCounter/$($PCList.count))..."
            try {
                # Here we run whatever command we need on the remote device
                $Data = Invoke-Command -ComputerName $PC -ScriptBlock {Get-Process} -ErrorAction Stop
            }
            catch {
                Write-Host " -> Unable to connect to $PC, skipping" -ForegroundColor Gray
                write-Verbose $_.Exception.GetType().FullName
                write-Verbose $_.Exception.Message
            }

            if ($Data) {
                try {
                    Write-Host " -> Successfully got data from $PC" -ForegroundColor Green
                    # Do whatever we need with $Data, write to a file, etc. 
                    $TempList.Remove($PC)
                    $TempList | Out-File PCList.txt -Force
                }
                catch {
                    Write-Warning $_.Exception.GetType().FullName
                    Write-Warning $_.Exception.Message
                }
            }

            $ProgressCounter ++
        }

        $PCList = $TempList
        Start-Sleep -Seconds 20
    }
}