This is something that might be better suited to one of my Friday Fun columns, but I'm enjoying this so much I couldn't wait to share it. I don't know about you but I spend much of my day in PowerShell or at least with a PowerShell session running. I have an ongoing quest to do as much as I can from PowerShell. This includes just about any sort of task that might be automated.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
In the past I've blogged about my tickle system that run when I start PowerShell. But often I have short term tickler or reminder needs. For example, since I work at home I may need to remember to switch over the laundry or that I have a phone call at 2:00PM. Sure, I could set up calendar alerts in an email client but I'd rather have something as quick and easy as a PowerShell command. So I wrote this script, New-Reminderjob.ps1.
#requires -version 3.0 <# .Synopsis Create a reminder background job. .Description This command uses the MSG.EXE command line tool to send a reminder message to currently logged on user. You can specify how many minutes to wait before displaying the message or you can set the alert to run at a specific date and time. This command creates a background job in the current PowerShell session. If you close the session, the job will also be removed. This command is intended to set ad-hoc reminders for the current user. The message will automatically dismiss after 1 minute unless you use -Wait. Even though Start-Job doesn't support -WhatIf, this command does. This script will also add some custom properties to the job object so that you can track that status of your reminders. See the examples. NOTE: Be aware that each running reminder will start a new PowerShell process. .Parameter Message The text to display in the popup. .Parameter Time The date and time to display the popup. If you enter just a time, it will default to the current day. See examples. This parameter has aliases of date and dt. .Parameter Minutes The number of minutes to wait before displaying the popup message. .Parameter Wait Force the user to acknowledge the popup message. .Example PS C:\> c:\scripts\new-reminderjob.ps1 "Switch over laundry" -minutes 40 -wait This command creates a new job that will display a message in 40 minutes and wait for the user to acknowledge. .Example PS C:\> c:\scripts\new-reminderjob.ps1 "Go home" -time "5:00PM" -passthru Id Name PSJobTypeName State HasMoreData Location Command -- ---- ------------- ----- ----------- -------- ------- 49 Reminder7 BackgroundJob Running True localhost ... Create a reminder to be displayed at 5:00PM today. The job object is written to the pipeline because of -Passthru .Example PS C:\> get-job remind* | Sort Time | Select ID,Name,State,Message,Time,Wait | format-table -auto Id Name State Message Time Wait -- ---- ----- ------- ---- ---- 67 Reminder1 Running switch over laundry 5/27/2014 2:34:33 PM True 69 Reminder2 Running Budget meeting 5/27/2014 3:00:00 PM False 71 Reminder3 Running reboot WSUS 5/27/2014 3:21:33 PM False In this example, PowerShell is getting all reminder jobs sorted by the time they will "kick off" and displays the necessary properties. .Notes Last Updated: 5/27/2014 Version : 0.9 Author : Jeff Hicks (@JeffHicks) https://jdhitsolutions.com/blog Learn more: PowerShell in Depth: An Administrator's Guide (http://www.manning.com/jones2/) PowerShell Deep Dives (http://manning.com/hicks/) Learn PowerShell 3 in a Month of Lunches (http://manning.com/jones3/) Learn PowerShell Toolmaking in a Month of Lunches (http://manning.com/jones4/) **************************************************************** * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED * * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK. IF * * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, * * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING. * **************************************************************** .Link https://jdhitsolutions.com/blog/2014/05/powershell-reminder-jobs .Link msg.exe Start-Sleep Start-Job .Inputs None .Outputs custom System.Management.Automation.PSRemotingJob #> [cmdletbinding(DefaultParameterSetName="Minutes",SupportsShouldProcess)] Param( [Parameter(Position=0,Mandatory,HelpMessage="Enter the alert message text")] [string]$Message, [Parameter(ParameterSetName="Time")] [ValidateNotNullorEmpty()] [Alias("date","dt")] [datetime]$Time, [Parameter(ParameterSetName="Minutes")] [ValidateNotNullorEmpty()] [int]$Minutes=1, [switch]$Wait, [switch]$Passthru ) Begin { Write-Verbose -Message "Starting $($MyInvocation.Mycommand)" Write-Verbose -Message "Using parameter set $($PSCmdlet.ParameterSetName)" Switch ($PSCmdlet.ParameterSetName) { "Time" { [int]$sleep = ($Time - (Get-Date)).TotalSeconds } "Minutes" { [int]$sleep = $minutes*60} } #get last job ID $lastjob = Get-Job -Name "Reminder*" | sort ID | select -last 1 if ($lastjob) { #define a regular expression [regex]$rx ="\d+$" [string]$counter = ([int]$rx.Match($lastJob.name).Value +1) } else { [string]$counter = 1 } } #begin Process { Write-Verbose -message "Sleeping for $sleep seconds" $sb = { Param($sleep,$cmd) Start-Sleep -seconds $sleep ; Invoke-Expression $cmd } [string]$cmd = "msg.exe $env:username" if ($Wait) { Write-Verbose "Reminder will wait for user" $cmd+=" /W" } $cmd+=" $message" $jobName = "Reminder$Counter" Write-Verbose -Message "Creating job $jobname" #WhatIf $whatif = "'{0}' in {1} seconds" -f $message,$sleep if ($PSCmdlet.ShouldProcess( $whatif )) { $job = Start-Job -ScriptBlock $sb -ArgumentList $sleep,$cmd -Name $jobName #add some custom properties to the job object $job | Add-Member -MemberType NoteProperty -Name Message -Value $message $job | Add-Member -MemberType NoteProperty -Name Time -Value (Get-Date).AddSeconds($sleep) $job | Add-Member -MemberType NoteProperty -Name Wait -Value $Wait if ($passthru) { #if -Passthru write the job object to the pipeline $job } } } #process End { Write-Verbose -Message "Do not close this PowerShell session or you will lose the reminder job" Write-Verbose -Message "Ending $($MyInvocation.Mycommand)" } #end
In short, the script creates a background job that will use the MSG.EXE command line tool to display a message to myself. The job action sleeps the specified number of seconds and then runs my MSG.EXE command. I could have used a variety of ways to display the message, but MSG.EXE is built in and I didn't see any reason to re-invent the wheel.
The script, which you could revise into a function if you want, requires the reminder text and then when to "deliver" the reminder. You can specify a number of minutes, the default is 1, or a date and/or time. If you specify a time like "9:00AM" the script will assume you mean 9AM today. The script converts your start time into a number of seconds it has to wait and builds that into the job scriptblock.
PS C:\scripts> .\New-ReminderJob.ps1 "Budget meeting" -date "5/27/2014 3:00PM" -wait
When the time comes I get a popup message like this.
The message will automatically dismiss after about 1 minute. Or I've configure my script to allow you to require that you acknowledge the message through the -Wait parameter. This is useful for important reminders you want to make sure you don't miss.
Because I might have several daily reminders, I wanted an easy way to identify them. One thing I did with my script is to give all of my reminder jobs a custom name that starts with 'Reminder'. I use a regular expression to find the number from the most recent reminder job and increment it by one. The other useful step is that I added some custom properties to the job object itself. These properties embed values from the script into the job object. Now I can do interesting commands like this:
The custom properties have no effect on any other job objects. If I find myself using these properties a lot, I might create some additional functions to save some typing.
This system is meant for ad-hoc, daily reminders to myself which is why I didn't use scheduled jobs. I didn't want to have to deal with cleaning up a bunch of one time jobs. These reminder jobs only last for as long as my PowerShell session is open. But be aware, that each running reminder will start a new PowerShell process so I wouldn't recommend setting this up with dozens of reminders. Actually, if you need that many reminders you either need to get a new job or an assistant!
I hope you'll try it out and let me know what you think or where you think it can be improved. Enjoy!
1 thought on “PowerShell Reminder Jobs”
Comments are closed.