diff --git a/CHANGELOG.md b/CHANGELOG.md index f39c6d78..f5ffd9bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index f3b14933..2a258312 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/appveyor.yml b/appveyor.yml index c4f83055..6c74f473 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -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 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4420aa40..ebc2919a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -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)'." @@ -309,4 +309,3 @@ stages: GitHubToken: $(GitHubToken) ReleaseBranch: main MainGitBranch: main - diff --git a/source/DSCResources/DSC_ScheduledTask/DSC_ScheduledTask.psm1 b/source/DSCResources/DSC_ScheduledTask/DSC_ScheduledTask.psm1 index 0e0694ea..29ea8db3 100644 --- a/source/DSCResources/DSC_ScheduledTask/DSC_ScheduledTask.psm1 +++ b/source/DSCResources/DSC_ScheduledTask/DSC_ScheduledTask.psm1 @@ -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. @@ -424,6 +430,10 @@ function Set-TargetResource [System.String] $EventSubscription, + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $EventValueQueries, + [Parameter()] [System.String] $Delay = '00:00:00' @@ -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 } } @@ -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. @@ -1317,6 +1334,10 @@ function Test-TargetResource [System.String] $EventSubscription, + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $EventValueQueries, + [Parameter()] [System.String] $Delay = '00:00:00' @@ -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. #> @@ -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']) } @@ -1535,6 +1556,7 @@ function Test-TargetResource return Test-DscParameterState ` -CurrentValues $currentValues ` -DesiredValues $desiredValues ` + -SortArrayValues ` -Verbose:$VerbosePreference } @@ -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 } @@ -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. diff --git a/source/DSCResources/DSC_ScheduledTask/DSC_ScheduledTask.schema.mof b/source/DSCResources/DSC_ScheduledTask/DSC_ScheduledTask.schema.mof index 0c6957ea..ac62fd48 100644 --- a/source/DSCResources/DSC_ScheduledTask/DSC_ScheduledTask.schema.mof +++ b/source/DSCResources/DSC_ScheduledTask/DSC_ScheduledTask.schema.mof @@ -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; }; diff --git a/source/Examples/Resources/ScheduledTask/13-ScheduledTask_CreateScheduledTasksOnEvent_Config.ps1 b/source/Examples/Resources/ScheduledTask/13-ScheduledTask_CreateScheduledTasksOnEvent_Config.ps1 index f606213e..5d6ad7bd 100644 --- a/source/Examples/Resources/ScheduledTask/13-ScheduledTask_CreateScheduledTasksOnEvent_Config.ps1 +++ b/source/Examples/Resources/ScheduledTask/13-ScheduledTask_CreateScheduledTasksOnEvent_Config.ps1 @@ -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 = '' + EventValueQueries = @{ + "Service" = "Event/EventData/Data[@Name='param1']" + "DependsOnService" = "Event/EventData/Data[@Name='param2']" + "ErrorCode" = "Event/EventData/Data[@Name='param3']" + } Delay = '00:00:30' } } diff --git a/tests/Integration/DSC_ScheduledTask.config.ps1 b/tests/Integration/DSC_ScheduledTask.config.ps1 index df114a44..9cc1bac8 100644 --- a/tests/Integration/DSC_ScheduledTask.config.ps1 +++ b/tests/Integration/DSC_ScheduledTask.config.ps1 @@ -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 = '' + EventValueQueries = @{ + "Service" = "Event/EventData/Data[@Name='param1']" + "DependsOnService" = "Event/EventData/Data[@Name='param2']" + "ErrorCode" = "Event/EventData/Data[@Name='param3']" + } Delay = '00:00:30' } } @@ -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 = '' + EventValueQueries = @{ + "Service" = "Event/EventData/Data[@Name='param1']" + "DependsOnService" = "Event/EventData/Data[@Name='param2']" + "ErrorCode" = "Event/EventData/Data[@Name='param3']" + } Delay = '00:00:45' } } @@ -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 = '' + EventValueQueries = @{ + "Service" = "Event/EventData/Data[@Name='param1']" + "DependsOnService" = "Event/EventData/Data[@Name='param2']" + "ErrorCode" = "Event/EventData/Data[@Name='param3']" + } Delay = '00:00:30' } } diff --git a/tests/Unit/DSC_ScheduledTask.Tests.ps1 b/tests/Unit/DSC_ScheduledTask.Tests.ps1 index 95dc9bd0..3a86a241 100644 --- a/tests/Unit/DSC_ScheduledTask.Tests.ps1 +++ b/tests/Unit/DSC_ScheduledTask.Tests.ps1 @@ -1599,6 +1599,13 @@ try ScheduleType = 'OnEvent' ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' EventSubscription = '' + 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 @@ -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' } @@ -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 } @@ -1644,6 +1663,13 @@ try ScheduleType = 'OnEvent' ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' EventSubscription = '' + 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 @@ -1671,6 +1697,13 @@ try ScheduleType = 'OnEvent' ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' EventSubscription = '' + 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 @@ -1686,6 +1719,17 @@ try Triggers = [pscustomobject] @{ Delay = 'PT1M' Subscription = '' + 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' }