Created
February 1, 2019 20:19
-
-
Save evetsleep/093c9e93919ce8645ff6a44c4acbf1bf to your computer and use it in GitHub Desktop.
Dynamic group update suggestions: https://www.reddit.com/r/PowerShell/comments/aly6d9/dynamic_groups_based_on_attribute/
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
[CmdletBinding(SupportsShouldProcess)]Param() | |
#region Collect user and group information | |
<# | |
Use a custom object to store what the group membership SHOULD look like. | |
We'll use this later to do a comparison with the current group members | |
to see who we need to add\remove. | |
#> | |
$correctGroupState = [PSCustomObject]@{ | |
Administrative = New-Object -TypeName System.Collections.Generic.List[String] | |
Advertising = New-Object -TypeName System.Collections.Generic.List[String] | |
} | |
# 1st AD query | |
# Since we only care about people who have a certain segment, we are specific with the AD query. | |
Get-ADUser -Filter "segment -like 'T90' -or segment -like 'A14' -or segment -like 'B19'" -Properties segment | ForEach-Object { | |
# Pulling the entry out is necessary because we're using switch. Placeholder variables get funky if you don't. | |
$userEntry = $PSItem | |
switch -Regex ($userEntry.segment) { | |
'^T90$' { | |
$correctGroupState.Administrative.Add($userEntry.distinguishedname) | |
} | |
'^A14$|^B19$' { | |
$correctGroupState.Advertising.Add($userEntry.distinguishedname) | |
} | |
# If we got here the users segment didn't match and we do nothing. | |
default {} | |
} | |
} | |
#endregion | |
#region Examine groups for membership changes | |
<# | |
First we create a hash table where the key is the group name and the value is a custom PSObject | |
which contains the group name, and if there are any members to add or remove. This is helpful | |
to see what it WOULD have done when we're debugging and using -WhatIf. | |
#> | |
$groupNames = 'Administrative Staff','Advertising' | |
$groupUpdates = @{} | |
$groupNames | ForEach-Object { | |
$groupObject = [PSCustomObject]@{ | |
GroupName = $PSItem | |
ToAdd = New-Object -TypeName System.Collections.Generic.List[String] | |
ToRemove = New-Object -TypeName System.Collections.Generic.List[String] | |
} | |
$groupUpdates.Add($PSItem,$groupObject) | |
} | |
<# | |
Go through each group and get it from Active Directory. Well use the object | |
that is returned to determine if we need to add or remove any members. Since | |
we are storing the membership as distinguishedNames in $correctGroupState and | |
the membership in AD is stored as DN's this means we don't need to query AD | |
multiple times to do comparisons. We just compare DN's. | |
#> | |
$groupNames | ForEach-Object { | |
# 2nd (and 3rd) AD query | |
$groupData = Get-ADGroup -Identity $PSItem -Properties member | |
$groupName = $groupData.Name | |
<# | |
First we need to see if we need to add anyone. We use a switch statement | |
in place of a whole bunch of if statements. Not critical, but a design choice. In each | |
switch block, if the group name matches, we go through each member of who SHOULD | |
be a member of that group and if they're not we add them to the ToAdd queue. | |
#> | |
switch ($groupName) { | |
'Administrative Staff' { | |
$correctGroupState.Administrative | ForEach-Object { | |
if ($groupData.member -notcontains $PSItem) { | |
$groupUpdates[$groupName].ToAdd.Add($PSItem) | |
} | |
} | |
} | |
'Advertising' { | |
$correctGroupState.Advertising | ForEach-Object { | |
if ($groupData.member -notcontains $PSItem) { | |
$groupUpdates[$groupName].ToAdd.Add($PSItem) | |
} | |
} | |
} | |
default {} | |
} | |
<# | |
Now we do the reverse. Go through each group and, based on a match to its name in the | |
switch statement, we go through each existing member to see if they are supposed to be | |
in the group. If not, then we add them to the ToRemove queue. | |
#> | |
$groupData.member | ForEach-Object { | |
$member = $PSItem | |
switch ($groupName) { | |
'Administrative Staff' { | |
if ($correctGroupState.Administrative -notcontains $member) { | |
$groupUpdates[$groupName].ToRemove.Add($member) | |
} | |
} | |
'Advertising' { | |
if ($correctGroupState.Advertising -notcontains $member) { | |
$groupUpdates[$groupName].ToRemove.Add($member) | |
} | |
} | |
default { | |
} | |
} | |
} | |
} | |
#endregion | |
#region Perform the updates, if necessary. | |
# Possibly 4th\5th AD transation, but ONLY if there are updates to be made. | |
foreach ($group in $groupUpdates.Keys) { | |
Write-Debug ('Processing {0}' -f $group) | |
if ($groupUpdates[$group].ToAdd.Count -gt 0 -and $PSCmdlet.ShouldProcess( ('ADD {0} members from {1}' -f $groupUpdates[$group].ToAdd.Count,$group) ) ) { | |
$groupUpdates[$group].ToAdd | ForEach-Object { Write-Verbose -Message ('Adding {0} to {1}' -f $PSItem,$group) } | |
Add-ADGroupMember -Identity $group -Members $groupUpdates[$group].ToAdd | |
} | |
if ($groupUpdates[$group].ToRemove.Count -gt 0 -and $PSCmdlet.ShouldProcess( ('REMOVE {0} members from {1}' -f $groupUpdates[$group].ToRemove.Count,$group) ) ) { | |
$groupUpdates[$group].ToRemove | ForEach-Object { Write-Verbose -Message ('Removing {0} from {1}' -f $PSItem,$group) } | |
Remove-ADGroupMember -Identity $group -Members $groupUpdates[$group].ToRemove -Confirm:$false | |
} | |
} | |
#endregion |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment