Bulk Move AD Computer Accounts to New OU Using PowerShell Script


2 views

When dealing with Active Directory environments containing thousands of computer objects, administrative tasks like moving accounts between organizational units (OUs) can become time-consuming. In this specific scenario, we need to move 580 computer accounts from one OU to another using only a list of computer names (without FQDNs).

PowerShell provides the most efficient way to handle bulk operations in Active Directory. The ActiveDirectory module offers cmdlets specifically designed for this purpose. Here's why PowerShell is superior to batch files for this task:

  • Native AD integration through modules
  • Better error handling and logging
  • Ability to process input files efficiently
  • Support for pipeline operations

Here's a production-ready PowerShell script that accomplishes this task:


# Import required module
Import-Module ActiveDirectory

# Define paths and parameters
$sourceOU = "OU=Computers,DC=domain,DC=com"
$destinationOU = "OU=NewComputers,DC=domain,DC=com"
$computerListFile = "C:\temp\computers.txt"
$logFile = "C:\temp\computer_move_log_$(Get-Date -Format 'yyyyMMdd').csv"

# Initialize log array
$moveResults = @()

# Read computer names from file
$computers = Get-Content $computerListFile

foreach ($computer in $computers) {
    try {
        # Search for computer in current OU
        $computerObj = Get-ADComputer -Filter {Name -eq $computer} -SearchBase $sourceOU
        
        if ($computerObj) {
            # Move the computer to new OU
            Move-ADObject -Identity $computerObj.DistinguishedName -TargetPath $destinationOU
            
            # Log success
            $moveResults += [PSCustomObject]@{
                ComputerName = $computer
                Status = "Success"
                Timestamp = Get-Date
                Message = "Moved to $destinationOU"
            }
        }
        else {
            # Log failure (computer not found)
            $moveResults += [PSCustomObject]@{
                ComputerName = $computer
                Status = "Failed"
                Timestamp = Get-Date
                Message = "Computer not found in source OU"
            }
        }
    }
    catch {
        # Log error
        $moveResults += [PSCustomObject]@{
            ComputerName = $computer
            Status = "Error"
            Timestamp = Get-Date
            Message = $_.Exception.Message
        }
    }
}

# Export log to CSV
$moveResults | Export-Csv -Path $logFile -NoTypeInformation

Error Handling and Logging

The script includes comprehensive error handling that:

  • Catches individual computer move failures without stopping the entire process
  • Logs detailed information about each operation
  • Generates a timestamped CSV log file for auditing

Performance Optimization

For large operations (500+ computers), consider these enhancements:


# Process computers in parallel (PowerShell 7+)
$computers | ForEach-Object -Parallel {
    Import-Module ActiveDirectory
    # Move logic here
} -ThrottleLimit 10

Input File Formatting

The script expects a simple text file with one computer name per line. Example:


WS001
WS002
SRV045
...

For more complex scenarios where you need additional metadata:


$computers = Import-Csv -Path "C:\temp\computers.csv"
foreach ($computer in $computers) {
    # Access properties as $computer.Name, $computer.Department, etc.
}

After running the script:

  1. Spot check several computers in the new OU using AD Users and Computers
  2. Review the log file for any failures
  3. Compare count of moved computers against expected number

When managing large Active Directory environments, administrators often need to reorganize computer objects. In this scenario, we have:

  • Source OU containing approximately 2,500 computer objects
  • Target OU where 580 specific computers need to be moved
  • Text file containing simple computer names (without domain suffix)

While batch files can handle basic operations, PowerShell provides superior capabilities for AD management:


# Import ActiveDirectory module
Import-Module ActiveDirectory

# Define paths
$sourceOU = "OU=Computers,DC=domain,DC=com"
$targetOU = "OU=NewComputers,DC=domain,DC=com"
$computerList = Get-Content "C:\temp\computers.txt"

foreach ($computer in $computerList) {
    try {
        Get-ADComputer -Identity $computer | 
            Move-ADObject -TargetPath $targetOU
        Write-Host "Successfully moved $computer" -ForegroundColor Green
    }
    catch {
        Write-Host "Failed to move $computer : $_" -ForegroundColor Red
    }
}

For production environments, consider adding these improvements:


# Add logging functionality
$logFile = "C:\temp\ADMoveLog_$(Get-Date -Format yyyyMMdd).csv"
$results = @()

foreach ($computer in $computerList) {
    $result = [PSCustomObject]@{
        ComputerName = $computer
        Timestamp = Get-Date
        Status = $null
        Error = $null
    }

    try {
        $computerObj = Get-ADComputer -Identity $computer -ErrorAction Stop
        Move-ADObject -Identity $computerObj -TargetPath $targetOU -ErrorAction Stop
        $result.Status = "Success"
    }
    catch {
        $result.Status = "Failed"
        $result.Error = $_.Exception.Message
    }
    
    $results += $result
}

$results | Export-Csv -Path $logFile -NoTypeInformation

For large operations, implement parallel processing:


# Requires PowerShell 7+ for ForEach-Object -Parallel
$computerList | ForEach-Object -Parallel {
    Import-Module ActiveDirectory
    $computer = $_
    
    try {
        $compObj = Get-ADComputer -Identity $computer
        Move-ADObject -Identity $compObj -TargetPath $using:targetOU
        [PSCustomObject]@{
            Computer = $computer
            Status = "Moved"
        }
    }
    catch {
        [PSCustomObject]@{
            Computer = $computer
            Status = "Error: $_"
        }
    }
} -ThrottleLimit 10 | Export-Csv -Path "C:\temp\ParallelResults.csv" -NoTypeInformation

For more complex scenarios with additional attributes:


# CSV format: ComputerName,NewOU,Department
$computers = Import-Csv "C:\temp\computer_moves.csv"

foreach ($item in $computers) {
    try {
        $computer = Get-ADComputer -Identity $item.ComputerName
        Move-ADObject -Identity $computer -TargetPath $item.NewOU
        Set-ADComputer -Identity $computer -Department $item.Department
    }
    catch {
        Write-Warning "Error processing $($item.ComputerName): $_"
    }
}