Classes/printer.ps1

class Printer {
    <#
    .SYNOPSIS
        Represents a printer object, providing methods to manage printer drivers, ports, and printers.
 
    .DESCRIPTION
        The Printer class allows for the management of printer configurations, including checking for existing drivers, ports, and printers. It provides methods to add and remove printer drivers, ports, and printers.
 
    .PROPERTIES
        - Name: The name of the printer.
        - Driver: The name of the driver associated with the printer.
        - PortName: The name of the port associated with the printer.
        - PrinterHostAddress: The host address of the printer for port configuration.
 
    .METHODS
        - DriverExists
            Checks if the printer driver already exists.
 
        .EXAMPLE
            $printer = [Printer]::new('OfficePrinter', 'HP LaserJet', 'OfficePort', '192.168.1.10')
            $exists = $printer.DriverExists()
            Write-Host "Driver exists: $exists"
 
        - PortExists
            Checks if the specified printer port already exists.
 
        .EXAMPLE
            $printer = [Printer]::new('OfficePrinter', 'HP LaserJet', 'OfficePort', '192.168.1.10')
            $exists = $printer.PortExists()
            Write-Host "Port exists: $exists"
 
        - PrinterExists
            Checks if the printer already exists.
 
        .EXAMPLE
            $printer = [Printer]::new('OfficePrinter', 'HP LaserJet', 'OfficePort', '192.168.1.10')
            $exists = $printer.PrinterExists()
            Write-Host "Printer exists: $exists"
 
        - AddDriver
            Adds a printer driver if it does not already exist.
 
        .EXAMPLE
            $printer = [Printer]::new('OfficePrinter', 'HP LaserJet', 'OfficePort', '192.168.1.10')
            $printer.AddDriver()
 
        - AddPort
            Adds a printer port if it does not already exist.
 
        .EXAMPLE
            $printer = [Printer]::new('OfficePrinter', 'HP LaserJet', 'OfficePort', '192.168.1.10')
            $printer.AddPort()
 
        - AddPrinter
            Adds the printer if it does not already exist.
 
        .EXAMPLE
            $printer = [Printer]::new('OfficePrinter', 'HP LaserJet', 'OfficePort', '192.168.1.10')
            $printer.AddPrinter()
 
        - RemovePrinter
            Removes the printer if it exists.
 
        .EXAMPLE
            $printer = [Printer]::new('OfficePrinter', 'HP LaserJet', 'OfficePort', '192.168.1.10')
            $printer.RemovePrinter()
 
        - RemovePort
            Removes the printer port if it exists.
 
        .EXAMPLE
            $printer = [Printer]::new('OfficePrinter', 'HP LaserJet', 'OfficePort', '192.168.1.10')
            $printer.RemovePort()
 
        - RemoveDriver
            Removes the printer driver if it exists.
 
        .EXAMPLE
            $printer = [Printer]::new('OfficePrinter', 'HP LaserJet', 'OfficePort', '192.168.1.10')
            $printer.RemoveDriver()
 
        - InstallPrinterComponents
            Installs the printer, including the driver and port, if they do not already exist.
 
        .EXAMPLE
            $printer = [Printer]::new('OfficePrinter', 'HP LaserJet', 'OfficePort', '192.168.1.10')
            $printer.InstallPrinterComponents()
 
        - RemovePrinterComponents
            Removes the printer, port, and driver.
 
        .EXAMPLE
            $printer = [Printer]::new('OfficePrinter', 'HP LaserJet', 'OfficePort', '192.168.1.10')
            $printer.RemovePrinterComponents()
 
        - FindUnassignedTCPIPPorts
            Find all TCP/IP ports that are unassigned i.e not in use
 
        .EXAMPLE
        # Create a Printer object
        $printer = [Printer]::new('null', 'null', 'null', 'null')
 
        # Find unassigned TCP/IP ports
        #$unassignedPorts = $printer.FindUnassignedTCPIPPorts()
 
        - RemoveUnassignedTCPIPPorts
            Remove any unassigned TCP/IP ports i.e delete any TCP/IP ports not in use
 
        .EXAMPLE
            # Create a Printer object
            $printer = [Printer]::new('null', 'null', 'null', 'null')
 
            # Remove unassigned TCP/IP ports
            $printer.RemoveUnassignedTCPIPPorts()
 
        .EXAMPLE
            Setting printer permissions (Assign directly to the method, not via a instantiated object parameter)
 
            $newSDDL = "O:SYG:SYD:(A;;FA;;;BA)(A;;FA;;;SY)(A;;0x1301ff;;;AU)"
            $printer.SetPrinterPermissions($newSDDL)
 
