Print Server Migration Strategies for Windows Server 2003
Critical Notice
⚠️ Windows Server 2003 print servers are vulnerable to PrintNightmare and other exploits. Immediate migration to Windows Server 2022 is essential for security.
Overview
Print server migration from Windows Server 2003 requires careful planning to ensure continuous printing services. This guide provides comprehensive strategies for migrating print services while minimizing disruption to users.
Current State Assessment
1. Print Server Inventory
@echo off
:: PrintServerInventory.bat - Document existing print infrastructure
echo Print Server Inventory Report > PrintInventory.txt
echo Generated: %date% %time% >> PrintInventory.txt
echo ================================ >> PrintInventory.txt
:: List all printers
echo. >> PrintInventory.txt
echo Installed Printers: >> PrintInventory.txt
cscript %WINDIR%\System32\Printing_Admin_Scripts\en-US\prnmngr.vbs -l >> PrintInventory.txt
:: List printer drivers
echo. >> PrintInventory.txt
echo Installed Drivers: >> PrintInventory.txt
cscript %WINDIR%\System32\Printing_Admin_Scripts\en-US\prndrvr.vbs -l >> PrintInventory.txt
:: List printer ports
echo. >> PrintInventory.txt
echo Configured Ports: >> PrintInventory.txt
cscript %WINDIR%\System32\Printing_Admin_Scripts\en-US\prnport.vbs -l >> PrintInventory.txt
:: Get spooler settings
echo. >> PrintInventory.txt
echo Spooler Configuration: >> PrintInventory.txt
reg query "HKLM\SYSTEM\CurrentControlSet\Control\Print" /s >> PrintInventory.txt
echo Inventory saved to PrintInventory.txt
2. Print Queue Analysis
' AnalyzePrintQueues.vbs - Analyze print queue usage
Set objWMI = GetObject("winmgmts:\\.\root\cimv2")
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.CreateTextFile("PrintQueueAnalysis.csv", True)
' Write header
objFile.WriteLine "Printer Name,Jobs Printed,Total Pages,Average Pages/Job,Status"
' Get all printers
Set colPrinters = objWMI.ExecQuery("SELECT * FROM Win32_Printer")
For Each objPrinter In colPrinters
' Get job statistics
jobCount = objPrinter.JobCountSinceLastReset
If jobCount > 0 Then
avgPages = objPrinter.TotalPagesPrinted / jobCount
Else
avgPages = 0
End If
' Write data
objFile.WriteLine objPrinter.Name & "," & jobCount & "," & _
objPrinter.TotalPagesPrinted & "," & avgPages & "," & _
objPrinter.PrinterStatus
Next
objFile.Close
WScript.Echo "Analysis saved to PrintQueueAnalysis.csv"
3. Driver Compatibility Check
# CheckDriverCompatibility.ps1
$drivers = Get-WmiObject Win32_PrinterDriver
$report = @()
foreach($driver in $drivers) {
$driverInfo = @{
Name = $driver.Name
Version = $driver.DriverVersion
Architecture = $driver.SupportedPlatform
Date = $driver.DriverDate
Provider = $driver.ProviderName
Compatible2022 = "Unknown"
}
# Check known compatibility issues
if($driver.DriverDate -lt (Get-Date "2010-01-01")) {
$driverInfo.Compatible2022 = "Likely Incompatible - Too Old"
}
elseif($driver.SupportedPlatform -notmatch "x64") {
$driverInfo.Compatible2022 = "Incompatible - 32-bit Only"
}
$report += New-Object PSObject -Property $driverInfo
}
$report | Export-Csv -Path "DriverCompatibility.csv" -NoTypeInformation
Write-Host "Driver compatibility report saved to DriverCompatibility.csv"
Migration Strategies
Strategy 1: Print Management Migration Tool
Prerequisites
@echo off
:: PrepareMigration.bat - Prepare for print server migration
:: Create migration directory
mkdir C:\PrintMigration
:: Export print configuration
echo Exporting print server configuration...
cd /d %WINDIR%\System32\spool\tools
:: Export printers
Printbrm -s \\%COMPUTERNAME% -b -f C:\PrintMigration\PrintConfig.printerExport
:: Backup registry
reg export "HKLM\SYSTEM\CurrentControlSet\Control\Print" C:\PrintMigration\PrintRegistry.reg
echo Migration files prepared in C:\PrintMigration
Migration Process
# MigratePrintServer.ps1 - Automated print server migration
param(
[string]$SourceServer = "OLDPRINT",
[string]$TargetServer = "NEWPRINT",
[string]$BackupPath = "\\FileServer\PrintMigration"
)
# Step 1: Export from source
Write-Host "Exporting print configuration from $SourceServer..."
$exportFile = "$BackupPath\PrintExport_$(Get-Date -Format 'yyyyMMdd').printerExport"
# Use Print Management backup
$exportCmd = "printbrm -s \\$SourceServer -b -f $exportFile"
Invoke-Expression $exportCmd
# Step 2: Import to target
Write-Host "Importing to $TargetServer..."
$importCmd = "printbrm -s \\$TargetServer -r -f $exportFile"
Invoke-Expression $importCmd
Write-Host "Migration complete. Verify printer functionality."
Strategy 2: Manual Migration with Scripts
Export Individual Printers
' ExportPrinters.vbs - Export printer configurations
Dim objFSO, objFile, objWMI, colPrinters
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.CreateTextFile("PrinterExport.xml", True)
Set objWMI = GetObject("winmgmts:\\.\root\cimv2")
objFile.WriteLine "<?xml version=""1.0""?>"
objFile.WriteLine "<PrinterConfigurations>"
Set colPrinters = objWMI.ExecQuery("SELECT * FROM Win32_Printer")
For Each objPrinter In colPrinters
objFile.WriteLine " <Printer>"
objFile.WriteLine " <Name>" & objPrinter.Name & "</Name>"
objFile.WriteLine " <DriverName>" & objPrinter.DriverName & "</DriverName>"
objFile.WriteLine " <PortName>" & objPrinter.PortName & "</PortName>"
objFile.WriteLine " <ShareName>" & objPrinter.ShareName & "</ShareName>"
objFile.WriteLine " <Location>" & objPrinter.Location & "</Location>"
objFile.WriteLine " <Comment>" & objPrinter.Comment & "</Comment>"
objFile.WriteLine " <Default>" & objPrinter.Default & "</Default>"
objFile.WriteLine " <Published>" & objPrinter.Published & "</Published>"
objFile.WriteLine " </Printer>"
Next
objFile.WriteLine "</PrinterConfigurations>"
objFile.Close
WScript.Echo "Printer configurations exported to PrinterExport.xml"
Create Printers on New Server
# CreatePrinters.ps1 - Recreate printers on Windows Server 2022
[xml]$printers = Get-Content "PrinterExport.xml"
foreach($printer in $printers.PrinterConfigurations.Printer) {
Write-Host "Creating printer: $($printer.Name)"
# Create printer port if needed
$port = Get-PrinterPort -Name $printer.PortName -ErrorAction SilentlyContinue
if(!$port) {
if($printer.PortName -match "^\d+\.\d+\.\d+\.\d+") {
# TCP/IP port
Add-PrinterPort -Name $printer.PortName -PrinterHostAddress $printer.PortName
}
}
# Add printer
Add-Printer -Name $printer.Name `
-DriverName $printer.DriverName `
-PortName $printer.PortName `
-Location $printer.Location `
-Comment $printer.Comment
# Share printer if needed
if($printer.ShareName) {
Set-Printer -Name $printer.Name -Shared $true -ShareName $printer.ShareName
}
# Publish in AD if needed
if($printer.Published -eq "True") {
Set-Printer -Name $printer.Name -Published $true
}
}
Strategy 3: Side-by-Side Migration
Parallel Operation Setup
@echo off
:: ParallelSetup.bat - Configure parallel print servers
echo Setting up parallel print environment...
:: Configure old server to coexist
echo Modifying old server settings...
netsh firewall set service type=fileandprint mode=enable
net share print$ /grant:everyone,read
:: Create migration share
net share PrintMigration=C:\PrintMigration /grant:administrators,full
:: Document current connections
echo Current printer connections: > UserPrinters.txt
wmic printer where "Network=TRUE" get Name, SystemName >> UserPrinters.txt
echo Parallel environment ready.
Client Migration Script
' MigrateClientPrinters.vbs - Migrate user printer connections
Dim objNetwork, objWMI, colPrinters, dictMappings
Set objNetwork = CreateObject("WScript.Network")
Set objWMI = GetObject("winmgmts:\\.\root\cimv2")
Set dictMappings = CreateObject("Scripting.Dictionary")
' Define printer mappings (old to new)
dictMappings.Add "\\OLDPRINT\HP_Laser", "\\NEWPRINT\HP_Laser"
dictMappings.Add "\\OLDPRINT\Color_Printer", "\\NEWPRINT\Color_Printer"
' Get current network printers
Set colPrinters = objWMI.ExecQuery("SELECT * FROM Win32_Printer WHERE Network=True")
' Migrate each printer
For Each objPrinter In colPrinters
oldPrinter = objPrinter.Name
If dictMappings.Exists(oldPrinter) Then
newPrinter = dictMappings.Item(oldPrinter)
' Remove old connection
objNetwork.RemovePrinterConnection oldPrinter, True, True
' Add new connection
objNetwork.AddWindowsPrinterConnection newPrinter
' Set as default if it was
If objPrinter.Default Then
objNetwork.SetDefaultPrinter newPrinter
End If
WScript.Echo "Migrated: " & oldPrinter & " -> " & newPrinter
End If
Next
WScript.Echo "Printer migration complete"
Driver Management
1. Driver Extraction
@echo off
:: ExtractDrivers.bat - Extract printer drivers from Windows Server 2003
set DriverPath=C:\PrintDrivers
mkdir %DriverPath%
echo Extracting printer drivers...
:: Copy driver files
xcopy /E /H /Y "%WINDIR%\System32\spool\drivers\*.*" "%DriverPath%\Drivers\"
:: Export driver information
reg export "HKLM\SYSTEM\CurrentControlSet\Control\Print\Environments" "%DriverPath%\DriverRegistry.reg"
:: Create driver manifest
echo Driver Manifest > %DriverPath%\manifest.txt
dir /s /b "%DriverPath%\Drivers\*.inf" >> %DriverPath%\manifest.txt
echo Drivers extracted to %DriverPath%
2. Driver Compatibility Testing
# TestDrivers.ps1 - Test driver compatibility on Windows Server 2022
param(
[string]$DriverPath = "C:\PrintDrivers"
)
$results = @()
$infFiles = Get-ChildItem -Path $DriverPath -Filter "*.inf" -Recurse
foreach($inf in $infFiles) {
Write-Host "Testing driver: $($inf.Name)"
$testResult = @{
Driver = $inf.Name
Path = $inf.FullName
Compatible = $false
Error = ""
}
try {
# Attempt to stage driver
pnputil /add-driver $inf.FullName
$testResult.Compatible = $true
}
catch {
$testResult.Error = $_.Exception.Message
}
$results += New-Object PSObject -Property $testResult
}
$results | Export-Csv -Path "DriverTestResults.csv" -NoTypeInformation
3. Universal Print Driver Deployment
@echo off
:: DeployUniversalDriver.bat - Deploy universal print drivers
echo Deploying Universal Print Drivers...
:: Download and install HP Universal Print Driver
powershell -Command "Invoke-WebRequest -Uri 'https://hp.com/upd' -OutFile 'HP_UPD.exe'"
HP_UPD.exe /quiet /norestart
:: Download and install Xerox Global Print Driver
powershell -Command "Invoke-WebRequest -Uri 'https://xerox.com/gpd' -OutFile 'Xerox_GPD.exe'"
Xerox_GPD.exe /quiet /norestart
:: Configure as defaults for unmatched printers
cscript %WINDIR%\System32\Printing_Admin_Scripts\en-US\prndrvr.vbs -a -m "HP Universal Printing PCL 6" -v 3 -e "Windows x64"
echo Universal drivers deployed.
Group Policy Migration
1. Export Print Policies
@echo off
:: ExportPrintGPO.bat - Export print-related Group Policy
:: Create export directory
mkdir C:\GPOExport
:: Export printer GPOs
echo Exporting printer deployment GPOs...
cscript %WINDIR%\System32\gpresult.vbs /s computer /scope computer > C:\GPOExport\ComputerGPO.txt
cscript %WINDIR%\System32\gpresult.vbs /s user /scope user > C:\GPOExport\UserGPO.txt
:: Export specific printer preferences
reg export "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Printers" C:\GPOExport\PrinterPolicies.reg
echo GPO export complete.
2. Create New Print Deployment GPO
# CreatePrintGPO.ps1 - Create printer deployment GPO for Windows Server 2022
Import-Module GroupPolicy
$gpoName = "Printer Deployment - Migrated"
$domain = Get-ADDomain
# Create new GPO
$gpo = New-GPO -Name $gpoName -Domain $domain.DNSRoot
# Configure printer deployments
$printers = @(
@{Name="HP_Laser"; Path="\\NEWPRINT\HP_Laser"; Default=$false},
@{Name="Color_Printer"; Path="\\NEWPRINT\Color_Printer"; Default=$false}
)
foreach($printer in $printers) {
# Add printer connection
Set-GPRegistryValue -Name $gpoName -Key "HKCU\Printers\Connections\$($printer.Path)" `
-ValueName "Server" -Type String -Value $printer.Path.Split('\')[2]
Set-GPRegistryValue -Name $gpoName -Key "HKCU\Printers\Connections\$($printer.Path)" `
-ValueName "Provider" -Type String -Value "win32spl.dll"
}
Write-Host "Printer GPO created: $gpoName"
Testing and Validation
1. Print Test Automation
# TestPrintQueues.ps1 - Automated print queue testing
$testDocument = "C:\PrintTest.txt"
"Print Queue Test Document - $(Get-Date)" | Out-File $testDocument
$printers = Get-Printer
$results = @()
foreach($printer in $printers) {
Write-Host "Testing printer: $($printer.Name)"
$testResult = @{
PrinterName = $printer.Name
TestTime = Get-Date
Success = $false
JobId = $null
Error = $null
}
try {
# Send test print
$job = Get-Content $testDocument | Out-Printer -Name $printer.Name -PassThru
$testResult.JobId = $job.JobId
$testResult.Success = $true
# Wait and check job status
Start-Sleep -Seconds 5
$jobStatus = Get-PrintJob -PrinterName $printer.Name -JobId $job.JobId
$testResult.JobStatus = $jobStatus.JobStatus
}
catch {
$testResult.Error = $_.Exception.Message
}
$results += New-Object PSObject -Property $testResult
}
$results | Export-Csv -Path "PrintTestResults.csv" -NoTypeInformation
Remove-Item $testDocument
2. User Acceptance Testing
' UserPrintTest.vbs - Interactive print test for users
Dim objShell, objFSO, objNetwork
Set objShell = CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objNetwork = CreateObject("WScript.Network")
' Get user's printers
WScript.Echo "Available Printers:"
WScript.Echo "=================="
Set objWMI = GetObject("winmgmts:\\.\root\cimv2")
Set colPrinters = objWMI.ExecQuery("SELECT * FROM Win32_Printer")
i = 1
Dim arrPrinters()
For Each objPrinter In colPrinters
ReDim Preserve arrPrinters(i)
arrPrinters(i-1) = objPrinter.Name
WScript.Echo i & ". " & objPrinter.Name
i = i + 1
Next
' Select printer
printerNum = InputBox("Enter printer number to test:", "Printer Test")
selectedPrinter = arrPrinters(CInt(printerNum) - 1)
' Create test page
Set objFile = objFSO.CreateTextFile("TestPage.txt", True)
objFile.WriteLine "PRINTER TEST PAGE"
objFile.WriteLine "================="
objFile.WriteLine "Date: " & Now
objFile.WriteLine "User: " & objNetwork.UserName
objFile.WriteLine "Computer: " & objNetwork.ComputerName
objFile.WriteLine "Printer: " & selectedPrinter
objFile.Close
' Print test page
objShell.Run "notepad /p TestPage.txt", 0, True
' Cleanup
objFSO.DeleteFile "TestPage.txt"
MsgBox "Test page sent to " & selectedPrinter, vbInformation, "Print Test"
Rollback Procedures
1. Quick Rollback Script
@echo off
:: RollbackPrint.bat - Emergency rollback to old print server
echo EMERGENCY PRINT SERVER ROLLBACK
echo ==============================
echo.
set /p confirm="This will revert to the old print server. Continue? (Y/N): "
if /i not "%confirm%"=="Y" exit
:: Stop new print spooler
sc \\NEWPRINT stop spooler
:: Update DNS if using print server alias
echo Updating DNS...
dnscmd /recorddelete domain.local printserver A /f
dnscmd /recordadd domain.local printserver A 192.168.1.10
:: Restart old server spooler
sc \\OLDPRINT start spooler
:: Notify users
msg * "Print services have been reverted to the old server. Please restart your applications."
echo Rollback complete.
2. Backup Restoration
# RestorePrintBackup.ps1 - Restore print configuration from backup
param(
[string]$BackupFile = "\\FileServer\Backups\PrintConfig.printerExport",
[string]$TargetServer = "localhost"
)
Write-Host "Restoring print configuration from backup..."
# Stop spooler
Stop-Service -Name Spooler -Force
# Clear existing configuration
Remove-Item -Path "$env:WINDIR\System32\spool\PRINTERS\*" -Force -ErrorAction SilentlyContinue
# Restore from backup
$restoreCmd = "printbrm -s \\$TargetServer -r -f $BackupFile"
Invoke-Expression $restoreCmd
# Restart spooler
Start-Service -Name Spooler
Write-Host "Print configuration restored."
Post-Migration Tasks
1. Decommission Old Server
@echo off
:: DecommissionOldPrint.bat - Safely decommission old print server
echo Decommissioning Windows Server 2003 Print Server
echo ===============================================
:: Export final configuration
Printbrm -s \\%COMPUTERNAME% -b -f "FinalBackup_%date:~-4,4%%date:~-10,2%%date:~-7,2%.printerExport"
:: Remove printer shares
echo Removing printer shares...
for /f "tokens=1" %%a in ('net share ^| findstr "Printer"') do (
net share %%a /delete /y
)
:: Stop services
net stop spooler
sc config spooler start= disabled
:: Archive spool folder
echo Archiving spool folder...
xcopy /E /H /Y "%WINDIR%\System32\spool" "C:\PrintArchive\spool\"
:: Clear spool
del /f /s /q "%WINDIR%\System32\spool\PRINTERS\*.*"
echo.
echo Decommission complete. Server ready for shutdown.
2. Performance Monitoring
# MonitorNewPrintServer.ps1 - Monitor new print server performance
$counters = @(
"\Print Queue(_Total)\Jobs",
"\Print Queue(_Total)\Jobs Spooling",
"\Print Queue(_Total)\Total Jobs Printed",
"\Print Queue(_Total)\Total Pages Printed",
"\Processor(_Total)\% Processor Time",
"\Memory\Available MBytes"
)
while($true) {
Clear-Host
Write-Host "Print Server Performance Monitor" -ForegroundColor Green
Write-Host "================================" -ForegroundColor Green
Write-Host "Time: $(Get-Date)" -ForegroundColor Yellow
Write-Host ""
foreach($counter in $counters) {
try {
$value = (Get-Counter $counter).CounterSamples[0].CookedValue
Write-Host "$counter : $([math]::Round($value, 2))"
}
catch {
Write-Host "$counter : Error reading" -ForegroundColor Red
}
}
Start-Sleep -Seconds 5
}
Best Practices Summary
Migration Checklist
- [ ] Complete printer inventory
- [ ] Test driver compatibility
- [ ] Create comprehensive backups
- [ ] Establish rollback procedures
- [ ] Schedule migration window
- [ ] Notify users in advance
- [ ] Prepare support documentation
- [ ] Test all printers post-migration
- [ ] Monitor for 48 hours
- [ ] Decommission old server
Common Issues and Solutions
Issue | Solution |
---|---|
Driver incompatibility | Use universal print drivers |
Port configuration errors | Verify IP addresses and port numbers |
Permission issues | Check share and NTFS permissions |
Spool folder corruption | Clear spool and restart service |
Client connection failures | Update GPO and login scripts |
Conclusion
Print server migration from Windows Server 2003 requires careful planning and execution. Following these strategies ensures minimal disruption while improving security and reliability. Always maintain backups and test thoroughly before final cutover.
Support Resources
- Tyler on Tech Louisville: (202) 948-8888
- 24/7 Print Support: Available
- Email: printsupport@tylerontechlouisville.com
Last Updated: January 2024
Author: Tyler Maginnis, Tyler on Tech Louisville