MyReminder.psm1
#region defined functions Function New-ScheduledReminderJob { [cmdletbinding(DefaultParameterSetName = "Minutes", SupportsShouldProcess)] [OutputType("My.ScheduledReminder")] [Alias("remind","nsrj")] Param( [Parameter( Position = 0, Mandatory, HelpMessage = "Enter the alert message text", ValueFromPipelineByPropertyName )] [string]$Message, [Parameter(ValueFromPipelineByPropertyName)] [Parameter(ParameterSetName = 'Daily')] [Parameter(ParameterSetName = 'Weekly')] [Parameter(ParameterSetName = 'Once')] [ValidateNotNullorEmpty()] [Alias("date", "dt", "when", "time")] [datetime]$At, [Parameter(ParameterSetName = 'Minutes')] [int]$Minutes = 1, [Parameter(ParameterSetName = 'Once', ValueFromPipelineByPropertyName)] [switch]$Once, [Parameter(ParameterSetName = 'Daily', ValueFromPipelineByPropertyName)] [switch]$Daily, [Parameter(ParameterSetName = 'Daily', ValueFromPipelineByPropertyName)] [int]$DaysInterval = 1, [Parameter(ParameterSetName = 'Weekly', ValueFromPipelineByPropertyName)] [switch]$Weekly, [Parameter(ParameterSetName = 'Weekly', ValueFromPipelineByPropertyName)] [int]$WeeksInterval = 1, [Parameter(ParameterSetName = 'Weekly', Mandatory, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [System.DayOfWeek[]]$DaysOfWeek, [Parameter( HelpMessage = "The name of a scheduled reminder job.", ValueFromPipelineByPropertyName )] [Alias("Name")] [string]$JobName, [Parameter( HelpMessage = "The number of minutes to display the popup message", ValueFromPipelineByPropertyName )] [ValidateScript( {$_ -ge 1})] [int]$Wait = 1 ) Begin { Write-Verbose "[BEGIN ] Starting $($MyInvocation.Mycommand)" } #begin Process { Write-Verbose "[PROCESS] Parameter set = $($PSCmdlet.ParameterSetName)" [string]$pb = ($PSBoundParameters | Format-Table -AutoSize | Out-String).TrimEnd() Write-Verbose "[PROCESS] PSBoundparameters: `n$($pb.split("`n").Foreach({"$("`t"*4)$_"}) | Out-String) `n" If (-Not $At) { #calculate the scheduled job time from the number of minutes Write-Verbose "[PROCESS] Calculating AT $minutes minute(s) from now" [datetime]$At = (Get-Date).AddMinutes($minutes) #Add to PSBoundParameters for splatting purposes $PSBoundParameters.Add("At", $At) #this also implies -Once $PSBoundParameters.Add("Once", $True) } #create the scheduled job trigger #remove bound parameters that don't belong to New-JobTrigger $PSBoundParameters.remove("JobName") | Out-Null $PSBoundParameters.remove("Message") | Out-Null $PSBoundParameters.remove("Wait") | Out-Null $PSBoundParameters.remove("Whatif") | Out-Null $PSBoundParameters.remove("Confirm") | Out-Null $PSBoundParameters.remove("Minutes") | Out-Null $Trigger = New-JobTrigger @PSBoundParameters Write-Verbose "[PROCESS] Reminder Time = $At" if (-Not $JobName) { #get last job ID to build the jobname #ignore any errors if job not found Write-Verbose "[PROCESS] Checking for previous Reminder scheduled jobs" $lastjob = Get-ScheduledJob -Name "Reminder*" -ErrorAction SilentlyContinue | sort-object -property ID | select-object -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 "[PROCESS] Jobname = $jobname" #define the msg.exe expression [int]$WaitTime = $Wait * 60 Write-Verbose "[PROCESS] Display Wait time = $WaitTime seconds" Write-Verbose "[PROCESS] Defining expression" #define the command to run #replace single quotes with curly quotes if ($message -match "'") { $message = $message.replace("'",[char]700) } [string]$cmd = "msg.exe $env:username /Time:$WaitTime $Message" Write-Verbose "[PROCESS] Command to execute = $cmd" <# The scriptblock to run. Don't remove the comment for ScheduledReminderJob as that is used identify these type of jobs. #> $action = @" #ScheduledReminderJob Param(`$cmd,`$jobname) Invoke-Expression `$cmd "@ If ($PSboundParameters.ContainsKey("Once")) { $action += @" #forcibly remove the scheduledjob Get-ScheduledJob -Name `$jobname | Unregister-ScheduledJob -Force "@ } $sb = [scriptblock]::Create($action) Write-Verbose "[PROCESS] Scriptblock = $($sb | Out-String)" #add some options to support laptop users Write-Verbose "[PROCESS] Creating scheduled job options" $options = New-ScheduledJobOption -ContinueIfGoingOnBattery -WakeToRun #create the scheduled job Write-Verbose "[PROCESS] Registering the scheduled reminder job" $regParams = @{ ScriptBlock = $sb Name = $jobName MaxResultCount = 1 Trigger = $Trigger ArgumentList = @($cmd, $jobname) ScheduledJobOption = $options } $obj = Register-ScheduledJob @regParams #insert the new typename $obj.psobject.Typenames.Insert(0, "My.ScheduledReminder") $obj } #process End { Write-Verbose "[END ] Ending $($MyInvocation.Mycommand)" } #end } #end function Function Get-ScheduledReminderJob { [cmdletbinding()] [OutputType("My.ScheduledReminder")] [Alias("gsrj")] Param( [Parameter(Position = 0,HelpMessage = "The name of a scheduled reminder job.")] [string]$Name ) Begin { Write-Verbose "[BEGIN ] Starting: $($MyInvocation.Mycommand)" Write-Verbose "[BEGIN ] Getting all scheduled reminder jobs" $jobs = (Get-ScheduledJob).Where( {$_.command -match "#ScheduledReminderJob"}) } #begin Process { if ($jobs) { Write-Verbose "[PROCESS] Found $($jobs.count) scheduled reminder job(s)" if ($name) { #filter results for name Write-Verbose "[PROCESS] filtering for $name" $jobs.where( {$_.name -like $name}).foreach( { $_.psobject.Typenames.Insert(0, "My.ScheduledReminder") $_ }) } else { #write results Write-Verbose "[PROCESS] writing all reminder jobs to the pipeline" ($jobs).foreach( { $_.psobject.Typenames.Insert(0, "My.ScheduledReminder") $_ }) } } #if $jobs else { Write-Host "No scheduled reminder jobs found." -ForegroundColor magenta } } #process End { Write-Verbose "[END ] Ending: $($MyInvocation.Mycommand)" } #end } #end function Function Remove-ScheduledReminderJob { [cmdletbinding(SupportsShouldProcess)] Param( [Parameter(Position = 0, Mandatory, ParameterSetName = "Name")] #the assigned reminder job name [ValidateNotNullOrEmpty()] [string]$Name, [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = "ID")] #the scheduled job ID number [int]$ID ) Begin { Write-Verbose "[BEGIN ] Starting: $($MyInvocation.Mycommand)" #display PSBoundparameters formatted nicely for Verbose output [string]$pb = ($PSBoundParameters | Format-Table -AutoSize | Out-String).TrimEnd() Write-Verbose "[BEGIN ] PSBoundparameters: `n$($pb.split("`n").Foreach({"$("`t"*4)$_"}) | Out-String) `n" } #begin Process { Write-Verbose "[PROCESS] Using Parameter set: $($PSCmdlet.ParameterSetName)" Try { #remove -Whatif $PSBoundParameters.Remove("Whatif") | Out-Null $task = Get-Scheduledjob @PSBoundParameters -ErrorAction Stop } Catch { Write-Warning $_.exception.Message #bail out Return } Write-Verbose "[PROCESS] Removing reminder job $($task.id)" $task | Unregister-ScheduledJob } #process End { Write-Verbose "[END ] Ending: $($MyInvocation.Mycommand)" } #end } #endfunction Function Export-ScheduledReminderJob { #THIS COMMAND IS NOT USED AT THIS TIME UNTIL IT CAN EXPORT THE FULL OBJECT #LOOK AT INCORPORATING CODE FROM SCHEDULEDJOBTOOLS MODULE [cmdletbinding(SupportsShouldProcess)] Param( [ValidateScript( {$_.EndsWith(".csv")})] [string]$Path = ".\ScheduledReminders_$((get-date).ToShortDateString().Replace("/","_")).csv", [switch]$Append, [System.Text.Encoding]$Encoding ) Write-Verbose "Starting: $($MyInvocation.Mycommand)" #display PSBoundparameters formatted nicely for Verbose output [string]$pb = ($PSBoundParameters | format-table -AutoSize | Out-String).TrimEnd() Write-Verbose "PSBoundparameters: `n$($pb.split("`n").Foreach({"$("`t"*4)$_"}) | out-string) `n" #get jobs $jobs = Get-ScheduledReminderJob if ($jobs) { #only export if there are reminder jobs Write-Verbose "Exporting reminder jobs to $Path" if (-Not ($PSBoundParameters.ContainsKey("Path"))) { #use the default path value $PSBoundParameters.Path = $Path } $jobs | Select-Object Name, When, Wait, Message | Export-Csv @PSBoundParameters } else { Write-Warning "No reminder jobs found to export" } Write-Verbose "Ending: $($MyInvocation.Mycommand)" } #end function #endregion #region Type/format extensions Update-TypeData -TypeName My.ScheduledReminder -MemberName When -MemberType ScriptProperty -Value { #use the SCHTASKS.EXE command line tool to get next run time $find = schtasks /query /tn "\Microsoft\Windows\PowerShell\ScheduledJobs\$($this.Name)" /fo csv /nh | convertFrom-CSV -Header "TaskPath", "NextRun", "Status" $find.NextRun -as [datetime] } -force Update-TypeData -TypeName My.ScheduledReminder -MemberName Schedule -MemberType ScriptProperty -Value { #use the SCHTASKS.EXE command line tool to get next run time $find = schtasks /query /tn "\Microsoft\Windows\PowerShell\ScheduledJobs\$($this.Name)" /fo csv /v | convertFrom-CSV if ($find.'Start Time' -match "One") { "Once" } else { $find.'Start Time' } } -force Update-TypeData -TypeName My.ScheduledReminder -MemberName Wait -MemberType ScriptProperty -Value { [regex]$rx = "(?<=Time:)\d+" $data = $this.InvocationInfo.parameters.item(0).where( {$_.name -eq 'ArgumentList'}).value[0] $rx.Match($data).Value } -force Update-TypeData -TypeName My.ScheduledReminder -MemberName Message -MemberType ScriptProperty -Value { [regex]$rx = "(?<=\d+\s).*$" $data = $this.InvocationInfo.parameters.item(0).where( {$_.name -eq 'ArgumentList'}).value[0] $rx.Match($data).Value } -force Update-TypeData -typeName My.ScheduledReminder -DefaultDisplayPropertySet "ID", "Name", "When", "Wait", "Message" -Force #endregion |