Skip to content

Commit

Permalink
ScheduledTask: Add Event ValueQueries support, prevent DaysOfWeek ord…
Browse files Browse the repository at this point in the history
…ering causing drift (#431)
  • Loading branch information
Borgquite authored Aug 31, 2024
1 parent 58f77e4 commit c8977b0
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 11 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- ScheduledTask
- Added support for configuring Event ValueQueries, allowing the triggering event to be
parsed for values which are sent to the scheduled task script.
Fixes [Issue #392](https://github.com/dsccommunity/ComputerManagementDsc/issues/392).

### Fixed

- ScheduledTask
- Resolved an issue where DaysOfWeek array ordering can cause configuration drift.
Fixes [Issue #354](https://github.com/dsccommunity/ComputerManagementDsc/issues/354).
- Update build process to pin GitVersion to 5.* to resolve errors
(https://github.com/gaelcolas/Sampler/issues/477).

### Changed

- CI Pipeline
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ The **ComputerManagementDsc** module contains the following resources:
settings on the local machine.
- **SmbShare**: This resource is used to manage SMB shares on a machine.
- **SystemLocale**: This resource is used to set the system locale on a
Windows machine
Windows machine.
- **TimeZone**: This resource is used for setting the time zone on a machine.
- **UserAccountControl**: This resource allows you to configure the notification
level or granularly configure the User Account Control for the computer.
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ build_script:
if (-not $env:APPVEYOR_PULL_REQUEST_NUMBER) { Write-Host -ForegroundColor 'Yellow' -Object 'Not a pull request, skipping.'; return }
# Set module version using GitVersion
dotnet tool install --global GitVersion.Tool
dotnet tool install --global GitVersion.Tool --version 5.*
$env:IGNORE_NORMALISATION_GIT_HEAD_MOVE = 1
dotnet-gitversion
$gitVersionObject = dotnet-gitversion | ConvertFrom-Json
Expand Down
3 changes: 1 addition & 2 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ stages:
vmImage: 'windows-latest'
steps:
- pwsh: |
dotnet tool install --global GitVersion.Tool
dotnet tool install --global GitVersion.Tool --version 5.*
$gitVersionObject = dotnet-gitversion | ConvertFrom-Json
$gitVersionObject.PSObject.Properties.ForEach{
Write-Host -Object "Setting Task Variable '$($_.Name)' with value '$($_.Value)'."
Expand Down Expand Up @@ -309,4 +309,3 @@ stages:
GitHubToken: $(GitHubToken)
ReleaseBranch: main
MainGitBranch: main

95 changes: 92 additions & 3 deletions source/DSCResources/DSC_ScheduledTask/DSC_ScheduledTask.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,12 @@ function Get-TargetResource
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 EventValueQueries
Specifies event value queries. This parameter is only valid in combination with the OnEvent
Schedule Type. Receives a hashtable where the key is a property value for an event and
the value is an XPath event query. For more detailed syntax check:
https://learn.microsoft.com/en-us/windows/win32/taskschd/eventtrigger-valuequeries.
.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.
Expand Down Expand Up @@ -424,6 +430,10 @@ function Set-TargetResource
[System.String]
$EventSubscription,

[Parameter()]
[Microsoft.Management.Infrastructure.CimInstance[]]
$EventValueQueries,

[Parameter()]
[System.String]
$Delay = '00:00:00'
Expand Down Expand Up @@ -689,6 +699,7 @@ function Set-TargetResource
$trigger = New-CimInstance -CimClass $cimTriggerClass -ClientOnly
$trigger.Enabled = $true
$trigger.Subscription = $EventSubscription
$trigger.ValueQueries = ConvertTo-TaskNamedValuePairCollectionFromKeyValuePairArray -Array $EventValueQueries
}
}

Expand Down Expand Up @@ -1120,6 +1131,12 @@ function Set-TargetResource
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 EventValueQueries
Specifies event value queries. This parameter is only valid in combination with the OnEvent
Schedule Type. Receives a hashtable where the key is a property value for an event and
the value is an XPath event query. For more detailed syntax check:
https://learn.microsoft.com/en-us/windows/win32/taskschd/eventtrigger-valuequeries.
.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.
Expand Down Expand Up @@ -1317,6 +1334,10 @@ function Test-TargetResource
[System.String]
$EventSubscription,

[Parameter()]
[Microsoft.Management.Infrastructure.CimInstance[]]
$EventValueQueries,

[Parameter()]
[System.String]
$Delay = '00:00:00'
Expand Down Expand Up @@ -1486,8 +1507,8 @@ function Test-TargetResource
The WeeksInterval parameter of this function defaults to 1,
even though the value of the WeeksInterval property maybe
unset/undefined in the object $currentValues returned from
Get-TargetResouce. To avoid Test-TargetResouce returning false
and generating spurious calls to Set-TargetResouce, default
Get-TargetResource. To avoid Test-TargetResource returning false
and generating spurious calls to Set-TargetResource, default
an undefined $currentValues.WeeksInterval to the value of
$WeeksInterval.
#>
Expand Down Expand Up @@ -1525,7 +1546,7 @@ function Test-TargetResource
{
<#
Initialise a missing or null Verbose to avoid spurious
calls to Set-TargetResouce
calls to Set-TargetResource
#>
$currentValues.Add('Verbose', $desiredValues['Verbose'])
}
Expand All @@ -1535,6 +1556,7 @@ function Test-TargetResource
return Test-DscParameterState `
-CurrentValues $currentValues `
-DesiredValues $desiredValues `
-SortArrayValues `
-Verbose:$VerbosePreference
}

Expand Down Expand Up @@ -1922,6 +1944,7 @@ function Get-CurrentResource
RunLevel = [System.String] $task.Principal.RunLevel
LogonType = [System.String] $task.Principal.LogonType
EventSubscription = $trigger.Subscription
EventValueQueries = ConvertTo-HashtableFromTaskNamedValuePairCollection -Array $trigger.ValueQueries
Delay = ConvertTo-TimeSpanStringFromScheduledTaskString -TimeSpan $trigger.Delay
}

Expand All @@ -1943,6 +1966,72 @@ function Get-CurrentResource
return $result
}

