I am writing a script for work that will query all of our AD domains and then return the computers that have admin privileges. The below script works and returns the expected results but it is very slow on our larger domains.
When working with 23k objects and less it runs in only a few minutes (6min or so), but when its has to handle 90k+ it gets into hours.
I am new to PowerShell and have no idea what operation here would have an exponential runtime increase so I have been unable to narrow it down. My hunch is that it has to deal with that way PowerShell is expanding the arrays to continuously add more objects? I was also thinking about making better use of the pipeline... but being new and coming from bash I am unfamiliar with this concept and how to utilize it in this code
Is there a way I can speed this up to run faster than a few hours? Any help would be greatly appreciated.
$date = Get-Date -uFormat %d-%m-%y
ForEach($domain in $domains)
{
$all_computers = Get-ADComputer -Server $domain -filter * -Properties enabled | select-object name,enabled,@{name="distinguishedname";expression={$_.DistinguishedName -replace "(CN[^,]+,)"}}
#Gets all of the objects that are in a group and pulls their names this is to get the admin flag that is appended to names in this group
$group_name = (Get-ADGroup -Server $domain -Filter{name -like "*admin*"}).Name
#Counts the devices pulled from the computer query
$DevNum = $all_computers.count
echo "Number of devices: " $DevNum > "$domain LARGE $date.txt"
#Remove servers from the list
$all_computers = $all_computers | ?{ $_ -NotMatch "Servers" }
#Counts the number of servers we removed
$NumSkipped = $DevNum - $all_computers.count
Switch($all_computers){
#Finding all of the accounts where both types of admins exist and removing them from the master list
{$group_name -contains $($_.name + "Admins") -and $group_name -contains $($_.name + "UPEPAdmins")} {$_ | Add-Member "admintype" "both";Continue}
#Finding all of the accounts with only exception admins and removing them from the master list
{$group_name -contains $($_.name + "Admins")} {$_ | Add-Member "admintype" "old";Continue}
#Finding all of the accounts with only upep admins and removing them from the master list
{$group_name -contains $($_.name + "UPEPAdmins")} {$_ | Add-Member "admintype" "UPEP";Continue}
#These accounts have no admin
default {$_ | Add-Member "admintype" "No"}
}
echo "Number of servers skipped: " $NumSkipped >> "$domain LARGE $date.txt"
echo "Number of workstations: " $all_computers.count >> "$domain LARGE $date.txt"
echo "Number of Exception admins found: " $($all_computers|?{$_.admintype -match "old|both"}).count >> "$domain LARGE $date.txt"
echo "Number of UPEP admins found: " $($all_computers|?{$_.admintype -match "upep|both"}).count >> "$domain LARGE $date.txt"
echo "Number of both UPEP and Exception admins found: " $($all_computers|?{$_.admintype -eq "both"}).count >> "$domain LARGE $date.txt"
#output
$all_computers | Export-Csv "$domain LARGE $date.csv"
}
Edit 1:
Updated code to reflect suggestions from SodaWillow, TheMadTechnician, and removing trim and replacing it with -replace decreased runtime by a little bit.
Edit 2:
Updated the code to the correct solution, ontop of TheMadTechnician's suggestions I filtered the groups to decrease their number and also inserted group names into an array rather than a table. The use of an array sped up operations significantly when combined with the decreased group numbers.
Current bugs: The "Both" admin types are being exported to CSV correctly but not being reported in the text file at all, I think it has to do with the logic in the if statement. I am looking at that currently
Edit 3:
Fixed Both admin type logic bug, this is the final solution for this problem. the $domains var is declared out of view of this code block due to private information.
Thank you everyone for your time!
System.DirectoryServices.DirectorySearcher(examples here : powershelladmin.com/wiki/…). Measure the time consumed by each part of your code to find the longest queries, and then begin by optimizing those ones (if you haven't done this already)Get-ADGroupmaybe ?