Forum Discussion
Adding AD users to a specific security group
Hi UC_451435,
Here's a more efficient script that will scale much better in large environments.
The primary reason it's more efficient is that it's minimising the number of calls to Active Directory, which are costly in nature. It's also using hash matching rather than full string comparisons on the distinguishedName, though this won't matter in smaller environments.
The basic approach is:
- Pull down the eligible members into one list (only storing the hash and the distinguishedName, since storing the whole object is wasteful and will harm performance in larger environments);
- Pull down the current list of direct and indirect members;
- Remove those no longer eligible;
- Add those who are eligible and not yet in the group.
This example is very basic and doesn't support nested group memberships in the target group, but I figured keeping it simple to start with is best.
As an aside, you don't need to check for whether or not the ActiveDirectory module is already loaded. If it is already loaded then it won't load again, while if it's not loaded, PowerShell will automatically load it on the first call to any commandlet contained within the module.
There's no harm in your current check, but there's also no benefit.
It's always been best practice to not run anything at all on domain controllers - not even (or especially) other Windows roles or services. You're better off running the script on a member server where the account under which the scheduled task runs has the minimum permissions necessary to maintain the group.
If the member server doesn't have the RSAT role feature installed, just install that one component. Don't accidentally install the full Active Directory Domain Services role, for example.
$OrganisationalUnit = "OU=Users,OU=Staff,OU=RobertsonPayne,DC=robertsonpayne,DC=com";
$Group = Get-ADObject -Identity "CN=Friendly Name,OU=Groups,OU=Staff,OU=RobertsonPayne,DC=robertsonpayne,DC=com";
# Get the list of enabled users from within and below the nominated organisational unit. We're going to store a hash code to allow for significantly faster lookups, which will scale better in larger environments.
$SourceList = [System.Collections.Generic.Dictionary[[int64], [string]]]::new();
Get-ADObject -Filter { (objectCategory -eq "person") -and (objectClass -eq "user") -and -not (userAccountControl -band 2) } -SearchBase $OrganisationalUnit | ForEach-Object {
$SourceList.Add($_.distinguishedName.GetHashCode(), $_.distinguishedName);
};
# Get the list of direct and indirect members of the nominated group, which requires a base search to access the msds-memberTransitive attribute.
$TargetList = [System.Collections.Generic.Dictionary[[int64], [string]]]::new();
(Get-ADObject -Filter { cn -like "*" } -SearchBase ($Group.distinguishedName) -SearchScope Base -Properties "msds-memberTransitive")."msds-memberTransitive" | ForEach-Object {
$TargetList.Add($_.GetHashCode(), $_);
};
# Remove non-existent or disabled entries from the group.
$Members = $TargetList.GetEnumerator() | ForEach-Object { if (-not $SourceList.ContainsKey($_.Key)) { $_.value; } };
if (0 -lt $Members.Count)
{
Remove-ADGroupMember -Members $Members;
}
# Add new entries not currently in the group (directly or indirectly).
$Members = $SourceList.GetEnumerator() | ForEach-Object { if (-not $TargetList.ContainsKey($_.Key)) { $_.value; } };
if (0 -lt $Members.Count)
{
Add-ADGroupMember -Identity $Group.ObjectGUID -Members $Members;
}
Cheers,
Lain