This script reads two CSV files — a list of source computers and a matching list of target computers — and copies every AD group membership from each source to its corresponding target. You’d run this when migrating machines, replacing hardware, or provisioning new endpoints that need to inherit an existing computer’s group assignments without doing it manually one by one.
How It Works
File Path Validation
Before touching AD, the script checks that both hardcoded CSV paths exist on disk. If either file is missing, it exits immediately with a clear error. No silent failures.CSV Import and Format Checks
Both CSVs are imported and validated for aComputerName column. The script also enforces that the two lists have the same number of entries — source and target are paired by row index, so a mismatch would mean copying to the wrong machines.
Computer Lookup in AD
For each pair, the script queries AD for both the source and target computer objects, pulling their currentMemberOf attribute. If either object isn’t found in AD, that pair is skipped and logged as failed.
Group Diff and Addition
The script compares the source and target group lists and builds a delta — only groups the target doesn’t already have. It then iterates that list and callsAdd-ADGroupMember for each one. Groups the target already belongs to are left untouched.
Per-Pair and Final Summary
Success and failure counts are tracked at both the individual group level and the computer pair level. A summary prints at the end showing how many pairs completed cleanly and how many had issues.Usage
Prerequisites
- RSAT with the ActiveDirectory PowerShell module installed
- An account with permissions to read AD computer objects and modify group memberships (typically Domain Admin or a delegated account with
Write Memberson the target groups)
CSV Format
Both files must have a header row with aComputerName column:
ComputerName
WS-001
WS-002
Row order matters. Row 1 in the source file maps to row 1 in the target file.
Setup
1. Edit the two path variables near the top of the script:$SourceComputersFile = "C:scriptsDOMAINuser_1File1.csv"
$TargetComputersFile = "C:scriptsDOMAINuser_1File2.csv"Code language: PowerShell (powershell)
2. Run from an elevated PowerShell session:
.Copy-ADComputerGroupMemberships_Hardcoded.ps1Code language: PowerShell (powershell)
No parameters. No flags. Everything is driven by the two CSV files.
Caveats
Pairing is positional, not named
Source row 1 maps to target row 1 — full stop. If your CSVs are sorted differently or have blank rows, you will copy memberships to the wrong machines. Double-check your CSVs before running.No rollback
There’s no undo. Group memberships added by this script won’t be removed if you re-run with different inputs. If you need a rollback path, export the target computer’s current group memberships before running.Partial failures count as failed pairs
If even one group addition fails for a pair, that pair is counted as failed in the summary. The script still attempts all remaining groups for that pair — it doesn’t abort mid-pair — but the summary won’t mark it as successful.MemberOf doesn’t include the primary group
AD’s MemberOf attribute omits a computer’s primary group (usually Domain Computers). That group won’t be copied, which is almost always fine, but worth knowing.
Runs against whichever DC resolves by default
No-Server parameter is specified, so AD queries go to whatever domain controller your machine resolves to. In multi-site environments with replication lag, you could get inconsistent results mid-run. Pin it to a specific DC if that’s a concern.
Clear-Host runs unconditionally
The script calls Clear-Host at startup. If you’re running this as part of a larger pipeline or logging session, your console buffer will be wiped before any output appears.
Full Script
#Requires -Modules ActiveDirectory
<#
.SYNOPSIS
Copies AD group memberships from a list of source computers to a list of target computers.
Source and Target computer lists are hardcoded in this script.
.Command to Run:
.Copy-ADComputerGroupMemberships_Hardcoded.ps1
(Ensure you have edited the file paths inside the script first)
#>
# --- HARDCODED FILE PATHS ---
# !! EDIT THESE LINES with the correct paths to your CSV files !!
$SourceComputersFile = "C:scriptsDOMAINuser_1File1.csv"
$TargetComputersFile = "C:scriptsDOMAINuser_1File2.csv"
# --- END HARDCODED FILE PATHS ---
# Function to write colored output
function Write-ColorOutput {
param(
[string]$Message,
[string]$Color = "White"
)
Write-Host $Message -ForegroundColor $Color
}
# Function to validate files exist
function Test-FilePath {
param(
[string]$Path,
[string]$FileType
)
if (-not (Test-Path -Path $Path)) {
Write-ColorOutput "❌ Error: $FileType file not found at path: $Path" "Red"
return $false
}
return $true
}
# Main script execution begins here
Clear-Host
Write-ColorOutput "════════════════════════════════════════" "Cyan"
Write-ColorOutput "🔄 AD COMPUTER GROUP MEMBERSHIP COPY TOOL (Hardcoded Paths)" "Cyan"
Write-ColorOutput "════════════════════════════════════════" "Cyan"
# Check if ActiveDirectory module is available
Write-ColorOutput "🔍 Checking ActiveDirectory module..." "Yellow"
if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) {
Write-ColorOutput "❌ Error: ActiveDirectory module not found. Please run this script on a computer with RSAT tools installed." "Red"
exit 1
}
Write-ColorOutput "✅ ActiveDirectory module found." "Green"
# Validate input files using the hardcoded paths
Write-ColorOutput "🔍 Validating hardcoded file paths..." "Yellow"
Write-ColorOutput " ▶️ Source File: $SourceComputersFile" "White"
Write-ColorOutput " ▶️ Target File: $TargetComputersFile" "White"
if (-not (Test-FilePath -Path $SourceComputersFile -FileType "Source computers")) { exit 1 }
if (-not (Test-FilePath -Path $TargetComputersFile -FileType "Target computers")) { exit 1 }
Write-ColorOutput "✅ File paths validated." "Green"
# Import computer lists
try {
Write-ColorOutput "🔄 Importing CSV files..." "Yellow"
$sourceComputers = Import-Csv -Path $SourceComputersFile
$targetComputers = Import-Csv -Path $TargetComputersFile
}
catch {
Write-ColorOutput "❌ Error importing CSV files: $($_.Exception.Message)" "Red"
exit 1
}
# Validate CSV format
if (-not ($sourceComputers | Get-Member -Name "ComputerName")) {
Write-ColorOutput "❌ Error: Source computers CSV ($SourceComputersFile) must have a 'ComputerName' column." "Red"
exit 1
}
if (-not ($targetComputers | Get-Member -Name "ComputerName")) {
Write-ColorOutput "❌ Error: Target computers CSV ($TargetComputersFile) must have a 'ComputerName' column." "Red"
exit 1
}
# Validate equal entries in CSVs
if ($sourceComputers.Count -ne $targetComputers.Count) {
Write-ColorOutput "❌ Error: Source and target computer lists have different numbers of entries." "Red"
Write-ColorOutput "📌 Source computers ($SourceComputersFile): $($sourceComputers.Count)" "Yellow"
Write-ColorOutput "📌 Target computers ($TargetComputersFile): $($targetComputers.Count)" "Yellow"
exit 1
}
Write-ColorOutput "✅ Loaded $($sourceComputers.Count) computer pairs." "Green"
# Process each computer pair
$totalPairs = $sourceComputers.Count
$currentPair = 0
$successfulPairs = 0
$failedPairs = 0
foreach ($idx in 0..($sourceComputers.Count - 1)) {
$currentPair++
$sourceComputer = $sourceComputers[$idx].ComputerName
$targetComputer = $targetComputers[$idx].ComputerName
Write-ColorOutput "`n════════════════════════════════════════" "Cyan"
Write-ColorOutput "🔄 Processing Pair $currentPair of $totalPairs" "Cyan"
Write-ColorOutput "════════════════════════════════════════" "Cyan"
Write-ColorOutput "📌 Source: $sourceComputer → Target: $targetComputer" "White"
try {
# Find source computer in AD
Write-ColorOutput "🔍 Looking for source computer: $sourceComputer" "Yellow"
$sourceADComputer = Get-ADComputer -Filter "Name -eq '$sourceComputer'" -Properties MemberOf -ErrorAction Stop
if (-not $sourceADComputer) {
throw "Source computer '$sourceComputer' not found in Active Directory."
}
Write-ColorOutput "✅ Source computer found: $($sourceADComputer.DistinguishedName)" "Green"
# Find target computer in AD
Write-ColorOutput "🔍 Looking for target computer: $targetComputer" "Yellow"
$targetADComputer = Get-ADComputer -Filter "Name -eq '$targetComputer'" -Properties MemberOf -ErrorAction Stop
if (-not $targetADComputer) {
throw "Target computer '$targetComputer' not found in Active Directory."
}
Write-ColorOutput "✅ Target computer found: $($targetADComputer.DistinguishedName)" "Green"
# Get groups
$sourceGroups = $sourceADComputer.MemberOf
$targetGroups = $targetADComputer.MemberOf
$groupsToAdd = $sourceGroups | Where-Object { $targetGroups -notcontains $_ }
Write-ColorOutput "📊 Group Analysis:" "Cyan"
Write-ColorOutput " 🔹 Source computer's group count: $($sourceGroups.Count)" "White"
Write-ColorOutput " 🔹 Target computer's current group count: $($targetGroups.Count)" "White"
Write-ColorOutput " 🔹 Groups to be added: $($groupsToAdd.Count)" "Yellow"
if ($groupsToAdd.Count -eq 0) {
Write-ColorOutput "✅ No new groups to add. Target computer already has all source computer's groups." "Yellow"
$successfulPairs++
continue
}
# Add groups
Write-ColorOutput "➕ Starting group addition process..." "Yellow"
$successCount = 0
$failCount = 0
foreach ($group in $groupsToAdd) {
$groupName = "Unknown Group" # Default in case Get-ADGroup fails
try {
$groupObject = Get-ADGroup $group
$groupName = $groupObject.Name
Add-ADGroupMember -Identity $groupObject -Members $targetADComputer -ErrorAction Stop
Write-ColorOutput " ✅ Added to: $groupName" "Green"
$successCount++
}
catch {
# Try to get group name even on failure if possible, otherwise use DistinguishedName from $group
if($groupObject) {$groupName = $groupObject.Name} else {$groupName = $group}
Write-ColorOutput " ❌ Failed to add to: $groupName - $($_.Exception.Message)" "Red"
$failCount++
}
}
if ($failCount -eq 0) {
$successfulPairs++
}
else {
$failedPairs++
}
Write-ColorOutput "📌 Pair Summary:" "Cyan"
Write-ColorOutput " ✅ Successfully added to $successCount groups" "Green"
if ($failCount -gt 0) {
Write-ColorOutput " ❌ Failed to add to $failCount groups" "Red"
}
}
catch {
Write-ColorOutput "❌ Error processing computer pair ($sourceComputer -> $targetComputer): $($_.Exception.Message)" "Red"
$failedPairs++
}
}
# Final summary
Write-ColorOutput "`n════════════════════════════════════════" "Cyan"
Write-ColorOutput "📊 OPERATION SUMMARY" "Cyan"
Write-ColorOutput "════════════════════════════════════════" "Cyan"
Write-ColorOutput "📌 Total computer pairs processed: $totalPairs" "White"
Write-ColorOutput "✅ Successfully processed pairs: $successfulPairs" "Green"
if ($failedPairs -gt 0) {
Write-ColorOutput "❌ Failed pairs: $failedPairs" "Red"
}
Write-ColorOutput "`n✅ Script execution completed!" "Cyan"Code language: PowerShell (powershell)