        .EXAMPLE
            Export Printer settings
 
            # Create a Printer object
            $printer = [Printer]::new("YourPrinterName")
 
            # Export settings to a valid file path
            $printer.ExportSettings("C:\ValidPath\printerConfig")
 
            # Export settings to an invalid file path (throws error)
            $printer.ExportSettings("C:\InvalidPath\printerConfig")
 
        .EXAMPLE
            Import Printer settings
            # Instantiate a printer object
            $printer = [Printer]::new("MyPrinterName")
 
            # Import settings from a .dat file
            $printer.ImportSettings("C:\Temp\printerConfig.dat")
 
 
        .NOTES
        Author : owen.heaume
        Version: 1.0 - Initial release
                    1.1 - Add methods: FindUnassignedTCPIPPorts and RemoveUnassignedTCPIPPorts
    #>


    [string]$Name
    [string]$Driver
    [string]$PortName
    [string]$PrinterHostAddress

    # Default constructor with all parameters
    Printer([string]$name, [string]$driver, [string]$portName, [string]$printerHostAddress) {
        $this.Name = $name
        $this.Driver = $driver
        $this.PortName = $portName
        $this.PrinterHostAddress = $printerHostAddress
    }

    # Overloaded constructor with default values for PortName and PrinterHostAddress
    Printer([string]$name, [string]$driver) {
        $this.Name = $name
        $this.Driver = $driver
        $this.PortName = 'DefaultPort'      # Default value for PortName
        $this.PrinterHostAddress = '127.0.0.1'  # Default value for PrinterHostAddress
    }

    # Overloaded constructor with default values for Driver, PortName, and PrinterHostAddress
    Printer([string]$name) {
        $this.Name = $name
        $this.Driver = 'DefaultDriver'     # Default value for Driver
        $this.PortName = 'DefaultPort'     # Default value for PortName
        $this.PrinterHostAddress = '127.0.0.1'  # Default value for PrinterHostAddress
    }


    # ---- DRIVER MANAGEMENT ----
    # Add a printer driver if it doesn't exist
    [void] AddDriver() {
        if (-not $this.DriverExists()) {
            try {
                #Write-Host "Adding driver: $($this.Driver) for printer: $($this.Name)"
                Add-PrinterDriver -Name $this.Driver -ea Stop
            } catch {
                #Write-Error "Failed to add driver $($this.Driver) for printer $($this.Name): $_"
                throw "Driver installation failed. $_"
            }
        } else {
            Write-Host "Driver $($this.Driver) already exists."
        }
    }

    # Remove the driver
    [void] RemoveDriver() {
        if ($this.DriverExists()) {
            try {
                # Write-Host "Removing driver: $($this.Driver)"
                Remove-PrinterDriver -Name $this.Driver -ErrorAction Stop
            } catch {
                #Write-Error "Failed to remove driver $($this.Driver): $_"
                throw "Driver removal failed. $_"
            }
        } else {
            Write-Host "Driver $($this.Driver) does not exist."
        }
    }

    # Check if a printer driver already exists
    [bool] DriverExists() {
        return Get-PrinterDriver -Name $this.Driver -ErrorAction SilentlyContinue
    }


    # ---- PORT MANAGEMENT ----
    # Add a port if it doesn't exist
    [void] AddPort() {
        if (-not $this.PortExists()) {
            try {
                #Write-Host "Adding port: $($this.PortName) for printer: $($this.Name)"
                # Add the printer port using PrinterHostAddress
                Add-PrinterPort -Name $this.PortName -PrinterHostAddress $this.PrinterHostAddress -ea Stop
            } catch {
                #Write-Error "Failed to add port $($this.PortName) for printer $($this.Name): $_"
                throw "Port installation failed. $_ "
            }
        } else {
            Write-Host "Port $($this.PortName) already exists."
        }
    }

    # Remove the port
    [void] RemovePort() {
        if ($this.PortExists()) {
            try {
                # Write-Host "Removing port: $($this.PortName)"
                Remove-PrinterPort -Name $this.PortName -ErrorAction Stop
            } catch {
                # Write-Error "Failed to remove port $($this.PortName): $_"
                throw "Port removal failed. $_"
            }
        } else {
            Write-Host "Port $($this.PortName) does not exist."
        }
    }