<#
.SYNOPSIS
Converts CimInstance array of type MSFT_TaskNamedValue to hashtable
.PARAMETER Array
The array of MSFT_TaskNamedValue to convert to a hashtable.
#>
function ConvertTo-HashtableFromTaskNamedValuePairCollection
{
[CmdletBinding()]
[OutputType([System.Collections.Hashtable])]
param
(
[Parameter()]
[Microsoft.Management.Infrastructure.CimInstance[]]
$Array
)

$hashtable = @{}

foreach ($item in $Array)
{
$hashtable += @{
$item.Name = $item.Value
}
}

return $hashtable
}

<#
.SYNOPSIS
Converts CimInstance array of type MSFT_KeyValuePair to array of type MSFT_TaskNamedValue
.PARAMETER Array
The array of MSFT_KeyValuePair to convert to an array of type MSFT_TaskNamedValue.
#>
function ConvertTo-TaskNamedValuePairCollectionFromKeyValuePairArray
{
[CmdletBinding()]
[OutputType([Microsoft.Management.Infrastructure.CimInstance[]])]
param
(
[Parameter()]
[Microsoft.Management.Infrastructure.CimInstance[]]
$Array
)

$cimNamedValueClass = Get-CimClass -ClassName MSFT_TaskNamedValue -Namespace Root/Microsoft/Windows/TaskScheduler:MSFT_TaskNamedValue

$namedValueArray = [Microsoft.Management.Infrastructure.CimInstance[]]@()

foreach ($item in $Array)
{
$namedValue = New-CimInstance -CimClass $cimNamedValueClass `
-Property @{
Name = $item.key
Value = $item.Value
} `
-ClientOnly
$namedValueArray += $namedValue
}

return $namedValueArray
}

