
The definition of a shadowgroup is simply the synchronization of members in an Active Directory OU to the memberships of an Active Directory Group.
Thanks goes to David K. Sutton for his post at ravingroo.com.
One caveat of his concise script was that Get-ADGroupMember, by default, has a limit of 5000 objects returned. Other internet sources reported an easy workaround by using the
member
property of the get-adgroup
cmdlet.So I present to you modified versions of a concise ShadowGroup powershell script. One no-frills version, and another with email support.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$OU="OU=TheOUName,DC=yourdomain,DC=com" | |
$ShadowGroup="CN=ShadowGroupName,OU=TheOUName,DC=yourdomain,DC=com" | |
Import-Module ActiveDirectory | |
(Get-ADGroup -Identity $ShadowGroup -properties members).Members | Get-ADUser | Where-Object {$_.distinguishedName –NotMatch $OU} | ForEach-Object {Remove-ADPrincipalGroupMembership –Identity $_ –MemberOf $ShadowGroup –Confirm:$false} | |
Get-ADUser –SearchBase $OU –SearchScope OneLevel –LDAPFilter "(!memberOf=$ShadowGroup)" | ForEach-Object {Add-ADPrincipalGroupMembership –Identity $_ –MemberOf $ShadowGroup} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$OU="OU=TheOUName,DC=yourdomain,DC=com" | |
$ShadowGroup="CN=ShadowGroupName,OU=TheOUName,DC=yourdomain,DC=com" | |
$WhatIf=$true #set $true for testing and $false for action | |
Import-Module ActiveDirectory | |
Write-Host "Removing non-existent members" | |
#$RemoveMembers = (Get-ADGroupMember -Identity $ShadowGroup | Where-Object {$_.distinguishedName -NotMatch $OU}) #Fails for more than 5000 | |
$RemoveMembers = ((Get-ADGroup -Identity $ShadowGroup -properties members).Members | Get-ADUser | Where-Object {$_.distinguishedName -NotMatch $OU}) #workaround for 5000 limit | |
$RemoveMembers | ForEach-Object {Write-Host -NoNewline $_.SamAccountName ": " ; Remove-ADPrincipalGroupMembership -Identity $_ -MemberOf $ShadowGroup -Confirm:$false -WhatIf:$WhatIf -Verbose} | |
Write-Host "Adding members" | |
$AddMembers=(Get-ADUser -SearchBase $OU -SearchScope OneLevel -LDAPFilter "(!memberOf=$ShadowGroup)") | |
$AddMembers | ForEach-Object {Write-Host -NoNewline $_.SamAccountName ": " ; Add-ADPrincipalGroupMembership -Identity $_ -MemberOf $ShadowGroup -WhatIf:$WhatIf -Verbose} | |
# Emailing | |
if ($RemoveMembers -or $AddMembers) { | |
$adminEmailAddr="admin1@yourdomain.com","admin2@yourdomain.com" | |
$smtpServer="mailserver.yourdomain.com" | |
$from = "$env:COMPUTERNAME <noreply@yourdomain.com>" | |
$subject = "Automated script: ShadowGroup: "+($ShadowGroup -split ',*..=')[1] | |
$body="The following shadows applied between: <br>" | |
$body+="OU: $OU <br>" | |
$body+="Group: $ShadowGroup <br><br>" | |
if ($WhatIf) { $body+="TESTING ONLY<br><br>"} | |
foreach ($rm in $RemoveMembers) { | |
$sName=$rm.SamAccountName | |
$body+="Removed $sName <br>" | |
} | |
if ($RemoveMembers) {$body+="<br>"} | |
foreach ($am in $AddMembers) { | |
$sName=$am.SamAccountName | |
$body+="Added $sName <br>" | |
} | |
$body+="<br>" | |
Write-host "Emailing $adminEmailAddr" | |
$textEncoding = [System.Text.Encoding]::UTF8 | |
try { | |
Send-Mailmessage -smtpServer $smtpServer -from $from -to $adminEmailAddr -subject $subject -body $body -bodyasHTML -priority High -Encoding $textEncoding -ErrorAction Stop -ErrorVariable err | |
} catch { | |
write-host "Error: Failed to email $adminEmailAddr via $smtpServer" | |
} finally { | |
if ($err.Count -eq 0) { | |
write-host "Successfully emailed $adminEmailAddr" | |
} | |
} | |
} else { | |
Write-Host "Nothing to email." | |
} |