    # Method to find unassigned TCP/IP ports with minimal output
    [System.Object[]] FindUnassignedTCPIPPorts() {
        try {
            # Get all TCP/IP printer ports
            $tcpPorts = Get-PrinterPort -ea stop | Where-Object { $_.PortMonitor -eq 'TCPMON.dll' }

            # Get ports that are currently assigned to printers
            $assignedPorts = Get-Printer -ea stop | Select-Object -ExpandProperty PortName

            # Find ports that are not assigned to any printer
            $unassignedPorts = $tcpPorts | Where-Object { $assignedPorts -notcontains $_.Name }

            # Count unassigned ports using a loop
            [int]$count = 0
            $unassignedPorts | ForEach-Object { $count++ }

            # Output the count of unassigned ports
            if ($count -gt 0) {
                Write-Host "Found $count unassigned port(s):" -ForegroundColor DarkYellow
                foreach ($port in $unassignedPorts) {
                    Write-Host "Unassigned Port: $($port.Name)" -ForegroundColor Yellow
                }
            } else {
                Write-Host 'No unassigned TCP/IP ports found.' -ForegroundColor DarkGray
            }

            return $unassignedPorts  # Return the found unassigned ports

        } catch {
            throw "An error occurred while finding unassigned TCP/IP ports: $_"
            #return @()
        }
    }

    # Method to remove unassigned TCP/IP ports
    [void] RemoveUnassignedTCPIPPorts() {
        try {
            $unassignedPorts = $this.FindUnassignedTCPIPPorts()

            if ($unassignedPorts) {
                foreach ($port in $unassignedPorts) {
                    try {
                        Write-Host "Removing port: $($port.Name)..." -ForegroundColor DarkGray -NoNewline
                        Remove-PrinterPort -Name $port.Name -ea Stop
                        Write-Host 'Removed' -ForegroundColor DarkGreen
                    } catch {
                        Write-Error "An error occurred while removing the port: $_"
                    }
                }
            } else {
                Write-Host 'No unassigned ports to remove.' -ForegroundColor DarkGray
            }
        } catch {
            #Write-Error "An error occurred while removing unassigned TCP/IP ports: $_"
            throw "Port removal process failed. $_"
        }
    }

    # Check if a port already exists
    [bool] PortExists() {
        return Get-PrinterPort -Name $this.PortName -ErrorAction SilentlyContinue
    }

    # Method to check if the printer's port is USB
    [bool] IsPortUSB() {
        <# We can check if the printer exists first if you like...
            if (!($this.PrinterExists())) {
                write-host "The printer '$($this.name)' does not exist on this system"
                return $false
            } #>


        try {
            # Define the registry path for USB printer ports
            $usbPortsRegistryPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Print\Monitors\USB Monitor\Ports'

            # Get the USB printer ports from the registry
            $usbPorts = Get-ChildItem -Path $usbPortsRegistryPath -ErrorAction SilentlyContinue |
                Select-Object -ExpandProperty PSChildName

            # Ensure both $usbPorts and $this.PortName are valid
            if ($usbPorts -and $this.PortName) {
                # Fetch the actual port name assigned to the printer
                $actualPortName = (Get-Printer -Name $this.Name -ErrorAction SilentlyContinue).PortName

                if ($actualPortName) {
                    # Explicit string comparison to check if the printer's port is a USB port
                    foreach ($usbPort in $usbPorts) {
                        if ($usbPort -eq $actualPortName) {
                            return $true
                        }
                    }
                }
            }

            # Default case: not a USB port
            return $false
        } catch {
            throw "An error occurred while checking if the port is USB: $_"
        }
    }

    # Method to check if the printer's port is TCP/IP
    [bool] IsPortTcp() {
        try {
            # Define the correct registry path for TCP/IP printer ports
            $tcpPortsRegistryPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Print\Monitors\Standard TCP/IP Port\Ports'

            # Get the TCP/IP printer ports from the registry
            $tcpPorts = Get-ChildItem -Path $tcpPortsRegistryPath -ErrorAction SilentlyContinue |
                Select-Object -ExpandProperty PSChildName

            # Ensure both $tcpPorts and $this.PortName are valid
            if ($tcpPorts -and $this.PortName) {
                # Fetch the actual port name assigned to the printer
                $actualPortName = (Get-Printer -Name $this.Name -ErrorAction SilentlyContinue).PortName

                if ($actualPortName) {
                    # Explicit string comparison to check if the printer's port is a TCP/IP port
                    foreach ($tcpPort in $tcpPorts) {
                        if ($tcpPort -eq $actualPortName) {
                            return $true
                        }
                    }
                }
            }

            # Default case: not a TCP/IP port
            return $false
        } catch {
            throw "An error occurred while checking if the port is TCP/IP: $_"
        }
    }


