Forum Discussion
Help with Powershell scripts to backup and restore printers
Hello all.
I'm rather new to PowerShell. I hope I'm not causing any grief here. I whipped together a proof of concept because I need to protect certain computers' printing abilities as they are crucial to our business. I know I can use Print Migration to export and import the set of printers. I don't know how to start Printer Migration giving it a destination of the UNC to the server, the current computer's name. It does not seem to restore all the printers either.
I really would appreciate the ability to clone an existing printer to a new name and new IP address.
I wanted to see how far Perplexity.AI could take this. It made a start but then started forgetting requirements, re-writing whole scripts, and losing functionality already obtained.
My premise is that copying is backup, change, and restore. So I made Backup-SinglePrinter, Restore-SinglePrinter, and Copy-ExistingPrinter. A settings file is included. I expect that once backup-singleprinter and restore-singleprinter work, I could make a BackupPrinters and RestorePrinters to handle all existing printers.
One final thing is I would like to have the scripts locate each other by using the single settings file or by knowing the starting location of the first script.
Mike
Copy-ExistingPrinter.ps1
<#
.SYNOPSIS
Copies an existing printer to a new printer with a different name and a new IP address.
.DESCRIPTION
This script copies an existing printer by backing it up, modifying the backup,
and then restoring it with a new name and a new IP address.
.PARAMETER ExistingPrinterName
The name of the existing printer to copy from.
.PARAMETER NewPrinterName
The name of the new printer to create.
.EXAMPLE
.\Copy-ExistingPrinter.ps1 -ExistingPrinterName "Printer1" -NewPrinterName "Printer2"
#>
param (
[Parameter(Mandatory=$true)]
[string]$ExistingPrinterName,
[Parameter(Mandatory=$true)]
[string]$NewPrinterName
)
# Import settings
$settingsPath = Join-Path $PSScriptRoot "settings.ps1"
if (Test-Path $settingsPath) {
. $settingsPath
} else {
Write-Error "Settings file not found at $settingsPath"
exit 1
}
# Verify that $BaseFolder is set in settings.ps1
if (-not $BaseFolder) {
Write-Error "BaseFolder is not set in settings.ps1"
exit 1
}
# Manually import required modules
$modulesToImport = @(
"Backup-SinglePrinter.psm1",
"Restore-SinglePrinter.psm1",
"Find-AvailableIPPort.psm1",
"Get-PrinterExtendedConfig.psm1"
)
foreach ($module in $modulesToImport) {
$modulePath = Join-Path $BaseFolder $module
if (Test-Path $modulePath) {
Import-Module $modulePath -Force
Write-Host "Imported module: $module" -ForegroundColor Green
} else {
Write-Error "Module $module not found at $modulePath"
exit 1
}
}
# Verify that the functions are available
$requiredFunctions = @("Backup-SinglePrinter", "Restore-SinglePrinter", "Find-AvailableIPPort", "Get-PrinterExtendedConfig")
foreach ($func in $requiredFunctions) {
if (-not (Get-Command -Name $func -ErrorAction SilentlyContinue)) {
Write-Error "Required function $func is not available."
exit 1
} else {
Write-Host "Function $func is available." -ForegroundColor Green
}
}
try {
# Create backup folder structure
$backupsFolder = Join-Path $BaseFolder "Backups"
$computerName = $env:COMPUTERNAME
$computerBackupFolder = Join-Path $backupsFolder $computerName
$driversFolder = Join-Path $computerBackupFolder "Drivers"
if (-not (Test-Path $computerBackupFolder)) {
New-Item -ItemType Directory -Path $computerBackupFolder -Force | Out-Null
}
if (-not (Test-Path $driversFolder)) {
New-Item -ItemType Directory -Path $driversFolder -Force | Out-Null
}
# Backup the existing printer
Write-Host "Backing up existing printer: $ExistingPrinterName" -ForegroundColor Cyan
$backupFileName = "$ExistingPrinterName.xml"
$backupFile = Join-Path $computerBackupFolder $backupFileName
$backupResult = Backup-SinglePrinter -PrinterName $ExistingPrinterName -BackupFile $backupFile
if (-not $backupResult) {
throw "Failed to backup the existing printer."
}
# Get the IP address of the existing printer
$existingPrinter = Get-Printer -Name $ExistingPrinterName
$existingPort = Get-PrinterPort -Name $existingPrinter.PortName
$BaseIP = $existingPort.PrinterHostAddress
if ([string]::IsNullOrEmpty($BaseIP)) {
Write-Error "Unable to retrieve IP address for existing printer $ExistingPrinterName"
exit 1
}
Write-Host "Existing printer IP address: $BaseIP" -ForegroundColor Cyan
# Find an available IP port for the new printer
Write-Host "Finding an available IP port for the new printer" -ForegroundColor Cyan
$NewIPAddress = Find-AvailableIPPort -BaseIP $BaseIP
if ($NewIPAddress) {
Write-Host "Available IP port found: $NewIPAddress" -ForegroundColor Green
} else {
throw "No available IP address found."
}
# Create a new port with the new IP address
$NewPortName = $NewIPAddress
Add-PrinterPort -Name $NewPortName -PrinterHostAddress $NewIPAddress
# Restore the modified backup as a new printer
Write-Host "Restoring modified backup as new printer: $NewPrinterName" -ForegroundColor Cyan
$restoreResult = Restore-SinglePrinter -BackupFile $backupFile -NewPrinterName $NewPrinterName -NewPortName $NewPortName
if ($restoreResult) {
Write-Host "Printer '$ExistingPrinterName' successfully copied to '$NewPrinterName' with IP $NewIPAddress." -ForegroundColor Green
# Create a backup summary
$summaryFile = Join-Path $computerBackupFolder "BackupSummary.txt"
$summaryContent = @"
Backup performed on: $(Get-Date)
Original Printer: $ExistingPrinterName
New Printer: $NewPrinterName
New IP Address: $NewIPAddress
"@
Add-Content -Path $summaryFile -Value $summaryContent
} else {
throw "Failed to restore the new printer."
}
}
catch {
Write-Error "An error occurred during the printer copying process: $_"
# Clean up the new printer if it was created
if (Get-Printer -Name $NewPrinterName -ErrorAction SilentlyContinue) {
Remove-Printer -Name $NewPrinterName -ErrorAction SilentlyContinue
}
# Clean up the new port if it was created
if ($NewPortName -and (Get-PrinterPort -Name $NewPortName -ErrorAction SilentlyContinue)) {
Remove-PrinterPort -Name $NewPortName -ErrorAction SilentlyContinue
}
}
# Script Version: 3.9
Backup-SinglePrinter.psm1
function Backup-SinglePrinter {
param (
[Parameter(Mandatory=$true)]
[string]$PrinterName,
[Parameter(Mandatory=$true)]
[string]$BackupFile
)
# Import settings
$settingsPath = Join-Path $PSScriptRoot "settings.ps1"
if (Test-Path $settingsPath) {
. $settingsPath
} else {
Write-Error "Settings file not found at $settingsPath"
return $false
}
# Import printer properties
$propertiesModulePath = Join-Path $BaseFolder "Get-PrinterExtendedConfig.psm1"
if (Test-Path $propertiesModulePath) {
Import-Module $propertiesModulePath -Force
} else {
Write-Error "Get-PrinterExtendedConfig module not found at $propertiesModulePath"
return $false
}
$computerName = $env:COMPUTERNAME
$computerBackupFolder = Join-Path $BackupsFolder $computerName
$driversFolder = Join-Path $computerBackupFolder "Drivers"
if (-not (Test-Path $computerBackupFolder)) {
New-Item -ItemType Directory -Path $computerBackupFolder -Force | Out-Null
}
if (-not (Test-Path $driversFolder)) {
New-Item -ItemType Directory -Path $driversFolder -Force | Out-Null
}
try {
$printer = Get-Printer -Name $PrinterName -Full
$printerConfig = Get-PrintConfiguration -PrinterName $PrinterName
$printerProperties = Get-PrinterProperty -PrinterName $PrinterName
$backupObject = @{
Printer = $printer
Configuration = $printerConfig
Properties = $printerProperties
IsShared = $printer.Shared
ShareName = $printer.ShareName
}
$backupObject | Export-Clixml -Path $BackupFile -Depth 10
# Backup driver files
$driverName = $printer.DriverName
$driverInfo = Get-PrinterDriver -Name $driverName
$driverPath = $driverInfo.InfPath
if ($driverPath) {
Copy-Item -Path $driverPath -Destination $driversFolder -Force
}
Write-Host "Printer '$PrinterName' backed up successfully to $BackupFile"
return $true
}
catch {
Write-Error "Printer backup process failed: $_"
return $false
}
}
Export-ModuleMember -Function Backup-SinglePrinter
Restore-SinglePrinter.psm1
function Restore-SinglePrinter {
param (
[Parameter(Mandatory=$true)]
[string]$BackupFile,
[Parameter(Mandatory=$true)]
[string]$NewPrinterName,
[Parameter(Mandatory=$false)]
[string]$NewPortName
)
try {
$backupObject = Import-Clixml -Path $BackupFile
$printer = $backupObject.Printer
$printerConfig = $backupObject.Configuration
$printerProperties = $backupObject.Properties
# Import printer properties module
$propertiesModulePath = Join-Path $PSScriptRoot "Get-PrinterExtendedConfig.psm1"
if (Test-Path $propertiesModulePath) {
Import-Module $propertiesModulePath -Force
} else {
Write-Error "Get-PrinterExtendedConfig module not found at $propertiesModulePath"
return $false
}
$printerExtendedConfig = Get-PrinterExtendedConfig
# If NewPortName is not provided, use the original port
if (-not $NewPortName) {
$NewPortName = $printer.PortName
}
# Create or update the printer
if (Get-Printer -Name $NewPrinterName -ErrorAction SilentlyContinue) {
Set-Printer -Name $NewPrinterName -DriverName $printer.DriverName -PortName $NewPortName
} else {
Add-Printer -Name $NewPrinterName -DriverName $printer.DriverName -PortName $NewPortName
}
# Restore printer settings
$validPrinterParams = $printerExtendedConfig.PrinterProperties
$printerParams = @{}
foreach ($param in $validPrinterParams) {
if ($null -ne $printer.$param) {
$printerParams[$param] = $printer.$param
}
}
if ($printerParams.Count -gt 0) {
Set-Printer -Name $NewPrinterName @printerParams
}
# Restore configuration
$validConfigParams = $printerExtendedConfig.PrinterConfiguration
$configParams = @{}
foreach ($param in $validConfigParams) {
if ($null -ne $printerConfig.$param -and $printerConfig.$param -isnot [System.Management.Automation.PSMethod]) {
$configParams[$param] = $printerConfig.$param
}
}
if ($configParams.Count -gt 0) {
Set-PrintConfiguration -PrinterName $NewPrinterName @configParams
}
# Restore properties
foreach ($prop in $printerProperties) {
if (-not [string]::IsNullOrWhiteSpace($prop.Name) -and $null -ne $prop.Value) {
try {
Set-PrinterProperty -PrinterName $NewPrinterName -PropertyName $prop.Name -Value $prop.Value -ErrorAction Stop
} catch {
Write-Warning "Unable to set printer property $($prop.Name): $_"
}
}
}
# Restore sharing settings
if ($printer.Shared) {
Set-Printer -Name $NewPrinterName -Shared $true -ShareName $NewPrinterName
}
Write-Host "Printer '$NewPrinterName' restored successfully from $BackupFile"
return $true
}
catch {
Write-Error "Printer restoration process failed: $_"
return $false
}
}
Export-ModuleMember -Function Restore-SinglePrinter
Settings.ps1
# settings.ps1
$BaseFolder = "\\testserver\developer\printerBackup"
$BackupsFolder = Join-Path $BaseFolder "Backups"
Get-PrinterExtendedConfig.psm1 I hoped would be a single place to list all settings to capture. Maybe that is not needed.
function Get-PrinterExtendedConfig {
return @{
PrinterProperties = @(
'Comment',
'Location',
'Shared',
'Published'
)
PrinterConfiguration = @(
'Collate',
'Color',
'DuplexingMode',
'PaperSize',
'Orientation'
)
}
}
Export-ModuleMember -Function Get-PrinterExtendedConfig
Find-AvailablePort.psm1
# Find-AvailableIPPort.psm1
function Find-AvailableIPPort {
param (
[string]$BaseIP
)
$existingPorts = Get-PrinterPort | Where-Object { $_.Name -like "$BaseIP*" } | Select-Object -ExpandProperty Name
# If the base IP is not in use, return it
if ($BaseIP -notin $existingPorts) {
return $BaseIP
}
# Check for available ports with suffixes
for ($i = 1; $i -lt 100; $i++) {
$newIP = "${BaseIP}_$i"
if ($newIP -notin $existingPorts) {
return $newIP
}
}
# If no available port found, create a new one with the next available suffix
$maxSuffix = $existingPorts |
Where-Object { $_ -match "${BaseIP}_(\d+)" } |
ForEach-Object { [int]($Matches[1]) } |
Measure-Object -Maximum |
Select-Object -ExpandProperty Maximum
return "${BaseIP}_$($maxSuffix + 1)"
}
Export-ModuleMember -Function Find-AvailableIPPort
2 Replies
here is the refined version of your scripts
<# .SYNOPSIS Copies an existing printer to a new printer with a different name and IP address. ... (rest of your synopsis and description) #> param ( [Parameter(Mandatory = $true)] [string]$ExistingPrinterName, [Parameter(Mandatory = $true)] [string]$NewPrinterName ) # Load Settings - Dynamic Path Resolution $settingsPath = Resolve-Path (Join-Path $PSScriptRoot "settings.ps1") . $settingsPath # Dot-source the settings file # Error Handling for Missing Settings if (-not $BaseFolder) { Write-Error "ERROR: BaseFolder is not defined in settings.ps1" exit 1 } # Function Auto-Loading $requiredFunctions = "Backup-SinglePrinter", "Restore-SinglePrinter", "Find-AvailableIPPort", "Get-PrinterExtendedConfig" foreach ($func in $requiredFunctions) { if (-not (Get-Command -Name $func -ErrorAction SilentlyContinue)) { $modulePath = Resolve-Path (Join-Path $BaseFolder "$func.psm1") Import-Module $modulePath -Force } } try { # Backup and Restore Logic (Unchanged) # ... (your existing backup and restore code) } catch { # Error Handling and Cleanup (Unchanged) # ... (your existing error handling and cleanup code) }
- mikeyearwoodCopper Contributor
sdtslmn Sorry for taking so long to get back to you. I refined my requirements to perfectly clone one of the installed printers (from a numbered list on screen) to a new printer with a new name, sharename, IP address and every possible setting/property/detail, regardless of printer make/model. I've got some of it working, but the detailed copy is not working. The IP address should start at 132.147.162.254 (the original printer's IP). I would like the script to offer a derivative of the existing IP such as 132.147.162.254_1 or whichever such IP is not in use. It should not make 132.147.162.254_1_2.
I'm still a powershell noob, and the A.I. is not as good as I hoped. I keep telling it to add Write-Host modulename at the top of the scripts, but it forgets. Funnier than that, it just reminded me that we should do that, as if it was not my idea in the first place.
Care to take a look?
Mike