?
Solved

Synchronize/Mirror AD Security Group Membership Script

Posted on 2016-10-17
8
Medium Priority
?
103 Views
Last Modified: 2016-11-04
I need to synchronize group membership for several AD security groups.  I was able to put together a script that will add any new group members from the source security group to the destination security group, however, I also need to be able to have the script remove members from the destination security group, when they are removed from the source.  How can I modify this script to achieve this?

$source = Get-ADGroupMember -Identity SourceADGroup
foreach ($user in $source) { 
    Add-ADGroupMember -Identity DestADGroup -Members $user.distinguishedname 
}

Open in new window


Thank you in advance.
0
Comment
Question by:fireguy1125
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 4
8 Comments
 
LVL 85

Expert Comment

by:oBdA
ID: 41847986
Copy-ADGroupMember.ps1; -WhatIf is supported.
Will by default only copy; use -Mirror to remove members from destination that aren't in the source anymore.
Use -Confirm:$False to suppress the confirmation prompt (or change ConfirmImpact to 'None' if you never want to be asked)
[CmdletBinding(SupportsShouldProcess=$True, ConfirmImpact='High')]
Param(
	[PSObject]$Identity,
	[PSObject]$Destination,
	[Switch]$Mirror
)
$SourceMembers = Get-ADGroupMember -Identity $Identity | Select-Object -ExpandProperty distinguishedName
$TargetMembers = Get-ADGroupMember -Identity $Destination | Select-Object -ExpandProperty distinguishedName
$DifferenceMembers = Compare-Object -ReferenceObject $SourceMembers -DifferenceObject $TargetMembers
$ProcessMembers = $DifferenceMembers | Where-Object {$_.SideIndicator -eq '<='} | Select-Object -ExpandProperty InputObject
If ($PSCmdlet.ShouldProcess($Destination, "Add objects:`r`n`t$($ProcessMembers -join "`r`n`t")`r`n")) {
	Add-ADGroupMember -Identity $Destination -Members $ProcessMembers
}
If ($Mirror) {
	$ProcessMembers = $DifferenceMembers | Where-Object {$_.SideIndicator -eq '=>'} | Select-Object -ExpandProperty InputObject
	If ($PSCmdlet.ShouldProcess($Destination, "Remove objects:`r`n`t$($ProcessMembers -join "`r`n`t")`r`n")) {
		Remove-ADGroupMember -Identity $Destination -Members $ProcessMembers
	}
}

Open in new window

0
 
LVL 1

Author Comment

by:fireguy1125
ID: 41849320
Thank you, where would I specify the source and destination group? Am I just replacing [PSObject] in lines 3 and 4?
0
 
LVL 85

Expert Comment

by:oBdA
ID: 41849507
No. It's a complete script accepting arguments.
.\Copy-ADGroupMember.ps1 -Identity SourceADGroup -Destination DestADGroup -Mirror -WhatIf

If you really want to hard code that, make a function out of it and call the function:
Function Copy-ADGroupMember {
[CmdletBinding(SupportsShouldProcess=$True, ConfirmImpact='High')]
Param(
	[Parameter(ValueFromPipeline=$True, Position=0)]
	[PSObject]$Identity,
	[PSObject]$Destination,
	[Switch]$Mirror
)
	$SourceMembers = Get-ADGroupMember -Identity $Identity | Select-Object -ExpandProperty distinguishedName
	$TargetMembers = Get-ADGroupMember -Identity $Destination | Select-Object -ExpandProperty distinguishedName
	$DifferenceMembers = Compare-Object -ReferenceObject $SourceMembers -DifferenceObject $TargetMembers
	$ProcessMembers = $DifferenceMembers | Where-Object {$_.SideIndicator -eq '<='} | Select-Object -ExpandProperty InputObject
	If ($PSCmdlet.ShouldProcess($Destination, "Add objects:`r`n`t$($ProcessMembers -join "`r`n`t")`r`n")) {
		Add-ADGroupMember -Identity $Destination -Members $ProcessMembers
	}
	If ($Mirror) {
		$ProcessMembers = $DifferenceMembers | Where-Object {$_.SideIndicator -eq '=>'} | Select-Object -ExpandProperty InputObject
		If ($PSCmdlet.ShouldProcess($Destination, "Remove objects:`r`n`t$($ProcessMembers -join "`r`n`t")`r`n")) {
			Remove-ADGroupMember -Identity $Destination -Members $ProcessMembers
		}
	}
}

