# DynamicGroup.ps1 # PowerShell script to ensure all users meeting specified conditions are # members of a corresponding dynamic group. Also makes sure users not meeting # the specified conditions are not members of the group. # This script can be used to maintain a dynamic group of users. # A Fine Grained Password Policy can be applied to the dynamic group and it # will apply to all users in the group. # # Copyright (c) 2019 Richard L. Mueller # Version 1.0 - March 11, 2019 # # ---------------------------------------------------------------------- # You have a royalty-free right to use, modify, reproduce, and # distribute this script file in any way you find useful, provided that # you agree that the copyright owner above has no warranty, obligations, # or liability for such use. Write-Host "Please Standby..." ###### Start of Configuration Section ###### # The values of the variables in this section should be customized for # your specific situation. # Specify the DNS name of a DC. All additions to and removals from the dynamic # group should be done on the same Domain Controller to avoid replication # problems. This must be a DC that supports the Active Directory module cmdlets. $Server = "dc0321.mydomain.com" # Specify log file. $LogFile = "c:\PowerShell\Dynamic\DynamicGroup.log" # If $Update is $False, the script only logs what it would do, without actually # updating the dynamic group. If $Update is $True, the script will update the group. $Update = $False # If $EnabledOnly is $True only enabled users are to be in the group. # If $False, all users meeting the specified conditions will be in the group. $EnabledOnly = $True # Specify the LDAP syntax filter to retrieve users to be members of the dynamic group. $Filter = "(|(title=*Engineer)(title=Accountant)(title=Manager))" # Specify the distinguished name of the corresponding dynamic group. # The group can be empty, but it must exist. $GroupDN = "cn=SalesDynamic,ou=Sales,ou=West,dc=MyDomain,dc=com" ###### End of Configuration Section ###### # Script version and date. $Version = "Version 1.0 - March 11, 2019" # Modify the LDAP filter to find users to add to the dynamic group. If ($EnabledOnly) { # Add users that are not in the group, are not disabled, and meet the specified conditions. $AddFilter = "(&(!(memberOf=$GroupDN))(!(userAccountControl:1.2.840.113556.1.4.803:=2))$Filter)" } Else { # Add users not in the group that meet the specified conditions. $AddFilter = "(&(!(memberOf=$GroupDN))$Filter)" } # Modify the LDAP filter to find users to remove from the dynamic group. If ($EnabledOnly) { # Remove users in the group if they are disabled or do not meet the specified conditions. $RemoveFilter = "(&(memberOf=$GroupDN)(|(userAccountControl:1.2.840.113556.1.4.803:=2)(!$Filter)))" } Else { # Remove users in the group if they do not meet the specified conditions. $RemoveFilter = "(&(memberOf=$GroupDN)(!$Filter))" } Try {Import-Module ActiveDirectory -ErrorAction Stop -WarningAction Stop} Catch { Write-Host "ActiveDirectory module (or DC with ADWS) not found!!" ` -ForegroundColor Red -BackgroundColor Black Write-Host "Script Aborted." -ForegroundColor Red -BackgroundColor Black # Abort the script. Break } # Check if DN of dynamic group is valid. $Y = [ADSI]"LDAP://$GroupDN" If ($Y.Name) { If ($Y.objectCategory -NotLike "CN=Group,*") { Write-Host "Error: GroupDN $GroupDN not a group, script aborted." ` -foregroundcolor red -backgroundcolor black Break } } Else { Write-Host "Error: GroupDN $GroupDN invalid, script aborted." ` -foregroundcolor red -backgroundcolor black Break } # Ensure that distinguished name is properly formated. # This will correct situations where the DN provided included # spaces after any commas, such as "CN=My Group, OU=East, dc=mydomain, dc=com". $GroupDN = $($Y.distinguishedName) # Check if the designated computer can be contacted. $Ping = Test-Connection -ComputerName $Server -Count 1 -Quiet If ($Ping -eq $False) { Write-Host "Error: Unable to connect to $Server, script aborted." ` -foregroundcolor red -backgroundcolor black Break } # Check if the computer is a DC that supports the AD modules. # Retrieve users to be removed from the dynamic group. Try { $Members = Get-ADUser -LDAPFilter $RemoveFilter ` -Server $Server | Select distinguishedName } Catch { Write-Host "Error: $Server does not support the AD modules, script aborted." ` -foregroundcolor red -backgroundcolor black Break } # The text written to the log depends on $Update. If ($Update -eq $True) { $AddText = "added" $RemText = "removed" } Else { $AddText = "would be added" $RemText = "would be removed" } # Add information to the log file. Try { Add-Content -Path $LogFile ` -Value "------------------------------------------------" -ErrorAction Stop } Catch { Write-Host "Error: Logfile $LogFile invalid or protected, script aborted." ` -foregroundcolor red -backgroundcolor black Break } Add-Content -Path $LogFile -Value "DynamicGroup.ps1 ($Version)" Add-Content -Path $LogFile -Value $("Started: " + (Get-Date).ToString()) Add-Content -Path $LogFile -Value "Log file: $LogFile" Add-Content -Path $LogFile -Value "LDAP syntax filter: $Filter" Add-Content -Path $LogFile -Value "Filter to remove users from the dynamic group:" Add-Content -Path $LogFile -Value " $RemoveFilter" Add-Content -Path $LogFile -Value "Filter to add users to the dynamic group:" Add-Content -Path $LogFile -Value " $AddFilter" Add-Content -Path $LogFile -Value "DN of the dynamic group: $GroupDN" Add-Content -Path $LogFile -Value "DC used for updates: $Server" Add-Content -Path $LogFile -Value "Only enabled users: $EnabledOnly" Add-Content -Path $LogFile -Value "Update the dynamic group: $Update" Add-Content -Path $LogFile -Value "------------------------------------------------" # Initialize counters. $Removed = 0 $Added = 0 # Flags if too many users removed from or added to the dynamic group. # A maximum of 4000 users should be removed or added at a time # to avoid excessive network traffic and long running transactions. $TooManyRemoved = $False $TooManyAdded = $False # Array of users to be added to the dynamic group. $UsersToAdd = @() # Array of users to be removed from the dynamic group. $UsersToRemove = @() # Enumerate users to be removed from the dynamic group. # $Members was retrieved above to test if the DC supports AD modules. ForEach ($Member In $Members) { $DN = $Member.distinguishedName # Add this user to the array of users # to be removed from the dynamic group. $UsersToRemove = $UsersToRemove + $DN $Removed = $Removed + 1 Add-Content -Path $LogFile ` -Value "$RemText from group: $DN" If ($Removed -gt 3999) { $TooManyRemoved = $True # Break out of the ForEach loop, but not out of the script. Break } } # Remove the users from the dynamic group. If (($Update -eq $True) -and ($Removed -gt 0)) { Remove-ADGroupMember -Identity $GroupDN -Members $UsersToRemove ` -Server $Server -Confirm:$False # Short pause. Start-Sleep -Seconds 10 } # Retrieve all users to be added to the dynamic group. $Members = Get-ADUser -LDAPFilter $AddFilter ` -Server $Server | Select distinguishedName ForEach ($Member In $Members) { $DN = $Member.distinguishedName # Add this user to the array of users to be added to the dynamic group. $UsersToAdd = $UsersToAdd + $DN $Added = $Added + 1 Add-Content -Path $LogFile -Value "$AddText to group: $DN" If ($Added -gt 3999) { $TooManyAdded = $True # Break out of the ForEach loop, but not out of the script. Break } } # Add the missing users to the dynamic group. If (($Update -eq $True) -and ($Added -gt 0)) { Add-ADGroupMember -Identity $GroupDN -Members $UsersToAdd -Server $Server } # Update the log file. Add-Content -Path $LogFile -Value "------------------------------------------------" Add-Content -Path $LogFile -Value $("Finished: " + (Get-Date).ToString()) Add-Content -Path $LogFile ` -Value "Number of users $RemText from the group: $('{0:n0}' -f $Removed)" Add-Content -Path $LogFile ` -Value "Number of users $AddText to the group: $('{0:n0}' -f $Added)" If ($TooManyRemoved -eq $True) { Add-Content -Path $LogFile -Value "Caution: 4000 users $RemText from the group." Add-Content -Path $LogFile -Value "This is the maximum allowed." Add-Content -Path $LogFile -Value "Run the script again to process more." } If ($TooManyAdded -eq $True) { Add-Content -Path $LogFile -Value "Caution: 4000 users $AddText to the group." Add-Content -Path $LogFile -Value "This is the maximum allowed." Add-Content -Path $LogFile -Value "Run the script again to process more." } Write-Host "Done. See log file: $LogFile" If ($TooManyRemoved -eq $True) { Write-Host "Caution: 4000 users $RemText from the group." ` -foregroundcolor yellow -backgroundcolor black Write-Host " Run the script again to process more." ` -foregroundcolor yellow -backgroundcolor black } Else {Write-Host "Users $RemText from the group: $Removed"} If ($TooManyAdded -eq $True) { Write-Host "Caution: 4000 users $AddedText to the group." ` -foregroundcolor yellow -backgroundcolor black Write-Host " Run the script again to process more." ` -foregroundcolor yellow -backgroundcolor black } Else {Write-Host "Users $AddText to the group: $Added"}