Add Get-EmptySecurityGroups script to list empty Active Directory security groups
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Lists all Active Directory security groups that have no members.
|
||||
|
||||
.DESCRIPTION
|
||||
Uses Active Directory cmdlets to retrieve every security group and checks
|
||||
whether each one has any members. Groups with no members are returned as
|
||||
objects (GroupName, CanonicalName, DistinguishedName). By default the whole
|
||||
domain is searched; optionally the search can be limited to one or more
|
||||
Organizational Units, searched recursively.
|
||||
|
||||
.PARAMETER SearchBase
|
||||
One or more OU distinguished names to search recursively for security
|
||||
groups. Optional - if omitted, the entire domain is searched.
|
||||
|
||||
.EXAMPLE
|
||||
.\Get-EmptySecurityGroups.ps1
|
||||
Displays all empty security groups in Active Directory.
|
||||
|
||||
.EXAMPLE
|
||||
.\Get-EmptySecurityGroups.ps1 | Export-Csv -Path .\Export_EmptySecurityGroups.csv -NoTypeInformation -Encoding UTF8
|
||||
Exports all empty security groups in Active Directory to a CSV file.
|
||||
|
||||
.EXAMPLE
|
||||
.\Get-EmptySecurityGroups.ps1 -SearchBase "OU=Sales,DC=contoso,DC=com"
|
||||
Searches recursively only within the Sales OU.
|
||||
|
||||
.EXAMPLE
|
||||
.\Get-EmptySecurityGroups.ps1 -SearchBase "OU=Sales,DC=contoso,DC=com","OU=IT,DC=contoso,DC=com"
|
||||
Searches recursively within multiple OUs.
|
||||
|
||||
.NOTES
|
||||
Author: Petr Štěpán
|
||||
Created: 2024-10-20
|
||||
Version: 1.1.0
|
||||
Changelog:
|
||||
1.1.0 - Added optional -SearchBase parameter for recursive search within specific OUs.
|
||||
1.0.0 - Initial version
|
||||
#>
|
||||
|
||||
#Requires -Modules ActiveDirectory
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Mandatory = $false, HelpMessage = 'One or more OU distinguished names to search recursively. Defaults to the entire domain.')]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string[]]
|
||||
$SearchBase
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
# Read-only script (queries Active Directory only) - no ShouldProcess needed.
|
||||
$emptyGroups = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
$counter = 0
|
||||
|
||||
if ($SearchBase) {
|
||||
$securityGroups = [System.Collections.Generic.List[object]]::new()
|
||||
$failedOuCount = 0
|
||||
foreach ($ou in $SearchBase) {
|
||||
Write-Verbose "Retrieving security groups from OU '$ou' (recursive)..."
|
||||
try {
|
||||
$ouGroups = Get-ADGroup -Filter { GroupCategory -eq 'Security' } -SearchBase $ou -SearchScope Subtree -ErrorAction Stop
|
||||
foreach ($g in $ouGroups) { $securityGroups.Add($g) }
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Skipping OU '$ou': failed to query security groups - $_"
|
||||
$failedOuCount++
|
||||
}
|
||||
}
|
||||
|
||||
if ($failedOuCount -eq $SearchBase.Count) {
|
||||
Write-Error "Failed to retrieve security groups from any of the specified OUs."
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Verbose "Retrieving all security groups from Active Directory..."
|
||||
try {
|
||||
$securityGroups = @(Get-ADGroup -Filter { GroupCategory -eq 'Security' } -ErrorAction Stop)
|
||||
}
|
||||
catch {
|
||||
Write-Error "Failed to retrieve security groups from Active Directory: $_"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($group in $securityGroups) {
|
||||
$counter++
|
||||
Write-Progress -Activity "Checking group $($group.Name)" -Status "Processing $counter of $($securityGroups.Count)" -PercentComplete (($counter / $securityGroups.Count) * 100)
|
||||
|
||||
try {
|
||||
$members = @(Get-ADGroupMember -Identity $group.DistinguishedName -ErrorAction Stop)
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Skipping group '$($group.Name)': failed to read members - $_"
|
||||
continue
|
||||
}
|
||||
|
||||
if ($members.Count -eq 0) {
|
||||
# Record the empty group as a structured object
|
||||
$emptyGroups.Add([PSCustomObject]@{
|
||||
GroupName = $group.Name
|
||||
Description = $group.Description
|
||||
DistinguishedName = $group.DistinguishedName
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Write-Progress -Activity "Checking group" -Completed
|
||||
|
||||
if ($emptyGroups.Count -gt 0) {
|
||||
$emptyGroups
|
||||
} else {
|
||||
Write-Host "No empty security groups were found."
|
||||
}
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user