Forum Discussion

mikeyearwood's avatar
mikeyearwood
Copper Contributor
Jul 24, 2024

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

  • mikeyearwood 

     

    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)
    }

     

     

    • mikeyearwood's avatar
      mikeyearwood
      Copper 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

Resources