ninja-one/add-ip-printer.ps1

<#
.SYNOPSIS
    Adds an IP printer as an "All User Printer".
#>


[CmdletBinding()]
param (
    [Parameter()]
    [String]$PrinterName,
    [Parameter()]
    [String]$Hostname,
    [Parameter()]
    [String]$PrinterModel,
    [Parameter()]
    [Switch]$Remove = [System.Convert]::ToBoolean($env:removePrinter)
)

begin {
    # Check for required PowerShell version (7+)
    if (!($PSVersionTable.PSVersion.Major -ge 7)) {
        try {
            # Install PowerShell 7 if missing
            if (!(Test-Path "$env:SystemDrive\Program Files\PowerShell\7")) {
                Write-Output '[INFO] Installing PowerShell version 7...'
                Invoke-Expression "& { $( Invoke-RestMethod https://aka.ms/install-powershell.ps1 ) } -UseMSI -Quiet"
            }
        
            # Refresh PATH
            $env:Path = [System.Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' + [System.Environment]::GetEnvironmentVariable('Path', 'User')
        
            # Restart script in PowerShell 7
            pwsh -File "`"$PSCommandPath`"" @PSBoundParameters
        
        }
        catch {
            Write-Output '[ERROR] PowerShell 7 was not installed. Update PowerShell and try again.'
            throw $Error
        }
        finally {
            exit $LASTEXITCODE
        }
    }
    else {
        $PSStyle.OutputRendering = 'PlainText'
    }
    
        
    if ($env:printerName -and $env:printerName -notlike "null") {
        $PrinterName = $env:printerName
    }

    if ($env:hostname -and $env:hostname -notlike "null") {
        $Hostname = $env:hostname
    }

    if ($env:printerModel -and $env:printerModel -notlike "null") {
        $PrinterModel = $env:printerModel
    }

    if (-not $PrinterName) {
        Write-Host "[Error] Please specify a Printer Name."
        exit 1
    }

    if (-not $Hostname -and $Remove -eq $false) {
        Write-Host "[Error] Please specify a Hostname."
        exit 1
    }

    if (-not $PrinterModel -and $Remove -eq $false) {
        Write-Host "[Error] Please specify a Printer Model."
        exit 1
    }

    # Modify $PrinterModel as Ninja does not allow "-" in the name
    if ($PrinterModel -eq "Canon TX3000") {
        $PrinterModel = "Canon TX-3000"
    }
    elseif ($PrinterModel -eq "Oce PlotWave 350 WPD2") {
        $PrinterModel = "Oce PlotWave 350 - WPD2"
    }
    elseif ($PrinterModel -eq "Oce PlotWave 365 WPD2") {
        $PrinterModel = "Oce PlotWave 365 - WPD2"
    }
    
    # Create a dictionary to map driver names to driver locations
    $driverLocations = @{
        "Kyocera CS 4053ci KX"       = "https://jreappstorage.blob.core.windows.net/public/PrintDrivers/KX_DRIVER.zip"
        "Kyocera TASKalfa 4052ci KX" = "https://jreappstorage.blob.core.windows.net/public/PrintDrivers/KX_DRIVER.zip"
        "Kyocera CS 307ci KX"        = "https://jreappstorage.blob.core.windows.net/public/PrintDrivers/KX_DRIVER.zip"
        "Kyocera ECOSYS M2640idw KX" = "https://jreappstorage.blob.core.windows.net/public/PrintDrivers/KX_DRIVER.zip"
        "Canon TX-3000"              = "https://jreappstorage.blob.core.windows.net/public/PrintDrivers/Canon-TX-3000.zip"
        "Oce PlotWave 350 - WPD2"    = "https://jreappstorage.blob.core.windows.net/public/PrintDrivers/Oce-Plotwave.zip"
        "Oce PlotWave 365 - WPD2"    = "https://jreappstorage.blob.core.windows.net/public/PrintDrivers/Oce-Plotwave-365.zip"
    }
    
    # Create a dictionary of file hashes for the driver files where the key is the DriverLocation
    $driverHashes = @{
        "https://jreappstorage.blob.core.windows.net/public/PrintDrivers/KX_DRIVER.zip"     = "AF0EF97F790CEECF324B45FBBCA21F70FCE1B43C4DF50BF2C958D7C42B5F2E1E"
        "https://jreappstorage.blob.core.windows.net/public/PrintDrivers/Canon-TX-3000.zip" = "BEB5B7C10F9BA3A46E26268DBB79E2B51FC0EAF60975F3332E79B7E18D852244"
        "https://jreappstorage.blob.core.windows.net/public/PrintDrivers/Oce-Plotwave.zip"  = "5F8B3C4C8B2EDB8F3B75880852575444F968D824ACED9500F318FA52AD54E57B"
        "https://jreappstorage.blob.core.windows.net/public/PrintDrivers/Oce-Plotwave-365.zip"  = "5E893E0267AE12A5CC8670204E4AC22807EAC8BB19685ADEEEA91BD20B296F87"
    }
    
}
process {
    try {
        # Ensure the print spooler service is running
        $spooler = Get-Service -Name "spooler"
        if ($spooler.Status -ne 'Running') {
            Start-Service -Name "spooler" -ErrorAction SilentlyContinue
            Write-Host "[INFO] Starting the print spooler service..."

            # Wait for up to 1 minute for the spooler service to start
            $timeout = 60
            $interval = 5
            $elapsed = 0
            while ($spooler.Status -ne 'Running' -and $elapsed -lt $timeout) {
                Start-Sleep -Seconds $interval
                $spooler = Get-Service -Name "spooler"
                $elapsed += $interval
            }

            if ($spooler.Status -ne 'Running') {
                Write-Host "[Error] Print spooler service failed to start."
                exit 1
            }
            Write-Host "[INFO] Print spooler service is running."
        }

        if ($Remove) {
            Remove-Printer -Name $PrinterName -ErrorAction Stop
            Write-Host "[INFO] Removed printer $PrinterName"
            exit 0
        }

        $PortName = "IP_$Hostname"

        # Check if the printer already exists
        $existingPrinter = Get-Printer -Name $PrinterName -ErrorAction SilentlyContinue
        if ($existingPrinter) {
            #check to see if the driver and port match, so that if they do not, we can replace the printer
            if ($existingPrinter.DriverName -ne $PrinterModel -or $existingPrinter.PortName -ne $PortName) {
                Remove-Printer -Name $PrinterName -ErrorAction Stop
                Write-Host "[INFO] Removing printer $PrinterName with new configuration."
            }
            else {
                Write-Host "[INFO] Printer $PrinterName is already configured correctly."
                exit 0
            }
        }

        # check to make sure the computer can connect to the server
        if (-not (Test-Connection -ComputerName $Hostname -Count 1 -Quiet)) {
            Write-Host "[ERROR] Unable to connect to $Hostname."
            exit 1
        }

        # check to see if the print driver is installed
        $driver = Get-PrinterDriver -Name $PrinterModel -PrinterEnvironment "Windows x64" -ErrorAction SilentlyContinue
        if ($driver) {
            Write-Host "[INFO] Printer driver $PrinterModel already exists."
        }
        else {
            Write-Host "[INFO] Printer driver $PrinterModel not found. Installing..."

            # Get the driver location from the dictionary
            $DriverLocation = $driverLocations[$PrinterModel]

            # if the driver location is not found, throw an error
            if (-not $DriverLocation) {
                Write-Host "[ERROR] Driver location not found for $PrinterModel."
                exit 1
            }

            # check to see if the driver location is set
            if (-not $DriverLocation) {
                throw "[ERROR] Driver location not specified."
            }

            # Handle local or remote driver location
            if ($DriverLocation.StartsWith("http")) {
                # Download from web
                $fileName = Split-Path $DriverLocation -Leaf
                $zipPath = Join-Path "$env:TEMP" $fileName
                $extractPath = "$env:TEMP\$([System.IO.Path]::GetFileNameWithoutExtension($fileName))"

                # Download the driver
                Write-Host "[INFO] Downloading driver from $DriverLocation to $zipPath"
                Invoke-WebRequest -Uri $DriverLocation -OutFile $zipPath

                # Check if the filehash matches
                $fileHash = Get-FileHash -Path $zipPath -Algorithm SHA256
                if ($fileHash.Hash -ne $driverHashes[$DriverLocation]) {
                    Write-Host "[ERROR] File hash does not match. Downloaded file may be corrupted."
                    exit 1
                }
                else {
                    Write-Host "[INFO] File hash matches."
                }
    
                # Unzip the driver
                Expand-Archive -Path $zipPath -DestinationPath $extractPath -Force
                Write-Host "[INFO] Unzipped driver to $extractPath"

            }
            else {
                Write-Host "[INFO] Using local driver path: $DriverLocation"

                # Use local path
                if (Test-Path $DriverLocation) {
                    Write-Host "[INFO] Driver location exists, proceeding with installation."
                    if ($DriverLocation.EndsWith(".zip")) {
                        $extractPath = "$env:TEMP\$([System.IO.Path]::GetFileNameWithoutExtension($DriverLocation))"
                        Expand-Archive -Path $DriverLocation -DestinationPath $extractPath -Force
                        Write-Host "[INFO] Unzipped driver to $extractPath"
                    }
                    else {
                        $extractPath = $DriverLocation
                    }
                }
                else {
                    Write-Error "[ERROR] Local driver path not found: $DriverLocation"
                    exit 1
                }
            }

            # Get the INF file
            $infFile = Get-ChildItem -Path $extractPath -Filter "*.inf" -Recurse | Select-Object -First 1     
            Write-Host "[DEBUG] INF file found: $($infFile.FullName)"     
            # Run pnputil and capture the output
            $pnpOutput = & pnputil /add-driver $infFile.FullName | Out-String

            # Extract the Published Name (any .inf file) using regex
            if ($pnpOutput -match "Published Name:\s+(?<published>[^:\r\n]+\.inf)") {
                $publishedName = $matches['published']
                Write-Host "[INFO] Published Name found: $publishedName"

                # Get the driver info from the driver store using the published name
                $driverInfo = Get-WindowsDriver -Online -All | Where-Object { $_.Driver -eq $publishedName }

                if ($driverInfo) {
                    Write-Host "[INFO] Driver information found:"
                    # Add the printer driver
                    try {
                        Add-PrinterDriver -Name $PrinterModel -infPath $driverInfo.OriginalFileName -PrinterEnvironment "Windows x64" -ErrorAction Stop
                        Write-Host "[INFO] Printer driver added successfully"
                    }
                    catch {
                        Write-Host "[ERROR] Failed to add printer driver: $_"
                        exit 1
                    }
                }
                else {
                    Write-Host "[ERROR] Driver not found in Windows driver store"
                    exit 1
                }
            }
            else {
                Write-Host "[ERROR] Could not extract Published Name from pnputil output"
                Write-Host "[DEBUG] pnputil output was: $pnpOutput"
                exit 1
            }
        }

        try {
            # Add the printer port
            # Check if the printer port already exists
            $existingPort = Get-PrinterPort -Name $PortName -ErrorAction SilentlyContinue
            if ($existingPort) {
                Write-Host "[INFO] Printer port $PortName already exists."
            }
            else {
                Write-Host "[INFO] Printer port $PortName not found. Adding..."
                # Add the printer port
                if ($PrinterModel -eq "Oce PlotWave 350 - WPD2") {
                    Add-PrinterPort -Name $PortName -LprHostAddress $Hostname -LprQueueName "PW350_WPD2"
                }
                elseif ($PrinterModel -eq "Oce PlotWave 365 - WPD2") {
                    Add-PrinterPort -Name $PortName -LprHostAddress $Hostname -LprQueueName "PW365_WPD2"
                }
                else {
                    Add-PrinterPort -Name $PortName -PrinterHostAddress $Hostname -SNMP $true -SNMPCommunity "public"
                }
            }
        }
        catch {
            Write-Error "[ERROR] Failed to add printer port: $_"
            exit 1
        }

        try {
            Add-Printer -Name $PrinterName -DriverName $PrinterModel -PortName $PortName
            Write-Host "[INFO] Printer added successfully"
        }
        catch {
            Write-Error "[ERROR] Failed to add printer: $_"

            $ErrorLine = $_.InvocationInfo.ScriptLineNumber
            Write-Error "[ERROR] Line Number: $ErrorLine"
            
            exit 1
        }
    }
    catch {
        Write-Error "[ERROR] Failed to add network printer: $_"
        $ErrorLine = $_.InvocationInfo.ScriptLineNumber
        Write-Error "[ERROR] Line Number: $ErrorLine"
        exit 1
    }
    exit 0
}
end {


}