Efficient Printer Management with PowerShell: An Efficient Solution to Clear Print Queues

0
Clear Print Queues

Managing printers, especially on a large scale, can be daunting. Overseeing print jobs, handling errors, quickly being able to Clear Print Queues and ensuring a smooth flow in the print queue requires significant time and effort. We can address these challenges effectively thanks to PowerShell, a popular task automation and configuration management framework.

Our script written in PowerShell is specifically designed to monitor and manage print jobs on any server. This script checks the print queue every 10 seconds, detects jobs that are in an error state, and automatically clears them, promoting a seamless printing environment.

The Benefits

Automation

The greatest benefit of this PowerShell script is automation. This script works around the clock, constantly scanning the print queue and clearing error states. It frees IT administrators from the routine task of manually monitoring and managing print queues.

Efficiency

The script works to prevent the buildup of jobs in an error state, ensuring the print queue runs efficiently. Clearing the print spooler of any error state jobs minimizes disruptions and allows other print jobs to proceed unimpeded.

Easy Troubleshooting

The script logs all its actions, providing a clear history of cleared jobs and performed actions. This information can be valuable for troubleshooting and understanding what happened in the case of printing problems.

Resilience

The script incorporates error handling to manage scenarios where print jobs cannot be deleted or if the spooler service is not running. It skips over undeletable jobs and restarts the spooler service if it’s stopped, ensuring continuous operation.

How to Use the Script

This script is designed to be easy to use. You only need to run it with your server’s administrative privileges. It automatically installs itself as a scheduled task that starts on system startup, ensuring it’s always running and managing your print queues.

Once the script is running, it does all the work. It regularly checks the print queue, handles jobs in error states, and maintains a log of its actions.

Please remember to always test scripts in a controlled environment before implementing them in a production setting.

This PowerShell script represents a powerful tool for anyone managing printers, offering an automated, efficient, and resilient solution for print queue management.

How does the script work?

  1. Set Spooler Directory and Task Name: The script starts by defining the path of the spooler directory and the name of the scheduled task it will manage.
  2. Define LogWrite Function: This function takes a string as input and appends it to a log file. It’s used throughout the script to record actions and errors.
  3. Define ManagePrintJobs Function: This function checks the print queue for errors, stops the spooler service if any non-printing jobs are found, deletes related files in the spooler directory, and then restarts the service. It also ensures the spooler service is running, starting it if it’s stopped.
    • It first checks for print jobs with a status that is not ‘Printing’, ‘Printed’, or null (no status).
    • If such jobs are found, it logs an error message, stops the spooler service, and clears related files in the spooler directory. The script assumes that related files are those created within 10 seconds of the job being submitted. (THIS IS ONLY TRUE OF THE BETA SCRIPT AT THE END OF THE ARTICLE) If any file fails to delete, an error is logged.
    • The function then logs the clearing of each problematic job including the print job name and owner then restarts the spooler service.
    • Finally, if the spooler service was found to be stopped, it starts the service and logs this action.
  4. Define SetupTask Function: This function checks if the old scheduled task “Clear Spooler” exists, if it does it removes it and logs this action. Then it checks if the new task (defined at the beginning) exists. If it doesn’t, the function creates a new scheduled task to run this script at startup under the SYSTEM account. If an error occurs during setup, it’s logged.
  5. Create Log Directory: If the specified log directory doesn’t exist, it’s created.
  6. Run Setup Task: The setup task function is then run to ensure the scheduled task is properly configured.
  7. Run Manage Print Jobs: Finally, in an infinite loop, the script calls the ManagePrintJobs function every 10 seconds to monitor and manage print jobs continuously.

The Script

# Specify the path to the spooler directory
$spoolerDir = "$env:SystemRoot\System32\spool\PRINTERS"

# Define the name of the task
$taskName = "PrintQueueManager"

# Function to write logs
function LogWrite {
    Param ([string]$logstring)

    Add-content "$env:SystemDrive\Logs\PrintQueueManager.log" -value $logstring
}

# Function to manage print jobs
function ManagePrintJobs {
    try {
        # Check the print queues for errors
        $printJobs = Get-WmiObject -Query "SELECT * FROM Win32_PrintJob" -ErrorAction Stop

        $jobsInError = $printJobs | Where-Object { $_.JobStatus -like '*Error*' }

        if ($jobsInError) {
            LogWrite "$(Get-Date) - Error detected. Clearing spooler..."

            # Stop the spooler service
            Stop-Service -Name Spooler -Force -ErrorAction Stop

            # Clear out the spooler directory for error-related files
            foreach ($job in $jobsInError) {
                $jobTime = $job.ConvertToDateTime($job.TimeSubmitted)
                Get-ChildItem -Path $spoolerDir -Include "*.SPL", "*.SHD" -File -Recurse | ForEach-Object {
                    $fileTime = $_.CreationTime
                    if ([Math]::Abs(($fileTime - $jobTime).TotalSeconds) -le 10) {
                        try {
                            Remove-Item $_.FullName -Force -ErrorAction SilentlyContinue
                        }
                        catch {
                            LogWrite "$(Get-Date) - Failed to delete file: $($_.FullName)"
                        }
                    }
                }

                LogWrite "Cleared job $($job.JobId) from printer $($job.DriverName). Job name was '$($job.Document)', owned by '$($job.Owner)'"
            }

            # Start the spooler service
            Start-Service -Name Spooler -ErrorAction Stop
        }

        # Check the status of the Spooler service and start it if it's stopped
        $spoolerService = Get-Service -Name Spooler -ErrorAction SilentlyContinue
        if ($spoolerService.Status -eq 'Stopped') {
            Start-Service -Name Spooler -ErrorAction SilentlyContinue
            LogWrite "$(Get-Date) - The Spooler service was stopped and has been started again"
        }
    }
    catch {
        LogWrite "$(Get-Date) - An error occurred: $_"
    }
}

