Pulling all GPOs for a computer

I am working on a script for a company to decommission a large number of servers, both virtual and physical. As part of the process they would like to ensure A.D./DNS/Group Policy is cleaned up. In the past, the computer object has been deleted from A.D. first, leaving a ghost SID in GPOs. I am looking for suggestions on the best way to grab all of the GPOs (in two domains) in which the server is a principal. I thought about doing a GPResult, but that outputs to an html or xml report which I then have to parse through to get the info that I want.

In the end, I basically have an array of all GPOs for each domain returned from Get-GPO -All, an array of all of the servers from a csv, and am running a nested for loop to check every GPO for every server, something like this (Write-Host just for testing of course):

$aServers = Import-Csv -Path 
$domain = "myCompany.com"
$GPOs = Get-GPO -Domain $domain -All | Select DisplayName
$aReport = @()
        foreach ($server in $aServers) {
            foreach($gpo in $GPOs) {
                $myObject = New-Object PSObject
                try {
                    If (!((Get-GPPermissions -DomainName $domain -Name $gpo.DisplayName -TargetName $server.ComputerName -TargetType Computer -ErrorAction Stop) -eq "")) {                   
                        $myObject | Add-Member -MemberType NoteProperty -Name "Server" -Value $($server.ComputerName)
                        $myObject | Add-Member -MemberType NoteProperty -Name "GPO" -Value $($gpo.DisplayName)
                    }
                    $aReport += $myObject
                } catch {
                    Write-Host "$($server.ComputerName) not found in $($gpo.DisplayName)"
                }
            }

The code works, but it is ugly and slow - on 80 servers looking through ~100 GPOs takes about 35 minutes per domain. Just looking for a better solution.

What you have here if fine, but you can speed this up by taking one of the following options…

Details here:

Parallel processing with PowerShell

Working sequentially

When you’re working with a relatively low number of servers you can afford to work sequentially – your script accesses the first server, and then the second, and then the third and so on.

Working in parallel

Whichever approach you end up taking you will be getting PowerShell to run tasks in parallel. That will often require you to have additional instances of PowerShell running. The resources on your admin machine – CPU, memory and network bandwidth – are finite. Keep those in mind so you don’t overload the machine and end up getting nothing back.

PowerShell jobs

Ideally, it would be great if you could get the system to run the scripts in different PowerShell instances. There is a mechanism to do this – it’s called PowerShell jobs. When you run a script as a job it runs in a separate, background instance of PowerShell and you can carry on working.

PowerShell Workflows

PowerShell workflows were one of the headline items in PowerShell 3.0. Workflows are a really strong option if you need parallel processing and/or you need your code to manage and survive a reboot of the remote machine. After an initial flurry the use of workflows seems to have diminished. In the right place they perform admirably.

PowerShell runspaces

Your last option is to use PowerShell runspaces to run your tasks. This is a much more programmatic approach that involves running PowerShell instances – much as you did with PowerShell jobs but without some of the overheads inherent in the PowerShell job system.

This approach involves diving into .NET to a degree so if you’re not happy with that you may want to stick with the earlier options.

Parallel processing with PowerShell | Microsoft Learn

Thanks, I have been looking into this. Running some timings on it now

Using a workflow seemed to work very well, cut the time from 38 minutes per domain down to about 9 (albeit pegging CPU the whole time).

In the end, though, it occurred to me I was probably overthinking the whole thing. Rather than doing all of this work up-front to prevent unknown SIDs in the GPOs, I figured just delete the A.D. object then remove any unknown SIDs after the fact. In testing this in the sandbox, with ~50 orphaned SIDs across the two domains, it is taking less than 30 seconds to run. Seems I have a knack for constructing Rube Goldberg machines. Thanks for the suggestions