.\Copy-ADGroupMember.ps1 -Identity SourceADGroup -Destination DestADGroup -Mirror -WhatIf

Open in new window

0
NFR key for Veeam Agent for Linux

Veeam is happy to provide a free NFR license for one year.  It allows for the non‑production use and valid for five workstations and two servers. Veeam Agent for Linux is a simple backup tool for your Linux installations, both on‑premises and in the public cloud.

 
LVL 1

Author Comment

by:fireguy1125
ID: 41858773
Thank you. The script seems to work, but throws errors.  Can these be suppressed?

PS C:\scripts> .\Copy-ADGroupMember.ps1 -Identity SourceADGroup -Destination DestDL -Mirror -Confirm:$False
Remove-ADGroupMember : Cannot validate argument on parameter 'Members'. The argument is null or empty. Provide an
argument that is not null or empty, and then try the command again.
At C:\scripts\Copy-ADGroupMember.ps1:17 char:56
+ ...  Remove-ADGroupMember -Identity $Destination -Members $ProcessMembers
+                                                           ~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Remove-ADGroupMember], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.ActiveDirectory.Management.Commands.RemoveADG
   roupMember

PS C:\scripts> .\Copy-ADGroupMember.ps1 -Identity SourceADGroup -Destination DestDL -Mirror -Confirm:$False
Add-ADGroupMember : Cannot validate argument on parameter 'Members'. The argument is null or empty. Provide an
argument that is not null or empty, and then try the command again.
At C:\scripts\Copy-ADGroupMember.ps1:12 char:52
+     Add-ADGroupMember -Identity $Destination -Members $ProcessMembers
+                                                       ~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Add-ADGroupMember], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.ActiveDirectory.Management.Commands.AddADGrou
   pMember

PS C:\scripts>
0
 
LVL 85

Expert Comment

by:oBdA
ID: 41858876
[CmdletBinding(SupportsShouldProcess=$True, ConfirmImpact='High')]
Param(
	[PSObject]$Identity,
	[PSObject]$Destination,
	[Switch]$Mirror
)
$SourceMembers = Get-ADGroupMember -Identity $Identity | Select-Object -ExpandProperty distinguishedName
$TargetMembers = Get-ADGroupMember -Identity $Destination | Select-Object -ExpandProperty distinguishedName
$DifferenceMembers = Compare-Object -ReferenceObject $SourceMembers -DifferenceObject $TargetMembers
$ProcessMembers = $DifferenceMembers | Where-Object {$_.SideIndicator -eq '<='} | Select-Object -ExpandProperty InputObject
If ($ProcessMembers -and $PSCmdlet.ShouldProcess($Destination, "Add objects:`r`n`t$($ProcessMembers -join "`r`n`t")`r`n")) {
	Add-ADGroupMember -Identity $Destination -Members $ProcessMembers
}
If ($Mirror) {
	$ProcessMembers = $DifferenceMembers | Where-Object {$_.SideIndicator -eq '=>'} | Select-Object -ExpandProperty InputObject
	If ($ProcessMembers -and $PSCmdlet.ShouldProcess($Destination, "Remove objects:`r`n`t$($ProcessMembers -join "`r`n`t")`r`n")) {
		Remove-ADGroupMember -Identity $Destination -Members $ProcessMembers
	}
}

Open in new window

0
 
LVL 1

Author Comment

