I was taked with trying to reduce the amount of groups in a customers Active Directory. There are multiple factors that can help, if the company does it often, have good naming convension, removes groups when the thing they are used for is removed. But more often than not that is missed. First we need to decide what is an unused group? Any group we aren’t using, so all without any member. The problem is that a group might be newly created. So should be just delete any group that is old and without members, what if we used it yesterday.

So lets agree with these frames:

  • Group should not contain any members
  • The group should have been empty for atleast a year
  • The group should not be one of the default ones. With default I am excluding the following containers:
    • CN=Users
    • CN=Built-in
  • The RID of the SID is or higher than 1000.

So lets start with getting all groups and the information we require:

$DomainController = Get-ADDomainController
Get-ADGroup -Ldapfilter '(!member=*)' -Properties 'member' -PipelineVariable Group| `
  Select-Object DistinguishedName, Name, SID, @{l='Member count';e={([array]$group.member).count}}, `
  @{l='Last member change';e={(Get-ADReplicationAttributeMetadata -Object $Group.distinguishedname -Properties 'member' -Server $DomainController).LastOriginatingChangeTime}}
But this was all groups with the amount of members and when they last changed. Lets follow the above requirements:
$DomainController = Get-ADDomainController
$Groups = Get-ADGroup -Ldapfilter '(!member=*)' -Properties 'member' -PipelineVariable Group| `
  Select-Object DistinguishedName, Name, SID, @{l='Member count';e={([array]$group.member).count}}, `
  @{l='Last member change';e={(Get-ADReplicationAttributeMetadata -Object $Group.distinguishedname -Properties 'member' -Server $DomainController).LastOriginatingChangeTime}}
$DomainDN = (Get-ADDomain).DistinguishedName
$Groups | Where-Object {$_.'Member Count' -eq 0 -and ($_.'last member change' -le (Get-Date).AddYears(-1) -or $_.'last member change' -eq $Null) -and `
  $_.Distinguishedname -notlike "*CN=Users,$($DomainDN)" -and $_.Distinguishedname -notlike "*CN=BuiltIn,$($DomainDN)" -and `
  $_.SID -notmatch '.*-[0-9]{3}$'}

So what should we do with this information. Well that is something for you to consider. Also in a really old environment I would build on this. I would take the groups to be removed, check all groups that contains just them as members and there has been no changed for a while. Repeat until diminishing returns.