Learn about Windows PowerShell
Hey, Scripting Guy! I need to use Windows PowerShell 2.0 to find groups in Active Directory Domain Services (AD DS) that are not being used. Over the years, we have had several network administrators. Some of them were good, and some had challenges in the “good” category. As a result, there are groups that do not have any members. I would like to be able to run a Windows PowerShell script that would go through all the groups in the domain and delete the ones that are not being used. Is this something that can be accomplished?
-- EM
Hello EM,
Microsoft Scripting Guy Ed Wilson here. Have I mentioned recently how much I love working with Windows PowerShell? The cool thing about Windows PowerShell is that a script is never finished. There is always something else that can be done, and the Get-UnusedGroups.ps1 script that I wrote for you is no exception. It started out as a simple “one liner” and it kind of grew from there. I used the Get-MyModule module to ensure the script has access to the Active Directory cmdlets. I then decided to add a function to create bogus empty groups in Active Directory to ensure you had groups you could use for testing purposes. Later, I decided you might want to control where the bogus groups are created. Then I added the Get-UnusedGroups function.
As I was working with it, I thought it might be a good idea to exclude critical system objects from the retrieval process. But, what if Microsoft Exchange is installed? Unfortunately, Microsoft Exchange does not mark its groups as critical system objects, but it does place them in a default organizational unit. So I added that OU to the exclusion list. Later I found there are a couple of DNS groups that exist that should be excluded from the search, and that completed the Get-UnusedGroups function. I then decided that producing nice formatted output would be worthwhile, so I created the Format-Output function to create a nice table. Lastly, I created the Remove-UnusedGroups function to remove the unused groups that might be discovered. I also added some command-line switches and other parameters to allow you to create bogus groups or not, and to allow you to remove unused groups or not. The report is always created. The complete Get-UnusedGroups.ps1 script is shown here.
Get-UnusedGroups.ps1
The New-BogusTestGroups function is used to create a number of global groups that do not have any members assigned to them. This function can be used to provide a control to your script to ensure it is picking up groups that do not have any members in it. You can use the script simply to create bogus groups to use for other purposes as well. There is no requirement to delete the empty groups when the script has finished running. You will need to supply two values to the New-BogusTestGroups function. The first is the -numberGroups parameter. This value is used to control how many empty groups will be created. The second parameter is the path to the location that will hold the newly created groups. Normally this would be the distinguishedName attribute of an organizational unit.
The New-BogusTestGroups pipes an array of numbers to the ForEach-Object cmdlet. Inside the scriptblock for the Foreach-Object cmdlet, the New-ADGroup cmdlet is used to create the new groups. The name of the group will be test and the number from the piped array. Because both the numberGroups and the path parameters are marked as mandatory, an error will be displayed if the values are not supplied. The New-BogusTestGroups function is shown here:
When the script is run, and only the numberGroups, CreateGroups, and Path parameters are used, no output is displayed. This is shown in the following image.
When the destination is checked in the Active Directory Users and Computers MMC, the test groups appear. This is shown in the following image.
On the other hand, if one of the required parameters is not supplied, an error will be generated. The script is smart enough to tell you which parameter is missing. In the following image, it is the path parameter that was not supplied.
The Get-UnusedGroups function accepts a single parameter, the SearchBase. It uses the Get-ADGroup cmdlet to search for all groups. The members property is not returned by default, nor is the isCriticalSystemObject property; therefore, these properties are requested in the command. The Where-Object cmdlet is used to filter out the resulting groups. The Where-Object cmdlet looks for groups that do not have any members and are not critical system objects. In addition, it skips the exchange security groups and any group that has the letters DNS in the distinguishedName. The Get-UnusedGroups function is only called if the SearchBase parameter is supplied:
When the Get-UnusedGroups function is called, the Format-Output function is automatically called. This is not due to the way the Get-UnusedGroups function is written, but due to the way the function is called in the script. This is shown here:
The Format-Output function uses the $input automatic variable to accept the pipeline results from the Get-UnusedGroups function. The list of groups is first sorted by groupscope, and then a table is produced. The Format-Output function is shown here:
The resulting output from the Format-Output function is seen in the following image.
When the Remove-UnusedGroups function is called, it accepts the input from the Get-UnusedGroups function and pipes the groups to the Remove-ADGroup cmdlet. Unfortunately, the Remove-ADGroup cmdlet does not have a –force switch, so it will prompt you when you wish to delete a group. This is shown in the following image. Fortunately, you can tell it to remove all, but this behavior prevents an unattended operation.
EM, that is all there is to using Active Directory cmdlets to delete unused groups in Active Directory. This also concludes Active Directory Week. Join us tomorrow for Quick-Hits Friday.
We would love for you to follow us on Twitter and Facebook. If you have any questions, send email to us at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson and Craig Liebendorfer, Scripting Guys
I'm trying to clean up AD, the distributions list right not, do you have a script that will tell when a distribution list was last used and by who?
That's a cool code.
But doesn't work on domains that do not run win 2008 r2.
here is my function:
http://poshcode.org/2023
That code seems cool for sure but what about this one liner ;-) ?
adfind -f "&(objectclass=group)(!member=*)" -dsq|admod -unsafe -rm
Joe rules !!
...one attribute that contains a date/time stamp which shows the "last time a group was queried", and one attribute which shows "what queried that group", this would help significantly in removing old groups from large AD deployments that aren't well documented...please please please get the Directory Services team to consider :)
Hi Guys,
I am really a Fanatic of powershell, and also thank you for the additional knowledge that you posted here and its very useful..but one thing that i did not find in any site on how to remove global security group in an AD user using powershell v2. someone can help me please :)..i really need it in my daily task as an IT systems.
thank you in advance
Gerry
This Powershell command will give me a listing of all the security groups in my AD:
Get-ADGroup -filter {GroupCategory -eq "Security"}
How do I modify the command to give me a list of all groups created or changed before a certain date? I've opened the properties of the group in ADSIEDIT.msc and I can see these two fields:
whenChanged
whenCreated
Before you just delete the "empty groups", make sure you're checking for primaryGroupID (e.g. 'Domain Users')
This is good if no one is in the groups not a real world example though, The real question would be how to tell if the group memebership has been not used. Expiring the groups is not really a good way to accomplish this. More administrative effort if you have thousands of distro /user/ computer groups.