    # ---- PRINTER MANAGEMENT ----
    # Add the printer if it doesn't exist
    [void] AddPrinter() {
        if (-not $this.PrinterExists()) {
            try {
                # Write-Host "Adding printer: $($this.Name) using driver: $($this.Driver) on port: $($this.PortName)"
                Add-Printer -Name $this.Name -DriverName $this.Driver -PortName $this.PortName -ea Stop
            } catch {
                #Write-Error "Failed to add printer $($this.Name): $_"
                throw "Printer installation failed. $_"
            }
        } else {
            Write-Host "Printer $($this.Name) already exists."
        }
    }

    # Remove the printer
    [void] RemovePrinter() {
        if ($this.PrinterExists()) {
            try {
                # Write-Host "Removing printer: $($this.Name)"
                Remove-Printer -Name $this.Name -ErrorAction Stop
            } catch {
                #Write-Error "Failed to remove printer $($this.Name): $_"
                throw "Printer removal failed. $_"
            }
        } else {
            Write-Host "Printer $($this.Name) does not exist."
        }
    }

    # Check if a printer already exists
    [bool] PrinterExists() {
        return Get-Printer -Name $this.Name -ErrorAction SilentlyContinue
    }


    # ---- PRINTER INSTALLATION AND REMOVAL ----
    # Install all: driver, port, and printer
    [void] InstallPrinterComponents() {
        try {
            $this.AddDriver()
            $this.AddPort()
            $this.AddPrinter()
        } catch {
            #Write-Error "Failed to install the printer $($this.Name): $_"
            throw "Printer installation process failed. $_"
        }
    }

    # Remove all: printer, port, and driver
    [void] RemovePrinterComponents() {
        try {
            $this.RemovePrinter()
            $this.RemovePort()
            $this.RemoveDriver()
        } catch {
            #Write-Error "Failed to remove the printer $($this.Name): $_"
            throw "Printer removal process failed. $_"
        }
    }


    # ---- PRINTER PERMISSIONS MANAGEMENT ----
    # Get the printer permissions
    [string] GetPrinterPermissions() {
        try {
            #Write-Host "Retrieving current printer SDDL permissions for $($this.Name)"
            $permissions = Get-Printer -Full -Name $this.Name -ea stop | Select-Object -ExpandProperty PermissionSDDL
            return $permissions
        } catch {
            throw "Unable to retrieve current printer SDDL permissions for $($this.Name) $_"
        }
    }

    # Set the printer permissions
    [void] SetPrinterPermissions([string]$newSDDL) {
        try {
            #Write-host "Setting printer permissions for $($this.Name) to $newSDDL"
            Set-Printer -Name $this.Name -PermissionSDDL $newSDDL -ea Stop
        } catch {
            #Write-Error "Failed to set printer permissions for $($this.Name): $_"
            throw "Permission setting failed. $_"
        }
    }


    # ---- PRINTER SETTINGS MANAGEMENT ----
    [void]ExportSettings([string]$OutputFile) {
        # Ensure the output file has a .dat extension
        if (-not $OutputFile.EndsWith('.dat')) {
            $OutputFile += '.dat'
        }

        # Extract the directory path from the provided file path
        $directoryPath = [System.IO.Path]::GetDirectoryName($OutputFile)

        # Check if the directory exists
        if (-not (Test-Path -Path $directoryPath)) {
            throw [System.IO.DirectoryNotFoundException] "The directory '$directoryPath' does not exist. Please provide a valid path."
        }

        try {
            # Execute the printer settings export command
            & 'C:\Windows\System32\rundll32.exe' printui.dll, PrintUIEntry /Ss /n $this.Name /a $OutputFile
            Write-Host "Successfully exported printer settings for $($this.Name) to $OutputFile." -ForegroundColor DarkGray
        } catch {
            Write-Error "Failed to export printer settings for $($this.Name). $_"
        }
    }

    [void]ImportSettings([string]$InputFile) {
        # Ensure the input file has a .dat extension
        if (-not $InputFile.EndsWith('.dat')) {
            throw [System.ArgumentException] "The file '$InputFile' does not have a valid .dat extension."
        }

        # Check if the file exists
        if (-not (Test-Path -Path $InputFile)) {
            throw [System.IO.FileNotFoundException] "The file '$InputFile' does not exist. Please provide a valid file path."
        }

        try {
            # Execute the printer settings import command
            & 'C:\Windows\System32\rundll32.exe' printui.dll, PrintUIEntry /Sr /n $this.Name /a $InputFile
            Write-Host "Successfully imported printer settings for $($this.Name) from $InputFile." -ForegroundColor DarkGray
        } catch {
            Write-Error "Failed to import printer settings for $($this.Name). $_"
        }
    }
}