Print Queue Status Monitor with Email
I have a brand new Windows Server 2012 R2 Datacenter virtual server that is setup as a print server. I saw the custom filters in the Print Management console but found out that notifications only work with that console open. Since I'm not logged
into that machine all the time, I need a powershell script that can do what the custom filters do in the Print Management console. Here's what I'm looking for:
1. When the print server starts the script starts. This script should always stay active.
2. The script polls the print queues for a change in status to anything other than Ready every 5 minutes or so.
3. If the printer name contains "CC2", then email certain people the status of that particular printer. Only email them once so as to not spam them with constant emails (maybe 1 email per day per status change).
So basically I'm looking to email my customer service department if one of their printers goes to a status of anything other than Ready. Is something like this possible with Powershell?
January 29th, 2015 5:10pm
Hi James,
yes indeed, that is possible. You may want to consider running this script frequently by task scheduler however, instead of having it run perpetually. If you don't watch your memory usage it may slow down your server over time.
Cheers,
Fred
January 29th, 2015 5:16pm
Hi James,
yes indeed, that is possible. You may want to consider running this script frequently by task scheduler however, instead of having it run perpetually. If you don't watch your memory usage it may slow down your server over time.
Cheers,
January 29th, 2015 5:21pm
I'm fairly new to Powershell so is there a script already written that can do something like this or maybe a template I could modify?
January 29th, 2015 5:24pm
[...] but spinning up and tearing down a PS session every few minutes to do something fairly trivial wastes a lot of CPU resource.
Definitely a point to keep in mind.
About templates, James:
I don't know - you may want to check the Gallery for that kind of thing. However, I have written a guide you may find helpful if you want to tackle this yourself:
Scripting without knowing PowerShell.
Adding to that, you can set up a timer as Mjolinor mentioned by wrapping the script into this:
# This goes before your script:
# Time limit when the script expires
$Limit = (Get-Date).AddDays(1)
# Seconds to wait after each execution
$SleepSeconds = 300
while ((Get-Date) -lt $Limit)
{
######################
# Insert Script here #
######################
# This goes at the end of the script:
# Wait a little before repeating
Start-Sleep $SleepSeconds
}
Cheers,
Fred
January 29th, 2015 5:50pm
I was thinking of an infinite loop like this:
$SleepSeconds = 300
while ($true)
{
# Script
Start-Sleep $SleepSeconds
}
In the task scheduler, I would run the script daily at 6:00am and in the settings I would have these options:
If running task does not end when requested, force it stop.
If the task is already running then the following rule applies:
Stop the existing instance.
I believe this would allow the script to run without expiring and let the task scheduler kill the task and start a new one each day. Does this seem correct?
January 29th, 2015 6:16pm
Hi James,
that would certainly work well, however I try not to kill a process if I can have it end naturally without a major effort (And my snippet isn't that much extra effort now, is it? :) )
Cheers,
Fred
January 29th, 2015 6:28pm
Yeah, I think I'll add your limit as a safeguard so if the task scheduler doesn't kill the task it will end on its own.
Could you point me in the right direction as to where I could find some example scripts of print queue status scripts?
January 29th, 2015 6:55pm
[...] but spinning up and tearing down a PS session every few minutes to do something fairly trivial wastes a lot of CPU resource.
Definitely a point to keep in mind.
About templates, James:
I don't know - you may want to check the Gallery for that kind of thing. However, I have written a guide you may find helpful if you want to tackle this yourself:
Scripting without knowing PowerShell.
Adding to that, you can set up a timer as Mjolinor mentioned by wrapping the script into this:
# This goes before your script:
# Time limit when the script expires
$Limit = (Get-Date).AddDays(1)
# Seconds to wait after each execution
$SleepSeconds = 300
while ((Get-Date) -lt $Limit)
{
######################
# Insert Script here #
######################
# This goes at the end of the script:
# Wait a little before repeating
Start-Sleep $SleepSeconds
}
Cheers,
January 29th, 2015 7:07pm
PS sessions are not expensive. They start and run verry efficiently. PS wass designed to be run this way. Just besure to load with no profile and explicitly load all modules you need,
January 29th, 2015 10:11pm
I'm looking into this command:
Get-Printer -computername MYSERVER | Where {($_.PrinterStatus -eq "Offline") -and ($_.Name -Match "CC2")} | Select Name,PrinterStatus,Comment,Location,Portname
I'm guessing I need to put that information in an array and save it so I can compare further runs to the saved state and see if any status changes have occurred. This way the users will only get an email if the status changes and not every 5 minutes
when the script loops. I'm still trying to figure out that part.
January 29th, 2015 10:16pm
Do you know you can have vents emamiled from the eventlog including changes in printer status. This would be the better way to administer a Windows system.
Find the printer que event IDs and righ click and make a task out of the event.
January 29th, 2015 10:25pm
PS sessions are not expensive. They start and run verry efficiently. PS wass designed to be run this way. Just besure to load with no profile and explicitly load all modules
January 29th, 2015 10:41pm
That says your system has nothing better to do so you are getting full CPU access. Windows NT optimizes loads if the CPU is not real busy. THe overhead is trivial. If you are staritng it every 5 minutes it will pose no issue.
We do that all the time. I have processes that can run once a minute.
January 29th, 2015 10:51pm
Do you know you can have vents emamiled from the eventlog including changes in printer status. This would be the better way to administer a Windows system.
Find the printer que event IDs and righ click and make a task out of the event.
January 29th, 2015 10:54pm
I recommend searching for printer events
January 29th, 2015 10:58pm
That says your system has nothing better to do so you are getting full CPU access. Windows NT optimizes loads if the CPU is not real busy. THe overhead is trivial. If you are staritng it every 5 minutes it will pose no issue.
We do that all the time. I have processes that can run once a minute.
January 30th, 2015 12:04am
That says your system has nothing better to do so you are getting full CPU access. Windows NT optimizes loads if the CPU is not real busy. THe overhead is trivial. If you are staritng it every 5 minutes it will pose no issue.
We do that all the time. I have processes that can run once a minute.
January 30th, 2015 1:02am
It's quantified. That's a start.
January 30th, 2015 1:09am
If you go to performmance school they will teach you that, when you cannot pin the processor by creating a high losd it means that you are runnning out of resources. ht does "used up" mean. How fast did you sample.
The basic processor display in W8 and W7 both give poor renditions of processor usage. Use perfmon on a fast scan to get a more accuratie. view. What was happenig with memory and disk? Did any processes or services complain or fail?
When starting fromm the GUI the ssystem throttles the uer I/F so it may see sluggish. Perf tools do thing lie send commm packets at a high speed to try and measure impact.
Starting a copy of PowerShell ever 5 minutes to do a small query is very trivial. It has little impact.
I have a small sscript that runs every minute. It just check a couple of directories. I buit it as a test a year ago and forgot about it. It is still running. I see no eveidence of it and it is on a very slow laptop. (dual core 1.5
mhz w/4gb) Outlook retrieving mail is more disruptive.
I also have VS2013, SQLServer, REprt Server and web server all running and the processor is mmostle at 3% to 6%. If I start any process the processor pins at 100% for a cople of seconds then returns to normal. Only
notepad has no noticeable impact.
Somewhere on MSDN are some good docs explaining the launch process and how it uses the CPU.
January 30th, 2015 7:15am
It was, admittedly a "rough" test. I monitored it using Resource Monitor on W7. The processor cores went to 100%, physical memory usage to about 35%. Disk I/O was high but the queue length didn't get over 5 and I suspect most
of the reads were from cache.
Nothing crashed or failed, but the UI was totally unresponsive.
January 30th, 2015 2:11pm
It was, admittedly a "rough" test. I monitored it using Resource Monitor on W7. The processor cores went to 100%, physical memory usage to about 35%. Disk I/O was high but the queue length didn't get over 5 and I suspect most
of the reads were from cache.
Nothing crashed or failed, but the UI was totally unrespo
January 30th, 2015 8:07pm
It was, admittedly a "rough" test. I monitored it using Resource Monitor on W7. The processor cores went to 100%, physical memory usage to about 35%. Disk I/O was high but the queue length didn't get over 5 and I suspect most
of the reads were from cache.
Nothing crashed or failed, but the UI was totally unrespo
January 30th, 2015 8:22pm
My scripts compares the customer service printers (printer names starting with CC2) status every 5 minutes. If the status has changed and there are Offline printers then customer service gets an email. I'm using Task Scheduler to start the script
at 6:00am every day and the script will quit around 11:00pm. It's not very elegant but it will get the job done. Thanks for all your help!
# Time limit when the script expires
$Limit = (Get-Date).AddDays(1)
$SleepSeconds = 300
$PreviousStatus = @()
while ((Get-Date) -lt $Limit)
{
$CurrentStatus = Get-Printer -computername SERVER | Where {($_.PrinterStatus -ne "Normal") -and ($_.Name -Match "CC2")} | Select Name,PrinterStatus
if ($CurrentStatus)
{
if (Compare-Object $CurrentStatus $PreviousStatus -property Name,PrinterStatus)
{
# Establish Connection to SMTP server
$a = "<style>"
$a = $a + "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}"
$a = $a + "TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;}"
$a = $a + "TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;}"
$a = $a + "</style>"
$filedate = get-date
$smtpserver = smtp.gmail.com
$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient($smtpServer )
$smtp.EnableSsl = $True
$smtp.Credentials = New-Object System.Net.NetworkCredential(from@nobody.com, password); # Put username without the @GMAIL.com or @gmail.com
$msg.IsBodyHtml = $True
$msg.From = from@nobody.com
$msg.To.Add(me@nobody.com)
$msg.To.Add(cserve1@nobody.com)
$msg.To.Add(cserve2@nobody.com)
$msg.To.Add(cserve3@nobody.com)
$msg.To.Add("cserve4@nobody.com")
$msg.Subject = "Printer Status Errors $($filedate)"
$msg.Body = Write-Output -InputObject (Get-Printer -computername SERVER | Where {($_.PrinterStatus -ne "Normal") -and ($_.Name -Match "CC2")} | Select Name,PrinterStatus,Comment,Location,Portname) | ConvertTo-Html -Head $a
$smtp.Send($msg)
$PreviousStatus = $CurrentStatus
}
}
Start-Sleep -Seconds $SleepSeconds
if ((get-date).Hour -eq 23) {Exit}
}
January 30th, 2015 9:03pm
It was, admittedly a "rough" test. I monitored it using Resource Monitor on W7. The processor cores went to 100%, physical memory usage to about 35%. Disk I/O was high but the queue length didn't get over 5 and I suspect most
of the reads were from cache.
Nothing crashed or failed, but the UI was totally unrespo
January 30th, 2015 9:06pm
You can still just schedule it to run every 5 minutes. That would be safer and easier to manage as you could update the script and it would run next time.
You script will also only test one pronter and fail if there is more than one matching the name.
January 30th, 2015 9:08pm
It also gave me some approximate metrics on "trivial".
January 30th, 2015 9:09pm
Here is an approach that can be scheduled ever 5 minutes. It is unnoticeable when it runs a scheduled task.
If you want better performance then we would place the status in the registry instead of in files
# constants
$laststatusfile = 'c:\temp\laststatus.clixml'
$currentstatusfile = 'c:\temp\currentstatus.clixml'
$server = 'server'
$style = @'
<style>
TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}
TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;}
TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;}
</style>
'@
$mailprops = @{
Smtpserver = 'smtp.gmail.com'
UseSSL = $true
Credentials = $creds
IsBodyHtml = $true
From = 'from@nobody.com'
To = 'me@nobody.com', 'cserve1@nobody.com', 'cserve2@nobody.com', 'serve3@nobody.com', 'cserve4@nobody.com'
Subject = "Printer Status Errors $($filedate)"
}
# persist
Get-Printer CC2* -computername $server | Export-Clixml $currentstatusfile
# conditionally import the last status
$PreviousStatus=Import-Clixml $laststatusfile -ea 0
if($PreviousStatus){
$CurrentStatus = Import-Clixml $currentstatusfile
if (Compare-Object $CurrentStatus $PreviousStatus -property Name, PrinterStatus) {
$body = Get-Printer CC2*-computername $server |
Where{ $_.PrinterStatus -ne 'Normal' } |
Select Name, PrinterStatus, Comment, Location, Portname |
ConvertTo-Html -Head $style |
Out-String
Send-MailMessage @mailprops -Body $body
}
Remove-Item $laststatusfile
Rename-Item $currentstatusfile $laststatusfile
}
Even if you don't want to use it it shows some better methods for coding this kind of thing.
January 30th, 2015 9:51pm
Using some of jrv's code, I trimmed down my script. I like putting the static variables outside the loop since there's no need to have reset inside the loop.
# Time limit when the script expires
$Limit = (Get-Date).AddDays(1)
# Sleep Time
$SleepSeconds = 300
# Previous Status array
$PreviousStatus = @()
$server = "SERVER"
$smtpserver = smtp.gmail.com
$style = @"
<style>
TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}
TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;}
TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;}
</style>
"@
$secpasswd = ConvertTo-SecureString "Password" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential("nobody@nowhere.com",$secpasswd)
$mailprops = @{
Smtpserver = "smtp.gmail.com"
UseSSL = $true
Credential = $cred
BodyAsHtml = $true
From = "nobody@nowhere.com"
To = "me@nowhere.com", "cserve1@nowhere.com"
}
while ((Get-Date) -lt $Limit)
{
$CurrentStatus = Get-Printer CC2* -computername $server | Where {($_.PrinterStatus -ne "Normal")} | Select Name, PrinterStatus, Comment, Location, Portname
if ($CurrentStatus)
{
if (Compare-Object $CurrentStatus $PreviousStatus -property Name,PrinterStatus)
{
# Establish Connection to SMTP server
$body = $CurrentStatus | ConvertTo-Html -Head $style | Out-String
$subject = "Printer Status Errors $(get-date)"
Send-MailMessage @mailprops -Body $body -Subject $subject
$PreviousStatus = $CurrentStatus
}
}
Start-Sleep -Seconds $SleepSeconds
if ((get-date).Hour -eq 23) {Exit}
}
January 31st, 2015 12:32am
I am glad you found it useful.
January 31st, 2015 12:52am