# Function to set up the task
function SetupTask {
    try {
        # Check if the old task exists and remove it
        if (Get-ScheduledTask -TaskName "Clear Spooler" -ErrorAction SilentlyContinue) {
            Unregister-ScheduledTask -TaskName "Clear Spooler" -Confirm:$false
            LogWrite "$(Get-Date) - Removed old task Clear Spooler"
        }

        # Check if the new task exists
        if (-not (Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue)) {
            # Register new task to run the script at startup
            $action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument "-File `"$PSCommandPath`""
            $trigger = New-ScheduledTaskTrigger -AtStartup
            $principal = New-ScheduledTaskPrincipal -UserId "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount
            $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -DontStopOnIdleEnd -RestartInterval (New-TimeSpan -Minutes 1) -RestartCount 3

            Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings

            LogWrite "$(Get-Date) - Scheduled task $taskName has been created to run at startup"
        }
    }
    catch {
        LogWrite "$(Get-Date) - An error occurred while setting up the task: $_"
    }
}

# Create the log directory if it doesn't exist
if (-not (Test-Path "$env:SystemDrive\Logs")) {
    New-Item -ItemType Directory -Force -Path "$env:SystemDrive\Logs"
}

# Run setup task
SetupTask

# Run manage print jobs
while ($true) {
    ManagePrintJobs
    Start-Sleep -Seconds 10
}

Taking it one step further?

Yes, we can do that! The script below will compare the timestamps on the print job to the spooler files and try to delete only the files related to the spooled jobs. This should dramatically decrease the stress on users having to re send print jobs if the spooler gets cleared.

Be warned, though, this is experimental.

# Specify the path to the spooler directory
$spoolerDir = "$env:SystemRoot\System32\spool\PRINTERS"

# Define the name of the task
$taskName = "PrintQueueManager"

# Function to write logs
function LogWrite {
    Param ([string]$logstring)

    Add-content "$env:SystemDrive\Scripts\PrintQueueManager.log" -value $logstring
}

# Function to manage print jobs
function ManagePrintJobs {
    try {
        # Check the print queues for errors
        $printJobs = Get-WmiObject -Query "SELECT * FROM Win32_PrintJob" -ErrorAction Stop

        $jobsInError = $printJobs | Where-Object { $_.JobStatus -like '*Error*' }

        if ($jobsInError) {
            LogWrite "$(Get-Date) - Error detected. Clearing spooler..."

            # Stop the spooler service
            Stop-Service -Name Spooler -Force -ErrorAction Stop

            # Clear out the spooler directory for error-related files
            foreach ($job in $jobsInError) {
                $jobTime = $job.ConvertToDateTime($job.TimeSubmitted)
                Get-ChildItem -Path $spoolerDir -Include "*.SPL", "*.SHD" -File -Recurse | ForEach-Object {
                    $fileTime = $_.CreationTime
                    if ([Math]::Abs(($fileTime - $jobTime).TotalSeconds) -le 10) {
                        try {
                            Remove-Item $_.FullName -Force -ErrorAction SilentlyContinue
                        }
                        catch {
                            LogWrite "$(Get-Date) - Failed to delete file: $($_.FullName)"
                        }
                    }
                }

                LogWrite "Cleared job $($job.JobId) from printer $($job.DriverName). Job name was '$($job.Document)', owned by '$($job.Owner)'"
            }

            # Start the spooler service
            Start-Service -Name Spooler -ErrorAction Stop
        }

        # Check the status of the Spooler service and start it if it's stopped
        $spoolerService = Get-Service -Name Spooler -ErrorAction SilentlyContinue
        if ($spoolerService.Status -eq 'Stopped') {
            Start-Service -Name Spooler -ErrorAction SilentlyContinue
            LogWrite "$(Get-Date) - The Spooler service was stopped and has been started again"
        }
    }
    catch {
        LogWrite "$(Get-Date) - An error occurred: $_"
    }
}


# Function to set up the task
function SetupTask {
    try {
        # Check if the old task exists and remove it
        if (Get-ScheduledTask -TaskName "Clear Spooler" -ErrorAction SilentlyContinue) {
            Unregister-ScheduledTask -TaskName "Clear Spooler" -Confirm:$false
            LogWrite "$(Get-Date) - Removed old task Clear Spooler"
        }

        # Check if the new task exists
        if (-not (Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue)) {
            # Register new task to run the script at startup
            $action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument "-File `"$PSCommandPath`""
            $trigger = New-ScheduledTaskTrigger -AtStartup
            $principal = New-ScheduledTaskPrincipal -UserId "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount
            $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -DontStopOnIdleEnd -RestartInterval (New-TimeSpan -Minutes 1) -RestartCount 3

            Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings

            LogWrite "$(Get-Date) - Scheduled task $taskName has been created to run at startup"
        }
    }
    catch {
        LogWrite "$(Get-Date) - An error occurred while setting up the task: $_"
    }
}

# Create the log directory if it doesn't exist
if (-not (Test-Path "$env:SystemDrive\Alamo")) {
    New-Item -ItemType Directory -Force -Path "$env:SystemDrive\Alamo"
}

# Run setup task
SetupTask

# Run manage print jobs
while ($true) {
    ManagePrintJobs
    Start-Sleep -Seconds 300
}

Found priceless insights in this blog? Support the author’s creativity – buy them a coffee!

Leave a Reply

Your email address will not be published. Required fields are marked *