Skip to content

Instantly share code, notes, and snippets.

@r0ckbeard
Last active August 20, 2025 12:02
Show Gist options
  • Select an option

  • Save r0ckbeard/f5ff2c28a01a18a000d8bb927a8a3051 to your computer and use it in GitHub Desktop.

Select an option

Save r0ckbeard/f5ff2c28a01a18a000d8bb927a8a3051 to your computer and use it in GitHub Desktop.
<#
.SYNOPSIS
Transfers members from one Active Directory group to another.
.DESCRIPTION
This script transfers a specified number of members from one Active Directory group to another.
It can be used to stage the move of users from one system to another, facilitating gradual migrations
or testing of new environments with a subset of users.
The script performs the following actions:
1. Transfers specified number of members from a source group to a destination group.
2. Verifies the successful addition of members to the destination group.
3. If verification is successful, removes the transferred members from the source group.
.PARAMETER SourceGroup
The name of the AD group from which members will be transferred.
.PARAMETER DestinationGroup
The name of the AD group to which members will be added.
.PARAMETER NumberOfMembers
The number of members to transfer between groups.
.EXAMPLE
.\TransferADGroupMembers.ps1 -SourceGroup "OldGroup" -DestinationGroup "NewGroup" -NumberOfMembers 5
.NOTES
If run without parameters, the script will use the default values defined in the variables section.
#>
param(
[string]$SourceGroup,
[string]$DestinationGroup,
[int]$NumberOfMembers
)
# Import the Active Directory module
Import-Module ActiveDirectory
# Default variables (used if parameters are not provided)
$DefaultSourceGroup = "OldGroup"
$DefaultDestinationGroup = "NewGroup"
$DefaultNumberOfMembers = 5
# Logging setup
$LogPath = "C:\Windows\Logs\Software"
$LogFile = Join-Path -Path $LogPath -ChildPath "ADGroupTransfer_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
# Create log directory if it doesn't exist
if (-not (Test-Path -Path $LogPath)) {
New-Item -ItemType Directory -Path $LogPath -Force
}
# Logging function
function Write-Log {
param(
[string]$Message,
[string]$Color = "White"
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logMessage = "[$timestamp] $Message"
Add-Content -Path $LogFile -Value $logMessage
Write-Host $logMessage -ForegroundColor $Color
}
# Use parameters if provided, otherwise use default variables
$SourceGroup = if ($SourceGroup) { $SourceGroup } else { $DefaultSourceGroup }
$DestinationGroup = if ($DestinationGroup) { $DestinationGroup } else { $DefaultDestinationGroup }
$NumberOfMembers = if ($NumberOfMembers) { $NumberOfMembers } else { $DefaultNumberOfMembers }
Write-Log "Starting group member transfer operation"
Write-Log "Source Group: $SourceGroup"
Write-Log "Destination Group: $DestinationGroup"
Write-Log "Number of Members: $NumberOfMembers"
# Get all members from the source group and randomly select the specified number
$allMembers = Get-ADGroupMember -Identity $SourceGroup
if ($allMembers.Count -lt $NumberOfMembers) {
Write-Log "Warning: Source group only has $($allMembers.Count) members, selecting all available members" -Color Yellow
$members = $allMembers
} else {
$members = $allMembers | Get-Random -Count $NumberOfMembers
}
Write-Log "Adding $($members.Count) randomly selected members to $DestinationGroup" -Color Cyan
# Add members to the destination group
$addedCount = 0
foreach ($member in $members) {
try {
Add-ADGroupMember -Identity $DestinationGroup -Members $member.DistinguishedName
$addedCount++
}
catch {
Write-Log "Failed to add $($member.Name): $($_.Exception.Message)"
}
}
Write-Log "Successfully added $addedCount of $($members.Count) members to $DestinationGroup" -Color Green
# Verify membership in the new group
Write-Log "Verifying membership..." -Color Cyan
$verifiedCount = 0
$verifiedMembers = @()
$failedMembers = @()
# Get destination group membership once for performance
$destinationMembers = Get-ADGroupMember -Identity $DestinationGroup | Select-Object DistinguishedName
$destinationDNs = $destinationMembers.DistinguishedName
foreach ($member in $members) {
if ($destinationDNs -contains $member.DistinguishedName) {
$verifiedCount++
$verifiedMembers += $member
} else {
$failedMembers += $member
Write-Log "Failed to verify $($member.Name) in $DestinationGroup"
}
}
Write-Log "Verified $verifiedCount of $($members.Count) members in $DestinationGroup" -Color Green
if ($failedMembers.Count -gt 0) {
Write-Log "Failed to verify the following $($failedMembers.Count) member(s):" -Color Yellow
foreach ($failedMember in $failedMembers) {
Write-Log " - $($failedMember.Name)" -Color Yellow
}
}
if ($verifiedMembers.Count -gt 0) {
Write-Log "Removing $($verifiedMembers.Count) verified members from source group"
# Remove only the successfully verified members from the source group
$removedCount = 0
foreach ($member in $verifiedMembers) {
try {
Remove-ADGroupMember -Identity $SourceGroup -Members $member.DistinguishedName -Confirm:$false
$removedCount++
}
catch {
Write-Log "Failed to remove $($member.Name): $($_.Exception.Message)"
}
}
Write-Log "Successfully removed $removedCount of $($verifiedMembers.Count) members from source group" -Color Green
if ($failedMembers.Count -gt 0) {
Write-Log "Note: $($failedMembers.Count) members were NOT removed from source group due to verification failure"
}
Write-Log "Transfer operation completed" -Color Green
} else {
Write-Log "No members were successfully verified. No members removed from source group"
}
# ==============================================
# CSV Export (Uncomment the section you want)
# ==============================================
# Option 1: Detailed CSV - One row per transferred member
<#
if ($verifiedMembers.Count -gt 0) {
$csvPath = Join-Path -Path $LogPath -ChildPath "ADGroupTransfer_Details_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
$csvData = @()
foreach ($member in $verifiedMembers) {
$csvData += [PSCustomObject]@{
MemberName = $member.Name
MemberType = $member.ObjectClass
SourceGroup = $SourceGroup
DestinationGroup = $DestinationGroup
TransferDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
}
$csvData | Export-Csv -Path $csvPath -NoTypeInformation
Write-Log "Detailed transfer results exported to: $csvPath" -Color Green
}
#>
# Option 2: Summary CSV - One row per transfer operation
<#
$csvPath = Join-Path -Path $LogPath -ChildPath "ADGroupTransfer_Summary_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
$summaryData = [PSCustomObject]@{
SourceGroup = $SourceGroup
DestinationGroup = $DestinationGroup
MembersRequested = $NumberOfMembers
MembersAdded = $addedCount
MembersVerified = $verifiedCount
MembersRemoved = $removedCount
FailedVerifications = $failedMembers.Count
TransferDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
$summaryData | Export-Csv -Path $csvPath -NoTypeInformation
Write-Log "Summary transfer results exported to: $csvPath" -Color Green
#>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment