Public/07_Printing_Shares/Get-VBPrintPrintingInfo.ps1
|
# ============================================================ # FUNCTION : Get-VBPrintPrintingInfo # VERSION : 1.0.2 # CHANGED : 10-04-2026 -- Initial VB-compliant release # AUTHOR : Vibhu Bhatnagar # PURPOSE : Comprehensive print job analysis and printer monitoring # ENCODING : UTF-8 with BOM # ============================================================ <# .SYNOPSIS Comprehensive print job analysis and printer monitoring on local and remote systems. .DESCRIPTION Retrieves detailed print job history, printer status, and usage statistics from Windows Print Service event logs. Supports multiple analysis modes: Jobs, Printers, Stats, Monitor, or All-inclusive reports. Data can be exported to Object, Table, or CSV format. .PARAMETER ComputerName Target computer(s). Defaults to local machine. Accepts pipeline input. Supports aliases: Name, Server. .PARAMETER Credential Alternate credentials for remote execution. .PARAMETER Mode Analysis mode: Jobs, Stats, Printers, Monitor, All. Default: Jobs. .PARAMETER Days Look-back period in days. Range: 1-365. Default: 7. .PARAMETER MaxEvents Maximum events to retrieve. Range: 100-10000. Default: 1000. .PARAMETER OutputFormat Output format: Object, Table, CSV. Default: Object. .PARAMETER OutputPath File path for CSV export. Auto-generated if not specified with CSV format. .EXAMPLE Get-VBPrintPrintingInfo .EXAMPLE Get-VBPrintPrintingInfo -ComputerName SERVER01 -Mode Jobs .EXAMPLE 'SRV01','SRV02' | Get-VBPrintPrintingInfo -Mode All -OutputFormat CSV -OutputPath C:\Reports\PrintReport.csv .OUTPUTS [PSCustomObject]: ComputerName, TimeCreated, EventID, Source, PrinterName, UserName, ClientMachine, DocumentName, PagesPrinted, JobSize, RawMessage, Status, CollectionTime .NOTES Version : 1.0.2 Author : Vibhu Bhatnagar Modified : 10-04-2026 Category : Printing #> function Get-VBPrintPrintingInfo { [CmdletBinding()] param( [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias('Name', 'Server')] [string[]]$ComputerName = $env:COMPUTERNAME, [PSCredential]$Credential, [ValidateSet('Jobs', 'Stats', 'Printers', 'Monitor', 'All')] [string]$Mode = 'Jobs', [ValidateRange(1, 365)] [int]$Days = 7, [ValidateRange(100, 10000)] [int]$MaxEvents = 1000, [ValidateSet('Object', 'Table', 'CSV')] [string]$OutputFormat = 'Object', [string]$OutputPath ) begin { $allResults = @() # Step 1 -- Generate output path for CSV if needed if (-not $OutputPath -and ($OutputFormat -eq 'CSV')) { $timestamp = Get-Date -Format 'yyyy-MM-dd_HHmm' $OutputPath = "PrintReport_$timestamp.csv" } # Step 2 -- Enable PrintService event log if disabled try { $logStatus = wevtutil get-log Microsoft-Windows-PrintService/Admin if ($logStatus -match 'enabled:\s*false') { Write-Verbose 'Enabling Microsoft-Windows-PrintService/Admin log' wevtutil set-log Microsoft-Windows-PrintService/Admin /enabled:true } $testEvents = Get-WinEvent -LogName 'Microsoft-Windows-PrintService/Admin' -MaxEvents 1 -ErrorAction SilentlyContinue if (-not $testEvents) { Write-Verbose 'No events found in Microsoft-Windows-PrintService/Admin log.' } } catch { Write-Verbose "Failed to check or enable PrintService log: $($_.Exception.Message)" } } process { foreach ($computer in $ComputerName) { if (-not $computer) { Write-Verbose 'ComputerName is null or empty. Skipping.' continue } try { Write-Verbose "Processing $computer with Mode: $Mode" # Step 3 -- Define nested helper functions and main logic $scriptBlock = { param($Mode, $Days, $MaxEvents, $ComputerTarget) $startTime = (Get-Date).AddDays(-$Days) # Nested helper: Get print job events function Get-PrintJobs { $printEvents = @() try { $filterHash = @{ LogName = 'Microsoft-Windows-PrintService/Admin' StartTime = $startTime ID = 307 } $printEvents = Get-WinEvent -FilterHashtable $filterHash -MaxEvents $MaxEvents -ErrorAction Stop Write-Verbose "Found $($printEvents.Count) print job events." } catch { Write-Verbose "PrintService log not accessible: $($_.Exception.Message)" } foreach ($event in $printEvents) { $eventData = @{} try { $eventXml = [xml]$event.ToXml() if ($eventXml.Event.EventData.Data) { for ($i = 0; $i -lt $eventXml.Event.EventData.Data.Count; $i++) { $eventData["Param$($i+1)"] = $eventXml.Event.EventData.Data[$i].'#text' } } } catch { Write-Verbose "XML parsing failed: $($_.Exception.Message)" } $message = $event.Message $printer = if ($eventData.Param1 -and $eventData.Param1.Trim()) { $eventData.Param1.Trim() } else { 'Unknown' } if ($printer -match "^(.+?)(?:\.|:\s+)(.+?)\.\s*$") { $printer = $matches[2].Trim() } elseif ($message -match "printer[:\s]+([^\r\n,]+)") { $printer = $matches[1].Trim() } $user = if ($eventData.Param2 -and $eventData.Param2.Trim()) { $eventData.Param2.Trim() } else { 'Unknown' } if ($message -match "user[:\s]+([^\r\n,]+)") { $user = $matches[1].Trim() } $document = if ($eventData.Param3 -and $eventData.Param3.Trim()) { $eventData.Param3.Trim() } else { 'Unknown' } if ($message -match "document[:\s]+([^\r\n,]+)") { $document = $matches[1].Trim() } $client = if ($eventData.Param4 -and $eventData.Param4.Trim()) { $eventData.Param4.Trim() } else { 'Unknown' } if ($message -match "(?:client|computer)[:\s]+([^\r\n,.]+)") { $client = $matches[1].Trim() } $pages = 0 if ($eventData.Param5 -and $eventData.Param5 -match '^\d+$') { $pages = [int]$eventData.Param5 } elseif ($message -match "(?:pages?|pages printed)[:\s]*(\d+)") { $pages = [int]$matches[1] } $size = 0 if ($eventData.Param6 -and $eventData.Param6 -match '^\d+$') { $size = [int]$eventData.Param6 } elseif ($message -match "size[:\s]*(\d+)") { $size = [int]$matches[1] } [PSCustomObject]@{ ComputerName = $ComputerTarget TimeCreated = $event.TimeCreated EventID = $event.Id Source = $event.LogName PrinterName = $printer UserName = $user ClientMachine = $client DocumentName = $document PagesPrinted = $pages JobSize = $size RawMessage = $message Status = 'Success' } } try { $activeJobs = Get-CimInstance -ClassName Win32_PrintJob -ErrorAction Stop | Where-Object { $_.TimeSubmitted -ge $startTime } foreach ($job in $activeJobs) { $printerName = if ($job.Name) { ($job.Name -split ',')[0].Trim() } else { 'Unknown' } $owner = if ($job.Owner) { $job.Owner } else { 'Unknown' } $documentName = if ($job.Document) { $job.Document } else { 'Unknown' } $totalPages = if ($job.TotalPages) { $job.TotalPages } else { 0 } $clientMachine = if ($job.HostComputerName) { $job.HostComputerName -replace '.*\\', '' } else { 'Unknown' } [PSCustomObject]@{ ComputerName = $ComputerTarget TimeCreated = $job.TimeSubmitted EventID = 0 Source = 'ActiveJob' PrinterName = $printerName UserName = $owner ClientMachine = $clientMachine DocumentName = $documentName PagesPrinted = $totalPages JobSize = if ($job.Size) { $job.Size } else { 0 } RawMessage = "Active job: $($job.Status)" Status = 'Active' } } } catch { Write-Verbose "Could not retrieve active print jobs: $($_.Exception.Message)" } } # Nested helper: Get printer information function Get-PrinterInfo { try { $printers = Get-CimInstance -ClassName Win32_Printer -ErrorAction Stop $printJobs = Get-PrintJobs foreach ($printer in $printers) { $printerJobs = $printJobs | Where-Object { $_.PrinterName -like "*$($printer.Name)*" } $totalJobs = ($printerJobs | Measure-Object).Count $totalPages = ($printerJobs | Measure-Object PagesPrinted -Sum).Sum $status = switch ($printer.PrinterStatus) { 1 { 'Other' } 2 { 'Unknown' } 3 { 'Idle' } 4 { 'Printing' } 5 { 'Warmup' } 6 { 'Stopped Printing' } 7 { 'Offline' } default { 'Unknown' } } [PSCustomObject]@{ ComputerName = $ComputerTarget PrinterName = $printer.Name Status = $status Location = if ($printer.Location) { $printer.Location } else { 'Not Set' } DriverName = if ($printer.DriverName) { $printer.DriverName } else { 'Unknown' } PortName = if ($printer.PortName) { $printer.PortName } else { 'Unknown' } Shared = $printer.Shared RecentJobs = $totalJobs RecentPages = $totalPages QueuedJobs = if ($printer.JobCount) { $printer.JobCount } else { 0 } LastUsed = if ($printerJobs) { ($printerJobs | Sort-Object TimeCreated -Descending | Select-Object -First 1).TimeCreated } else { 'Never' } } } } catch { [PSCustomObject]@{ ComputerName = $ComputerTarget PrinterName = 'Error' Status = 'Failed' Error = $_.Exception.Message } } } # Nested helper: Get print statistics function Get-PrintStats { $printJobs = Get-PrintJobs | Where-Object { $_.PagesPrinted -gt 0 } if (-not $printJobs) { Write-Verbose 'No print jobs with pages printed found for stats.' return } $stats = $printJobs | Group-Object UserName | ForEach-Object { $totalPages = ($_.Group | Measure-Object PagesPrinted -Sum).Sum $totalJobs = $_.Count $averagePages = if ($totalJobs -gt 0) { [math]::Round($totalPages / $totalJobs, 2) } else { 0 } [PSCustomObject]@{ ComputerName = $ComputerTarget UserName = $_.Name TotalJobs = $totalJobs TotalPages = $totalPages AveragePages = $averagePages AnalysisPeriod = "$Days days" } } return $stats | Sort-Object TotalPages -Descending } # Step 4 -- Execute mode-specific logic switch ($Mode) { 'Jobs' { Get-PrintJobs } 'Printers' { Get-PrinterInfo } 'Stats' { Get-PrintStats } 'All' { @{ Jobs = Get-PrintJobs Printers = Get-PrinterInfo Stats = Get-PrintStats } } 'Monitor' { Get-PrintJobs | Where-Object { $_.TimeCreated -gt (Get-Date).AddMinutes(-5) } } } } # Step 5 -- Execute script block locally or remotely if ($computer -eq $env:COMPUTERNAME) { $result = & $scriptBlock $Mode $Days $MaxEvents $computer } else { $params = @{ ComputerName = $computer ScriptBlock = $scriptBlock ArgumentList = $Mode, $Days, $MaxEvents, $computer } if ($Credential) { $params.Credential = $Credential } $result = Invoke-Command @params } # Step 6 -- Handle Monitor mode output if ($Mode -eq 'Monitor') { if ($result) { Write-Verbose "Recent print activity on $computer" $result | Format-Table TimeCreated, UserName, PrinterName, DocumentName, PagesPrinted -AutoSize } else { Write-Verbose "No recent print activity on $computer" } continue } # Step 7 -- Accumulate results $allResults += $result } catch { $errorResult = [PSCustomObject]@{ ComputerName = $computer Error = $_.Exception.Message Status = 'Failed' TimeCreated = Get-Date CollectionTime = (Get-Date).ToString('dd-MM-yyyy HH:mm:ss') } $allResults += $errorResult } } } end { # Step 8 -- Handle Monitor mode termination if ($Mode -eq 'Monitor') { return } # Step 9 -- Validate results if (-not $allResults) { Write-Verbose 'No data retrieved from any computer. Verify print services and event logs.' } # Step 10 -- Flatten results from All mode if ($Mode -eq 'All') { $consolidatedResults = @() foreach ($result in $allResults) { if ($result -is [Hashtable]) { $consolidatedResults += $result.Jobs $consolidatedResults += $result.Printers $consolidatedResults += $result.Stats } else { $consolidatedResults += $result } } $allResults = $consolidatedResults } # Step 11 -- Output in requested format switch ($OutputFormat) { 'Object' { $allResults } 'Table' { $allResults | Format-Table -AutoSize | Out-String } 'CSV' { $allResults | Export-Csv -Path $OutputPath -NoTypeInformation Write-Verbose "Results exported to: $OutputPath" } } } } |