by:fireguy1125
ID: 41864371
Thanks, that resolved those errors.  However I just tested the script, and find that when the destination group has no members yet, it fails to populate with members in the source group.  It works when the destination group contains at least one member.  here is the error I get:

PS C:\scripts> .\Copy-ADGroupMember.ps1 -Identity SourceADGroup -Destination DestDL -Mirror -Confirm:$False
Compare-Object : Cannot bind argument to parameter 'DifferenceObject' because it is null.
At C:\scripts\Copy-ADGroupMember.ps1:13 char:87
+ ... ject -ReferenceObject $SourceMembers -DifferenceObject $TargetMembers
+                                                            ~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Compare-Object], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.CompareObje
   ctCommand

Open in new window



Please note that the line 13 referenced in the error is
$DifferenceMembers = Compare-Object -ReferenceObject $SourceMembers -DifferenceObject $TargetMembers

Open in new window

(I added 4 commented lines to the top of your script when I ran, so for your original script line error would likely be line 9) Hope this didn't confuse you!
0
 
LVL 85

Accepted Solution

by:
oBdA earned 2000 total points
ID: 41869782
[CmdletBinding(SupportsShouldProcess=$True, ConfirmImpact='High')]
Param(
	[Parameter(ValueFromPipeline=$True, Position=0)]
	[PSObject]$Identity,
	[PSObject]$Destination,
	[Switch]$Mirror
)
Try {$SourceMembers = @(Get-ADGroupMember -Identity $Identity -ErrorAction Stop | Select-Object -ExpandProperty distinguishedName)} Catch {Throw $_}
Try {$TargetMembers = @(Get-ADGroupMember -Identity $Destination -ErrorAction Stop | Select-Object -ExpandProperty distinguishedName)} Catch {Throw $_}
If ($DifferenceMembers = Compare-Object -ReferenceObject $SourceMembers -DifferenceObject $TargetMembers) {
	$ProcessMembers = $DifferenceMembers | Where-Object {$_.SideIndicator -eq '<='} | Select-Object -ExpandProperty InputObject
	If ($ProcessMembers -and $PSCmdlet.ShouldProcess($Destination, "Add objects:`r`n`t$($ProcessMembers -join "`r`n`t")`r`n")) {
		Add-ADGroupMember -Identity $Destination -Members $ProcessMembers
	}
	If ($Mirror) {
		$ProcessMembers = $DifferenceMembers | Where-Object {$_.SideIndicator -eq '=>'} | Select-Object -ExpandProperty InputObject
		If ($ProcessMembers -and $PSCmdlet.ShouldProcess($Destination, "Remove objects:`r`n`t$($ProcessMembers -join "`r`n`t")`r`n")) {
			Remove-ADGroupMember -Identity $Destination -Members $ProcessMembers
		}
	}
} Else {
	Write-Verbose -Message "'$($Identity)' --> '$($Destination)': No differences found."
}

Open in new window

0
 
LVL 1

Author Closing Comment

by:fireguy1125
ID: 41874605
Works beautifully, thank you!
0

Featured Post

What is SQL Server and how does it work?

The purpose of this paper is to provide you background on SQL Server. It’s your self-study guide for learning fundamentals. It includes both the history of SQL and its technical basics. Concepts and definitions will form the solid foundation of your future DBA expertise.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

My attempt to use PowerShell and other great resources found online to simplify the deployment of Office 365 ProPlus client components to any workstation that needs it, regardless of existing Office components that may be needing attention.
Compliance and data security require steps be taken to prevent unauthorized users from copying data.  Here's one method to prevent data theft via USB drives (and writable optical media).
This tutorial will walk an individual through the process of configuring their Windows Server 2012 domain controller to synchronize its time with a trusted, external resource. Use Google, Bing, or other preferred search engine to locate trusted NTP …
There are cases when e.g. an IT administrator wants to have full access and view into selected mailboxes on Exchange server, directly from his own email account in Outlook or Outlook Web Access. This proves useful when for example administrator want…
Suggested Courses

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question