<#
.SYNOPSIS
Test if a date string contains a time zone.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,6 @@ class DSC_ScheduledTask : OMI_BaseResource
[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 the EventValueQueries. Receives a hashtable where the key is a property value for an event and the value is an XPath event query. For more detailed syntax check: https://learn.microsoft.com/en-us/windows/win32/taskschd/eventtrigger-valuequeries."), EmbeddedInstance("MSFT_KeyValuePair")] String EventValueQueries[];
[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 @@ -37,8 +37,13 @@ Configuration ScheduledTask_CreateScheduledTasksOnEvent_Config
Ensure = 'Present'
ScheduleType = 'OnEvent'
ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe'
ActionArguments = '-Command Set-Content -Path c:\temp\seeme.txt -Value ''Worked!'''
ActionArguments = '-Command Set-Content -Path c:\temp\seeme.txt -Value ''$(Service) $(DependsOnService) $(ErrorCode) 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>'
EventValueQueries = @{
"Service" = "Event/EventData/Data[@Name='param1']"
"DependsOnService" = "Event/EventData/Data[@Name='param2']"
"ErrorCode" = "Event/EventData/Data[@Name='param3']"
}
Delay = '00:00:30'
}
}
Expand Down
21 changes: 18 additions & 3 deletions tests/Integration/DSC_ScheduledTask.config.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,13 @@ Configuration ScheduledTaskOnEventAdd
Ensure = 'Present'
ScheduleType = 'OnEvent'
ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe'
ActionArguments = '-Command Set-Content -Path c:\temp\seeme.txt -Value ''Worked!'''
ActionArguments = '-Command Set-Content -Path c:\temp\seeme.txt -Value ''$(Service) $(DependsOnService) $(ErrorCode) 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>'
EventValueQueries = @{
"Service" = "Event/EventData/Data[@Name='param1']"
"DependsOnService" = "Event/EventData/Data[@Name='param2']"
"ErrorCode" = "Event/EventData/Data[@Name='param3']"
}
Delay = '00:00:30'
}
}
Expand Down Expand Up @@ -456,8 +461,13 @@ Configuration ScheduledTaskOnEventMod
Ensure = 'Present'
ScheduleType = 'OnEvent'
ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe'
ActionArguments = '-Command Set-Content -Path c:\temp\seeme.txt -Value ''Worked!'''
ActionArguments = '-Command Set-Content -Path c:\temp\seeme.txt -Value ''$(Service) $(DependsOnService) $(ErrorCode) 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>'
EventValueQueries = @{
"Service" = "Event/EventData/Data[@Name='param1']"
"DependsOnService" = "Event/EventData/Data[@Name='param2']"
"ErrorCode" = "Event/EventData/Data[@Name='param3']"
}
Delay = '00:00:45'
}
}
Expand Down Expand Up @@ -653,8 +663,13 @@ Configuration ScheduledTaskOnEventDel
Ensure = 'Absent'
ScheduleType = 'OnEvent'
ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe'
ActionArguments = '-Command Set-Content -Path c:\temp\seeme.txt -Value ''Worked!'''
ActionArguments = '-Command Set-Content -Path c:\temp\seeme.txt -Value ''$(Service) $(DependsOnService) $(ErrorCode) 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>'
EventValueQueries = @{
"Service" = "Event/EventData/Data[@Name='param1']"
"DependsOnService" = "Event/EventData/Data[@Name='param2']"
"ErrorCode" = "Event/EventData/Data[@Name='param3']"
}
Delay = '00:00:30'
}
}
Expand Down
44 changes: 44 additions & 0 deletions tests/Unit/DSC_ScheduledTask.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -1599,6 +1599,13 @@ try
ScheduleType = 'OnEvent'
ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe'
EventSubscription = '<QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name=''User32''] and EventID=1600]]</Select></Query></QueryList>'
EventValueQueries = [Microsoft.Management.Infrastructure.CimInstance[]] (
ConvertTo-CimInstance -Hashtable @{
Service = "Event/EventData/Data[@Name='param1']"
DependsOnService = "Event/EventData/Data[@Name='param2']"
ErrorCode = "Event/EventData/Data[@Name='param3']"
}
)
Delay = '00:01:00'
Enable = $true
Verbose = $true
Expand All @@ -1614,6 +1621,17 @@ try
Triggers = [pscustomobject] @{
Delay = 'PT1M'
Subscription = $testParameters.EventSubscription
ValueQueries = @(
$testParameters.EventValueQueries | ForEach-Object {
New-CimInstance -ClassName MSFT_TaskNamedValue `
-Namespace Root/Microsoft/Windows/TaskScheduler:MSFT_TaskNamedValue `
-Property @{
Name = $_.Key
Value = $_.Value
} `
-ClientOnly
}
)
CimClass = @{
CimClassName = 'MSFT_TaskEventTrigger'
}
Expand All @@ -1631,6 +1649,7 @@ try
$result.Ensure | Should -Be 'Present'
$result.ScheduleType | Should -Be 'OnEvent'
$result.EventSubscription | Should -Be $testParameters.EventSubscription
Test-DscParameterState -CurrentValues $result.EventValueQueries -DesiredValues $testParameters.EventValueQueries | Should -BeTrue
$result.Delay | Should -Be $testParameters.Delay
}

Expand All @@ -1644,6 +1663,13 @@ try
ScheduleType = 'OnEvent'
ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe'
EventSubscription = '<QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name=''User32''] and EventID=1600]]</Select></Query></QueryList>'
EventValueQueries = [Microsoft.Management.Infrastructure.CimInstance[]] (
ConvertTo-CimInstance -Hashtable @{
Service = "Event/EventData/Data[@Name='param1']"
DependsOnService = "Event/EventData/Data[@Name='param2']"
ErrorCode = "Event/EventData/Data[@Name='param3']"
}
)
Delay = '00:01:00'
Enable = $true
Verbose = $true
Expand Down Expand Up @@ -1671,6 +1697,13 @@ try
ScheduleType = 'OnEvent'
ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe'
EventSubscription = '<QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name=''User32''] and EventID=1600]]</Select></Query></QueryList>'
EventValueQueries = [Microsoft.Management.Infrastructure.CimInstance[]] (
ConvertTo-CimInstance -Hashtable @{
Service = "Event/EventData/Data[@Name='param1']"
DependsOnService = "Event/EventData/Data[@Name='param2']"
ErrorCode = "Event/EventData/Data[@Name='param3']"
}
)
Delay = '00:05:00'
Enable = $true
Verbose = $true
Expand All @@ -1686,6 +1719,17 @@ try
Triggers = [pscustomobject] @{
Delay = 'PT1M'
Subscription = '<QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name=''User32''] and EventID=1601]]</Select></Query></QueryList>'
ValueQueries = @(
$testParameters.EventValueQueries | Select-Object -SkipLast 1 | ForEach-Object {
New-CimInstance -ClassName MSFT_TaskNamedValue `
-Namespace Root/Microsoft/Windows/TaskScheduler:MSFT_TaskNamedValue `
-Property @{
Name = $_.Key
Value = $_.Value
} `
-ClientOnly
}
)
CimClass = @{
CimClassName = 'MSFT_TaskEventTrigger'
}
Expand Down

0 comments on commit c8977b0

Please sign in to comment.