Forum Discussion
Script to balance AD group membership.
The Security team is using Windows Event Forwarding (WEF) to collect logs from Windows workstations. Using Windows Event Collectors (WEC) pointed to AD groups or computers objects. But there is a maximum number of computers that each WEC can handle. So computers have been manually divided between several AD groups.
This is a huge maintenance headache for both the help desk who adds them initially, and the security team that is ultimately responsible for it. Especially during the constant workstation replacement projects.
They have requested a script that they can run (Probably nightly with Task Scheduler) to manage this, but I've been having a hard time finding the logic required to pull it off. Basically it needs to take an inventory of several groups, and then distribute the members across those groups evenly (as much as possible).
The most relevant script sample I could find was of all things ,this script that Google "AI search" spit out. It was a complete mess of context errors but it gave me somewhere to start. It now works up to the point of the "$MembersToAdd" line. Meaning it appears to hash the members of the groups, remove the right amount from the larger groups, but then it "Cannot validate argument on parameter 'Members'. The argument is null or empty." when trying to add the excessive computers to the other groups.
Anyone tried anything like this before, or have an idea of what I can do to fix the end of this script?
# Import the Active Directory module
Import-Module ActiveDirectory
# Get all of the AD groups
$groups = Get-ADGroup -Filter { Name -like "TEST-ADGroupMembershipBalance*" } -Properties *
# Create a hashtable to store the group membership counts
$groupMembershipCounts = @{}
# Iterate through each group and get the membership count
foreach ($group in $groups) {
$groupMembershipCounts[$group.Name] = $group.Members.Count
}
# Get the average group membership count
$averageGroupMembershipCount = $groupMembershipCounts.Values | Measure-Object -Average | Select-Object -ExpandProperty Average
# Iterate through each group and add or remove members to balance the group membership counts
foreach ($group in $groups) {
Write-Host $group $group.Members.Count
if ($groupMembershipCounts[$group.Name] -gt $averageGroupMembershipCount) {
# Remove members from the group
$membersToRemove = $group.Members | Sort-Object -Descending | Select-Object -First ($groupMembershipCounts[$group.Name] - $averageGroupMembershipCount)
Remove-ADGroupMember -Identity $group -Members $membersToRemove
} elseif ($groupMembershipCounts[$group.Name] -lt $averageGroupMembershipCount) {
# Add members to the group
$membersToAdd = Get-ADComputer -Filter * -SearchBase $group.DistinguishedName | Sort-Object {Get-Random} | Select-Object -First ($averageGroupMembershipCount - $groupMembershipCounts[$group.Name])
Add-ADGroupMember -Identity $group -Members $membersToAdd
}
}
- kyazaferrIron Contributor
# Define the AD groups
$groupNames = @(
"Group1", # Replace with actual group names
"Group2",
"Group3"
# Add more groups as needed
)# Function to get members of each group
function Get-GroupMembers {
param (
[string]$groupName
)
return Get-ADGroupMember -Identity $groupName | Where-Object { $_.ObjectClass -eq "user" }
}# Function to distribute members
function Distribute-Members {
param (
[array]$groupNames
)# Create an array to store members and group sizes
$groupMembers = @{}
$totalMembers = 0# Get the current members of each group
foreach ($groupName in $groupNames) {
$members = Get-GroupMembers -groupName $groupName
$groupMembers[$groupName] = $members
$totalMembers += $members.Count
}# Calculate the target number of members per group
$numGroups = $groupNames.Count
$targetMembersPerGroup = [math]::Ceiling($totalMembers / $numGroups)# Create an array to store the excess members to redistribute
$excessMembers = @()# Loop through each group and identify excess members
foreach ($groupName in $groupNames) {
$currentGroupSize = $groupMembers[$groupName].Count
if ($currentGroupSize > $targetMembersPerGroup) {
$excessMembers += $groupMembers[$groupName] | Select-Object -First ($currentGroupSize - $targetMembersPerGroup)
$groupMembers[$groupName] = $groupMembers[$groupName] | Select-Object -Skip ($currentGroupSize - $targetMembersPerGroup)
}
}# Now redistribute the excess members to the groups with fewer members
foreach ($groupName in $groupNames) {
$currentGroupSize = $groupMembers[$groupName].Count
while ($currentGroupSize < $targetMembersPerGroup -and $excessMembers.Count -gt 0) {
$groupMembers[$groupName] += $excessMembers[0]
$excessMembers = $excessMembers[1..($excessMembers.Count - 1)]
$currentGroupSize++
}
}# Add the redistributed members back to the AD groups
foreach ($groupName in $groupNames) {
$membersToAdd = $groupMembers[$groupName]
$existingMembers = Get-GroupMembers -groupName $groupName# Filter out existing members to avoid duplicates
$membersToAdd = $membersToAdd | Where-Object { $_.DistinguishedName -notin $existingMembers.DistinguishedName }if ($membersToAdd.Count -gt 0) {
# Add the members to the group
Add-ADGroupMember -Identity $groupName -Members $membersToAdd
Write-Host "Added $($membersToAdd.Count) members to $groupName"
}
}
}# Execute the function to redistribute the members
Distribute-Members -groupNames $groupNames