PowerShell Multi-Threading Template – Run Remote Script on all Domain Controllers

This is my Multi-Threading Template for PowerShell using Background Jobs to run a PowerShell script on all domain controllers in a forest.

Change domain.com to your forest name.

Edit the Job Script Section.

Edit the Return Handling Section.

Specify maxThreads.

Read the embedded comments for more info.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# MultiThreading PowerShell Template
#
#=========== OPTIONS ==============================
$maxThread = 20
$logFile = "C:\Users\Public\xLog_$(Get-Date -f yyyy-MM-dd_HH_mm_ss).txt"
#==========================
CLS
Stop-Job *
Remove-Job *
$currentJobs = New-Object System.Collections.ArrayList
$doneJobs = New-Object System.Collections.ArrayList
$servers = New-Object System.Collections.ArrayList
$doneServers = New-Object System.Collections.ArrayList
$start = Get-Date
(Get-ADForest -Server 'domain.com').Domains | % {
    $domainServers = Get-ADDomainController -Server $_ -Filter *
    ForEach ($server in $domainServers) {
        Write-Host "Found $($server.Name).$_ to process..." -ForegroundColor Cyan
        $null = $servers.Add("$($server.Name).$_")
    }
}
Write-Host "Found $($servers.Count) servers to process..." -ForegroundColor Yellow
Do {
    Write-Host "Current Jobs: $($currentJobs.count)"
    If ($currentJobs.Count -le $maxThread){
        $addMore = ($maxThread - $currentJobs.Count)
        ForEach ($server in ($servers | where {$doneServers -notcontains $_} | Select-Object -First $addMore)){
            $newJob = Start-Job -ScriptBlock {
            param ([string]$server)
            # This is the job we run on each computer
            $job = Test-Connection -ComputerName $server -Count 1 -ErrorVariable jobErr -ErrorAction SilentlyContinue;
            # edit below to mark results from jobs - they'll be handled later based off what you provide here
            # Results MUST be sent to Write-Output so that we can handle them later.
            If ($job -eq $null -and -not $jobErr){
                Write-Output "STATUS: $($server): $($job | Out-String)"
            }ElseIf ($jobErr){
                Write-Output "ERROR: $($server): $($jobErr | Out-String)"
            }Else{
                Write-Output "ERROR: $($server): $($job | Out-String)"
            }
            # edit above to mark results from jobs- they'll be handled later based off what you provide here
            } -ArgumentList $server
            Write-Host "Started Job for $server..." -ForegroundColor Cyan
            $null = $doneServers.Add($server)
            $null = $currentJobs.Add($newJob.Id)
        }
    }
    ForEach ($job in $currentJobs){
        If ($doneJobs -notcontains $job){
            $status = Get-Job -Id $job
            If ($status.State -eq 'Completed' -and $status.HasMoreData -eq 'True'){
                $end = Get-Date
                $dateDiff = New-TimeSpan $start $end
                $minutes = $dateDiff.Minutes
                $seconds = $dateDiff.Seconds
                Write-Host "Time Elapsed: $($minutes)m $($seconds)s" -ForegroundColor Cyan
                Do{
                    $result = Receive-Job -id $job
                    # Edit Below to handle the returned results from the jobs that have completed
                    If ($result -like "ERROR:*"){
                        Write-Host $result -ForegroundColor Red
                        $result | Out-File -Append -FilePath $logFile
                    }Else{
                        Write-Host $result -ForegroundColor Green
                    }
                    # Edit Above to handle the returned results from the jobs that have completed
                }Until($result -eq $null)
                $null = Stop-Job -Id $job
                $null = Remove-Job -Id $job                
                $null = $doneJobs.Add($job)                    
            }                
            sleep -s 1
        }
    }
    ForEach ($done in $doneJobs){
        $currentJobs.Remove($done)
    }
    Write-Host "Completed Jobs: $($doneJobs.Count)" -ForegroundColor Cyan
} Until ($doneJobs.Count -eq $servers.Count)
Write-Host "Processing is completed" -ForegroundColor Yellow
Stop-Job *
Remove-Job *
start notepad.exe $logFile

Leave a Reply

Your email address will not be published. Required fields are marked *