a1a17e81a1
Includes updated JS challenge scripts with Claude-User whitelist, same-site referer bypass, Blackbox-Exporter allowed bot, and all new exporters, cheat sheets, and automation scripts.
294 lines
12 KiB
PowerShell
294 lines
12 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Windows Print Server Prometheus Metrics Exporter
|
|
.DESCRIPTION
|
|
Prometheus exporter for Windows Print Server. Monitors print queues, job
|
|
counts, job status, printer errors, spooler health, and driver information.
|
|
Exports metrics as Prometheus-compatible text format for windows_exporter
|
|
textfile collector or standalone HTTP listener.
|
|
.PARAMETER Mode
|
|
Output mode: 'stdout' (default), 'textfile', or 'http'
|
|
.PARAMETER Port
|
|
HTTP port for http mode (default: 9197)
|
|
.PARAMETER TextfileDir
|
|
Directory for textfile collector output (default: C:\ProgramData\node_exporter)
|
|
.PARAMETER OutputFile
|
|
Custom output file path
|
|
.PARAMETER InstallScheduledTask
|
|
Switch to create a scheduled task for automatic collection
|
|
.PARAMETER TaskIntervalMinutes
|
|
Interval in minutes for the scheduled task (default: 2)
|
|
.NOTES
|
|
Author: Phil Connor
|
|
Contact: contact@mylinux.work
|
|
Website: https://mylinux.work
|
|
License: MIT
|
|
Version: 1.0
|
|
#>
|
|
|
|
param(
|
|
[switch]$TextFile,
|
|
[string]$OutFile = "",
|
|
[switch]$Install,
|
|
[string]$Listen = "",
|
|
[int]$Interval = 120
|
|
)
|
|
|
|
$ErrorActionPreference = "SilentlyContinue"
|
|
$Version = "1.0"
|
|
$TextfileDir = "C:\ProgramData\node_exporter"
|
|
$MetricPrefix = "windows_print"
|
|
|
|
# ============================================================================
|
|
# HELPER FUNCTIONS
|
|
# ============================================================================
|
|
|
|
function Get-PrometheusEscape {
|
|
param([string]$Value)
|
|
$Value -replace '\\', '\\' -replace '"', '\"' -replace "`n", ''
|
|
}
|
|
|
|
function Write-MetricHeader {
|
|
param([string]$Name, [string]$Help, [string]$Type)
|
|
"# HELP $Name $Help"
|
|
"# TYPE $Name $Type"
|
|
}
|
|
|
|
# ============================================================================
|
|
# METRIC GENERATION
|
|
# ============================================================================
|
|
|
|
function Get-PrintMetrics {
|
|
$startTime = Get-Date
|
|
$metrics = [System.Collections.ArrayList]::new()
|
|
|
|
# Check if Print Spooler is running
|
|
$spooler = Get-Service -Name "Spooler" -ErrorAction SilentlyContinue
|
|
$spoolerUp = if ($spooler -and $spooler.Status -eq "Running") { 1 } else { 0 }
|
|
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_up" "Print server exporter status (1=up, 0=down)" "gauge"))
|
|
[void]$metrics.Add("${MetricPrefix}_up $spoolerUp")
|
|
[void]$metrics.Add("")
|
|
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_exporter_info" "Exporter version information" "gauge"))
|
|
[void]$metrics.Add("${MetricPrefix}_exporter_info{version=`"$Version`"} 1")
|
|
[void]$metrics.Add("")
|
|
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_spooler_up" "Print Spooler service status" "gauge"))
|
|
[void]$metrics.Add("${MetricPrefix}_spooler_up $spoolerUp")
|
|
[void]$metrics.Add("")
|
|
|
|
if ($spoolerUp -eq 0) {
|
|
return ($metrics -join "`n")
|
|
}
|
|
|
|
# ========================================================================
|
|
# PRINTER METRICS
|
|
# ========================================================================
|
|
|
|
$printers = Get-Printer -ErrorAction SilentlyContinue
|
|
|
|
$totalPrinters = 0
|
|
$onlinePrinters = 0
|
|
$offlinePrinters = 0
|
|
$errorPrinters = 0
|
|
|
|
if ($printers) {
|
|
$totalPrinters = @($printers).Count
|
|
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_printers_total" "Total configured printers" "gauge"))
|
|
[void]$metrics.Add("${MetricPrefix}_printers_total $totalPrinters")
|
|
[void]$metrics.Add("")
|
|
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_printer_status" "Printer status (1=current state)" "gauge"))
|
|
|
|
foreach ($printer in $printers) {
|
|
$name = Get-PrometheusEscape $printer.Name
|
|
$status = $printer.PrinterStatus.ToString()
|
|
$shared = if ($printer.Shared) { "true" } else { "false" }
|
|
|
|
[void]$metrics.Add("${MetricPrefix}_printer_status{printer=`"$name`",status=`"$status`",shared=`"$shared`"} 1")
|
|
|
|
switch ($status) {
|
|
"Normal" { $onlinePrinters++ }
|
|
"Offline" { $offlinePrinters++ }
|
|
default { $errorPrinters++ }
|
|
}
|
|
}
|
|
[void]$metrics.Add("")
|
|
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_printers_online" "Online printers" "gauge"))
|
|
[void]$metrics.Add("${MetricPrefix}_printers_online $onlinePrinters")
|
|
[void]$metrics.Add("")
|
|
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_printers_offline" "Offline printers" "gauge"))
|
|
[void]$metrics.Add("${MetricPrefix}_printers_offline $offlinePrinters")
|
|
[void]$metrics.Add("")
|
|
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_printers_error" "Printers in error state" "gauge"))
|
|
[void]$metrics.Add("${MetricPrefix}_printers_error $errorPrinters")
|
|
[void]$metrics.Add("")
|
|
}
|
|
|
|
# ========================================================================
|
|
# PRINT JOB METRICS
|
|
# ========================================================================
|
|
|
|
$jobs = Get-PrintJob -PrinterName * -ErrorAction SilentlyContinue
|
|
|
|
$totalJobs = 0
|
|
$printingJobs = 0
|
|
$queuedJobs = 0
|
|
$errorJobs = 0
|
|
$totalPages = 0
|
|
$totalBytes = 0
|
|
|
|
if ($jobs) {
|
|
$totalJobs = @($jobs).Count
|
|
|
|
foreach ($job in $jobs) {
|
|
$totalPages += $job.TotalPages
|
|
$totalBytes += $job.Size
|
|
|
|
switch ($job.JobStatus) {
|
|
{ $_ -match "Printing" } { $printingJobs++ }
|
|
{ $_ -match "Error" } { $errorJobs++ }
|
|
default { $queuedJobs++ }
|
|
}
|
|
}
|
|
}
|
|
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_jobs_total" "Total print jobs in queue" "gauge"))
|
|
[void]$metrics.Add("${MetricPrefix}_jobs_total $totalJobs")
|
|
[void]$metrics.Add("")
|
|
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_jobs_printing" "Jobs currently printing" "gauge"))
|
|
[void]$metrics.Add("${MetricPrefix}_jobs_printing $printingJobs")
|
|
[void]$metrics.Add("")
|
|
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_jobs_queued" "Jobs waiting in queue" "gauge"))
|
|
[void]$metrics.Add("${MetricPrefix}_jobs_queued $queuedJobs")
|
|
[void]$metrics.Add("")
|
|
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_jobs_error" "Jobs in error state" "gauge"))
|
|
[void]$metrics.Add("${MetricPrefix}_jobs_error $errorJobs")
|
|
[void]$metrics.Add("")
|
|
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_jobs_pages_total" "Total pages across all queued jobs" "gauge"))
|
|
[void]$metrics.Add("${MetricPrefix}_jobs_pages_total $totalPages")
|
|
[void]$metrics.Add("")
|
|
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_jobs_bytes_total" "Total bytes across all queued jobs" "gauge"))
|
|
[void]$metrics.Add("${MetricPrefix}_jobs_bytes_total $totalBytes")
|
|
[void]$metrics.Add("")
|
|
|
|
# Per-printer job counts
|
|
if ($printers -and $jobs) {
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_queue_jobs" "Jobs per printer queue" "gauge"))
|
|
foreach ($printer in $printers) {
|
|
$name = Get-PrometheusEscape $printer.Name
|
|
$pJobs = @($jobs | Where-Object { $_.PrinterName -eq $printer.Name }).Count
|
|
[void]$metrics.Add("${MetricPrefix}_queue_jobs{printer=`"$name`"} $pJobs")
|
|
}
|
|
[void]$metrics.Add("")
|
|
}
|
|
|
|
# ========================================================================
|
|
# PRINT PORT METRICS
|
|
# ========================================================================
|
|
|
|
$ports = Get-PrinterPort -ErrorAction SilentlyContinue
|
|
if ($ports) {
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_ports_total" "Total configured printer ports" "gauge"))
|
|
[void]$metrics.Add("${MetricPrefix}_ports_total $(@($ports).Count)")
|
|
[void]$metrics.Add("")
|
|
}
|
|
|
|
# ========================================================================
|
|
# DRIVER METRICS
|
|
# ========================================================================
|
|
|
|
$drivers = Get-PrinterDriver -ErrorAction SilentlyContinue
|
|
if ($drivers) {
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_drivers_total" "Total installed printer drivers" "gauge"))
|
|
[void]$metrics.Add("${MetricPrefix}_drivers_total $(@($drivers).Count)")
|
|
[void]$metrics.Add("")
|
|
}
|
|
|
|
# ========================================================================
|
|
# PERFORMANCE COUNTERS
|
|
# ========================================================================
|
|
|
|
$perfCounter = Get-Counter -Counter "\Print Queue(_Total)\Total Jobs Printed" -ErrorAction SilentlyContinue
|
|
if ($perfCounter) {
|
|
$totalPrinted = [math]::Round($perfCounter.CounterSamples[0].CookedValue)
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_jobs_printed_total" "Total jobs printed (lifetime)" "counter"))
|
|
[void]$metrics.Add("${MetricPrefix}_jobs_printed_total $totalPrinted")
|
|
[void]$metrics.Add("")
|
|
}
|
|
|
|
# ========================================================================
|
|
# EXPORTER RUNTIME
|
|
# ========================================================================
|
|
|
|
$endTime = Get-Date
|
|
$duration = [math]::Round(($endTime - $startTime).TotalSeconds, 2)
|
|
$timestamp = [math]::Round((Get-Date -UFormat %s), 0)
|
|
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_exporter_duration_seconds" "Script execution time" "gauge"))
|
|
[void]$metrics.Add("${MetricPrefix}_exporter_duration_seconds $duration")
|
|
[void]$metrics.Add("")
|
|
|
|
[void]$metrics.Add((Write-MetricHeader "${MetricPrefix}_exporter_last_run_timestamp" "Unix timestamp of last run" "gauge"))
|
|
[void]$metrics.Add("${MetricPrefix}_exporter_last_run_timestamp $timestamp")
|
|
|
|
return ($metrics -join "`n")
|
|
}
|
|
|
|
# ============================================================================
|
|
# OUTPUT MODES
|
|
# ============================================================================
|
|
|
|
if ($Install) {
|
|
$scriptPath = $MyInvocation.MyCommand.Path
|
|
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
|
|
-Argument "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`" -TextFile"
|
|
$trigger = New-ScheduledTaskTrigger -RepetitionInterval (New-TimeSpan -Seconds $Interval) -Once -At (Get-Date)
|
|
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
|
|
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable
|
|
Register-ScheduledTask -TaskName "Print Server Metrics Exporter" -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Force
|
|
Write-Host "Scheduled task 'Print Server Metrics Exporter' created (interval: ${Interval}s)"
|
|
exit 0
|
|
}
|
|
|
|
if ($Listen -ne "") {
|
|
$port = $Listen -replace '.*:', ''
|
|
if (-not $port) { $port = "9197" }
|
|
$listener = [System.Net.HttpListener]::new()
|
|
$listener.Prefixes.Add("http://+:$port/")
|
|
$listener.Start()
|
|
Write-Host "Print Server exporter listening on port $port..."
|
|
|
|
while ($listener.IsListening) {
|
|
$context = $listener.GetContext()
|
|
$response = $context.Response
|
|
$output = Get-PrintMetrics
|
|
$buffer = [System.Text.Encoding]::UTF8.GetBytes($output)
|
|
$response.ContentType = "text/plain; version=0.0.4"
|
|
$response.ContentLength64 = $buffer.Length
|
|
$response.OutputStream.Write($buffer, 0, $buffer.Length)
|
|
$response.Close()
|
|
}
|
|
} elseif ($TextFile -or $OutFile -ne "") {
|
|
$outputPath = if ($OutFile -ne "") { $OutFile } else { Join-Path $TextfileDir "print-server.prom" }
|
|
$outputDir = Split-Path $outputPath -Parent
|
|
if (-not (Test-Path $outputDir)) { New-Item -ItemType Directory -Path $outputDir -Force | Out-Null }
|
|
$tempFile = Join-Path $outputDir ".print-server-metrics.tmp"
|
|
$metricsOutput = Get-PrintMetrics
|
|
$metricsOutput | Out-File -FilePath $tempFile -Encoding utf8 -NoNewline
|
|
Move-Item -Path $tempFile -Destination $outputPath -Force
|
|
Write-Host "Metrics written to $outputPath"
|
|
} else {
|
|
Get-PrintMetrics
|
|
}
|