I work at home for myself which means I have to act as my own assistant, reminding me of important events and tasks. And sometimes I need a little help. So why not use PowerShell? In the past I've used and written about using a background job to wait until a certain number of minutes have passed and then display a popup message using the MSG.EXE command line utility. The drawback to my previous approach is that if I close my PowerShell session I lose the background job. For reminders in the next 10-30 minutes perhaps that's ok. But for longer term reminders, I need a better solution.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
I don't know why I didn't go this route from the beginning, but I can accomplish the same result using a PowerShell scheduled job. A PowerShell scheduled job runs via the task scheduler which means it persists outside of PowerShell. All I really need are three things:
- The time to kick off the task
- The command to run
- A registered scheduled job object
The time to kick off is the Job Trigger. Here's how I can create a trigger for 30 minutes from now. My reminder only needs to run once.
$trigger = New-Jobtrigger -Once -at (Get-Date).AddMinutes(30)
The command will be the MSG.EXE command.
$cmd = "msg.exe $env:username Important phone call in 5 minutes"
This needs to be in the form of a script block.
$sb = { Param($cmd,$jobname) Invoke-Expression $cmd #forcibly remove the scheduledjob Get-ScheduledJob -Name $jobname | Unregister-ScheduledJob -Force }
In my scriptblock I'm also going to delete the scheduled job. Finally I need to register it.
$jobname = "phone call"
Register-ScheduledJob -ScriptBlock $sb -Name $jobName -MaxResultCount 1 -Trigger $Trigger -ArgumentList $cmd,$jobname
When the time arrives I get a popup message on my screen.
sample popup reminder
You can configure how long the message will be displayed before it is automatically dismissed. That's the essential part of the process. Here is the complete script to define the function, including an optional alias.
#requires -version 4.0 #requires -module PSScheduledJob Function New-ScheduledReminderJob { <# .Synopsis Create a scheduled reminder background job. .Description This command uses the MSG.EXE command line tool to send a reminder message to the currently logged on user, presumably yourself. The intention is to set ad-hoc popup reminders for the current user. The message will automatically dismiss after 1 minute unless you use the Wait parameter. You can schedule the reminder for a certain number of minutes set the reminder to run at a specific date and time. The default is to schedule a reminder in 1 minute. The function creates a scheduled background job so that you can close your PowerShell session without losing the job as well as persisting during reboots. The scheduled job will be removed upon completion. .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 JobName A name to assign to the job. If you don't specify a name, the function will use the name Reminder-N where N is an incrementing counter starting at 1. This parameter has an alias of Name. .Parameter Minutes The number of minutes to wait before displaying the popup message. .Parameter Wait The number of minutes to display the message before it automatically is dismissed. The default is 1 minute. .Example PS C:\> new-scheduledreminderjob "Switch over laundry" -minutes 40 -name SwitchLaundry Id Name JobTriggers Command Enabled -- ---- ----------- ------- ------- 1 SwitchLaundry 1 ... True This command creates a new job that will display a message in 40 minutes .Example PS C:\> new-scheduledreminderjob "Go home" -time "5:00PM" -wait 2 | out-null Create a reminder to be displayed at 5:00PM today for 2 minutes but don't display the job result. .Notes Last Updated: July 24, 2015 Version : 2.2 Author : Jeff Hicks (@JeffHicks) https://jdhitsolutions.com/blog Learn more about PowerShell:Essential PowerShell Learning Resources**************************************************************** * 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 msg.exe Register-ScheduledJob .Inputs None .Outputs ScheduledJob #> [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, [Alias("Name")] [string]$JobName, [int]$Wait = 1 ) Begin { Write-Verbose -Message "[Starting] $($MyInvocation.Mycommand)" } #begin Process { Write-Verbose -Message "[Trace] Parameter set = $($PSCmdlet.ParameterSetName)" If ($PSCmdlet.ParameterSetName -eq 'Minutes') { #calculate the scheduled job time from the number of minutes [datetime]$Time= (Get-Date).AddMinutes($minutes) } #create the scheduled job trigger $Trigger = New-JobTrigger -At $Time -Once Write-Verbose "[Trace] Reminder Time = $Time" if (-Not $JobName) { #get last job ID to build the jobname #ignore any errors if job not found Write-Verbose "[Status] Checking for previous Reminder scheduled jobs" $lastjob = Get-ScheduledJob -Name "Reminder*" -ErrorAction SilentlyContinue | sort ID | select -last 1 if ($lastjob) { #define a regular expression [regex]$rx ="\d+$" #extract the counter number [string]$counter = ([int]$rx.Match($lastJob.name).Value +1) } else { [string]$counter = 1 } #define the job name $jobName = "Reminder-$Counter" } #if no job name specified Write-Verbose -Message "[Trace] Jobname = $jobname" #define the msg.exe expression [int]$WaitTime = $Wait * 60 Write-Verbose "[Trace] Display Wait time = $WaitTime seconds" Write-Verbose "[Status] Defining expression" [string]$cmd = "msg.exe $env:username /Time:$WaitTime $Message" Write-Verbose "[Trace] Command to execute = $cmd" #the scriptblock to run $sb = { Param($cmd,$jobname) Invoke-Expression $cmd #forcibly remove the scheduledjob Get-ScheduledJob -Name $jobname | Unregister-ScheduledJob -Force } Write-Verbose "[Trace] Scriptblock = $($sb | Out-String)" #add some options to support laptop users Write-Verbose "[Status] Creating scheduled job options" $options = New-ScheduledJobOption -ContinueIfGoingOnBattery -WakeToRun #create the scheduled job Write-Verbose "[Status] Registering the scheduled reminder job" Register-ScheduledJob -ScriptBlock $sb -Name $jobName -MaxResultCount 1 -Trigger $Trigger -ArgumentList $cmd,$jobname } #process End { Write-Verbose -Message "[Status] Ending $($MyInvocation.Mycommand)" } #end } #end function #add an alias Set-Alias -Name Tickle -Value New-ScheduledReminderJob
Now I can easily set reminders for myself, even something tomorrow, next week or next month. I can use the scheduled job cmdlets to manage my reminders. I wrote this with the assumption that you are setting popup reminders for yourself.
Let me know what you think.
1 thought on “Friday Fun: Tickle Me PowerShell!”
Comments are closed.