Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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()]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -436,6 +453,12 @@ function Get-TargetResource
break
}

'MSFT_TaskEventTrigger'
{
$returnScheduleType = 'OnEvent'
break
}

default
{
$returnScheduleType = ''
Expand Down Expand Up @@ -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
}
}
}
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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()]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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()]
Expand Down Expand Up @@ -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
Expand All @@ -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'))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
};
Original file line number Diff line number Diff line change
Expand Up @@ -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}'.
Expand All @@ -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}'.
Expand Down
Original file line number Diff line number Diff line change
@@ -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 = '<QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name=''Service Control Manager''] and (Level=2) and (EventID=7001)]]</Select></Query></QueryList>'
Delay = '00:00:30'
}
}
}
57 changes: 57 additions & 0 deletions Tests/Integration/MSFT_ScheduledTask.Config.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '<QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name=''Service Control Manager''] and (Level=2) and (EventID=7001)]]</Select></Query></QueryList>'
Delay = '00:00:30'
}
}
}

Configuration ScheduledTaskOnceMod
{
Import-DscResource -ModuleName ComputerManagementDsc
Expand Down Expand Up @@ -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 = '<QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name=''Service Control Manager''] and (Level=2) and (EventID=7002)]]</Select></Query></QueryList>'
Delay = '00:00:45'
}
}
}

Configuration ScheduledTaskOnceDel
{
Import-DscResource -ModuleName ComputerManagementDsc
Expand Down Expand Up @@ -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 = '<QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name=''Service Control Manager''] and (Level=2) and (EventID=7001)]]</Select></Query></QueryList>'
Delay = '00:00:30'
}
}
}

Configuration ScheduledTaskDisableBuiltIn
{
Import-DscResource -ModuleName ComputerManagementDsc
Expand Down
1 change: 1 addition & 0 deletions Tests/Integration/MSFT_ScheduledTask.Integration.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ try
AtLogon = 'ScheduledTaskLogon'
AtStartup = 'ScheduledTaskStartup'
ExecuteAs = 'ScheduledTaskExecuteAs'
OnEvent = 'ScheduledTaskOnEvent'
}

$configData = @{
Expand Down
Loading