diff --git a/DSCResources/CommonResourceHelper.psm1 b/DSCResources/CommonResourceHelper.psm1 index bc298f01..7c0c9af9 100644 --- a/DSCResources/CommonResourceHelper.psm1 +++ b/DSCResources/CommonResourceHelper.psm1 @@ -38,6 +38,384 @@ function Get-LocalizedData return $localizedData } +<# +.SYNOPSIS + Removes common parameters from a hashtable +.DESCRIPTION + This function serves the purpose of removing common parameters and option common parameters from a parameter hashtable +.PARAMETER Hashtable + The parameter hashtable that should be pruned +#> +function Remove-CommonParameter +{ + [OutputType([hashtable])] + [cmdletbinding()] + param + ( + [Parameter(Mandatory = $true)] + [hashtable] + $Hashtable + ) + + $inputClone = $Hashtable.Clone() + $commonParameters = [System.Management.Automation.PSCmdlet]::CommonParameters + $commonParameters += [System.Management.Automation.PSCmdlet]::OptionalCommonParameters + + $Hashtable.Keys | Where-Object { $_ -in $commonParameters } | ForEach-Object { + $inputClone.Remove($_) + } + + return $inputClone +} + +<# +.SYNOPSIS + Tests the status of DSC resource parameters +.DESCRIPTION + This function tests the parameter status of DSC resource parameters against the current values present on the system +.PARAMETER CurrentValues + A hashtable with the current values on the system, obtained by e.g. Get-TargetResource +.PARAMETER DesiredValues + The hashtable of desired values +.PARAMETER ValuesToCheck + The values to check if not all values should be checked +.PARAMETER TurnOffTypeChecking + Indicates that the type of the parameter should not be checked +#> +function Test-DscParameterState +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [hashtable] + $CurrentValues, + + [Parameter(Mandatory = $true)] + [object] + $DesiredValues, + + [string[]] + $ValuesToCheck, + + [switch] + $TurnOffTypeChecking + ) + + $returnValue = $true + + $types = 'System.Management.Automation.PSBoundParametersDictionary', 'System.Collections.Hashtable', 'Microsoft.Management.Infrastructure.CimInstance' + + if ($DesiredValues.GetType().FullName -notin $types) + { + throw ("Property 'DesiredValues' in Test-DscParameterState must be either a Hashtable or CimInstance. Type detected was $($DesiredValues.GetType().FullName)") + } + + if ($DesiredValues -is [Microsoft.Management.Infrastructure.CimInstance] -and -not $ValuesToCheck) + { + throw ("If 'DesiredValues' is a CimInstance then property 'ValuesToCheck' must contain a value") + } + + $desiredValuesClean = Remove-CommonParameter -Hashtable $DesiredValues + + if (-not $ValuesToCheck) + { + $keyList = $desiredValuesClean.Keys + } + else + { + $keyList = $ValuesToCheck + } + + foreach ($key in $keyList) + { + if ($null -ne $desiredValuesClean.$key) + { + $desiredType = $desiredValuesClean.$key.GetType() + } + else + { + $desiredType = [psobject]@{ + Name = 'Unknown' + } + } + + if ($null -ne $CurrentValues.$key) + { + $currentType = $CurrentValues.$key.GetType() + } + else + { + $currentType = [psobject]@{ + Name = 'Unknown' + } + } + + if ($currentType.Name -ne 'Unknown' -and $desiredType.Name -eq 'PSCredential') + { + # This is a credential object. Compare only the user name + if ($currentType.Name -eq 'PSCredential' -and $CurrentValues.$key.UserName -eq $desiredValuesClean.$key.UserName) + { + Write-Verbose -Message ('MATCH: PSCredential username match. Current state is {0} and desired state is {1}' -f $CurrentValues.$key.UserName, $desiredValuesClean.$key.UserName) + continue + } + else + { + Write-Verbose -Message ('NOTMATCH: PSCredential username mismatch. Current state is {0} and desired state is {1}' -f $CurrentValues.$key.UserName, $desiredValuesClean.$key.UserName) + $returnValue = $false + } + + # Assume the string is our username when the matching desired value is actually a credential + if ($currentType.Name -eq 'string' -and $CurrentValues.$key -eq $desiredValuesClean.$key.UserName) + { + Write-Verbose -Message ('MATCH: PSCredential username match. Current state is {0} and desired state is {1}' -f $CurrentValues.$key, $desiredValuesClean.$key.UserName) + continue + } + else + { + Write-Verbose -Message ('NOTMATCH: PSCredential username mismatch. Current state is {0} and desired state is {1}' -f $CurrentValues.$key, $desiredValuesClean.$key.UserName) + $returnValue = $false + } + } + + if (-not $TurnOffTypeChecking) + { + if (($desiredType.Name -ne 'Unknown' -and $currentType.Name -ne 'Unknown') -and + $desiredType.FullName -ne $currentType.FullName) + { + Write-Verbose -Message "NOTMATCH: Type mismatch for property '$key' Current state type is '$($currentType.Name)' and desired type is '$($desiredType.Name)'" + continue + } + } + + if ($CurrentValues.$key -eq $desiredValuesClean.$key -and -not $desiredType.IsArray) + { + Write-Verbose -Message "MATCH: Value (type $($desiredType.Name)) for property '$key' does match. Current state is '$($CurrentValues.$key)' and desired state is '$($desiredValuesClean.$key)'" + continue + } + + if ($desiredValuesClean.GetType().Name -in 'HashTable', 'PSBoundParametersDictionary') + { + $checkDesiredValue = $desiredValuesClean.ContainsKey($key) + } + else + { + $checkDesiredValue = Test-DSCObjectHasProperty -Object $desiredValuesClean -PropertyName $key + } + + if (-not $checkDesiredValue) + { + Write-Verbose -Message "MATCH: Value (type $($desiredType.Name)) for property '$key' does match. Current state is '$($CurrentValues.$key)' and desired state is '$($desiredValuesClean.$key)'" + continue + } + + if ($desiredType.IsArray) + { + Write-Verbose -Message "Comparing values in property '$key'" + if (-not $CurrentValues.ContainsKey($key) -or -not $CurrentValues.$key) + { + Write-Verbose -Message "NOTMATCH: Value (type $($desiredType.Name)) for property '$key' does not match. Current state is '$($CurrentValues.$key)' and desired state is '$($desiredValuesClean.$key)'" + $returnValue = $false + continue + } + elseif ($CurrentValues.$key.Count -ne $DesiredValues.$key.Count) + { + Write-Verbose -Message "NOTMATCH: Value (type $($desiredType.Name)) for property '$key' does have a different count. Current state count is '$($CurrentValues.$key.Count)' and desired state count is '$($desiredValuesClean.$key.Count)'" + $returnValue = $false + continue + } + else + { + $desiredArrayValues = $DesiredValues.$key + $currentArrayValues = $CurrentValues.$key + + for ($i = 0; $i -lt $desiredArrayValues.Count; $i++) + { + if ($null -ne $desiredArrayValues[$i]) + { + $desiredType = $desiredArrayValues[$i].GetType() + } + else + { + $desiredType = [psobject]@{ + Name = 'Unknown' + } + } + + if ($null -ne $currentArrayValues[$i]) + { + $currentType = $currentArrayValues[$i].GetType() + } + else + { + $currentType = [psobject]@{ + Name = 'Unknown' + } + } + + if (-not $TurnOffTypeChecking) + { + if (($desiredType.Name -ne 'Unknown' -and $currentType.Name -ne 'Unknown') -and + $desiredType.FullName -ne $currentType.FullName) + { + Write-Verbose -Message "`tNOTMATCH: Type mismatch for property '$key' Current state type of element [$i] is '$($currentType.Name)' and desired type is '$($desiredType.Name)'" + $returnValue = $false + continue + } + } + + if ($desiredArrayValues[$i] -ne $currentArrayValues[$i]) + { + Write-Verbose -Message "`tNOTMATCH: Value [$i] (type $($desiredType.Name)) for property '$key' does match. Current state is '$($currentArrayValues[$i])' and desired state is '$($desiredArrayValues[$i])'" + $returnValue = $false + continue + } + else + { + Write-Verbose -Message "`tMATCH: Value [$i] (type $($desiredType.Name)) for property '$key' does match. Current state is '$($currentArrayValues[$i])' and desired state is '$($desiredArrayValues[$i])'" + continue + } + } + + } + } + else + { + if ($desiredValuesClean.$key -ne $CurrentValues.$key) + { + Write-Verbose -Message "NOTMATCH: Value (type $($desiredType.Name)) for property '$key' does not match. Current state is '$($CurrentValues.$key)' and desired state is '$($desiredValuesClean.$key)'" + $returnValue = $false + } + + } + } + + Write-Verbose -Message "Result is '$returnValue'" + return $returnValue +} + +<# +.SYNOPSIS + Tests of an object has a property +.PARAMETER Object + The object to test +.PARAMETER PropertyName + The property name +#> +function Test-DSCObjectHasProperty +{ + [CmdletBinding()] + [OutputType([bool])] + param + ( + [Parameter(Mandatory = $true)] + [object] + $Object, + + [Parameter(Mandatory = $true)] + [string] + $PropertyName + ) + + if ($Object.PSObject.Properties.Name -contains $PropertyName) + { + return [bool] $Object.$PropertyName + } + + return $false +} + +<# + .SYNOPSIS + Creates and throws an invalid argument exception + + .PARAMETER Message + The message explaining why this error is being thrown + + .PARAMETER ArgumentName + The name of the invalid argument that is causing this error to be thrown +#> +function New-InvalidArgumentException +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $Message, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $ArgumentName + ) + + $argumentException = New-Object -TypeName 'ArgumentException' ` + -ArgumentList @($Message, $ArgumentName) + $newObjectParams = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @($argumentException, $ArgumentName, 'InvalidArgument', $null) + } + $errorRecord = New-Object @newObjectParams + + throw $errorRecord +} + +<# + .SYNOPSIS + Creates and throws an invalid operation exception + + .PARAMETER Message + The message explaining why this error is being thrown + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error +#> +function New-InvalidOperationException +{ + [CmdletBinding()] + param + ( + [ValidateNotNullOrEmpty()] + [String] + $Message, + + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $Message) + { + $invalidOperationException = New-Object -TypeName 'InvalidOperationException' + } + elseif ($null -eq $ErrorRecord) + { + $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` + -ArgumentList @($Message) + } + else + { + $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` + -ArgumentList @($Message, $ErrorRecord.Exception) + } + + $newObjectParams = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( $invalidOperationException.ToString(), 'MachineStateIncorrect', + 'InvalidOperation', $null ) + } + + $errorRecordToThrow = New-Object @newObjectParams + throw $errorRecordToThrow +} + Export-ModuleMember -Function @( 'Get-LocalizedData' - ) + 'Remove-CommonParameter' + 'Test-DscParameterState' + 'Test-DSCObjectHasProperty' + 'New-InvalidOperationException' + 'New-InvalidArgumentException' +) diff --git a/DSCResources/MSFT_xScheduledTask/MSFT_xScheduledTask.psm1 b/DSCResources/MSFT_xScheduledTask/MSFT_xScheduledTask.psm1 index d1289880..8ae8e8ae 100644 --- a/DSCResources/MSFT_xScheduledTask/MSFT_xScheduledTask.psm1 +++ b/DSCResources/MSFT_xScheduledTask/MSFT_xScheduledTask.psm1 @@ -1,413 +1,1320 @@ +Add-Type -TypeDefinition @' +namespace xScheduledTask +{ + public enum DaysOfWeek + { + Sunday = 1, + Monday = 2, + Tuesday = 4, + Wednesday = 8, + Thursday = 16, + Friday = 32, + Saturday = 64 + } +} +'@ + +Import-Module -Name (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -ChildPath 'CommonResourceHelper.psm1') + +<# +.SYNOPSIS + Gets the current resource state +.PARAMETER TaskName + The name of the task +.PARAMETER TaskPath + The path to the task - defaults to the root directory +.PARAMETER Description + The task description +.PARAMETER ActionExecutable + The path to the .exe for this task +.PARAMETER ActionArguments + The arguments to pass the executable +.PARAMETER ActionWorkingPath + The working path to specify for the executable +.PARAMETER ScheduleType + When should the task be executed +.PARAMETER RepeatInterval + How many units (minutes, hours, days) between each run of this task? +.PARAMETER StartTime + The time of day this task should start at - defaults to 12:00 AM. Not valid for AtLogon and AtStartup tasks +.PARAMETER Ensure + Present if the task should exist, Absent if it should be removed +.PARAMETER Enable + True if the task should be enabled, false if it should be disabled +.PARAMETER ExecuteAsCredential + The credential this task should execute as. If not specified defaults to running as the local system account +.PARAMETER DaysInterval + Specifies the interval between the days in the schedule. An interval of 1 produces a daily schedule. An interval of 2 produces an every-other day schedule. +.PARAMETER RandomDelay + Specifies a random amount of time to delay the start time of the trigger. The delay time is a random time between the time the task triggers and the time that you specify in this setting. +.PARAMETER RepetitionDuration + Specifies how long the repetition pattern repeats after the task starts. +.PARAMETER DaysOfWeek + Specifies an array of the days of the week on which Task Scheduler runs the task. +.PARAMETER WeeksInterval + Specifies the interval between the weeks in the schedule. An interval of 1 produces a weekly schedule. An interval of 2 produces an every-other week schedule. +.PARAMETER User + Specifies the identifier of the user for a trigger that starts a task when a user logs on. +.PARAMETER DisallowDemandStart + Indicates whether the task is prohibited to run on demand or not. Defaults to $false +.PARAMETER DisallowHardTerminate + Indicates whether the task is prohibited to be terminated or not. Defaults to $false +.PARAMETER Compatibility + The task compatibility level. Defaults to Vista. +.PARAMETER AllowStartIfOnBatteries + Indicates whether the task should start if the machine is on batteries or not. Defaults to $false +.PARAMETER Hidden + Indicates that the task is hidden in the Task Scheduler UI. +.PARAMETER RunOnlyIfIdle + Indicates that Task Scheduler runs the task only when the computer is idle. +.PARAMETER IdleWaitTimeout + Specifies the amount of time that Task Scheduler waits for an idle condition to occur. +.PARAMETER NetworkName + Specifies the name of a network profile that Task Scheduler uses to determine if the task can run. + The Task Scheduler UI uses this setting for display purposes. Specify a network name if you specify the RunOnlyIfNetworkAvailable parameter. +.PARAMETER DisallowStartOnRemoteAppSession + Indicates that the task does not start if the task is triggered to run in a Remote Applications Integrated Locally (RAIL) session. +.PARAMETER StartWhenAvailable + Indicates that Task Scheduler can start the task at any time after its scheduled time has passed. +.PARAMETER DontStopIfGoingOnBatteries + Indicates that the task does not stop if the computer switches to battery power. +.PARAMETER WakeToRun + Indicates that Task Scheduler wakes the computer before it runs the task. +.PARAMETER IdleDuration + Specifies the amount of time that the computer must be in an idle state before Task Scheduler runs the task. +.PARAMETER RestartOnIdle + Indicates that Task Scheduler restarts the task when the computer cycles into an idle condition more than once. +.PARAMETER DontStopOnIdleEnd + Indicates that Task Scheduler does not terminate the task if the idle condition ends before the task is completed. +.PARAMETER ExecutionTimeLimit + Specifies the amount of time that Task Scheduler is allowed to complete the task. +.PARAMETER MultipleInstances + Specifies the policy that defines how Task Scheduler handles multiple instances of the task. +.PARAMETER Priority + Specifies the priority level of the task. Priority must be an integer from 0 (highest priority) to 10 (lowest priority). + The default value is 7. Priority levels 7 and 8 are used for background tasks. Priority levels 4, 5, and 6 are used for interactive tasks. +.PARAMETER RestartCount + Specifies the number of times that Task Scheduler attempts to restart the task. +.PARAMETER RestartInterval + Specifies the amount of time that Task Scheduler attempts to restart the task. +.PARAMETER RunOnlyIfNetworkAvailable + 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. +#> function Get-TargetResource { [OutputType([System.Collections.Hashtable])] param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.String] $TaskName, - [Parameter(Mandatory=$false)] + [Parameter()] [System.String] - $TaskPath = "\", + $TaskPath = '\', + + [Parameter()] + [System.String] + $Description, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.String] $ActionExecutable, - [Parameter(Mandatory=$false)] + [Parameter()] [System.String] $ActionArguments, - [Parameter(Mandatory=$false)] + [Parameter()] [System.String] $ActionWorkingPath, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.String] - [ValidateSet("Minutes", "Hourly", "Daily")] $ScheduleType, + [ValidateSet('Once', 'Daily', 'Weekly', 'AtStartup', 'AtLogOn')] + $ScheduleType, - [Parameter(Mandatory=$true)] - [System.UInt32] - $RepeatInterval, + [Parameter()] + [System.DateTime] + $RepeatInterval = [System.DateTime] '00:00:00', - [Parameter(Mandatory=$false)] - [System.String] - $StartTime = "12:00 AM", + [Parameter()] + [System.DateTime] + $StartTime = [System.DateTime]::Today, - [Parameter(Mandatory=$false)] + [Parameter()] [System.String] - [ValidateSet("Present","Absent")] - $Ensure = "Present", + [ValidateSet('Present', 'Absent')] + $Ensure = 'Present', - [Parameter(Mandatory=$false)] + [Parameter()] [System.Boolean] - $Enable, + $Enable = $true, - [Parameter(Mandatory=$false)] + [Parameter()] [System.Management.Automation.PSCredential] - $ExecuteAsCredential + $ExecuteAsCredential, + + [Parameter()] + [System.UInt32] + $DaysInterval = 1, + + [Parameter()] + [System.DateTime] + $RandomDelay = [System.DateTime] '00:00:00', + + [Parameter()] + [System.DateTime] + $RepetitionDuration = [System.DateTime] '00:00:00', + + [Parameter()] + [System.String[]] + $DaysOfWeek, + + [Parameter()] + [System.UInt32] + $WeeksInterval = 1, + + [Parameter()] + [System.String] + $User, + + [Parameter()] + [System.Boolean] + $DisallowDemandStart = $false, + + [Parameter()] + [System.Boolean] + $DisallowHardTerminate = $false, + + [Parameter()] + [ValidateSet('AT', 'V1', 'Vista', 'Win7', 'Win8')] + [System.String] + $Compatibility = 'Vista', + + [Parameter()] + [System.Boolean] + $AllowStartIfOnBatteries = $false, + + [Parameter()] + [System.Boolean] + $Hidden = $false, + + [Parameter()] + [System.Boolean] + $RunOnlyIfIdle = $false, + + [Parameter()] + [System.DateTime] + $IdleWaitTimeout = [System.DateTime] '02:00:00', + + [Parameter()] + [System.String] + $NetworkName, + + [Parameter()] + [System.Boolean] + $DisallowStartOnRemoteAppSession = $false, + + [Parameter()] + [System.Boolean] + $StartWhenAvailable = $false, + + [Parameter()] + [System.Boolean] + $DontStopIfGoingOnBatteries = $false, + + [Parameter()] + [System.Boolean] + $WakeToRun = $false, + + [Parameter()] + [System.DateTime] + $IdleDuration = [System.DateTime] '01:00:00', + + [Parameter()] + [System.Boolean] + $RestartOnIdle = $false, + + [Parameter()] + [System.Boolean] + $DontStopOnIdleEnd = $false, + + [Parameter()] + [System.DateTime] + $ExecutionTimeLimit = [System.DateTime] '8:00:00', + + [Parameter()] + [ValidateSet('IgnoreNew', 'Parallel', 'Queue')] + [System.String] + $MultipleInstances = 'Queue', + + [Parameter()] + [System.UInt32] + $Priority = 7, + + [Parameter()] + [System.UInt32] + $RestartCount = 0, + + [Parameter()] + [System.DateTime] + $RestartInterval = [System.DateTime] '00:00:00', + + [Parameter()] + [System.Boolean] + $RunOnlyIfNetworkAvailable = $false ) + + Write-Verbose -Message ('Retrieving existing task ({0} in {1})' -f $TaskName, $TaskPath) $task = Get-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath -ErrorAction SilentlyContinue if ($null -eq $task) { + Write-Verbose -Message ('No task found. returning empty task {0} with Ensure = "Absent"' -f $Taskname) return @{ TaskName = $TaskName - TaskPath = $TaskPath - Ensure = "Absent" - TriggerType = "Unknown" + ActionExecutable = $ActionExecutable + Ensure = 'Absent' + ScheduleType = $ScheduleType } } else { - $action = $task.Actions | Select -First 1 - $trigger = $task.Triggers | Select -First 1 - $repetition = $trigger.Repetition - $returnScheduleType = "Unknown" + Write-Verbose -Message ('Task {0} found in {1}. Retrieving settings, first action, first trigger and repetition settings' -f $TaskName, $TaskPath) + $action = $task.Actions | Select-Object -First 1 + $trigger = $task.Triggers | Select-Object -First 1 + $settings = $task.Settings + $returnScheduleType = 'Unknown' $returnInveral = 0 - - # Check for full formatting - if ($repetition.Interval -like "P*DT*H*M*S") + + switch ($trigger.CimClass.CimClassName) { - $timespan = [Timespan]::Parse(($repetition.Interval -replace "P" -replace "DT", ":" -replace "H", ":" -replace "M", ":" -replace "S")) - - if ($timespan.Days -ge 1) + 'MSFT_TaskTimeTrigger' { - $returnScheduleType = "Daily" - $returnInveral = $timespan.TotalDays + $returnScheduleType = 'Once' + break } - elseif ($timespan.Hours -ge 1 -and $timespan.Minutes -eq 0) + 'MSFT_TaskDailyTrigger' { - $returnScheduleType = "Hourly" - $returnInveral = $timespan.TotalHours + $returnScheduleType = 'Daily' + break } - elseif ($timespan.Minutes -ge 1) - { - $returnScheduleType = "Minutes" - $returnInveral = $timespan.TotalMinutes - } - } - else - { - if ($repetition.Duration -eq $null -and $repetition.Interval -eq $null) + + 'MSFT_TaskWeeklyTrigger' { - $returnScheduleType = "Daily" - $returnInveral = $trigger.DaysInterval + $returnScheduleType = 'Weekly' + break } - if ($repetition.Duration -eq $null -and $repetition.Interval -like "P*D") + + 'MSFT_TaskBootTrigger' { - $returnScheduleType = "Daily" - [System.Uint32]$returnInveral = $repetition.Interval -replace "P" -replace "D" + $returnScheduleType = 'AtStartup' + break } - if ($repetition.Duration -eq $null -and $repetition.Interval -like "P*D*") + + 'MSFT_TaskLogonTrigger' { - $returnScheduleType = "Daily" - [System.Uint32]$returnInveral = $repetition.Interval.Substring(0, $repetition.Interval.IndexOf('D')) -replace "P" + $returnScheduleType = 'AtLogon' + break } - if (($repetition.Duration -eq "P1D" -or $repetition.Duration -eq $null) ` - -and $repetition.Interval -like "PT*H") + + default { - $returnScheduleType = "Hourly" - [System.Uint32]$returnInveral = $repetition.Interval -replace "PT" -replace "H" - } - if (($repetition.Duration -eq "P1D" -or $repetition.Duration -eq $null) ` - -and $repetition.Interval -like "PT*M") + New-InvalidArgumentException -Message "Trigger type $_ not recognized." -ArgumentName CimClassName + } + } + + Write-Verbose -Message ('Detected schedule type {0} for first trigger' -f $returnScheduleType) + + Write-Verbose -Message 'Calculating timespans/datetimes from trigger repetition settings' + + $repInterval = $trigger.Repetition.Interval + $Days = $Hours = $Minutes = $Seconds = 0 + + if ($repInterval -match 'P(?\d{0,3})D') + { + $Days = $matches.Days + } + + if ($repInterval -match '(?\d{0,2})H') + { + $Hours = $matches.Hours + } + + if ($repInterval -match '(?\d{0,2})M') + { + $Minutes = $matches.Minutes + } + + if ($repInterval -match '(?\d{0,2})S') + { + $Seconds = $matches.Seconds + } + + $returnInveral = New-TimeSpan -Days $Days -Hours $Hours -Minutes $Minutes -Seconds $seconds + + $repDuration = $trigger.Repetition.Duration + $Days = $Hours = $Minutes = $Seconds = 0 + + if ($repDuration -match 'P(?\d{0,3})D') + { + $Days = $matches.Days + } + + if ($repDuration -match '(?\d{0,2})H') + { + $Hours = $matches.Hours + } + + if ($repDuration -match '(?\d{0,2})M') + { + $Minutes = $matches.Minutes + } + + if ($repDuration -match '(?\d{0,2})S') + { + $Seconds = $matches.Seconds + } + + $repetitionDurationReturn = New-TimeSpan -Days $Days -Hours $Hours -Minutes $Minutes -Seconds $seconds + + $resInterval = $settings.RestartInterval + $Days = $Hours = $Minutes = $Seconds = 0 + + if ($resInterval -match 'P(?\d{0,3})D') + { + $Days = $matches.Days + } + + if ($resInterval -match '(?\d{0,2})H') + { + $Hours = $matches.Hours + } + + if ($resInterval -match '(?\d{0,2})M') + { + $Minutes = $matches.Minutes + } + + if ($resInterval -match '(?\d{0,2})S') + { + $Seconds = $matches.Seconds + } + + $restartIntervalReturn = New-TimeSpan -Days $Days -Hours $Hours -Minutes $Minutes -Seconds $seconds + + $exeLim = $settings.ExecutionTimeLimit + $Days = $Hours = $Minutes = $Seconds = 0 + + if ($exeLim -match 'P(?\d{0,3})D') + { + $Days = $matches.Days + } + + if ($exeLim -match '(?\d{0,2})H') + { + $Hours = $matches.Hours + } + + if ($exeLim -match '(?\d{0,2})M') + { + $Minutes = $matches.Minutes + } + + if ($exeLim -match '(?\d{0,2})S') + { + $Seconds = $matches.Seconds + } + + $executionTimeLimitReturn = New-TimeSpan -Days $Days -Hours $Hours -Minutes $Minutes -Seconds $seconds + + $idleDur = $settings.IdleSettings.IdleDuration + $Days = $Hours = $Minutes = $Seconds = 0 + + if ($idleDur -match 'P(?\d{0,3})D') + { + $Days = $matches.Days + } + + if ($idleDur -match '(?\d{0,2})H') + { + $Hours = $matches.Hours + } + + if ($idleDur -match '(?\d{0,2})M') + { + $Minutes = $matches.Minutes + } + + if ($idleDur -match '(?\d{0,2})S') + { + $Seconds = $matches.Seconds + } + + $idleDurationReturn = New-TimeSpan -Days $Days -Hours $Hours -Minutes $Minutes -Seconds $seconds + + $idleWait = $settings.IdleSettings.IdleWaitTimeout + $Days = $Hours = $Minutes = $Seconds = 0 + + if ($idleWait -match 'P(?\d{0,3})D') + { + $Days = $matches.Days + } + + if ($idleWait -match '(?\d{0,2})H') + { + $Hours = $matches.Hours + } + + if ($idleWait -match '(?\d{0,2})M') + { + $Minutes = $matches.Minutes + } + + if ($idleWait -match '(?\d{0,2})S') + { + $Seconds = $matches.Seconds + } + + $idleWaitTimeoutReturn = New-TimeSpan -Days $Days -Hours $Hours -Minutes $Minutes -Seconds $seconds + + $rndDelay = $trigger.RandomDelay + $Days = $Hours = $Minutes = $Seconds = 0 + + if ($rndDelay -match 'P(?\d{0,3})D') + { + $Days = $matches.Days + } + + if ($rndDelay -match '(?\d{0,2})H') + { + $Hours = $matches.Hours + } + + if ($rndDelay -match '(?\d{0,2})M') + { + $Minutes = $matches.Minutes + } + + if ($rndDelay -match '(?\d{0,2})S') + { + $Seconds = $matches.Seconds + } + + $randomDelayReturn = New-TimeSpan -Days $Days -Hours $Hours -Minutes $Minutes -Seconds $seconds + + $DaysOfWeek = @() + foreach ($binaryAdductor in 1, 2, 4, 8, 16, 32, 64) + { + $Day = $trigger.DaysOfWeek -band $binaryAdductor + if ($Day -ne 0) { - $returnScheduleType = "Minutes" - if ($repetition.Interval.Contains('H')) - { - $timeToParse = ($repetition.Interval -replace "PT" -replace "H",":" -replace "M") - [System.Uint32]$returnInveral = [TimeSpan]::Parse($timeToParse).TotalMinutes - } - else - { - [System.Uint32]$returnInveral = $repetition.Interval -replace "PT" -replace "M" - } + $DaysOfWeek += [xScheduledTask.DaysOfWeek] $Day } } + + $startAt = $trigger.StartBoundary + + if ($startAt) + { + $startAt = [System.DateTime] $startAt + } + else + { + $startAt = $StartTime + } return @{ TaskName = $TaskName TaskPath = $TaskPath - Ensure = "Present" - ActionExecutable = $action.Execute - ActionArguments = $action.Arguments + StartTime = $startAt + Ensure = 'Present' + Description = $task.Description + ActionExecutable = $action.Execute + ActionArguments = $action.Arguments ActionWorkingPath = $action.WorkingDirectory ScheduleType = $returnScheduleType - RepeatInterval = $returnInveral + RepeatInterval = [System.DateTime]::Today.Add($returnInveral) ExecuteAsCredential = $task.Principal.UserId - Enable = $task.Settings.Enabled + Enable = $settings.Enabled + DaysInterval = $trigger.DaysInterval + RandomDelay = [System.DateTime]::Today.Add($randomDelayReturn) + RepetitionDuration = [System.DateTime]::Today.Add($repetitionDurationReturn) + DaysOfWeek = $DaysOfWeek + WeeksInterval = $trigger.WeeksInterval + User = $task.Principal.UserId + DisallowDemandStart = -not $settings.AllowDemandStart + DisallowHardTerminate = -not $settings.AllowHardTerminate + Compatibility = $settings.Compatibility + AllowStartIfOnBatteries = -not $settings.DisallowStartIfOnBatteries + Hidden = $settings.Hidden + RunOnlyIfIdle = $settings.RunOnlyIfIdle + IdleWaitTimeout = $idleWaitTimeoutReturn + NetworkName = $settings.NetworkSettings.Name + DisallowStartOnRemoteAppSession = $settings.DisallowStartOnRemoteAppSession + StartWhenAvailable = $settings.StartWhenAvailable + DontStopIfGoingOnBatteries = -not $settings.StopIfGoingOnBatteries + WakeToRun = $settings.WakeToRun + IdleDuration = [System.DateTime]::Today.Add($idleDurationReturn) + RestartOnIdle = $settings.IdleSettings.RestartOnIdle + DontStopOnIdleEnd = -not $settings.IdleSettings.StopOnIdleEnd + ExecutionTimeLimit = [System.DateTime]::Today.Add($executionTimeLimitReturn) + MultipleInstances = $settings.MultipleInstances + Priority = $settings.Priority + RestartCount = $settings.RestartCount + RestartInterval = [System.DateTime]::Today.Add($restartIntervalReturn) + RunOnlyIfNetworkAvailable = $settings.RunOnlyIfNetworkAvailable } } } +<# +.SYNOPSIS + Applies the desired resource state +.PARAMETER TaskName + The name of the task +.PARAMETER TaskPath + The path to the task - defaults to the root directory +.PARAMETER Description + The task description +.PARAMETER ActionExecutable + The path to the .exe for this task +.PARAMETER ActionArguments + The arguments to pass the executable +.PARAMETER ActionWorkingPath + The working path to specify for the executable +.PARAMETER ScheduleType + When should the task be executed +.PARAMETER RepeatInterval + How many units (minutes, hours, days) between each run of this task? +.PARAMETER StartTime + The time of day this task should start at - defaults to 12:00 AM. Not valid for AtLogon and AtStartup tasks +.PARAMETER Ensure + Present if the task should exist, Absent if it should be removed +.PARAMETER Enable + True if the task should be enabled, false if it should be disabled +.PARAMETER ExecuteAsCredential + The credential this task should execute as. If not specified defaults to running as the local system account +.PARAMETER DaysInterval + Specifies the interval between the days in the schedule. An interval of 1 produces a daily schedule. An interval of 2 produces an every-other day schedule. +.PARAMETER RandomDelay + Specifies a random amount of time to delay the start time of the trigger. The delay time is a random time between the time the task triggers and the time that you specify in this setting. +.PARAMETER RepetitionDuration + Specifies how long the repetition pattern repeats after the task starts. +.PARAMETER DaysOfWeek + Specifies an array of the days of the week on which Task Scheduler runs the task. +.PARAMETER WeeksInterval + Specifies the interval between the weeks in the schedule. An interval of 1 produces a weekly schedule. An interval of 2 produces an every-other week schedule. +.PARAMETER User + Specifies the identifier of the user for a trigger that starts a task when a user logs on. +.PARAMETER DisallowDemandStart + Indicates whether the task is prohibited to run on demand or not. Defaults to $false +.PARAMETER DisallowHardTerminate + Indicates whether the task is prohibited to be terminated or not. Defaults to $false +.PARAMETER Compatibility + The task compatibility level. Defaults to Vista. +.PARAMETER AllowStartIfOnBatteries + Indicates whether the task should start if the machine is on batteries or not. Defaults to $false +.PARAMETER Hidden + Indicates that the task is hidden in the Task Scheduler UI. +.PARAMETER RunOnlyIfIdle + Indicates that Task Scheduler runs the task only when the computer is idle. +.PARAMETER IdleWaitTimeout + Specifies the amount of time that Task Scheduler waits for an idle condition to occur. +.PARAMETER NetworkName + Specifies the name of a network profile that Task Scheduler uses to determine if the task can run. + The Task Scheduler UI uses this setting for display purposes. Specify a network name if you specify the RunOnlyIfNetworkAvailable parameter. +.PARAMETER DisallowStartOnRemoteAppSession + Indicates that the task does not start if the task is triggered to run in a Remote Applications Integrated Locally (RAIL) session. +.PARAMETER StartWhenAvailable + Indicates that Task Scheduler can start the task at any time after its scheduled time has passed. +.PARAMETER DontStopIfGoingOnBatteries + Indicates that the task does not stop if the computer switches to battery power. +.PARAMETER WakeToRun + Indicates that Task Scheduler wakes the computer before it runs the task. +.PARAMETER IdleDuration + Specifies the amount of time that the computer must be in an idle state before Task Scheduler runs the task. +.PARAMETER RestartOnIdle + Indicates that Task Scheduler restarts the task when the computer cycles into an idle condition more than once. +.PARAMETER DontStopOnIdleEnd + Indicates that Task Scheduler does not terminate the task if the idle condition ends before the task is completed. +.PARAMETER ExecutionTimeLimit + Specifies the amount of time that Task Scheduler is allowed to complete the task. +.PARAMETER MultipleInstances + Specifies the policy that defines how Task Scheduler handles multiple instances of the task. +.PARAMETER Priority + Specifies the priority level of the task. Priority must be an integer from 0 (highest priority) to 10 (lowest priority). + The default value is 7. Priority levels 7 and 8 are used for background tasks. Priority levels 4, 5, and 6 are used for interactive tasks. +.PARAMETER RestartCount + Specifies the number of times that Task Scheduler attempts to restart the task. +.PARAMETER RestartInterval + Specifies the amount of time that Task Scheduler attempts to restart the task. +.PARAMETER RunOnlyIfNetworkAvailable + 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. +#> function Set-TargetResource { param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.String] $TaskName, - [Parameter(Mandatory=$false)] + [Parameter()] + [System.String] + $TaskPath = '\', + + [Parameter()] [System.String] - $TaskPath = "\", + $Description, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.String] $ActionExecutable, - [Parameter(Mandatory=$false)] + [Parameter()] [System.String] $ActionArguments, - [Parameter(Mandatory=$false)] + [Parameter()] [System.String] $ActionWorkingPath, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.String] - [ValidateSet("Minutes", "Hourly", "Daily")] $ScheduleType, + [ValidateSet('Once', 'Daily', 'Weekly', 'AtStartup', 'AtLogOn')] + $ScheduleType, - [Parameter(Mandatory=$true)] - [System.UInt32] - $RepeatInterval, + [Parameter()] + [System.DateTime] + $RepeatInterval = [System.DateTime] '00:00:00', - [Parameter(Mandatory=$false)] - [System.String] - $StartTime = "12:00 AM", + [Parameter()] + [System.DateTime] + $StartTime = [System.DateTime]::Today, - [Parameter(Mandatory=$false)] + [Parameter()] [System.String] - [ValidateSet("Present","Absent")] - $Ensure = "Present", + [ValidateSet('Present', 'Absent')] + $Ensure = 'Present', - [Parameter(Mandatory=$false)] + [Parameter()] [System.Boolean] - $Enable, + $Enable = $true, - [Parameter(Mandatory=$false)] + [Parameter()] [System.Management.Automation.PSCredential] - $ExecuteAsCredential + $ExecuteAsCredential, + + [Parameter()] + [System.UInt32] + $DaysInterval = 1, + + [Parameter()] + [System.DateTime] + $RandomDelay = [System.DateTime] '00:00:00', + + [Parameter()] + [System.DateTime] + $RepetitionDuration = [System.DateTime] '00:00:00', + + [Parameter()] + [System.String[]] + $DaysOfWeek, + + [Parameter()] + [System.UInt32] + $WeeksInterval = 1, + + [Parameter()] + [System.String] + $User, + + [Parameter()] + [System.Boolean] + $DisallowDemandStart = $false, + + [Parameter()] + [System.Boolean] + $DisallowHardTerminate = $false, + + [Parameter()] + [ValidateSet('AT', 'V1', 'Vista', 'Win7', 'Win8')] + [System.String] + $Compatibility = 'Vista', + + [Parameter()] + [System.Boolean] + $AllowStartIfOnBatteries = $false, + + [Parameter()] + [System.Boolean] + $Hidden = $false, + + [Parameter()] + [System.Boolean] + $RunOnlyIfIdle = $false, + + [Parameter()] + [System.DateTime] + $IdleWaitTimeout = [System.DateTime] '02:00:00', + + [Parameter()] + [System.String] + $NetworkName, + + [Parameter()] + [System.Boolean] + $DisallowStartOnRemoteAppSession = $false, + + [Parameter()] + [System.Boolean] + $StartWhenAvailable = $false, + + [Parameter()] + [System.Boolean] + $DontStopIfGoingOnBatteries = $false, + + [Parameter()] + [System.Boolean] + $WakeToRun = $false, + + [Parameter()] + [System.DateTime] + $IdleDuration = [System.DateTime] '01:00:00', + + [Parameter()] + [System.Boolean] + $RestartOnIdle = $false, + + [Parameter()] + [System.Boolean] + $DontStopOnIdleEnd = $false, + + [Parameter()] + [System.DateTime] + $ExecutionTimeLimit = [System.DateTime] '8:00:00', + + [Parameter()] + [ValidateSet('IgnoreNew', 'Parallel', 'Queue')] + [System.String] + $MultipleInstances = 'Queue', + + [Parameter()] + [System.UInt32] + $Priority = 7, + + [Parameter()] + [System.UInt32] + $RestartCount = 0, + + [Parameter()] + [System.DateTime] + $RestartInterval = [System.DateTime] '00:00:00', + + [Parameter()] + [System.Boolean] + $RunOnlyIfNetworkAvailable = $false ) + Write-Verbose -Message ('Entering Set-TargetResource for {0} in {1}' -f $TaskName, $TaskPath) $currentValues = Get-TargetResource @PSBoundParameters - if ($Ensure -eq "Present") + if ($Ensure -eq 'Present') { + if ($RepetitionDuration.TimeOfDay -lt $RepeatInterval.TimeOfDay) + { + $exceptionMessage = 'Repetition duration {0} is less than repetition interval {1}. Please set RepeatInterval to a value lower or equal to RepetitionDuration' -f $RepetitionDuration.TimeOfDay, $RepeatInterval.TimeOfDay + New-InvalidArgumentException -Message $exceptionMessage -ArgumentName RepeatInterval + } + + if ($ScheduleType -eq 'Daily' -and $DaysInterval -eq 0) + { + $exceptionMessage = 'Schedules of the type Daily must have a DaysInterval greater than 0 (value entered: {0})' -f $DaysInterval + New-InvalidArgumentException -Message $exceptionMessage -ArgumentName DaysInterval + } + + if ($ScheduleType -eq 'Weekly' -and $WeeksInterval -eq 0) + { + $exceptionMessage = 'Schedules of the type Weekly must have a WeeksInterval greater than 0 (value entered: {0})' -f $WeeksInterval + New-InvalidArgumentException -Message $exceptionMessage -ArgumentName WeeksInterval + } + + if ($ScheduleType -eq 'Weekly' -and $DaysOfWeek.Count -eq 0) + { + $exceptionMessage = 'Schedules of the type Weekly must have at least one weekday selected' + New-InvalidArgumentException -Message $exceptionMessage -ArgumentName DaysOfWeek + } + $actionArgs = @{ Execute = $ActionExecutable } - if ($PSBoundParameters.ContainsKey("ActionArguments")) + + if ($ActionArguments) { - $actionArgs.Add("Argument", $ActionArguments) + $actionArgs.Add('Argument', $ActionArguments) } - if ($PSBoundParameters.ContainsKey("ActionWorkingPath")) + + if ($ActionWorkingPath) { - $actionArgs.Add("WorkingDirectory", $ActionWorkingPath) + $actionArgs.Add('WorkingDirectory', $ActionWorkingPath) } + $action = New-ScheduledTaskAction @actionArgs - $settingArgs = @{} - - if ($PSBoundParameters.ContainsKey("Enable")) - { - $settingArgs.Add("Disable", (-not $Enable)) + $settingArgs = @{ + DisallowDemandStart = $DisallowDemandStart + DisallowHardTerminate = $DisallowHardTerminate + Compatibility = $Compatibility + AllowStartIfOnBatteries = $AllowStartIfOnBatteries + Disable = -not $Enable + Hidden = $Hidden + RunOnlyIfIdle = $RunOnlyIfIdle + DisallowStartOnRemoteAppSession = $DisallowStartOnRemoteAppSession + StartWhenAvailable = $StartWhenAvailable + DontStopIfGoingOnBatteries = $DontStopIfGoingOnBatteries + WakeToRun = $WakeToRun + RestartOnIdle = $RestartOnIdle + DontStopOnIdleEnd = $DontStopOnIdleEnd + MultipleInstances = $MultipleInstances + Priority = $Priority + RestartCount = $RestartCount + RunOnlyIfNetworkAvailable = $RunOnlyIfNetworkAvailable } - $setting = New-ScheduledTaskSettingsSet @settingArgs + if ($IdleDuration.TimeOfDay -gt [System.TimeSpan] '00:00:00') + { + $settingArgs.Add('IdleDuration', $IdleDuration.TimeOfDay) + } - $date = (Get-Date).Date - $startTime = [DateTime]::Parse("$($date.ToShortDateString()) $StartTime") - switch ($ScheduleType) + if ($IdleWaitTimeout.TimeOfDay -gt [System.TimeSpan] '00:00:00') { - "Minutes" - { - $repeatAt = New-TimeSpan -Minutes $RepeatInterval - } - "Hourly" - { - $repeatAt = New-TimeSpan -Hours $RepeatInterval - } - "Daily" - { - $repeatAt = New-TimeSpan -Days $RepeatInterval - } + $settingArgs.Add('IdleWaitTimeout', $IdleWaitTimeout.TimeOfDay) } - try + + if ($ExecutionTimeLimit.TimeOfDay -gt [System.TimeSpan] '00:00:00') { - $trigger = New-ScheduledTaskTrigger -Once -At $startTime ` - -RepetitionInterval $repeatAt + $settingArgs.Add('ExecutionTimeLimit', $ExecutionTimeLimit.TimeOfDay) } - catch + + if ($RestartInterval.TimeOfDay -gt [System.TimeSpan] '00:00:00') + { + $settingArgs.Add('RestartInterval', $RestartInterval.TimeOfDay) + } + + if (-not [System.String]::IsNullOrWhiteSpace($NetworkName)) { - $trigger = New-ScheduledTaskTrigger -Once -At $startTime ` - -RepetitionInterval $repeatAt ` - -RepetitionDuration ([TimeSpan]::MaxValue) + $setting.Add('NetworkName', $NetworkName) } + $setting = New-ScheduledTaskSettingsSet @settingArgs - if ($currentValues.Ensure -eq "Absent") + $triggerArgs = @{} + if ($RandomDelay.TimeOfDay -gt [System.TimeSpan]::FromSeconds(0)) { - Write-Verbose -Message "Creating new scheduled task `"$TaskName`"" + $triggerArgs.Add('RandomDelay', $RandomDelay.TimeOfDay) + } - $scheduledTask = New-ScheduledTask -Action $action -Trigger $trigger -Settings $setting - $registerArgs = @{ - TaskName = $TaskName - TaskPath = $TaskPath - InputObject = $scheduledTask + switch ($ScheduleType) + { + 'Once' + { + $triggerArgs.Add('Once', $true) + $triggerArgs.Add('At', $StartTime) + break } - if ($PSBoundParameters.ContainsKey("ExecuteAsCredential") -eq $true) + 'Daily' { - $registerArgs.Add("User", $ExecuteAsCredential.UserName) - $registerArgs.Add("Password", $ExecuteAsCredential.GetNetworkCredential().Password) - } - else + $triggerArgs.Add('Daily', $true) + $triggerArgs.Add('At', $StartTime) + $triggerArgs.Add('DaysInterval', $DaysInterval) + break + } + 'Weekly' { - $registerArgs.Add("User", "NT AUTHORITY\SYSTEM") + $triggerArgs.Add('Weekly', $true) + $triggerArgs.Add('At', $StartTime) + if ($DaysOfWeek.Count -gt 0) + { + $triggerArgs.Add('DaysOfWeek', $DaysOfWeek) + } + + if ($WeeksInterval -gt 0) + { + $triggerArgs.Add('WeeksInterval', $WeeksInterval) + } + break + } + 'AtStartup' + { + $triggerArgs.Add('AtStartup', $true) + break + } + 'AtLogOn' + { + $triggerArgs.Add('AtLogOn', $true) + if (-not [System.String]::IsNullOrWhiteSpace($User)) + { + $triggerArgs.Add('User', $User) + } + break } - Register-ScheduledTask @registerArgs } - if ($currentValues.Ensure -eq "Present") + + $trigger = New-ScheduledTaskTrigger @triggerArgs -ErrorAction SilentlyContinue + if (-not $trigger) + { + New-InvalidOperationException -Message 'Error creating new scheduled task trigger' -ErrorRecord $_ + } + + # To overcome the issue of not being able to set the task repetition for tasks with a schedule type other than Once + if ($RepeatInterval.TimeOfDay -gt (New-TimeSpan -Seconds 0) -and $PSVersionTable.PSVersion.Major -gt 4) { - Write-Verbose -Message "Updating scheduled task `"$TaskName`"" + if ($RepetitionDuration.TimeOfDay -le $RepeatInterval.TimeOfDay) + { + $exceptionMessage ='Repetition interval is set to {0} but repetition duration is {1}' -f $RepeatInterval.TimeOfDay, $RepetitionDuration.TimeOfDay + New-InvalidArgumentException -Message $exceptionMessage -ArgumentName RepetitionDuration + } + + $tempTrigger = New-ScheduledTaskTrigger -Once -At 6:6:6 -RepetitionInterval $RepeatInterval.TimeOfDay -RepetitionDuration $RepetitionDuration.TimeOfDay + Write-Verbose -Message 'PS V5 Copying values from temporary trigger to property Repetition of $trigger.Repetition' - $setArgs = @{ - TaskName = $TaskName - TaskPath = $TaskPath - Action= $action - Trigger = $trigger - Settings = $setting + try + { + $trigger.Repetition = $tempTrigger.Repetition } - if ($PSBoundParameters.ContainsKey("ExecuteAsCredential") -eq $true) + catch { - $setArgs.Add("User", $ExecuteAsCredential.UserName) - $setArgs.Add("Password", $ExecuteAsCredential.GetNetworkCredential().Password) - } - else + $triggerRepetitionFailed = $true + } + } + + if ($currentValues.Ensure -eq 'Present') + { + Write-Verbose -Message ('Removing previous scheduled task' -f $TaskName) + $null = Unregister-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath -Confirm:$false + } + + Write-Verbose -Message ('Creating new scheduled task' -f $TaskName) + + $scheduledTask = New-ScheduledTask -Action $action -Trigger $trigger -Settings $setting + + if ($RepeatInterval.TimeOfDay -gt (New-TimeSpan -Seconds 0) -and ($PSVersionTable.PSVersion.Major -eq 4 -or $triggerRepetitionFailed)) + { + if ($RepetitionDuration.TimeOfDay -le $RepeatInterval.TimeOfDay) { - $setArgs.Add("User", "NT AUTHORITY\SYSTEM") + $exceptionMessage = 'Repetition interval is set to {0} but repetition duration is {1}' -f $RepeatInterval.TimeOfDay, $RepetitionDuration.TimeOfDay + New-InvalidArgumentException -Message $exceptionMessage -ArgumentName RepetitionDuration } - Set-ScheduledTask @setArgs + + $tempTrigger = New-ScheduledTaskTrigger -Once -At 6:6:6 -RepetitionInterval $RepeatInterval.TimeOfDay -RepetitionDuration $RepetitionDuration.TimeOfDay + $tempTask = New-ScheduledTask -Trigger $tempTrigger -Action $action + Write-Verbose -Message 'PS V4 Copying values from temporary trigger to property Repetition of $trigger.Repetition' + + $scheduledTask.Triggers[0].Repetition = $tempTask.Triggers[0].Repetition } + + if (-not [System.String]::IsNullOrWhiteSpace($Description)) + { + $scheduledTask.Description = $Description + } + + $registerArgs = @{ + TaskName = $TaskName + TaskPath = $TaskPath + InputObject = $scheduledTask + } + + if ($PSBoundParameters.ContainsKey('ExecuteAsCredential') -eq $true) + { + $registerArgs.Add('User', $ExecuteAsCredential.UserName) + $registerArgs.Add('Password', $ExecuteAsCredential.GetNetworkCredential().Password) + } + else + { + $registerArgs.Add('User', 'NT AUTHORITY\SYSTEM') + } + + $null = Register-ScheduledTask @registerArgs } - if ($Ensure -eq "Absent") + if ($Ensure -eq 'Absent') { - Write-Verbose -Message "Removing scheduled task `"$TaskName`"" + Write-Verbose -Message ('Removing scheduled task' -f $TaskName) Unregister-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath -Confirm:$false } } +<# +.SYNOPSIS + Tests if the current resource state matches the desired resource state +.PARAMETER TaskName + The name of the task +.PARAMETER TaskPath + The path to the task - defaults to the root directory +.PARAMETER Description + The task description +.PARAMETER ActionExecutable + The path to the .exe for this task +.PARAMETER ActionArguments + The arguments to pass the executable +.PARAMETER ActionWorkingPath + The working path to specify for the executable +.PARAMETER ScheduleType + When should the task be executed +.PARAMETER RepeatInterval + How many units (minutes, hours, days) between each run of this task? +.PARAMETER StartTime + The time of day this task should start at - defaults to 12:00 AM. Not valid for AtLogon and AtStartup tasks +.PARAMETER Ensure + Present if the task should exist, Absent if it should be removed +.PARAMETER Enable + True if the task should be enabled, false if it should be disabled +.PARAMETER ExecuteAsCredential + The credential this task should execute as. If not specified defaults to running as the local system account +.PARAMETER DaysInterval + Specifies the interval between the days in the schedule. An interval of 1 produces a daily schedule. An interval of 2 produces an every-other day schedule. +.PARAMETER RandomDelay + Specifies a random amount of time to delay the start time of the trigger. The delay time is a random time between the time the task triggers and the time that you specify in this setting. +.PARAMETER RepetitionDuration + Specifies how long the repetition pattern repeats after the task starts. +.PARAMETER DaysOfWeek + Specifies an array of the days of the week on which Task Scheduler runs the task. +.PARAMETER WeeksInterval + Specifies the interval between the weeks in the schedule. An interval of 1 produces a weekly schedule. An interval of 2 produces an every-other week schedule. +.PARAMETER User + Specifies the identifier of the user for a trigger that starts a task when a user logs on. +.PARAMETER DisallowDemandStart + Indicates whether the task is prohibited to run on demand or not. Defaults to $false +.PARAMETER DisallowHardTerminate + Indicates whether the task is prohibited to be terminated or not. Defaults to $false +.PARAMETER Compatibility + The task compatibility level. Defaults to Vista. +.PARAMETER AllowStartIfOnBatteries + Indicates whether the task should start if the machine is on batteries or not. Defaults to $false +.PARAMETER Hidden + Indicates that the task is hidden in the Task Scheduler UI. +.PARAMETER RunOnlyIfIdle + Indicates that Task Scheduler runs the task only when the computer is idle. +.PARAMETER IdleWaitTimeout + Specifies the amount of time that Task Scheduler waits for an idle condition to occur. +.PARAMETER NetworkName + Specifies the name of a network profile that Task Scheduler uses to determine if the task can run. + The Task Scheduler UI uses this setting for display purposes. Specify a network name if you specify the RunOnlyIfNetworkAvailable parameter. +.PARAMETER DisallowStartOnRemoteAppSession + Indicates that the task does not start if the task is triggered to run in a Remote Applications Integrated Locally (RAIL) session. +.PARAMETER StartWhenAvailable + Indicates that Task Scheduler can start the task at any time after its scheduled time has passed. +.PARAMETER DontStopIfGoingOnBatteries + Indicates that the task does not stop if the computer switches to battery power. +.PARAMETER WakeToRun + Indicates that Task Scheduler wakes the computer before it runs the task. +.PARAMETER IdleDuration + Specifies the amount of time that the computer must be in an idle state before Task Scheduler runs the task. +.PARAMETER RestartOnIdle + Indicates that Task Scheduler restarts the task when the computer cycles into an idle condition more than once. +.PARAMETER DontStopOnIdleEnd + Indicates that Task Scheduler does not terminate the task if the idle condition ends before the task is completed. +.PARAMETER ExecutionTimeLimit + Specifies the amount of time that Task Scheduler is allowed to complete the task. +.PARAMETER MultipleInstances + Specifies the policy that defines how Task Scheduler handles multiple instances of the task. +.PARAMETER Priority + Specifies the priority level of the task. Priority must be an integer from 0 (highest priority) to 10 (lowest priority). + The default value is 7. Priority levels 7 and 8 are used for background tasks. Priority levels 4, 5, and 6 are used for interactive tasks. +.PARAMETER RestartCount + Specifies the number of times that Task Scheduler attempts to restart the task. +.PARAMETER RestartInterval + Specifies the amount of time that Task Scheduler attempts to restart the task. +.PARAMETER RunOnlyIfNetworkAvailable + 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. +#> function Test-TargetResource { [OutputType([System.Boolean])] param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.String] $TaskName, - [Parameter(Mandatory=$false)] + [Parameter()] [System.String] - $TaskPath = "\", + $TaskPath = '\', + + [Parameter()] + [System.String] + $Description, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.String] $ActionExecutable, - [Parameter(Mandatory=$false)] + [Parameter()] [System.String] $ActionArguments, - [Parameter(Mandatory=$false)] + [Parameter()] [System.String] $ActionWorkingPath, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [System.String] - [ValidateSet("Minutes", "Hourly", "Daily")] $ScheduleType, + [ValidateSet('Once', 'Daily', 'Weekly', 'AtStartup', 'AtLogOn')] + $ScheduleType, - [Parameter(Mandatory=$true)] - [System.UInt32] - $RepeatInterval, + [Parameter()] + [System.DateTime] + $RepeatInterval = [System.DateTime] '00:00:00', - [Parameter(Mandatory=$false)] - [System.String] - $StartTime = "12:00 AM", + [Parameter()] + [System.DateTime] + $StartTime = [System.DateTime]::Today, - [Parameter(Mandatory=$false)] + [Parameter()] [System.String] - [ValidateSet("Present","Absent")] - $Ensure = "Present", + [ValidateSet('Present', 'Absent')] + $Ensure = 'Present', - [Parameter(Mandatory=$false)] + [Parameter()] [System.Boolean] - $Enable, + $Enable = $true, - [Parameter(Mandatory=$false)] + [Parameter()] [System.Management.Automation.PSCredential] - $ExecuteAsCredential + $ExecuteAsCredential, + + [Parameter()] + [System.UInt32] + $DaysInterval = 1, + + [Parameter()] + [System.DateTime] + $RandomDelay = [System.DateTime] '00:00:00', + + [Parameter()] + [System.DateTime] + $RepetitionDuration = [System.DateTime] '00:00:00', + + [Parameter()] + [System.String[]] + $DaysOfWeek, + + [Parameter()] + [System.UInt32] + $WeeksInterval = 1, + + [Parameter()] + [System.String] + $User, + + [Parameter()] + [System.Boolean] + $DisallowDemandStart = $false, + + [Parameter()] + [System.Boolean] + $DisallowHardTerminate = $false, + + [Parameter()] + [ValidateSet('AT', 'V1', 'Vista', 'Win7', 'Win8')] + [System.String] + $Compatibility = 'Vista', + + [Parameter()] + [System.Boolean] + $AllowStartIfOnBatteries = $false, + + [Parameter()] + [System.Boolean] + $Hidden = $false, + + [Parameter()] + [System.Boolean] + $RunOnlyIfIdle = $false, + + [Parameter()] + [System.DateTime] + $IdleWaitTimeout = [System.DateTime] '02:00:00', + + [Parameter()] + [System.String] + $NetworkName, + + [Parameter()] + [System.Boolean] + $DisallowStartOnRemoteAppSession = $false, + + [Parameter()] + [System.Boolean] + $StartWhenAvailable = $false, + + [Parameter()] + [System.Boolean] + $DontStopIfGoingOnBatteries = $false, + + [Parameter()] + [System.Boolean] + $WakeToRun = $false, + + [Parameter()] + [System.DateTime] + $IdleDuration = [System.DateTime] '01:00:00', + + [Parameter()] + [System.Boolean] + $RestartOnIdle = $false, + + [Parameter()] + [System.Boolean] + $DontStopOnIdleEnd = $false, + + [Parameter()] + [System.DateTime] + $ExecutionTimeLimit = [System.DateTime] '8:00:00', + + [Parameter()] + [ValidateSet('IgnoreNew', 'Parallel', 'Queue')] + [System.String] + $MultipleInstances = 'Queue', + + [Parameter()] + [System.UInt32] + $Priority = 7, + + [Parameter()] + [System.UInt32] + $RestartCount = 0, + + [Parameter()] + [System.DateTime] + $RestartInterval = [System.DateTime] '00:00:00', + + [Parameter()] + [System.Boolean] + $RunOnlyIfNetworkAvailable = $false ) - $currentValues = Get-TargetResource @PSBoundParameters - if ($Ensure -ne $currentValues.Ensure) - { - return $false + Write-Verbose -Message ('Testing scheduled task {0}' -f $TaskName) + + $CurrentValues = Get-TargetResource @PSBoundParameters + + Write-Verbose -Message 'Current values retrieved' + + if ($Ensure -eq 'Absent' -and $CurrentValues.Ensure -eq 'Absent') + { + return $true } - if ($Ensure -eq "Present") + + if ($null -eq $CurrentValues) { - if ($TaskPath -ne $currentValues.TaskPath) - { - Write-Verbose -Message "TaskPath does not match desired state. Current value: $($currentValues.TaskPath) - Desired Value: $TaskPath" - return $false - } - if ($ActionExecutable -ne $currentValues.ActionExecutable) - { - Write-Verbose -Message "ActionExecutable does not match desired state. Current value: $($currentValues.ActionExecutable) - Desired Value: $ActionExecutable" - return $false - } - if (($PSBoundParameters.ContainsKey("ActionArguments") -eq $true) ` - -and ($ActionArguments -ne $currentValues.ActionArguments)) - { - Write-Verbose -Message "ActionArguments does not match desired state. Current value: $($currentValues.ActionArguments) - Desired Value: $ActionArguments" - return $false - } - if (($PSBoundParameters.ContainsKey("ActionWorkingPath") -eq $true) ` - -and ($ActionWorkingPath -ne $currentValues.ActionWorkingPath)) - { - Write-Verbose -Message "ActionWorkingPath does not match desired state. Current value: $($currentValues.ActionWorkingPath) - Desired Value: $ActionWorkingPath" - return $false - } - if ($ScheduleType -ne $currentValues.ScheduleType) - { - Write-Verbose -Message "ScheduleType does not match desired state. Current value: $($currentValues.ScheduleType) - Desired Value: $ScheduleType" - return $false - } - if ($RepeatInterval -ne $currentValues.RepeatInterval) - { - Write-Verbose -Message "RepeatInterval does not match desired state. Current value: $($currentValues.RepeatInterval) - Desired Value: $RepeatInterval" - return $false - } - - if ($PSBoundParameters.ContainsKey("ExecuteAsCredential") -eq $true) - { - if ($ExecuteAsCredential.UserName -ne $currentValues.ExecuteAsCredential) - { - Write-Verbose -Message "ExecuteAsCredential does not match desired state. Current value: $($currentValues.ExecuteAsCredential) - Desired Value: $localUser" - return $false - } - } - - if ($PSBoundParameters.ContainsKey("Enable") -eq $true) - { - if ($Enable -ne ($currentValues.Enable)) - { - Write-Verbose -Message "Enable does not match desired state. Current value: $($currentValues.Enabled) - Desired Vale: $Enable" - return $false - } - } + Write-Verbose -Message 'Current values were null' + return $false } - - return $true + + Write-Verbose -Message 'Testing DSC parameter state' + return Test-DscParameterState -CurrentValues $CurrentValues -DesiredValues $PSBoundParameters } diff --git a/DSCResources/MSFT_xScheduledTask/MSFT_xScheduledTask.schema.mof b/DSCResources/MSFT_xScheduledTask/MSFT_xScheduledTask.schema.mof index a7952b71..3f1e1a69 100644 --- a/DSCResources/MSFT_xScheduledTask/MSFT_xScheduledTask.schema.mof +++ b/DSCResources/MSFT_xScheduledTask/MSFT_xScheduledTask.schema.mof @@ -3,13 +3,41 @@ class MSFT_xScheduledTask : OMI_BaseResource { [Key, Description("The name of the task")] string TaskName; [Write, Description("The path to the task - defaults to the root directory")] string TaskPath; + [Write, Description("The task description")] string Description; [Required, 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; - [Required, Description("How frequently should this task be executed? Minutes, Hourly or Daily"), ValueMap{"Minutes", "Hourly", "Daily"}, Values{"Minutes", "Hourly", "Daily"}] string ScheduleType; - [Required, Description("How many units (minutes, hours, days) between each run of this task?")] Uint32 RepeatInterval; - [Write, Description("The time of day this task should start at - defaults to 12:00 AM")] string StartTime; - [Write, Description("Present if the task should exist, false if it should be removed"), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] string Ensure; + [Required, Description("When should the task be executed"), ValueMap{"Once", "Daily", "Weekly", "AtStartup", "AtLogOn"}, Values{"Once", "Daily", "Weekly", "AtStartup", "AtLogOn"}] string ScheduleType; + [Write, Description("How many units (minutes, hours, days) between each run of this task?")] DateTime 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; [Write, Description("True if the task should be enabled, false if it should be disabled")] boolean Enable; [Write, Description("The credential this task should execute as. If not specified defaults to running as the local system account"), EmbeddedInstance("MSFT_Credential")] string ExecuteAsCredential; + [Write, Description("Specifies the interval between the days in the schedule. An interval of 1 produces a daily schedule. An interval of 2 produces an every-other day schedule.")] Uint32 DaysInterval; + [Write, Description("Specifies a random amount of time to delay the start time of the trigger. The delay time is a random time between the time the task triggers and the time that you specify in this setting.")] DateTime RandomDelay; + [Write, Description("Specifies how long the repetition pattern repeats after the task starts.")] DateTime RepetitionDuration; + [Write, Description("Specifies an array of the days of the week on which Task Scheduler runs the task.")] String DaysOfWeek[]; + [Write, Description("Specifies the interval between the weeks in the schedule. An interval of 1 produces a weekly schedule. An interval of 2 produces an every-other week schedule.")] Uint32 WeeksInterval; + [Write, Description("Specifies the identifier of the user for a trigger that starts a task when a user logs on.")] String User; + [Write, Description("Indicates whether the task is prohibited to run on demand or not. Defaults to $false")] Boolean DisallowDemandStart; + [Write, Description("Indicates whether the task is prohibited to be terminated or not. Defaults to $false")] Boolean DisallowHardTerminate; + [Write, Description("The task compatibility level. Defaults to Vista."), ValueMap{"AT","V1","Vista","Win7","Win8"}, Values{"AT","V1","Vista","Win7","Win8"}] String Compatibility; + [Write, Description("Indicates whether the task should start if the machine is on batteries or not. Defaults to $false")] Boolean AllowStartIfOnBatteries; + [Write, Description("Indicates that the task is hidden in the Task Scheduler UI.")] Boolean Hidden; + [Write, Description("Indicates that Task Scheduler runs the task only when the computer is idle.")] Boolean RunOnlyIfIdle; + [Write, Description("Specifies the amount of time that Task Scheduler waits for an idle condition to occur.")] DateTime IdleWaitTimeout; + [Write, Description("Specifies the name of a network profile that Task Scheduler uses to determine if the task can run. The Task Scheduler UI uses this setting for display purposes. Specify a network name if you specify the RunOnlyIfNetworkAvailable parameter.")] String NetworkName; + [Write, Description("Indicates that the task does not start if the task is triggered to run in a Remote Applications Integrated Locally (RAIL) session.")] Boolean DisallowStartOnRemoteAppSession; + [Write, Description("Indicates that Task Scheduler can start the task at any time after its scheduled time has passed.")] Boolean StartWhenAvailable; + [Write, Description("Indicates that the task does not stop if the computer switches to battery power.")] Boolean DontStopIfGoingOnBatteries; + [Write, Description("Indicates that Task Scheduler wakes the computer before it runs the task.")] Boolean WakeToRun; + [Write, Description("Specifies the amount of time that the computer must be in an idle state before Task Scheduler runs the task.")] DateTime IdleDuration; + [Write, Description("Indicates that Task Scheduler restarts the task when the computer cycles into an idle condition more than once.")] Boolean RestartOnIdle; + [Write, Description("Indicates that Task Scheduler does not terminate the task if the idle condition ends before the task is completed.")] Boolean DontStopOnIdleEnd; + [Write, Description("Specifies the amount of time that Task Scheduler is allowed to complete the task.")] DateTime ExecutionTimeLimit; + [Write, Description("Specifies the policy that defines how Task Scheduler handles multiple instances of the task."), ValueMap{"IgnoreNew","Parallel","Queue"}, Values{"IgnoreNew","Parallel","Queue"}] String MultipleInstances; + [Write, Description("Specifies the priority level of the task. Priority must be an integer from 0 (highest priority) to 10 (lowest priority). The default value is 7. Priority levels 7 and 8 are used for background tasks. Priority levels 4, 5, and 6 are used for interactive tasks.")] Uint32 Priority; + [Write, Description("Specifies the number of times that Task Scheduler attempts to restart the task.")] Uint32 RestartCount; + [Write, Description("Specifies the amount of time that Task Scheduler attempts to restart the task.")] DateTime RestartInterval; + [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; }; diff --git a/Examples/Sample_xScheduledTask.ps1 b/Examples/Sample_xScheduledTask.ps1 index bb853a61..5f5a3c46 100644 --- a/Examples/Sample_xScheduledTask.ps1 +++ b/Examples/Sample_xScheduledTask.ps1 @@ -1,21 +1,80 @@ -configuration Sample_xScheduledTask +<# +This example creates five tasks with the following schedules that start a new powershell process +- Once at 00:00 repeating every 15 minutes for 8 hours +- Daily at 00:00 repeating every 15 minutes for 8 hours +- Weekly at 00:00 repeating every 15 minutes for 8 hours on Mon, Wed, Sat +- At logon repeating every 15 minutes for 8 hours +- At startup repeating every 15 minutes for 8 hours +#> +Configuration Sample_xScheduledTask { - param - ( - [string[]]$NodeName = 'localhost' - ) - - Import-DSCResource -ModuleName xComputerManagement - - Node $NodeName + Import-DscResource -ModuleName xComputerManagement + node 'localhost' { - xScheduledTask MaintenanceScriptExample + xScheduledTask xScheduledTaskOnceAdd + { + TaskName = 'Test task once' + TaskPath = '\MyTasks' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + RepeatInterval = [datetime]::Today.AddMinutes(15) + RepetitionDuration = [datetime]::Today.AddHours(8) + ActionWorkingPath = (Get-Location).Path + Enable = $true + RandomDelay = [datetime]::Today.AddMinutes(60) + DisallowHardTerminate = $true + RunOnlyIfIdle = $false + Priority = 9 + } + + xScheduledTask xScheduledTaskDailyAdd + { + TaskName = 'Test task Daily' + TaskPath = '\MyTasks' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Daily' + DaysInterval = 1 + RepeatInterval = [datetime]::Today.AddMinutes(15) + RepetitionDuration = [datetime]::Today.AddHours(8) + RestartCount = 2 + RestartInterval = [datetime]::Today.AddMinutes(5) + RunOnlyIfNetworkAvailable = $true + WakeToRun = $true + } + + xScheduledTask xScheduledTaskWeeklyAdd + { + TaskName = 'Test task Weekly' + TaskPath = '\MyTasks' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Weekly' + WeeksInterval = 1 + DaysOfWeek = 'Monday','Wednesday','Saturday' + RepeatInterval = [datetime]::Today.AddMinutes(15) + RepetitionDuration = [datetime]::Today.AddHours(8) + AllowStartIfOnBatteries = $true + Compatibility = 'Win8' + Hidden = $true + } + + xScheduledTask xScheduledTaskLogonAdd + { + TaskName = 'Test task Logon' + TaskPath = '\MyTasks' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'AtLogOn' + RepeatInterval = [datetime]::Today.AddMinutes(15) + RepetitionDuration = [datetime]::Today.AddHours(8) + } + + xScheduledTask xScheduledTaskStartupAdd { - TaskName = "Custom maintenance tasks" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ActionArguments = "-File `"C:\scripts\my custom script.ps1`"" - ScheduleType = "Minutes" - RepeatInterval = 15 + TaskName = 'Test task Startup' + TaskPath = '\MyTasks' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'AtStartup' + RepeatInterval = [datetime]::Today.AddMinutes(15) + RepetitionDuration = [datetime]::Today.AddHours(8) } } } diff --git a/README.md b/README.md index 5bb07b08..91df9e0b 100644 --- a/README.md +++ b/README.md @@ -61,15 +61,44 @@ Tasks are created to run indefinitly based on the schedule defined. xScheduledTask has the following properties: * TaskName: The name of the task - * TaskPath: The path to the task - optional, defaults to '\' + * TaskPath: The path to the task - defaults to the root directory + * Description: The task description * ActionExecutable: The path to the .exe for this task - * ActionArguments: The arguments to pass the executable - optional - * ActionWorkingPath: The working path to specify for the executable - optional - * ScheduleType: How frequently should this task be executed? Minutes, Hourly or Daily + * ActionArguments: The arguments to pass the executable + * ActionWorkingPath: The working path to specify for the executable + * ScheduleType: When should the task be executed ("Once", "Daily", "Weekly", "AtStartup", "AtLogOn") * RepeatInterval: How many units (minutes, hours, days) between each run of this task? - * StartTime: The time of day this task should start at - optional, defaults to '12:00 AM' - * Ensure: Present if the task should exist, false if it should be removed - optional, defaults to 'Ensure' - * ExecuteAsCredential: The credential this task should execute as - Optional, defaults to running as 'NT AUTHORITY\SYSTEM' + * StartTime: The time of day this task should start at - defaults to 12:00 AM. Not valid for AtLogon and AtStartup tasks + * Ensure: Present if the task should exist, false if it should be removed + * Enable: True if the task should be enabled, false if it should be disabled + * ExecuteAsCredential: The credential this task should execute as. If not specified defaults to running as the local system account + * DaysInterval: Specifies the interval between the days in the schedule. An interval of 1 produces a daily schedule. An interval of 2 produces an every-other day schedule. + * RandomDelay: Specifies a random amount of time to delay the start time of the trigger. The delay time is a random time between the time the task triggers and the time that you specify in this setting. + * RepetitionDuration: Specifies how long the repetition pattern repeats after the task starts. + * DaysOfWeek: Specifies an array of the days of the week on which Task Scheduler runs the task. + * WeeksInterval: Specifies the interval between the weeks in the schedule. An interval of 1 produces a weekly schedule. An interval of 2 produces an every-other week schedule. + * User: Specifies the identifier of the user for a trigger that starts a task when a user logs on. + * DisallowDemandStart: Indicates whether the task is prohibited to run on demand or not. Defaults to $false + * DisallowHardTerminate: Indicates whether the task is prohibited to be terminated or not. Defaults to $false + * Compatibility: The task compatibility level. Defaults to Vista. Possible values: "AT","V1","Vista","Win7","Win8" + * AllowStartIfOnBatteries: Indicates whether the task should start if the machine is on batteries or not. Defaults to $false + * Hidden: Indicates that the task is hidden in the Task Scheduler UI. Defaults to $false + * RunOnlyIfIdle: Indicates that Task Scheduler runs the task only when the computer is idle. + * IdleWaitTimeout: Specifies the amount of time that Task Scheduler waits for an idle condition to occur. DateTime ; + * NetworkName: Specifies the name of a network profile that Task Scheduler uses to determine if the task can run. The Task Scheduler UI uses this setting for display purposes. Specify a network name if you specify the RunOnlyIfNetworkAvailable parameter. + * DisallowStartOnRemoteAppSession: Indicates that the task does not start if the task is triggered to run in a Remote Applications Integrated Locally (RAIL) session. + * StartWhenAvailable: Indicates that Task Scheduler can start the task at any time after its scheduled time has passed. + * DontStopIfGoingOnBatteries: Indicates that the task does not stop if the computer switches to battery power. + * WakeToRun: Indicates that Task Scheduler wakes the computer before it runs the task. + * IdleDuration: Specifies the amount of time that the computer must be in an idle state before Task Scheduler runs the task. + * RestartOnIdle: Indicates that Task Scheduler restarts the task when the computer cycles into an idle condition more than once. + * DontStopOnIdleEnd: Indicates that Task Scheduler does not terminate the task if the idle condition ends before the task is completed. + * ExecutionTimeLimit: Specifies the amount of time that Task Scheduler is allowed to complete the task. + * MultipleInstances: Specifies the policy that defines how Task Scheduler handles multiple instances of the task. Possible values: "IgnoreNew","Parallel","Queue" + * Priority: Specifies the priority level of the task. Priority must be an integer from 0 (highest priority) to 10 (lowest priority). The default value is 7. Priority levels 7 and 8 are used for background tasks. Priority levels 4, 5, and 6 are used for interactive tasks. + * RestartCount: Specifies the number of times that Task Scheduler attempts to restart the task. + * RestartInterval: Specifies the amount of time that Task Scheduler attempts to restart the task. + * RunOnlyIfNetworkAvailable: 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. ## xPowerPlan xPowerPlan resource has following properties: @@ -91,6 +120,10 @@ xVirtualMemory has the following properties: ### Unreleased +### 2.0.0.0 +* Updated resources + - BREAKING CHANGE: xScheduledTask: Added nearly all available parameters for tasks + ### 1.10.0.0 * Added resources - xVirtualMemory @@ -412,8 +445,9 @@ configuration Sample_xScheduledTask TaskName = "Custom maintenance tasks" ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" ActionArguments = "-File `"C:\scripts\my custom script.ps1`"" - ScheduleType = "Minutes" - RepeatInterval = 15 + ScheduleType = 'Once' + RepeatInterval = [datetime]::Today.AddMinutes(15) + RepetitionDuration = [datetime]::Today.AddHours(10) } } } diff --git a/Tests/Integration/MSFT_xScheduledTask.Config.ps1 b/Tests/Integration/MSFT_xScheduledTask.Config.ps1 index c97379c6..875aba91 100644 --- a/Tests/Integration/MSFT_xScheduledTask.Config.ps1 +++ b/Tests/Integration/MSFT_xScheduledTask.Config.ps1 @@ -1,133 +1,285 @@ -Configuration xScheduledTask_Add +Configuration xScheduledTaskOnceAdd { Import-DscResource -ModuleName xComputerManagement - node "localhost" { - xScheduledTask xScheduledTask_Add { - TaskName = "Test task" - TaskPath = "\xComputerManagement\" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Minutes" - RepeatInterval = 15 - } + node 'localhost' + { + xScheduledTask xScheduledTaskOnceAdd + { + TaskName = 'Test task once' + TaskPath = '\xComputerManagement\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + RepeatInterval = [datetime]::Today.AddMinutes(15) + RepetitionDuration = [datetime]::Today.AddHours(8) + ActionWorkingPath = (Get-Location).Path + Enable = $true + RandomDelay = [datetime]::Today.AddMinutes(60) + DisallowHardTerminate = $true + RunOnlyIfIdle = $false + Priority = 9 + } } } -Configuration xScheduledTask_Edit1 +Configuration xScheduledTaskDailyAdd { Import-DscResource -ModuleName xComputerManagement - node "localhost" { - xScheduledTask xScheduledTask_Edit1 { - TaskName = "Test task" - TaskPath = "\xComputerManagement\" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Minutes" - RepeatInterval = 45 - } + node 'localhost' + { + xScheduledTask xScheduledTaskDailyAdd + { + TaskName = 'Test task Daily' + TaskPath = '\xComputerManagement\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Daily' + DaysInterval = 1 + RepeatInterval = [datetime]::Today.AddMinutes(15) + RepetitionDuration = [datetime]::Today.AddHours(8) + RestartCount = 2 + RestartInterval = [datetime]::Today.AddMinutes(5) + RunOnlyIfNetworkAvailable = $true + WakeToRun = $true + } } } -Configuration xScheduledTask_Edit2 +Configuration xScheduledTaskWeeklyAdd { Import-DscResource -ModuleName xComputerManagement - node "localhost" { - xScheduledTask xScheduledTask_Edit2 { - TaskName = "Test task" - TaskPath = "\xComputerManagement\" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Hourly" - RepeatInterval = 4 - } + node 'localhost' + { + xScheduledTask xScheduledTaskWeeklyAdd + { + TaskName = 'Test task Weekly' + TaskPath = '\xComputerManagement\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Weekly' + WeeksInterval = 1 + DaysOfWeek = 'Monday', 'Wednesday', 'Saturday' + RepeatInterval = [datetime]::Today.AddMinutes(15) + RepetitionDuration = [datetime]::Today.AddHours(8) + AllowStartIfOnBatteries = $true + Compatibility = 'Win8' + Hidden = $true + } } } -Configuration xScheduledTask_Edit3 +Configuration xScheduledTaskLogonAdd { Import-DscResource -ModuleName xComputerManagement - node "localhost" { - xScheduledTask xScheduledTask_Edit3 { - TaskName = "Test task" - TaskPath = "\xComputerManagement\" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Daily" - RepeatInterval = 1 - } + node 'localhost' + { + xScheduledTask xScheduledTaskOnceAdd + { + TaskName = 'Test task Logon' + TaskPath = '\xComputerManagement\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'AtLogOn' + RepeatInterval = [datetime]::Today.AddMinutes(15) + RepetitionDuration = [datetime]::Today.AddHours(8) + } } } -Configuration xScheduledTask_Edit4 +Configuration xScheduledTaskStartupAdd { Import-DscResource -ModuleName xComputerManagement - node "localhost" { - xScheduledTask xScheduledTask_Edit3 { - TaskName = "Test task" - TaskPath = "\xComputerManagement\" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ActionWorkingPath = "C:\" - ScheduleType = "Daily" - RepeatInterval = 1 - } + node 'localhost' + { + xScheduledTask xScheduledTaskOnceAdd + { + TaskName = 'Test task Startup' + TaskPath = '\xComputerManagement\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'AtStartup' + RepeatInterval = [datetime]::Today.AddMinutes(15) + RepetitionDuration = [datetime]::Today.AddHours(8) + } } } -Configuration xScheduledTask_Edit5 +Configuration xScheduledTaskOnceMod { Import-DscResource -ModuleName xComputerManagement - node "localhost" { - xScheduledTask xScheduledTask_Edit5 { - TaskName = "Test task" - TaskPath = "\xComputerManagement\" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ActionWorkingPath = "C:\" - ActionArguments = "-Command 'Get-ChildItem'" - ScheduleType = "Daily" - RepeatInterval = 1 - } + node 'localhost' + { + xScheduledTask xScheduledTaskOnceMod + { + TaskName = 'Test task once' + TaskPath = '\xComputerManagement\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + RepeatInterval = [datetime]::Today.AddMinutes(20) + RepetitionDuration = [datetime]::Today.AddHours(8) + DisallowDemandStart = $true + } } } -Configuration xScheduledTask_Remove +Configuration xScheduledTaskDailyMod { Import-DscResource -ModuleName xComputerManagement - node "localhost" { - xScheduledTask xScheduledTask_Remove { - TaskName = "Test task" - TaskPath = "\xComputerManagement\" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Minutes" - RepeatInterval = 15 - Ensure="Absent" - } + node 'localhost' + { + xScheduledTask xScheduledTaskDailyMod + { + TaskName = 'Test task Daily' + TaskPath = '\xComputerManagement\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Daily' + DaysInterval = 2 + RepeatInterval = [datetime]::Today.AddMinutes(30) + RepetitionDuration = [datetime]::Today.AddHours(8) + Enable = $false + } } } -Configuration xScheduledTask_Enable +Configuration xScheduledTaskWeeklyMod { Import-DscResource -ModuleName xComputerManagement - node "localhost" { - xScheduledTask xScheduledTask_Remove { - TaskName = "Test task" - TaskPath = "\xComputerManagement\" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Minutes" - RepeatInterval = 15 - Enable = $true - Ensure="Present" - } + node 'localhost' + { + xScheduledTask xScheduledTaskWeeklyMod + { + TaskName = 'Test task Weekly' + TaskPath = '\xComputerManagement\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Weekly' + WeeksInterval = 1 + DaysOfWeek = 'Monday', 'Thursday', 'Saturday' + RepeatInterval = [datetime]::Today.AddMinutes(40) + RepetitionDuration = [datetime]::Today.AddHours(8) + } } } -Configuration xScheduledTask_Disable +Configuration xScheduledTaskLogonMod { Import-DscResource -ModuleName xComputerManagement - node "localhost" { - xScheduledTask xScheduledTask_Remove { - TaskName = "Test task" - TaskPath = "\xComputerManagement\" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Minutes" - RepeatInterval = 15 + node 'localhost' + { + xScheduledTask xScheduledTaskOnceMod + { + TaskName = 'Test task Logon' + TaskPath = '\xComputerManagement\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'AtStartup' + RepeatInterval = [datetime]::Today.AddMinutes(12) + RepetitionDuration = [datetime]::Today.AddHours(8) + } + } +} + +Configuration xScheduledTaskStartupMod +{ + Import-DscResource -ModuleName xComputerManagement + node 'localhost' + { + xScheduledTask xScheduledTaskOnceMod + { + TaskName = 'Test task Startup' + TaskPath = '\xComputerManagement\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'AtLogOn' + RepeatInterval = [datetime]::Today.AddMinutes(10) + RepetitionDuration = [datetime]::Today.AddHours(8) + } + } +} + +Configuration xScheduledTaskOnceDel +{ + Import-DscResource -ModuleName xComputerManagement + node 'localhost' + { + xScheduledTask xScheduledTaskOnceDel + { + TaskName = 'Test task once' + TaskPath = '\xComputerManagement\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + RepeatInterval = [datetime]::Today.AddMinutes(20) + RepetitionDuration = [datetime]::Today.AddHours(8) + DisallowDemandStart = $true + Ensure = 'Absent' + } + } +} + +Configuration xScheduledTaskDailyDel +{ + Import-DscResource -ModuleName xComputerManagement + node 'localhost' + { + xScheduledTask xScheduledTaskDailyDel + { + TaskName = 'Test task Daily' + TaskPath = '\xComputerManagement\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Daily' + DaysInterval = 2 + RepeatInterval = [datetime]::Today.AddMinutes(30) + RepetitionDuration = [datetime]::Today.AddHours(8) Enable = $false - Ensure="Present" - } + Ensure = 'Absent' + } + } +} + +Configuration xScheduledTaskWeeklyDel +{ + Import-DscResource -ModuleName xComputerManagement + node 'localhost' + { + xScheduledTask xScheduledTaskWeeklyDel + { + TaskName = 'Test task Weekly' + TaskPath = '\xComputerManagement\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Weekly' + WeeksInterval = 1 + DaysOfWeek = 'Monday', 'Thursday', 'Saturday' + RepeatInterval = [datetime]::Today.AddMinutes(40) + RepetitionDuration = [datetime]::Today.AddHours(8) + Ensure = 'Absent' + } + } +} + +Configuration xScheduledTaskLogonDel +{ + Import-DscResource -ModuleName xComputerManagement + node 'localhost' + { + xScheduledTask xScheduledTaskLogonDel + { + TaskName = 'Test task Logon' + TaskPath = '\xComputerManagement\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'AtStartup' + RepeatInterval = [datetime]::Today.AddMinutes(12) + RepetitionDuration = [datetime]::Today.AddHours(8) + Ensure = 'Absent' + } + } +} + +Configuration xScheduledTaskStartupDel +{ + Import-DscResource -ModuleName xComputerManagement + node 'localhost' + { + xScheduledTask xScheduledTaskStartupDel + { + TaskName = 'Test task Startup' + TaskPath = '\xComputerManagement\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'AtLogOn' + RepeatInterval = [datetime]::Today.AddMinutes(10) + RepetitionDuration = [datetime]::Today.AddHours(8) + Ensure = 'Absent' + } } } diff --git a/Tests/Integration/MSFT_xScheduledTask.Integration.Tests.ps1 b/Tests/Integration/MSFT_xScheduledTask.Integration.Tests.ps1 index 0da9f741..f3e75ff8 100644 --- a/Tests/Integration/MSFT_xScheduledTask.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xScheduledTask.Integration.Tests.ps1 @@ -1,22 +1,22 @@ #Requires -Version 5.0 -$Global:DSCModuleName = 'xComputerManagement' -$Global:DSCResourceName = 'MSFT_xScheduledTask' - +$Global:DSCModuleName = 'xComputerManagement' +$Global:DSCResourceName = 'MSFT_xScheduledTask' #region HEADER -# Unit Test Template Version: 1.1.0 -[String] $moduleRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $Script:MyInvocation.MyCommand.Path)) -if ( (-not (Test-Path -Path (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` - (-not (Test-Path -Path (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) +# Integration Test Template Version: 1.1.1 +[String] $script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` + (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) { - & git @('clone','https://github.com/PowerShell/DscResource.Tests.git',(Join-Path -Path $moduleRoot -ChildPath '\DSCResource.Tests\')) + & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) } -Import-Module (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force +Import-Module -Name (Join-Path -Path $script:moduleRoot -ChildPath (Join-Path -Path 'DSCResource.Tests' -ChildPath 'TestHelper.psm1')) -Force $TestEnvironment = Initialize-TestEnvironment ` -DSCModuleName $Global:DSCModuleName ` -DSCResourceName $Global:DSCResourceName ` - -TestType Integration + -TestType Integration +#endregion # Begin Testing try { @@ -25,214 +25,97 @@ try #region Pester Tests Describe $Global:DSCResourceName { - - Context "No scheduled task exists, but it should" { - $CurrentConfig = "xScheduledTask_Add" - $ConfigDir = (Join-Path $TestDrive $CurrentConfig) - $ConfigMof = (Join-Path $ConfigDir "localhost.mof") - - It "should compile a MOF file without error" { - { - . $CurrentConfig -OutputPath $ConfigDir - } | Should Not Throw - } - - It "should apply the MOF correctly" { - { - Start-DscConfiguration -Path $ConfigDir -Wait -Verbose -Force - } | Should Not Throw - } - - It "should return a compliant state after being applied" { - (Test-DscConfiguration -ReferenceConfiguration $ConfigMof -Verbose).InDesiredState | Should be $true - } - } - - Context "A scheduled task with minutes based repetition exists, but has the wrong settings" { - $CurrentConfig = "xScheduledTask_Edit1" - $ConfigDir = (Join-Path $TestDrive $CurrentConfig) - $ConfigMof = (Join-Path $ConfigDir "localhost.mof") - - It "should compile a MOF file without error" { - { - . $CurrentConfig -OutputPath $ConfigDir - } | Should Not Throw - } - - It "should apply the MOF correctly" { - { - Start-DscConfiguration -Path $ConfigDir -Wait -Verbose -Force - } | Should Not Throw - } - - It "should return a compliant state after being applied" { - (Test-DscConfiguration -ReferenceConfiguration $ConfigMof -Verbose).InDesiredState | Should be $true - } - } - - Context "A scheduled task with hourly based repetition exists, but has the wrong settings" { - $CurrentConfig = "xScheduledTask_Edit2" - $ConfigDir = (Join-Path $TestDrive $CurrentConfig) - $ConfigMof = (Join-Path $ConfigDir "localhost.mof") - - It "should compile a MOF file without error" { - { - . $CurrentConfig -OutputPath $ConfigDir - } | Should Not Throw - } - - It "should apply the MOF correctly" { - { - Start-DscConfiguration -Path $ConfigDir -Wait -Verbose -Force - } | Should Not Throw - } - - It "should return a compliant state after being applied" { - (Test-DscConfiguration -ReferenceConfiguration $ConfigMof -Verbose).InDesiredState | Should be $true - } - } - - Context "A scheduled task with daily based repetition exists, but has the wrong settings" { - $CurrentConfig = "xScheduledTask_Edit3" - $ConfigDir = (Join-Path $TestDrive $CurrentConfig) - $ConfigMof = (Join-Path $ConfigDir "localhost.mof") - - It "should compile a MOF file without error" { - { - . $CurrentConfig -OutputPath $ConfigDir - } | Should Not Throw - } - - It "should apply the MOF correctly" { - { - Start-DscConfiguration -Path $ConfigDir -Wait -Verbose -Force - } | Should Not Throw - } - - It "should return a compliant state after being applied" { - (Test-DscConfiguration -ReferenceConfiguration $ConfigMof -Verbose).InDesiredState | Should be $true - } - } - - Context "A scheduled task exists and is configured with the wrong working directory" { - $CurrentConfig = "xScheduledTask_Edit4" - $ConfigDir = (Join-Path $TestDrive $CurrentConfig) - $ConfigMof = (Join-Path $ConfigDir "localhost.mof") - - It "should compile a MOF file without error" { - { - . $CurrentConfig -OutputPath $ConfigDir - } | Should Not Throw - } - - It "should apply the MOF correctly" { - { - Start-DscConfiguration -Path $ConfigDir -Wait -Verbose -Force - } | Should Not Throw - } - - It "should return a compliant state after being applied" { - (Test-DscConfiguration -ReferenceConfiguration $ConfigMof -Verbose).InDesiredState | Should be $true - } - } - - Context "A scheduled task exists and is configured with the wrong executable arguments" { - $CurrentConfig = "xScheduledTask_Edit5" - $ConfigDir = (Join-Path $TestDrive $CurrentConfig) - $ConfigMof = (Join-Path $ConfigDir "localhost.mof") - - It "should compile a MOF file without error" { - { - . $CurrentConfig -OutputPath $ConfigDir - } | Should Not Throw - } - - It "should apply the MOF correctly" { - { - Start-DscConfiguration -Path $ConfigDir -Wait -Verbose -Force - } | Should Not Throw - } - - It "should return a compliant state after being applied" { - (Test-DscConfiguration -ReferenceConfiguration $ConfigMof -Verbose).InDesiredState | Should be $true - } - } - - Context "A scheduled task exists, but it shouldn't" { - $CurrentConfig = "xScheduledTask_Remove" - $ConfigDir = (Join-Path $TestDrive $CurrentConfig) - $ConfigMof = (Join-Path $ConfigDir "localhost.mof") - - It "should compile a MOF file without error" { - { - . $CurrentConfig -OutputPath $ConfigDir - } | Should Not Throw - } - - It "should apply the MOF correctly" { - { - Start-DscConfiguration -Path $ConfigDir -Wait -Verbose -Force - } | Should Not Throw - } - - It "should return a compliant state after being applied" { - (Test-DscConfiguration -ReferenceConfiguration $ConfigMof -Verbose).InDesiredState | Should be $true - } - } - - Context "A scheduled task exists, and should be enabled" { - $CurrentConfig = "xScheduledTask_Enable" - $ConfigDir = (Join-Path $TestDrive $CurrentConfig) - $ConfigMof = (Join-Path $ConfigDir "localhost.mof") - - It "should compile a MOF file without error" { - { - . $CurrentConfig -OutputPath $ConfigDir - } | Should Not Throw - } - - It "should apply the MOF correctly" { - { - Start-DscConfiguration -Path $ConfigDir -Wait -Verbose -Force - } | Should Not Throw - } - - It "should return a compliant state after being applied" { - (Test-DscConfiguration -ReferenceConfiguration $ConfigMof -Verbose).InDesiredState | Should be $true - } + + $contexts = @{ + Once = 'xScheduledTaskOnce' + Daily = 'xScheduledTaskDaily' + Weekly = 'xScheduledTaskWeekly' + AtLogon = 'xScheduledTaskLogon' + AtStartup = 'xScheduledTaskStartup' } - Context "A scheduled task exists, and should be disabled" { - $CurrentConfig = "xScheduledTask_Disable" - $ConfigDir = (Join-Path $TestDrive $CurrentConfig) - $ConfigMof = (Join-Path $ConfigDir "localhost.mof") - - It "should compile a MOF file without error" { - { - . $CurrentConfig -OutputPath $ConfigDir - } | Should Not Throw + foreach ($contextInfo in $contexts.GetEnumerator()) + { + Context "[$($contextInfo.Key)] No scheduled task exists but it should" { + $CurrentConfig = '{0}Add' -f $contextInfo.Value + $ConfigDir = (Join-Path -Path $TestDrive -ChildPath $CurrentConfig) + $ConfigMof = (Join-Path -Path $ConfigDir -ChildPath 'localhost.mof') + + It 'should compile and apply the MOF without throwing' { + { + . $CurrentConfig -OutputPath $ConfigDir + } | Should Not Throw + } + + It 'should apply the MOF correctly' { + { + Start-DscConfiguration -Path $ConfigDir -Wait -Force + } | Should Not Throw + } + + It 'should return a compliant state after being applied' { + (Test-DscConfiguration -ReferenceConfiguration $ConfigMof -Verbose).InDesiredState | Should be $true + } } - - It "should apply the MOF correctly" { - { - Start-DscConfiguration -Path $ConfigDir -Wait -Verbose -Force - } | Should Not Throw + + Context "[$($contextInfo.Key)] A scheduled task exists with the wrong settings" { + $CurrentConfig = '{0}Mod' -f $contextInfo.Value + $ConfigDir = (Join-Path -Path $TestDrive -ChildPath $CurrentConfig) + $ConfigMof = (Join-Path -Path $ConfigDir -ChildPath 'localhost.mof') + + It 'should compile and apply the MOF without throwing' { + { + . $CurrentConfig -OutputPath $ConfigDir + } | Should Not Throw + } + + It 'should apply the MOF correctly' { + { + Start-DscConfiguration -Path $ConfigDir -Wait -Force + } | Should Not Throw + } + + It 'should return a compliant state after being applied' { + (Test-DscConfiguration -ReferenceConfiguration $ConfigMof -Verbose).InDesiredState | Should be $true + } } - - It "should return a compliant state after being applied" { - (Test-DscConfiguration -ReferenceConfiguration $ConfigMof -Verbose).InDesiredState | Should be $true + + Context "[$($contextInfo.Key)] A scheduled tasks exists but it should not" { + $CurrentConfig = '{0}Del' -f $contextInfo.Value + $ConfigDir = (Join-Path -Path $TestDrive -ChildPath $CurrentConfig) + $ConfigMof = (Join-Path -Path $ConfigDir -ChildPath 'localhost.mof') + + It 'should compile and apply the MOF without throwing' { + { + . $CurrentConfig -OutputPath $ConfigDir + } | Should Not Throw + } + + It 'should apply the MOF correctly' { + { + Start-DscConfiguration -Path $ConfigDir -Wait -Force + } | Should Not Throw + } + + It 'should return a compliant state after being applied' { + (Test-DscConfiguration -ReferenceConfiguration $ConfigMof -Verbose).InDesiredState | Should be $true + } } } - - AfterEach { - Remove-DscConfigurationDocument -Stage Current, Pending, Previous -Force -Confirm:$false -WarningAction SilentlyContinue - } } - #endregion } finally { #region FOOTER + + # Remove any traces of the created tasks + Get-ScheduledTask -TaskPath '\xComputerManagement\' -ErrorAction SilentlyContinue | Unregister-ScheduledTask -ErrorAction SilentlyContinue -Confirm:$false + + $scheduler = New-Object -ComObject Schedule.Service + $scheduler.Connect() + $rootFolder = $scheduler.GetFolder('\') + $rootFolder.DeleteFolder('xComputerManagement', 0) + Restore-TestEnvironment -TestEnvironment $TestEnvironment #endregion } diff --git a/Tests/Unit/MSFT_xScheduledTask.Tests.ps1 b/Tests/Unit/MSFT_xScheduledTask.Tests.ps1 index 40594eb2..03073eba 100644 --- a/Tests/Unit/MSFT_xScheduledTask.Tests.ps1 +++ b/Tests/Unit/MSFT_xScheduledTask.Tests.ps1 @@ -5,6 +5,8 @@ param( $Global:DSCModuleName = 'xComputerManagement' $Global:DSCResourceName = 'MSFT_xScheduledTask' + + #region HEADER # Unit Test Template Version: 1.1.0 [String] $moduleRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $Script:MyInvocation.MyCommand.Path)) @@ -20,6 +22,7 @@ $TestEnvironment = Initialize-TestEnvironment ` -DSCResourceName $Global:DSCResourceName ` -TestType Unit +$VerbosePreference = 'Continue' # Begin Testing try { @@ -33,36 +36,38 @@ try Mock Set-ScheduledTask { } Mock Unregister-ScheduledTask { } - Context "No scheduled task exists, but it should" { + Context 'No scheduled task exists, but it should' { $testParams = @{ - TaskName = "Test task" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Minutes" - RepeatInterval = 15 + TaskName = 'Test task' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + RepeatInterval = [datetime]::Today + (New-TimeSpan -Minutes 15) + RepetitionDuration = [datetime]::Today + (New-TimeSpan -Minutes 150) } Mock Get-ScheduledTask { return $null } - It "should return absent from the get method" { - (Get-TargetResource @testParams).Ensure | Should Be "Absent" + It 'should return absent from the get method' { + (Get-TargetResource @testParams).Ensure | Should Be 'Absent' } - It "should return false from the test method" { + It 'should return false from the test method' { Test-TargetResource @testParams | Should Be $false } - It "should create the scheduled task in the set method" { - Set-TargetResource @testParams + It 'should create the scheduled task in the set method' { + Set-TargetResource @testParams -Verbose } } - Context "A scheduled task exists, but it shouldn't" { + Context 'A scheduled task exists, but it should not' { $testParams = @{ - TaskName = "Test task" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Minutes" - RepeatInterval = 15 - Ensure = "Absent" + TaskName = 'Test task' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + RepeatInterval = [datetime]::Today + (New-TimeSpan -Minutes 15) + RepetitionDuration = [datetime]::Today + (New-TimeSpan -Minutes 15) + Ensure = 'Absent' } Mock Get-ScheduledTask { return @{ @@ -73,55 +78,58 @@ try }) Triggers = @(@{ Repetition = @{ - Duration = $null - Interval = "PT$($testParams.RepeatInterval)M" + Duration = "PT$($testParams.RepetitionDuration.TimeOfDay.TotalMinutes)M" + Interval = "PT$($testParams.RepeatInterval.TimeOfDay.TotalMinutes)M" + } + CimClass = @{ + CimClassName = 'MSFT_TaskTimeTrigger' } }) Principal = @{ - UserId = "SYSTEM" + UserId = 'SYSTEM' } } } - It "should return present from the get method" { - (Get-TargetResource @testParams).Ensure | Should Be "Present" + It 'should return present from the get method' { + (Get-TargetResource @testParams).Ensure | Should Be 'Present' } - It "should return false from the test method" { + It 'should return false from the test method' { Test-TargetResource @testParams | Should Be $false } - It "should remove the scheduled task in the set method" { - Set-TargetResource @testParams + It 'should remove the scheduled task in the set method' { + Set-TargetResource @testParams -Verbose Assert-MockCalled Unregister-ScheduledTask } } - Context "A scheduled task doesnt exist, and it shouldn't" { + Context 'A scheduled task doesnt exist, and it should not' { $testParams = @{ - TaskName = "Test task" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Minutes" - RepeatInterval = 15 - Ensure = "Absent" + TaskName = 'Test task' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + Ensure = 'Absent' } Mock Get-ScheduledTask { return $null } - It "should return present from the get method" { - (Get-TargetResource @testParams).Ensure | Should Be "Absent" + It 'should return present from the get method' { + (Get-TargetResource @testParams).Ensure | Should Be 'Absent' } - It "should return true from the test method" { + It 'should return true from the test method' { Test-TargetResource @testParams | Should Be $true } } - Context "A scheduled task with minutes based repetition exists, but has the wrong settings" { + Context 'A scheduled task with Once based repetition exists, but has the wrong settings' { $testParams = @{ - TaskName = "Test task" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Minutes" - RepeatInterval = 15 + TaskName = 'Test task' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + RepeatInterval =[datetime]::Today + (New-TimeSpan -Minutes 15) + RepetitionDuration = [datetime]::Today + (New-TimeSpan -Minutes 150) } Mock Get-ScheduledTask { return @{ @@ -133,34 +141,39 @@ try Triggers = @(@{ Repetition = @{ Duration = $null - Interval = "PT$(($testParams.RepeatInterval) + 1)M" + Interval = "PT$(($testParams.RepeatInterval.TimeOfDay.TotalMinutes) + 1)M" + } + CimClass = @{ + CimClassName = 'MSFT_TaskTimeTrigger' } }) Principal = @{ - UserId = "SYSTEM" + UserId = 'SYSTEM' } } } - It "should return present from the get method" { - (Get-TargetResource @testParams).Ensure | Should Be "Present" + It 'should return present from the get method' { + (Get-TargetResource @testParams).Ensure | Should Be 'Present' } - It "should return false from the test method" { + It 'should return false from the test method' { Test-TargetResource @testParams | Should Be $false } - It "should update the scheduled task in the set method" { - Set-TargetResource @testParams - Assert-MockCalled Set-ScheduledTask + It 'should update the scheduled task in the set method' { + Set-TargetResource @testParams -Verbose + Assert-MockCalled -CommandName Unregister-ScheduledTask -Times 1 + Assert-Mockcalled -CommandName Register-ScheduledTask -Times 1 } } - Context "A scheduled task with minutes based repetition exists and has the correct settings" { + Context 'A scheduled task with minutes based repetition exists and has the correct settings' { $testParams = @{ - TaskName = "Test task" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Minutes" - RepeatInterval = 15 + TaskName = 'Test task' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + RepeatInterval = [datetime]::Today + (New-TimeSpan -Minutes 15) + RepetitionDuration = [datetime]::Today + (New-TimeSpan -Minutes 30) } Mock Get-ScheduledTask { return @{ @@ -171,30 +184,34 @@ try }) Triggers = @(@{ Repetition = @{ - Duration = $null - Interval = "PT$($testParams.RepeatInterval)M" + Duration = "PT$($testParams.RepetitionDuration.TimeOfDay.TotalMinutes)M" + Interval = "PT$($testParams.RepeatInterval.TimeOfDay.TotalMinutes)M" + } + CimClass = @{ + CimClassName = 'MSFT_TaskTimeTrigger' } }) Principal = @{ - UserId = "SYSTEM" + UserId = 'SYSTEM' } } } - It "should return present from the get method" { - (Get-TargetResource @testParams).Ensure | Should Be "Present" + It 'should return present from the get method' { + (Get-TargetResource @testParams).Ensure | Should Be 'Present' } - It "should return true from the test method" { + It 'should return true from the test method' { Test-TargetResource @testParams | Should Be $true } } - Context "A scheduled task with hourly based repetition exists, but has the wrong settings" { + Context 'A scheduled task with hourly based repetition exists, but has the wrong settings' { $testParams = @{ - TaskName = "Test task" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Hourly" - RepeatInterval = 4 + TaskName = 'Test task' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + RepeatInterval = [datetime]::Today + (New-TimeSpan -Hours 4) + RepetitionDuration = [datetime]::Today + (New-TimeSpan -Hours 8) } Mock Get-ScheduledTask { return @{ @@ -205,35 +222,40 @@ try }) Triggers = @(@{ Repetition = @{ - Duration = $null - Interval = "PT$(($testParams.RepeatInterval) + 1)H" + Duration = "PT$(($testParams.RepetitionDuration.TimeOfDay.TotalHours))H" + Interval = "PT$(($testParams.RepeatInterval.TimeOfDay.TotalHours) + 1)H" + } + CimClass = @{ + CimClassName = 'MSFT_TaskTimeTrigger' } }) Principal = @{ - UserId = "SYSTEM" + UserId = 'SYSTEM' } } } - It "should return present from the get method" { - (Get-TargetResource @testParams).Ensure | Should Be "Present" + It 'should return present from the get method' { + (Get-TargetResource @testParams).Ensure | Should Be 'Present' } - It "should return false from the test method" { + It 'should return false from the test method' { Test-TargetResource @testParams | Should Be $false } - It "should update the scheduled task in the set method" { - Set-TargetResource @testParams - Assert-MockCalled Set-ScheduledTask + It 'should update the scheduled task in the set method' { + Set-TargetResource @testParams -Verbose + Assert-MockCalled -CommandName Unregister-ScheduledTask -Times 1 + Assert-Mockcalled -CommandName Register-ScheduledTask -Times 1 } } - Context "A scheduled task with hourly based repetition exists and has the correct settings" { + Context 'A scheduled task with hourly based repetition exists and has the correct settings' { $testParams = @{ - TaskName = "Test task" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Hourly" - RepeatInterval = 4 + TaskName = 'Test task' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + RepeatInterval = [datetime]::Today + (New-TimeSpan -Hours 4) + RepetitionDuration = [datetime]::Today + (New-TimeSpan -Hours 8) } Mock Get-ScheduledTask { return @{ @@ -244,30 +266,33 @@ try }) Triggers = @(@{ Repetition = @{ - Duration = $null - Interval = "PT$($testParams.RepeatInterval)H" + Duration = "PT$($testParams.RepetitionDuration.TimeOfDay.TotalHours)H" + Interval = "PT$($testParams.RepeatInterval.TimeOfDay.TotalHours)H" + } + CimClass = @{ + CimClassName = 'MSFT_TaskTimeTrigger' } }) Principal = @{ - UserId = "SYSTEM" + UserId = 'SYSTEM' } } } - It "should return present from the get method" { - (Get-TargetResource @testParams).Ensure | Should Be "Present" + It 'should return present from the get method' { + (Get-TargetResource @testParams).Ensure | Should Be 'Present' } - It "should return true from the test method" { + It 'should return true from the test method' { Test-TargetResource @testParams | Should Be $true } } - Context "A scheduled task with daily based repetition exists, but has the wrong settings" { + Context 'A scheduled task with daily based repetition exists, but has the wrong settings' { $testParams = @{ - TaskName = "Test task" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Daily" - RepeatInterval = 3 + TaskName = 'Test task' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Daily' + DaysInterval = 3 } Mock Get-ScheduledTask { return @{ @@ -279,34 +304,38 @@ try Triggers = @(@{ Repetition = @{ Duration = $null - Interval = "P$(($testParams.RepeatInterval) + 1)D" + Interval = "P$(($testParams.DaysInterval) + 1)D" + } + CimClass = @{ + CimClassName = 'MSFT_TaskDailyTrigger' } }) Principal = @{ - UserId = "SYSTEM" + UserId = 'SYSTEM' } } } - It "should return present from the get method" { - (Get-TargetResource @testParams).Ensure | Should Be "Present" + It 'should return present from the get method' { + (Get-TargetResource @testParams).Ensure | Should Be 'Present' } - It "should return false from the test method" { + It 'should return false from the test method' { Test-TargetResource @testParams | Should Be $false } - It "should update the scheduled task in the set method" { - Set-TargetResource @testParams - Assert-MockCalled Set-ScheduledTask + It 'should update the scheduled task in the set method' { + Set-TargetResource @testParams -Verbose + Assert-MockCalled -CommandName Unregister-ScheduledTask -Times 1 + Assert-Mockcalled -CommandName Register-ScheduledTask -Times 1 } } - Context "A scheduled task with daily based repetition exists and has the correct settings" { + Context 'A scheduled task with daily based repetition exists and has the correct settings' { $testParams = @{ - TaskName = "Test task" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Daily" - RepeatInterval = 3 + TaskName = 'Test task' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Daily' + DaysInterval = 3 } Mock Get-ScheduledTask { return @{ @@ -316,32 +345,33 @@ try Execute = $testParams.ActionExecutable }) Triggers = @(@{ - Repetition = @{ - Duration = $null - Interval = "P$($testParams.RepeatInterval)D" + DaysInterval = $testParams.DaysInterval + CimClass = @{ + CimClassName = 'MSFT_TaskDailyTrigger' } }) Principal = @{ - UserId = "SYSTEM" + UserId = 'SYSTEM' } } } - It "should return present from the get method" { - (Get-TargetResource @testParams).Ensure | Should Be "Present" + It 'should return present from the get method' { + (Get-TargetResource @testParams).Ensure | Should Be 'Present' } - It "should return true from the test method" { + It 'should return true from the test method' { Test-TargetResource @testParams | Should Be $true } } - Context "A scheduled task exists and is configured with the wrong execution account" { + Context 'A scheduled task exists and is configured with the wrong execution account' { $testParams = @{ - TaskName = "Test task" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Minutes" - RepeatInterval = 15 - ExecuteAsCredential = New-Object System.Management.Automation.PSCredential ("DEMO\RightUser", (ConvertTo-SecureString "ExamplePassword" -AsPlainText -Force)) + TaskName = 'Test task' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + RepeatInterval = [DateTime]::Today.Add((New-TimeSpan -Minutes 15)) + RepetitionDuration = [datetime]::Today + (New-TimeSpan -Hours 8) + ExecuteAsCredential = New-Object System.Management.Automation.PSCredential ('DEMO\RightUser', (ConvertTo-SecureString 'ExamplePassword' -AsPlainText -Force)) } Mock Get-ScheduledTask { return @{ @@ -352,36 +382,41 @@ try }) Triggers = @(@{ Repetition = @{ - Duration = $null - Interval = "PT$($testParams.RepeatInterval)M" + Duration = "PT$($testParams.RepetitionDuration.TimeOfDay.TotalHours)H" + Interval = "PT$($testParams.RepeatInterval.TimeOfDay.TotalMinutes)M" + } + CimClass = @{ + CimClassName = 'MSFT_TaskTimeTrigger' } }) Principal = @{ - UserId = "WrongUser" + UserId = 'WrongUser' } } } - It "should return present from the get method" { - (Get-TargetResource @testParams).Ensure | Should Be "Present" + It 'should return present from the get method' { + (Get-TargetResource @testParams).Ensure | Should Be 'Present' } - It "should return false from the test method" { + It 'should return false from the test method' { Test-TargetResource @testParams | Should Be $false } - It "should update the scheduled task in the set method" { - Set-TargetResource @testParams - Assert-MockCalled Set-ScheduledTask + It 'should update the scheduled task in the set method' { + Set-TargetResource @testParams -Verbose + Assert-MockCalled -CommandName Unregister-ScheduledTask -Times 1 + Assert-Mockcalled -CommandName Register-ScheduledTask -Times 1 } } - Context "A scheduled task exists and is configured with the wrong working directory" { + Context 'A scheduled task exists and is configured with the wrong working directory' { $testParams = @{ - TaskName = "Test task" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ActionWorkingPath = "C:\Example" - ScheduleType = "Minutes" - RepeatInterval = 15 + TaskName = 'Test task' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ActionWorkingPath = 'C:\Example' + ScheduleType = 'Once' + RepeatInterval = [datetime]::Today + (New-TimeSpan -Minutes 15) + RepetitionDuration = [datetime]::Today + (New-TimeSpan -Hours 8) } Mock Get-ScheduledTask { return @{ @@ -389,40 +424,45 @@ try Path = $testParams.TaskPath Actions = @(@{ Execute = $testParams.ActionExecutable - WorkingDirectory = "C:\Wrong" + WorkingDirectory = 'C:\Wrong' }) Triggers = @(@{ Repetition = @{ Duration = $null - Interval = "PT$($testParams.RepeatInterval)M" + Interval = "PT$($testParams.RepeatInterval.TimeOfDay.TotalMinutes)M" + } + CimClass = @{ + CimClassName = 'MSFT_TaskTimeTrigger' } }) Principal = @{ - UserId = "SYSTEM" + UserId = 'SYSTEM' } } } - It "should return present from the get method" { - (Get-TargetResource @testParams).Ensure | Should Be "Present" + It 'should return present from the get method' { + (Get-TargetResource @testParams).Ensure | Should Be 'Present' } - It "should return false from the test method" { + It 'should return false from the test method' { Test-TargetResource @testParams | Should Be $false } - It "should update the scheduled task in the set method" { - Set-TargetResource @testParams - Assert-MockCalled Set-ScheduledTask + It 'should update the scheduled task in the set method' { + Set-TargetResource @testParams -Verbose + Assert-MockCalled -CommandName Unregister-ScheduledTask -Times 1 + Assert-Mockcalled -CommandName Register-ScheduledTask -Times 1 } } - Context "A scheduled task exists and is configured with the wrong executable arguments" { + Context 'A scheduled task exists and is configured with the wrong executable arguments' { $testParams = @{ - TaskName = "Test task" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ActionArguments = "-File `"C:\something\right.ps1`"" - ScheduleType = "Minutes" - RepeatInterval = 15 + TaskName = 'Test task' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ActionArguments = '-File "C:\something\right.ps1"' + ScheduleType = 'Once' + RepeatInterval = [datetime]::Today + (New-TimeSpan -Minutes 15) + RepetitionDuration = [datetime]::Today + (New-TimeSpan -Hours 8) } Mock Get-ScheduledTask { return @{ @@ -430,39 +470,44 @@ try Path = $testParams.TaskPath Actions = @(@{ Execute = $testParams.ActionExecutable - Arguments = "-File `"C:\something\wrong.ps1`"" + Arguments = '-File "C:\something\wrong.ps1"' }) Triggers = @(@{ Repetition = @{ - Duration = $null - Interval = "PT$($testParams.RepeatInterval)M" + Duration = "PT$($testParams.RepetitionDuration.TimeOfDay.TotalHours)H" + Interval = "PT$($testParams.RepeatInterval.TimeOfDay.TotalMinutes)M" + } + CimClass = @{ + CimClassName = 'MSFT_TaskTimeTrigger' } }) Principal = @{ - UserId = "SYSTEM" + UserId = 'SYSTEM' } } } - It "should return present from the get method" { - (Get-TargetResource @testParams).Ensure | Should Be "Present" + It 'should return present from the get method' { + (Get-TargetResource @testParams).Ensure | Should Be 'Present' } - It "should return false from the test method" { + It 'should return false from the test method' { Test-TargetResource @testParams | Should Be $false } - It "should update the scheduled task in the set method" { - Set-TargetResource @testParams - Assert-MockCalled Set-ScheduledTask + It 'should update the scheduled task in the set method' { + Set-TargetResource @testParams -Verbose + Assert-MockCalled -CommandName Unregister-ScheduledTask -Times 1 + Assert-Mockcalled -CommandName Register-ScheduledTask -Times 1 } } - Context "A scheduled task is enabled and should be disabled" { + Context 'A scheduled task is enabled and should be disabled' { $testParams = @{ - TaskName = "Test task" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Minutes" - RepeatInterval = 15 + TaskName = 'Test task' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + RepeatInterval = [datetime]::Today + (New-TimeSpan -Minutes 15) + RepetitionDuration = [datetime]::Today + (New-TimeSpan -Hours 8) Enable = $false } @@ -475,39 +520,44 @@ try }) Triggers = @(@{ Repetition = @{ - Duration = $null - Interval = "PT$($testParams.RepeatInterval)M" + Duration = "PT$($testParams.RepetitionDuration.TimeOfDay.TotalHours)H" + Interval = "PT$($testParams.RepeatInterval.TimeOfDay.TotalMinutes)M" + } + CimClass = @{ + CimClassName = 'MSFT_TaskTimeTrigger' } }) Settings = @(@{ Enabled = $true }) Principal = @{ - UserId = "SYSTEM" + UserId = 'SYSTEM' } } } - It "should return present from the get method" { - (Get-TargetResource @testParams).Ensure | Should Be "Present" + It 'should return present from the get method' { + (Get-TargetResource @testParams).Ensure | Should Be 'Present' } - It "should return false from the test method" { + It 'should return false from the test method' { Test-TargetResource @testParams | Should Be $false } - It "should update the scheduled task in the set method" { - Set-TargetResource @testParams - Assert-MockCalled Set-ScheduledTask + It 'should update the scheduled task in the set method' { + Set-TargetResource @testParams -Verbose + Assert-MockCalled -CommandName Unregister-ScheduledTask -Times 1 + Assert-Mockcalled -CommandName Register-ScheduledTask -Times 1 } } - Context "A scheduled task is enabled and has the correct settings" { + Context 'A scheduled task is enabled and has the correct settings' { $testParams = @{ - TaskName = "Test task" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Minutes" - RepeatInterval = 15 + TaskName = 'Test task' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + RepeatInterval = [datetime]::Today + (New-TimeSpan -Minutes 15) + RepetitionDuration = [datetime]::Today + (New-TimeSpan -Hours 8) Enable = $true } @@ -520,33 +570,37 @@ try }) Triggers = @(@{ Repetition = @{ - Duration = $null - Interval = "PT$($testParams.RepeatInterval)M" + Duration = "PT$($testParams.RepetitionDuration.TimeOfDay.TotalHours)H" + Interval = "PT$($testParams.RepeatInterval.TimeOfDay.TotalMinutes)M" + } + CimClass = @{ + CimClassName = 'MSFT_TaskTimeTrigger' } }) Settings = @(@{ Enabled = $true }) Principal = @{ - UserId = "SYSTEM" + UserId = 'SYSTEM' } } } - It "should return present from the get method" { - (Get-TargetResource @testParams).Ensure | Should Be "Present" + It 'should return present from the get method' { + (Get-TargetResource @testParams).Ensure | Should Be 'Present' } - It "should return false from the test method" { + It 'should return true from the test method' { Test-TargetResource @testParams | Should Be $true } } - Context "A scheduled task is disabled and has the correct settings" { + Context 'A scheduled task is disabled and has the correct settings' { $testParams = @{ - TaskName = "Test task" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Minutes" - RepeatInterval = 15 + TaskName = 'Test task' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + RepeatInterval = [datetime]::Today + (New-TimeSpan -Minutes 15) + RepetitionDuration = [datetime]::Today + (New-TimeSpan -Hours 8) Enable = $false } @@ -559,33 +613,37 @@ try }) Triggers = @(@{ Repetition = @{ - Duration = $null - Interval = "PT$($testParams.RepeatInterval)M" + Duration = "PT$($testParams.RepetitionDuration.TimeOfDay.TotalHours)H" + Interval = "PT$($testParams.RepeatInterval.TimeOfDay.TotalMinutes)M" + } + CimClass = @{ + CimClassName = 'MSFT_TaskTimeTrigger' } }) Settings = @(@{ Enabled = $false }) Principal = @{ - UserId = "SYSTEM" + UserId = 'SYSTEM' } } } - It "should return present from the get method" { - (Get-TargetResource @testParams).Ensure | Should Be "Present" + It 'should return present from the get method' { + (Get-TargetResource @testParams).Ensure | Should Be 'Present' } - It "should return true from the test method" { + It 'should return true from the test method' { Test-TargetResource @testParams | Should Be $true } } - Context "A scheduled task is disabled but should be enabled" { + Context 'A scheduled task is disabled but should be enabled' { $testParams = @{ - TaskName = "Test task" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Minutes" - RepeatInterval = 15 + TaskName = 'Test task' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + RepeatInterval = [datetime]::Today + (New-TimeSpan -Minutes 15) + RepetitionDuration = [datetime]::Today + (New-TimeSpan -Hours 8) Enable = $true } @@ -598,38 +656,43 @@ try }) Triggers = @(@{ Repetition = @{ - Duration = $null - Interval = "PT$($testParams.RepeatInterval)M" + Duration = "PT$($testParams.RepetitionDuration.TimeOfDay.TotalHours)H" + Interval = "PT$($testParams.RepeatInterval.TimeOfDay.TotalMinutes)M" + } + CimClass = @{ + CimClassName = 'MSFT_TaskTimeTrigger' } }) Settings = @(@{ Enabled = $false }) Principal = @{ - UserId = "SYSTEM" + UserId = 'SYSTEM' } } } - It "should return present from the get method" { - (Get-TargetResource @testParams).Ensure | Should Be "Present" + It 'should return present from the get method' { + (Get-TargetResource @testParams).Ensure | Should Be 'Present' } - It "should return false from the test method" { + It 'should return false from the test method' { Test-TargetResource @testParams | Should Be $false } - It "should update the scheduled task in the set method" { - Set-TargetResource @testParams - Assert-MockCalled Set-ScheduledTask + It 'should update the scheduled task in the set method' { + Set-TargetResource @testParams -Verbose + Assert-MockCalled -CommandName Unregister-ScheduledTask -Times 1 + Assert-Mockcalled -CommandName Register-ScheduledTask -Times 1 } } - Context "A Scheduled task exists, is disabled, and the optional parameter enable is not specified" -Fixture { + Context 'A Scheduled task exists, is disabled, and the optional parameter enable is not specified' -Fixture { $testParams = @{ - TaskName = "Test task" - ActionExecutable = "C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" - ScheduleType = "Minutes" - RepeatInterval = 15 + TaskName = 'Test task' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + RepeatInterval = [datetime]::Today + (New-TimeSpan -Minutes 15) + RepetitionDuration = [datetime]::Today + (New-TimeSpan -Hours 8) } Mock Get-ScheduledTask { return @{ @@ -641,23 +704,26 @@ try }) Triggers = @(@{ Repetition = @{ - Duration = $null - Interval = "PT$($testParams.RepeatInterval)M" + Duration = "PT$($testParams.RepetitionDuration.TimeOfDay.TotalHours)H" + Interval = "PT$($testParams.RepeatInterval.TimeOfDay.TotalMinutes)M" + } + CimClass = @{ + CimClassName = 'MSFT_TaskTimeTrigger' } }) Settings = @(@{ Enabled = $false }) Principal = @{ - UserId = "SYSTEM" + UserId = 'SYSTEM' } } } - It "should return present from the get method" { - (Get-TargetResource @testParams).Ensure | Should Be "Present" + It 'should return present from the get method' { + (Get-TargetResource @testParams).Ensure | Should Be 'Present' } - It "Should return true from the test method" { + It 'Should return true from the test method' { Test-TargetResource @testParams | Should Be $true } } diff --git a/xComputerManagement.psd1 b/xComputerManagement.psd1 index 5b4d796a..a252f506 100644 --- a/xComputerManagement.psd1 +++ b/xComputerManagement.psd1 @@ -1,6 +1,6 @@ @{ # Version number of this module. -ModuleVersion = '1.10.0.0' +ModuleVersion = '2.0.0.0' # ID used to uniquely identify this module GUID = 'B5004952-489E-43EA-999C-F16A25355B89' @@ -49,8 +49,8 @@ PrivateData = @{ # IconUri = '' # ReleaseNotes of this module - ReleaseNotes = '* Added resources - - xVirtualMemory + ReleaseNotes = '* Updated resources + - BREAKING CHANGE: xScheduledTask: Added nearly all available parameters for tasks ' @@ -62,3 +62,4 @@ PrivateData = @{ +