While developing a script to shut down computers for maintenance, I ran into a roadblock. The script put groups of computers into variables based on their Organizational Unit in Active Directory. I needed separate groups because the computers needed to be shut down in a specific order. My original script put each group into a variable and had a loop to shutdown each computer.
This worked, but I hated having a separate block of code to loop through each group. I thought, there must be a better way. Then it hit me… Nested Loops! I can put the ForEach loop to shutdown each group of computers inside of another ForEach loop to go through the group of groups. It sounds more confusing than it is.
The first thing I needed to do was to store the groups of computers in variables. This is easy enough to do with the Get-ADComputer CmdLet.
$ComputerGroup1 = Get-ADComputer -Server "SERVER.COM" -Filter * -SearchBase "OU=OUNAME,DC=DOMAIN,DC=COM" | Sort-Object DNSHostName | Select-Object -ExpandProperty DNSHostName
I have 7 groups, so I have 7 lines like the one above assigning computers to variables based on their Organizational Unit in Active Directory. Next, I need to put the computer group variables into another variable to hold the entire list.
$ComputerGroupsList = $ComputerGroup1, $ComputerGroup2, $ComputerGroup3, $ComputerGroup4, $ComputerGroup5, $ComputerGroup6, $ComputerGroup7
It is worth noting that the variable for the server group list must be placed after the code that populates the individual server group variables. If not it will only contain the empty variables.
Now to construct the first or outer ForNext loop.
#Outer loop for each computer group in the computer group list foreach ($ComputerGroup in $ComputerGroupsList) { }
The variable $ComputerGroupList is currently holding all of the computer group variables in a single array item as a space delimited list. To be useful in this script the list needs to be split into multiple array items. This is easily accomplished with one CmdLet.
#Turn the single line, space delimited list into individual array items $ComputerGroup = $ComputerGroup.Split(" ")
And now the second or inner loop to shutdown the computers.
#Inner loop for each computer in the computer group foreach ($Computer in $ComputerGroup) { Write-Output "Shutting down $Computer" Stop-Computer -ComputerName $Computer -Force }
When we put it all together it looks like this.
$ComputerGroup1 = Get-ADComputer -Server "SERVER.COM" -Filter * -SearchBase "OU=OUNAME,DC=DOMAIN,DC=COM" | Sort-Object DNSHostName | Select-Object -ExpandProperty DNSHostName $ComputerGroup2 = Get-ADComputer -Server "SERVER.COM" -Filter * -SearchBase "OU=OUNAME,DC=DOMAIN,DC=COM" | Sort-Object DNSHostName | Select-Object -ExpandProperty DNSHostName $ComputerGroup3 = Get-ADComputer -Server "SERVER.COM" -Filter * -SearchBase "OU=OUNAME,DC=DOMAIN,DC=COM" | Sort-Object DNSHostName | Select-Object -ExpandProperty DNSHostName $ComputerGroup4 = Get-ADComputer -Server "SERVER.COM" -Filter * -SearchBase "OU=OUNAME,DC=DOMAIN,DC=COM" | Sort-Object DNSHostName | Select-Object -ExpandProperty DNSHostName $ComputerGroup5 = Get-ADComputer -Server "SERVER.COM" -Filter * -SearchBase "OU=OUNAME,DC=DOMAIN,DC=COM" | Sort-Object DNSHostName | Select-Object -ExpandProperty DNSHostName $ComputerGroup6 = Get-ADComputer -Server "SERVER.COM" -Filter * -SearchBase "OU=OUNAME,DC=DOMAIN,DC=COM" | Sort-Object DNSHostName | Select-Object -ExpandProperty DNSHostName $ComputerGroup7 = Get-ADComputer -Server "SERVER.COM" -Filter * -SearchBase "OU=OUNAME,DC=DOMAIN,DC=COM" | Sort-Object DNSHostName | Select-Object -ExpandProperty DNSHostName $ComputerGroupsList = $ComputerGroup1, $ComputerGroup2, $ComputerGroup3, $ComputerGroup4, $ComputerGroup5, $ComputerGroup6, $ComputerGroup7 #Outer loop for each computer group in the computer group list foreach ($ComputerGroup in $ComputerGroupsList) { #Turn the single line, space delimited list into individual array items $ComputerGroup = $ComputerGroup.Split(" ") #Inner loop for each computer in the computer group foreach ($Computer in $ComputerGroup) { Write-Output "Shutting down $Computer" Stop-Computer -ComputerName $Computer -Force } }
That’s all there is to it. It took me a while to figure out all of the pieces, so I though I would share in the hopes that this will save someone else some time.
The greatest part is that the script is dynamic and scales easily. Since the computers are not hard coded, it will not need to be modified to add or remove computers (unless the OU’s change). Whether there are 7 computers or 700, this small bit of PowerShell will handle the job beautifully.