diff --git a/CHANGELOG.md b/CHANGELOG.md index c882fe49..197b3942 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ - Moved strings to localization file. - Changed the scope from Global to Script in MSFT_ScheduledTask.Integration.Tests.ps1 - Changed the scope from Global to Script ComputerManagementDsc.Common.Tests.ps1 +- ScheduledTask: + - Added support for event based triggers, implemented using the ScheduleType OnEvent + fixes [Issue #167](https://github.com/PowerShell/ComputerManagementDsc/issues/167) ## 5.1.0.0 diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 index 7d7329d9..4210a65e 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 @@ -201,6 +201,15 @@ $script:localizedData = Get-LocalizedData ` .PARAMETER LogonType Specifies the security logon method that Task Scheduler uses to run the tasks that are associated with the principal. Not used in Get-TargetResource. + + .PARAMETER EventSubscription + The event subscription in a string that can be parsed as valid XML. This parameter is only + valid in combination with the OnEvent Schedule Type. For the query schema please check: + https://docs.microsoft.com/en-us/windows/desktop/WES/queryschema-schema + + .PARAMETER Delay + The time to wait after an event based trigger was triggered. This parameter is only + valid in combination with the OnEvent Schedule Type. #> function Get-TargetResource { @@ -234,7 +243,7 @@ function Get-TargetResource [Parameter()] [System.String] - [ValidateSet('Once', 'Daily', 'Weekly', 'AtStartup', 'AtLogOn')] + [ValidateSet('Once', 'Daily', 'Weekly', 'AtStartup', 'AtLogOn', 'OnEvent')] $ScheduleType, [Parameter()] @@ -376,7 +385,15 @@ function Get-TargetResource [Parameter()] [ValidateSet('Group', 'Interactive', 'InteractiveOrPassword', 'None', 'Password', 'S4U', 'ServiceAccount')] [System.String] - $LogonType + $LogonType, + + [Parameter()] + [System.String] + $EventSubscription, + + [Parameter()] + [System.String] + $Delay = '00:00:00' ) $TaskPath = ConvertTo-NormalizedTaskPath -TaskPath $TaskPath @@ -436,6 +453,12 @@ function Get-TargetResource break } + 'MSFT_TaskEventTrigger' + { + $returnScheduleType = 'OnEvent' + break + } + default { $returnScheduleType = '' @@ -510,6 +533,8 @@ function Get-TargetResource RunOnlyIfNetworkAvailable = $settings.RunOnlyIfNetworkAvailable RunLevel = [System.String] $task.Principal.RunLevel LogonType = [System.String] $task.Principal.LogonType + EventSubscription = $trigger.Subscription + Delay = ConvertTo-TimeSpanStringFromScheduledTaskString -TimeSpan $trigger.Delay } } } @@ -665,6 +690,15 @@ function Get-TargetResource .PARAMETER LogonType Specifies the security logon method that Task Scheduler uses to run the tasks that are associated with the principal. + + .PARAMETER EventSubscription + The event subscription in a string that can be parsed as valid XML. This parameter is only + valid in combination with the OnEvent Schedule Type. For the query schema please check: + https://docs.microsoft.com/en-us/windows/desktop/WES/queryschema-schema + + .PARAMETER Delay + The time to wait after an event based trigger was triggered. This parameter is only + valid in combination with the OnEvent Schedule Type. #> function Set-TargetResource { @@ -697,7 +731,7 @@ function Set-TargetResource [Parameter()] [System.String] - [ValidateSet('Once', 'Daily', 'Weekly', 'AtStartup', 'AtLogOn')] + [ValidateSet('Once', 'Daily', 'Weekly', 'AtStartup', 'AtLogOn', 'OnEvent')] $ScheduleType, [Parameter()] @@ -839,7 +873,15 @@ function Set-TargetResource [Parameter()] [ValidateSet('Group', 'Interactive', 'InteractiveOrPassword', 'None', 'Password', 'S4U', 'ServiceAccount')] [System.String] - $LogonType + $LogonType, + + [Parameter()] + [System.String] + $EventSubscription, + + [Parameter()] + [System.String] + $Delay = '00:00:00' ) $TaskPath = ConvertTo-NormalizedTaskPath -TaskPath $TaskPath @@ -902,6 +944,13 @@ function Set-TargetResource -ArgumentName DaysOfWeek } + if ($ScheduleType -eq 'OnEvent' -and -not ([xml]$EventSubscription)) + { + New-InvalidArgumentException ` + -Message ($script:localizedData.OnEventSubscriptionError) ` + -ArgumentName EventSubscription + } + # Configure the action $actionParameters = @{ Execute = $ActionExecutable @@ -989,7 +1038,8 @@ function Set-TargetResource # Configure the trigger $triggerParameters = @{} - if ($RandomDelay -gt [System.TimeSpan]::FromSeconds(0)) + # A random delay is not supported when the scheduleType is set to OnEvent + if ($RandomDelay -gt [System.TimeSpan]::FromSeconds(0) -and $ScheduleType -ne 'OnEvent') { $triggerParameters.Add('RandomDelay', $RandomDelay) } @@ -1049,9 +1099,23 @@ function Set-TargetResource break } + + 'OnEvent' + { + Write-Verbose -Message ($script:localizedData.ConfigureTaskEventTrigger -f $TaskName) + + $cimTriggerClass = Get-CimClass -ClassName MSFT_TaskEventTrigger -Namespace Root/Microsoft/Windows/TaskScheduler:MSFT_TaskEventTrigger + $trigger = New-CimInstance -CimClass $cimTriggerClass -ClientOnly + $trigger.Enabled = $true + $trigger.Delay = [System.Xml.XmlConvert]::ToString([timespan]$Delay) + $trigger.Subscription = $EventSubscription + } } - $trigger = New-ScheduledTaskTrigger @triggerParameters -ErrorAction SilentlyContinue + if ($ScheduleType -ne 'OnEvent') + { + $trigger = New-ScheduledTaskTrigger @triggerParameters -ErrorAction SilentlyContinue + } if (-not $trigger) { @@ -1390,6 +1454,15 @@ function Set-TargetResource .PARAMETER LogonType Specifies the security logon method that Task Scheduler uses to run the tasks that are associated with the principal. + + .PARAMETER EventSubscription + The event subscription in a string that can be parsed as valid XML. This parameter is only + valid in combination with the OnEvent Schedule Type. For the query schema please check: + https://docs.microsoft.com/en-us/windows/desktop/WES/queryschema-schema + + .PARAMETER Delay + The time to wait after an event based trigger was triggered. This parameter is only + valid in combination with the OnEvent Schedule Type. #> function Test-TargetResource { @@ -1423,7 +1496,7 @@ function Test-TargetResource [Parameter()] [System.String] - [ValidateSet('Once', 'Daily', 'Weekly', 'AtStartup', 'AtLogOn')] + [ValidateSet('Once', 'Daily', 'Weekly', 'AtStartup', 'AtLogOn', 'OnEvent')] $ScheduleType, [Parameter()] @@ -1565,7 +1638,15 @@ function Test-TargetResource [Parameter()] [ValidateSet('Group', 'Interactive', 'InteractiveOrPassword', 'None', 'Password', 'S4U', 'ServiceAccount')] [System.String] - $LogonType + $LogonType, + + [Parameter()] + [System.String] + $EventSubscription, + + [Parameter()] + [System.String] + $Delay = '00:00:00' ) $TaskPath = ConvertTo-NormalizedTaskPath -TaskPath $TaskPath @@ -1580,7 +1661,16 @@ function Test-TargetResource if ($PSBoundParameters.ContainsKey('RandomDelay')) { - $PSBoundParameters['RandomDelay'] = (ConvertTo-TimeSpanFromTimeSpanString -TimeSpanString $RandomDelay).ToString() + if ($ScheduleType -eq 'OnEvent') + { + # A random delay is not supported when the ScheduleType is set to OnEvent. + Write-Verbose -Message ($script:localizedData.IgnoreRandomDelayWithTriggerTypeOnEvent -f $TaskName) + $null = $PSBoundParameters.Remove('RandomDelay') + } + else + { + $PSBoundParameters['RandomDelay'] = (ConvertTo-TimeSpanFromTimeSpanString -TimeSpanString $RandomDelay).ToString() + } } if ($PSBoundParameters.ContainsKey('RepetitionDuration')) diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.schema.mof b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.schema.mof index 494409fb..4d40f88a 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.schema.mof +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.schema.mof @@ -7,7 +7,7 @@ class MSFT_ScheduledTask : OMI_BaseResource [Write, Description("The path to the .exe for this task")] string ActionExecutable; [Write, Description("The arguments to pass the executable")] string ActionArguments; [Write, Description("The working path to specify for the executable")] string ActionWorkingPath; - [Write, Description("When should the task be executed"), ValueMap{"Once", "Daily", "Weekly", "AtStartup", "AtLogOn"}, Values{"Once", "Daily", "Weekly", "AtStartup", "AtLogOn"}] string ScheduleType; + [Write, Description("When should the task be executed"), ValueMap{"Once", "Daily", "Weekly", "AtStartup", "AtLogOn", "OnEvent"}, Values{"Once", "Daily", "Weekly", "AtStartup", "AtLogOn", "OnEvent"}] string ScheduleType; [Write, Description("How many units (minutes, hours, days) between each run of this task?")] String RepeatInterval; [Write, Description("The time of day this task should start at - defaults to 12:00 AM. Not valid for AtLogon and AtStartup tasks")] DateTime StartTime; [Write, Description("Present if the task should exist, Absent if it should be removed"), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] string Ensure; @@ -42,4 +42,6 @@ class MSFT_ScheduledTask : OMI_BaseResource [Write, Description("Indicates that Task Scheduler runs the task only when a network is available. Task Scheduler uses the NetworkID parameter and NetworkName parameter that you specify in this cmdlet to determine if the network is available.")] Boolean RunOnlyIfNetworkAvailable; [Write, Description("Specifies the level of user rights that Task Scheduler uses to run the tasks that are associated with the principal. Defaults to 'Limited'."), ValueMap{"Limited","Highest"}, Values{"Limited","Highest"}] String RunLevel; [Write, Description("Specifies the security logon method that Task Scheduler uses to run the tasks that are associated with the principal."), ValueMap{"Group","Interactive","InteractiveOrPassword","None","Password","S4U","ServiceAccount"}, Values{"Group","Interactive","InteractiveOrPassword","None","Password","S4U","ServiceAccount"}] String LogonType; + [Write, Description("Specifies the EventSubscription in XML. This can be easily generated using the Windows Eventlog Viewer. For the query schema please check: https://docs.microsoft.com/en-us/windows/desktop/WES/queryschema-schema. Can only be used in combination with ScheduleType OnEvent")] String EventSubscription; + [Write, Description("Specifies a delay to the start of the trigger. The delay is a static delay before the task is executed. Can only be used in combination with ScheduleType OnEvent")] String Delay; }; diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/en-US/MSFT_ScheduledTask.strings.psd1 b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/en-US/MSFT_ScheduledTask.strings.psd1 index 31a11ec6..d811266f 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/en-US/MSFT_ScheduledTask.strings.psd1 +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/en-US/MSFT_ScheduledTask.strings.psd1 @@ -10,6 +10,7 @@ ConvertFrom-StringData @' DaysIntervalError = DaysInterval must be greater than zero (0) for Daily schedules. DaysInterval specified is '{0}'. WeeksIntervalError = WeeksInterval must be greater than zero (0) for Weekly schedules. WeeksInterval specified is '{0}'. WeekDayMissingError = At least one weekday must be selected for Weekly schedule. + OnEventSubscriptionError = No (valid) XML Event Subscription was provided. This is required when the scheduletype is OnEvent. TriggerCreationError = Error creating new scheduled task trigger. ConfigureTriggerRepetitionMessage = Configuring trigger repetition. RepetitionIntervalError = Repetition interval is set to '{0}' but repetition duration is '{1}'. @@ -20,6 +21,8 @@ ConvertFrom-StringData @' CreateScheduledTaskPrincipalMessage = Creating scheduled task principal for account '{0}' using logon type '{1}'. RemovePreviousScheduledTaskMessage = Removing previous scheduled task '{0}' from '{1}'. CreateNewScheduledTaskMessage = Creating new scheduled task '{0}' in '{1}'. + ConfigureTaskEventTrigger = Setting up an event based trigger on task {0}. + IgnoreRandomDelayWithTriggerTypeOnEvent = The parameter RandomDelay in task {0} is ignored. A random delay is not supported when the trigger type is set to OnEvent. SetRepetitionTriggerMessage = Setting repetition trigger settings on task '{0}' in '{1}'. RegisterScheduledTaskMessage = Registering the scheduled task '{0}' in '{1}'. RetrieveScheduledTaskMessage = Retrieving the scheduled task '{0}' from '{1}'. diff --git a/Modules/ComputerManagementDsc/Examples/Resources/ScheduledTask/13-CreateScheduledTasksOnEvent.ps1 b/Modules/ComputerManagementDsc/Examples/Resources/ScheduledTask/13-CreateScheduledTasksOnEvent.ps1 new file mode 100644 index 00000000..c34b22b6 --- /dev/null +++ b/Modules/ComputerManagementDsc/Examples/Resources/ScheduledTask/13-CreateScheduledTasksOnEvent.ps1 @@ -0,0 +1,33 @@ +<# + .EXAMPLE + This example creates a scheduled task called 'TriggerOnServiceFailures' in the folder + root folder. The task is delayed by exactly 30 seconds each time. The task will run when + an error event 7001 of source Service Control Manager is generated in the system log. + When a service crashes, it waits for 30 seconds and then starts a new PowerShell instance, + in which the file c:\temp\seeme.txt get's created with the value 'Worked!' +#> +Configuration Example +{ + param + ( + [Parameter()] + [System.String[]] + $NodeName = 'localhost' + ) + + Import-DscResource -ModuleName ComputerManagementDsc + + Node $NodeName + { + ScheduledTask ServiceEventManager + { + TaskName = 'TriggerOnServiceFailures' + Ensure = 'Present' + ScheduleType = 'OnEvent' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ActionArguments = '-Command Set-Content -Path c:\temp\seeme.txt -Value ''Worked!''' + EventSubscription = '' + Delay = '00:00:30' + } + } +} diff --git a/Tests/Integration/MSFT_ScheduledTask.Config.ps1 b/Tests/Integration/MSFT_ScheduledTask.Config.ps1 index b3d85910..8195dff4 100644 --- a/Tests/Integration/MSFT_ScheduledTask.Config.ps1 +++ b/Tests/Integration/MSFT_ScheduledTask.Config.ps1 @@ -168,6 +168,25 @@ Configuration ScheduledTaskExecuteAsAdd } } +Configuration ScheduledTaskOnEventAdd +{ + Import-DscResource -ModuleName ComputerManagementDsc + node 'localhost' + { + ScheduledTask ScheduledTaskOnEventAdd + { + TaskName = 'Test task OnEvent' + TaskPath = '\ComputerManagementDsc\' + Ensure = 'Present' + ScheduleType = 'OnEvent' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ActionArguments = '-Command Set-Content -Path c:\temp\seeme.txt -Value ''Worked!''' + EventSubscription = '' + Delay = '00:00:30' + } + } +} + Configuration ScheduledTaskOnceMod { Import-DscResource -ModuleName ComputerManagementDsc @@ -294,6 +313,25 @@ Configuration ScheduledTaskExecuteAsMod } } +Configuration ScheduledTaskOnEventMod +{ + Import-DscResource -ModuleName ComputerManagementDsc + node 'localhost' + { + ScheduledTask ScheduledTaskOnEventMod + { + TaskName = 'Test task OnEvent' + TaskPath = '\ComputerManagementDsc\' + Ensure = 'Present' + ScheduleType = 'OnEvent' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ActionArguments = '-Command Set-Content -Path c:\temp\seeme.txt -Value ''Worked!''' + EventSubscription = '' + Delay = '00:00:45' + } + } +} + Configuration ScheduledTaskOnceDel { Import-DscResource -ModuleName ComputerManagementDsc @@ -425,6 +463,25 @@ Configuration ScheduledTaskExecuteAsDel } } +Configuration ScheduledTaskOnEventDel +{ + Import-DscResource -ModuleName ComputerManagementDsc + node 'localhost' + { + ScheduledTask ScheduledTaskOnEventDel + { + TaskName = 'Test task OnEvent' + TaskPath = '\ComputerManagementDsc\' + Ensure = 'Absent' + ScheduleType = 'OnEvent' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ActionArguments = '-Command Set-Content -Path c:\temp\seeme.txt -Value ''Worked!''' + EventSubscription = '' + Delay = '00:00:30' + } + } +} + Configuration ScheduledTaskDisableBuiltIn { Import-DscResource -ModuleName ComputerManagementDsc diff --git a/Tests/Integration/MSFT_ScheduledTask.Integration.Tests.ps1 b/Tests/Integration/MSFT_ScheduledTask.Integration.Tests.ps1 index 37575fc4..38ae8f2f 100644 --- a/Tests/Integration/MSFT_ScheduledTask.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_ScheduledTask.Integration.Tests.ps1 @@ -36,6 +36,7 @@ try AtLogon = 'ScheduledTaskLogon' AtStartup = 'ScheduledTaskStartup' ExecuteAs = 'ScheduledTaskExecuteAs' + OnEvent = 'ScheduledTaskOnEvent' } $configData = @{ diff --git a/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 b/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 index ffae5045..e3943a7a 100644 --- a/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 +++ b/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 @@ -102,7 +102,7 @@ try ScheduleType = 'Once' RepeatInterval = (New-TimeSpan -Minutes 15).ToString() RepetitionDuration = (New-TimeSpan -Minutes 150).ToString() - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { return $null } @@ -130,7 +130,7 @@ try RepeatInterval = (New-TimeSpan -Minutes 15).ToString() RepetitionDuration = (New-TimeSpan -Minutes 15).ToString() Ensure = 'Absent' - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -174,7 +174,7 @@ try TaskName = 'Test task' TaskPath = '\Test\' Enable = $false - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -219,7 +219,7 @@ try TaskName = 'Test task' TaskPath = '\Test\' Ensure = 'Absent' - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -271,7 +271,7 @@ try ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' ScheduleType = 'Once' Ensure = 'Absent' - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask @@ -294,7 +294,7 @@ try ScheduleType = 'Once' RepeatInterval = (New-TimeSpan -Minutes 15).ToString() RepetitionDuration = (New-TimeSpan -Minutes 150).ToString() - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -346,7 +346,7 @@ try ScheduleType = 'Once' RepeatInterval = (New-TimeSpan -Minutes 15).ToString() RepetitionDuration = (New-TimeSpan -Minutes 30).ToString() - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -393,7 +393,7 @@ try ScheduleType = 'Once' RepeatInterval = (New-TimeSpan -Hours 4).ToString() RepetitionDuration = (New-TimeSpan -Hours 8).ToString() - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -445,7 +445,7 @@ try ScheduleType = 'Once' RepeatInterval = (New-TimeSpan -Hours 4).ToString() RepetitionDuration = (New-TimeSpan -Hours 8).ToString() - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -491,7 +491,7 @@ try ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' ScheduleType = 'Daily' DaysInterval = 3 - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -542,7 +542,7 @@ try ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' ScheduleType = 'Daily' DaysInterval = 3 - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -587,7 +587,7 @@ try RepeatInterval = (New-TimeSpan -Minutes 15).ToString() RepetitionDuration = (New-TimeSpan -Hours 8).ToString() ExecuteAsCredential = New-Object System.Management.Automation.PSCredential ('DEMO\RightUser', (ConvertTo-SecureString 'ExamplePassword' -AsPlainText -Force)) - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -641,7 +641,7 @@ try RepetitionDuration = (New-TimeSpan -Hours 8).ToString() ExecuteAsCredential = New-Object System.Management.Automation.PSCredential ('DEMO\RightUser', (ConvertTo-SecureString 'ExamplePassword' -AsPlainText -Force)) LogonType = 'S4U' - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -697,7 +697,7 @@ try RepetitionDuration = (New-TimeSpan -Hours 8).ToString() ExecuteAsCredential = New-Object System.Management.Automation.PSCredential ('DEMO\RightUser', (ConvertTo-SecureString 'ExamplePassword' -AsPlainText -Force)) RunLevel = 'Highest' - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -752,7 +752,7 @@ try ScheduleType = 'Once' RepeatInterval = (New-TimeSpan -Minutes 15).ToString() RepetitionDuration = (New-TimeSpan -Hours 8).ToString() - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -806,7 +806,7 @@ try ScheduleType = 'Once' RepeatInterval = (New-TimeSpan -Minutes 15).ToString() RepetitionDuration = (New-TimeSpan -Hours 8).ToString() - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -860,7 +860,7 @@ try RepeatInterval = (New-TimeSpan -Minutes 15).ToString() RepetitionDuration = (New-TimeSpan -Hours 8).ToString() Enable = $false - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -919,7 +919,7 @@ try RepetitionDuration = (New-TimeSpan -Hours 8).ToString() ExecutionTimeLimit = (New-TimeSpan -Seconds 0).ToString() Enable = $true - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -982,7 +982,7 @@ try ExecutionTimeLimit = (New-TimeSpan -Minutes 7).ToString() RestartInterval = (New-TimeSpan -Minutes 8).ToString() Enable = $true - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -1041,7 +1041,7 @@ try RepeatInterval = (New-TimeSpan -Minutes 15).ToString() RepetitionDuration = (New-TimeSpan -Hours 8).ToString() Enable = $false - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -1093,7 +1093,7 @@ try RepeatInterval = (New-TimeSpan -Minutes 15).ToString() RepetitionDuration = (New-TimeSpan -Hours 8).ToString() Enable = $true - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -1149,7 +1149,7 @@ try ScheduleType = 'Once' RepeatInterval = (New-TimeSpan -Minutes 15).ToString() RepetitionDuration = (New-TimeSpan -Hours 8).ToString() - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -1227,7 +1227,7 @@ try IdleDuration = (New-TimeSpan -Minutes 6).ToString() ExecutionTimeLimit = (New-TimeSpan -Minutes 7).ToString() RestartInterval = (New-TimeSpan -Minutes 8).ToString() - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -1293,7 +1293,7 @@ try IdleDuration = (New-TimeSpan -Minutes 6).ToString() ExecutionTimeLimit = (New-TimeSpan -Minutes 7).ToString() RestartInterval = (New-TimeSpan -Minutes 8).ToString() - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -1355,7 +1355,7 @@ try ScheduleType = 'Once' RepeatInterval = (New-TimeSpan -Minutes 20).ToString() RepetitionDuration = 'Indefinitely' - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -1408,7 +1408,7 @@ try ScheduleType = 'Once' RepeatInterval = (New-TimeSpan -Minutes 20).ToString() RepetitionDuration = (New-TimeSpan -Hours 9).ToString() - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -1461,7 +1461,7 @@ try ScheduleType = 'Once' RepeatInterval = (New-TimeSpan -Minutes 20).ToString() RepetitionDuration = 'Indefinitely' - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -1506,7 +1506,7 @@ try TaskName = 'Test task' TaskPath = '\Test\' Enable = $false - Verbose = $True + Verbose = $true } Mock -CommandName Get-ScheduledTask -MockWith { @@ -1522,13 +1522,14 @@ try Interval = "PT15M" } CimClass = @{ - CimClassName = 'MSFT_TaskEventTrigger' + CimClassName = 'MSFT_TaskSessionStateChangeTrigger' } } Settings = [pscustomobject] @{ Enabled = $true } - } } + } + } It 'Should return the correct values from Get-TargetResource' { $result = Get-TargetResource @testParameters @@ -1546,6 +1547,187 @@ try Assert-MockCalled Register-ScheduledTask -Exactly -Times 1 } } + + Context 'When a scheduled task with an OnEvent scheduletype is in desired state' { + $testParameters = @{ + TaskName = 'Test task' + TaskPath = '\Test\' + ScheduleType = 'OnEvent' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + EventSubscription = '' + Delay = '00:01:00' + Enable = $true + Verbose = $true + } + + Mock -CommandName Get-ScheduledTask -MockWith { + @{ + TaskName = $testParameters.TaskName + TaskPath = $testParameters.TaskPath + Actions = [pscustomobject] @{ + Execute = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + } + Triggers = [pscustomobject] @{ + Delay = 'PT1M' + Subscription = $testParameters.EventSubscription + CimClass = @{ + CimClassName = 'MSFT_TaskEventTrigger' + } + } + Settings = [pscustomobject] @{ + Enabled = $true + } + } + } + + It 'Should return the correct values from Get-TargetResource' { + $result = Get-TargetResource @testParameters + $result.Enable | Should -Be $true + $result.Ensure | Should -Be 'Present' + $result.ScheduleType | Should -Be 'OnEvent' + $result.EventSubscription | Should -Be $testParameters.EventSubscription + $result.Delay | Should -Be $testParameters.Delay + } + + It 'Should return true from the test method' { + Test-TargetResource @testParameters | Should -Be $true + } + } + + Context 'When a scheduled task with an OnEvent scheduletype needs to be created' { + $testParameters = @{ + TaskName = 'Test task' + TaskPath = '\Test\' + ScheduleType = 'OnEvent' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + EventSubscription = '' + Delay = '00:01:00' + Enable = $true + Verbose = $true + } + + Mock -CommandName Get-ScheduledTask + + It 'Should return the correct values from Get-TargetResource' { + $result = Get-TargetResource @testParameters + $result.Ensure | Should -Be 'Absent' + } + + It 'Should return false from the test method' { + Test-TargetResource @testParameters | Should -Be $false + } + + It 'Should register the new scheduled task' { + Set-TargetResource @testParameters + Assert-MockCalled Register-ScheduledTask -Exactly -Times 1 -Scope It + } + } + + Context 'When a scheduled task with an OnEvent scheduletype needs to be updated' { + $testParameters = @{ + TaskName = 'Test task' + TaskPath = '\Test\' + ScheduleType = 'OnEvent' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + EventSubscription = '' + Delay = '00:05:00' + Enable = $true + Verbose = $true + } + + Mock -CommandName Get-ScheduledTask -MockWith { + @{ + TaskName = $testParameters.TaskName + TaskPath = $testParameters.TaskPath + Actions = [pscustomobject] @{ + Execute = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + } + Triggers = [pscustomobject] @{ + Delay = 'PT1M' + Subscription = '' + CimClass = @{ + CimClassName = 'MSFT_TaskEventTrigger' + } + } + Settings = [pscustomobject] @{ + Enabled = $true + } + } + } + + It 'Should return the correct values from Get-TargetResource' { + $result = Get-TargetResource @testParameters + $result.Enable | Should -Be $true + $result.Ensure | Should -Be 'Present' + $result.ScheduleType | Should -Be 'OnEvent' + } + + It 'Should return false from the test method' { + Test-TargetResource @testParameters | Should -Be $false + } + + It 'Should not call Register-ScheduledTask on an already registered task' { + Set-TargetResource @testParameters + Assert-MockCalled Register-ScheduledTask -Times 0 -Scope It + } + + It 'Should call Set-ScheduledTask to update the scheduled task with the new values' { + Set-TargetResource @testParameters + Assert-MockCalled Set-ScheduledTask -Times 1 -Scope It + } + } + + Context 'When a scheduled task with an OnEvent scheduletype is used on combination with unsupported parameters for this scheduletype' { + $testParameters = @{ + TaskName = 'Test task' + TaskPath = '\Test\' + ScheduleType = 'OnEvent' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + EventSubscription = '' + RandomDelay = '01:00:00' + Delay = '00:01:00' + Enable = $true + Verbose = $true + } + + Mock -CommandName Get-ScheduledTask -MockWith { + @{ + TaskName = $testParameters.TaskName + TaskPath = $testParameters.TaskPath + Actions = [pscustomobject] @{ + Execute = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + } + Triggers = [pscustomobject] @{ + Delay = 'PT1M' + Subscription = $testParameters.EventSubscription + CimClass = @{ + CimClassName = 'MSFT_TaskEventTrigger' + } + } + Settings = [pscustomobject] @{ + Enabled = $true + } + } + } + + It 'Should return the correct values from Get-TargetResource' { + $result = Get-TargetResource @testParameters + $result.Enable | Should -Be $true + $result.Ensure | Should -Be 'Present' + $result.ScheduleType | Should -Be 'OnEvent' + $result.RandomDelay | Should -Be '00:00:00' + } + + It 'Should return true from the test method - ignoring the RandomDelay parameter' { + Test-TargetResource @testParameters | Should -Be $true + } + + $testParameters.EventSubscription = 'InvalidXML' + + It 'When an EventSubscription cannot be parsed as valid XML an error is generated when changing the task' { + { Set-TargetResource @testParameters } | Should throw + } + } } } #endregion