diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 4b9bec30..00000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,13 +0,0 @@ - -**Details of the scenario you tried and the problem that is occurring:** - -**The DSC configuration that is using the resource (as detailed as possible):** - -**Version of the Operating System and PowerShell the DSC Target Node is running:** - -**Version of the DSC module you're using, or 'dev' if you're using current dev branch:** diff --git a/.github/ISSUE_TEMPLATE/General.md b/.github/ISSUE_TEMPLATE/General.md new file mode 100644 index 00000000..fbcdf240 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/General.md @@ -0,0 +1,7 @@ +--- +name: General question or documentation update +about: If you have a general question or documentation update suggestion around the resource module. +--- + diff --git a/.github/ISSUE_TEMPLATE/Problem_with_resource.md b/.github/ISSUE_TEMPLATE/Problem_with_resource.md new file mode 100644 index 00000000..2431f65c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Problem_with_resource.md @@ -0,0 +1,57 @@ +--- +name: Problem with a resource +about: If you have a problem, bug, or enhancement with a resource in this resource module. +--- + +#### Details of the scenario you tried and the problem that is occurring + +#### Verbose logs showing the problem + +#### Suggested solution to the issue + +#### The DSC configuration that is used to reproduce the issue (as detailed as possible) +```powershell +# insert configuration here +``` + +#### The operating system the target node is running + + +#### Version and build of PowerShell the target node is running + + +#### Version of the DSC module that was used ('dev' if using current dev branch) diff --git a/.github/ISSUE_TEMPLATE/Resource_proposal.md b/.github/ISSUE_TEMPLATE/Resource_proposal.md new file mode 100644 index 00000000..9f2a069a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Resource_proposal.md @@ -0,0 +1,21 @@ +--- +name: New resource proposal +about: If you have a new resource proposal that you think should be added to this resource module. +--- + +### Description + +### Proposed properties + +### Special considerations or limitations diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f400397c..925594ed 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,21 +1,47 @@ +#### Pull Request (PR) description + -**Pull Request (PR) description** - -**This Pull Request (PR) fixes the following issues:** - +#### This Pull Request (PR) fixes the following issues + -**Task list:** -- [ ] Change details added to Unreleased section of CHANGELOG.md? -- [ ] Added/updated documentation, comment-based help and descriptions in .schema.mof files where appropriate? -- [ ] Examples appropriately updated? -- [ ] New/changed code adheres to [Style Guidelines](https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md)? -- [ ] [Unit and (optional) Integration tests](https://github.com/PowerShell/DscResources/blob/master/TestsGuidelines.md) created/updated where possible? +#### Task list + +- [ ] Added an entry under the Unreleased section of the change log in the CHANGELOG.md. + Entry should say what was changed, and how that affects users (if applicable). +- [ ] Resource documentation added/updated in README.md in the resource folder. +- [ ] Resource parameter descriptions added/updated in schema.mof + and comment-based help. +- [ ] Comment-based help added/updated. +- [ ] Localization strings added/updated in all localization files as appropriate. +- [ ] Examples appropriately added/updated. +- [ ] Unit tests added/updated. See [DSC Resource Testing Guidelines](https://github.com/PowerShell/DscResources/blob/master/TestsGuidelines.md). +- [ ] Integration tests added/updated (where possible). See [DSC Resource Testing Guidelines](https://github.com/PowerShell/DscResources/blob/master/TestsGuidelines.md). +- [ ] New/changed code adheres to [DSC Resource Style Guidelines](https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.md) and [Best Practices](https://github.com/PowerShell/DscResources/blob/master/BestPractices.md). diff --git a/.vscode/analyzersettings.psd1 b/.vscode/analyzersettings.psd1 new file mode 100644 index 00000000..be415e4d --- /dev/null +++ b/.vscode/analyzersettings.psd1 @@ -0,0 +1,53 @@ +@{ + <# + For the custom rules to work, the DscResource.Tests repo must be + cloned. It is automatically clone as soon as any unit or + integration tests are run. + #> + CustomRulePath = '.\DSCResource.Tests\DscResource.AnalyzerRules' + + IncludeRules = @( + # DSC Resource Kit style guideline rules. + 'PSAvoidDefaultValueForMandatoryParameter', + 'PSAvoidDefaultValueSwitchParameter', + 'PSAvoidInvokingEmptyMembers', + 'PSAvoidNullOrEmptyHelpMessageAttribute', + 'PSAvoidUsingCmdletAliases', + 'PSAvoidUsingComputerNameHardcoded', + 'PSAvoidUsingDeprecatedManifestFields', + 'PSAvoidUsingEmptyCatchBlock', + 'PSAvoidUsingInvokeExpression', + 'PSAvoidUsingPositionalParameters', + 'PSAvoidShouldContinueWithoutForce', + 'PSAvoidUsingWMICmdlet', + 'PSAvoidUsingWriteHost', + 'PSDSCReturnCorrectTypesForDSCFunctions', + 'PSDSCStandardDSCFunctionsInResource', + 'PSDSCUseIdenticalMandatoryParametersForDSC', + 'PSDSCUseIdenticalParametersForDSC', + 'PSMisleadingBacktick', + 'PSMissingModuleManifestField', + 'PSPossibleIncorrectComparisonWithNull', + 'PSProvideCommentHelp', + 'PSReservedCmdletChar', + 'PSReservedParams', + 'PSUseApprovedVerbs', + 'PSUseCmdletCorrectly', + 'PSUseOutputTypeCorrectly', + 'PSAvoidGlobalVars', + 'PSAvoidUsingConvertToSecureStringWithPlainText', + 'PSAvoidUsingPlainTextForPassword', + 'PSAvoidUsingUsernameAndPasswordParams', + 'PSDSCUseVerboseMessageInDSCResource', + 'PSShouldProcess', + 'PSUseDeclaredVarsMoreThanAssignments', + 'PSUsePSCredentialType', + + <# + This is to test all the DSC Resource Kit custom rules. + The name of the function-blocks of each custom rule start + with 'Measure*'. + #> + 'Measure-*' + ) +} diff --git a/.vscode/settings.json b/.vscode/settings.json index cd3d82e8..43f01cf9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,5 +10,6 @@ "powershell.codeFormatting.ignoreOneLineBlock": false, "powershell.codeFormatting.preset": "Custom", "files.trimTrailingWhitespace": true, - "files.insertFinalNewline": true + "files.insertFinalNewline": true, + "powershell.scriptAnalysis.settingsPath": ".vscode\\analyzersettings.psd1" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 95311714..c283e48e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ ## Unreleased +## 6.0.0.0 + +- ScheduledTask: + - Added support for Group Managed Service Accounts, implemented using the ExecuteAsGMSA + parameter. Fixes [Issue #111](https://github.com/PowerShell/ComputerManagementDsc/issues/111) + - Added support to set the Synchronize Across Time Zone option. Fixes [Issue #109](https://github.com/PowerShell/ComputerManagementDsc/issues/109) +- Added .VSCode settings for applying DSC PSSA rules - fixes [Issue #189](https://github.com/PowerShell/ComputerManagementDsc/issues/189). +- BREAKING CHANGE: PowerPlan: + - Added IsActive Read-Only Property - Fixes [Issue #171](https://github.com/PowerShell/ComputerManagementDsc/issues/171). + - InActive power plans are no longer returned with their Name set to null. + Now, the name is always returned and the Read-Only property of IsActive + is set accordingly. + ## 5.2.0.0 - PowershellExecutionPolicy: diff --git a/Modules/ComputerManagementDsc/ComputerManagementDsc.psd1 b/Modules/ComputerManagementDsc/ComputerManagementDsc.psd1 index 866d4683..ad637fee 100644 --- a/Modules/ComputerManagementDsc/ComputerManagementDsc.psd1 +++ b/Modules/ComputerManagementDsc/ComputerManagementDsc.psd1 @@ -1,6 +1,6 @@ @{ # Version number of this module. -moduleVersion = '5.2.0.0' +moduleVersion = '6.0.0.0' # ID used to uniquely identify this module GUID = 'B5004952-489E-43EA-999C-F16A25355B89' @@ -49,16 +49,16 @@ PrivateData = @{ # IconUri = '' # ReleaseNotes of this module - ReleaseNotes = '- PowershellExecutionPolicy: - - Updated to meet HQRM guidelines. - - Migrated the xPowershellExecutionPolicy from [xPowershellExecutionPolicy](https://github.com/PowerShell/xPowerShellExecutionPolicy) - and renamed to PowershellExecutionPolicy. - - Moved strings to localization file. -- Changed the scope from Global to Script in MSFT_ScheduledTask.Integration.Tests.ps1 -- Changed the scope from Global to Script ComputerManagementDsc.Common.Tests.ps1 -- ScheduledTask: - - Added support for event based triggers, implemented using the ScheduleType OnEvent - fixes [Issue 167](https://github.com/PowerShell/ComputerManagementDsc/issues/167) + ReleaseNotes = '- ScheduledTask: + - Added support for Group Managed Service Accounts, implemented using the ExecuteAsGMSA + parameter. Fixes [Issue 111](https://github.com/PowerShell/ComputerManagementDsc/issues/111) + - Added support to set the Synchronize Across Time Zone option. Fixes [Issue 109](https://github.com/PowerShell/ComputerManagementDsc/issues/109) +- Added .VSCode settings for applying DSC PSSA rules - fixes [Issue 189](https://github.com/PowerShell/ComputerManagementDsc/issues/189). +- BREAKING CHANGE: PowerPlan: + - Added IsActive Read-Only Property - Fixes [Issue 171](https://github.com/PowerShell/ComputerManagementDsc/issues/171). + - InActive power plans are no longer returned with their Name set to null. + Now, the name is always returned and the Read-Only property of IsActive + is set accordingly. ' @@ -69,3 +69,4 @@ PrivateData = @{ + diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_PowerPlan/MSFT_PowerPlan.psm1 b/Modules/ComputerManagementDsc/DSCResources/MSFT_PowerPlan/MSFT_PowerPlan.psm1 index 372dcf15..d174ba8a 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_PowerPlan/MSFT_PowerPlan.psm1 +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_PowerPlan/MSFT_PowerPlan.psm1 @@ -67,12 +67,10 @@ function Get-TargetResource if ($plan.IsActive) { Write-Verbose -Message ($script:localizedData.PowerPlanIsActive -f $Name) - $activePlanName = $Name } else { Write-Verbose -Message ($script:localizedData.PowerPlanIsNotActive -f $Name) - $activePlanName = $null } } else @@ -83,7 +81,8 @@ function Get-TargetResource return @{ IsSingleInstance = $IsSingleInstance - Name = $activePlanName + Name = $Name + IsActive = $plan.IsActive } } @@ -177,18 +176,11 @@ function Test-TargetResource $Name ) - $returnValue = $false - Write-Verbose -Message ($script:localizedData.PowerPlanIsBeingValidated -f $Name) $getTargetResourceResult = Get-TargetResource -IsSingleInstance $IsSingleInstance -Name $Name - if ($getTargetResourceResult.Name -eq $Name) - { - $returnValue = $true - } - - return $returnValue + return $getTargetResourceResult.IsActive } Export-ModuleMember -Function *-TargetResource diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_PowerPlan/MSFT_PowerPlan.schema.mof b/Modules/ComputerManagementDsc/DSCResources/MSFT_PowerPlan/MSFT_PowerPlan.schema.mof index 26c9a2e9..9d4a885c 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_PowerPlan/MSFT_PowerPlan.schema.mof +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_PowerPlan/MSFT_PowerPlan.schema.mof @@ -3,4 +3,5 @@ class MSFT_PowerPlan : OMI_BaseResource { [Key, Description("Specifies the resource is a single instance, the value must be 'Yes'."), ValueMap{"Yes"}, Values{"Yes"}] String IsSingleInstance; [Required, Description("The name of the power plan to activate.")] String Name; + [Read, Description("Determines if the power plan is active.")] Boolean IsActive; }; diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 index 4210a65e..53f9d8c3 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 @@ -64,6 +64,11 @@ $script:localizedData = Get-LocalizedData ` The time of day this task should start at - defaults to 12:00 AM. Not valid for AtLogon and AtStartup tasks. Not used in Get-TargetResource. + .PARAMETER SynchronizeAcrossTimeZone + Enable the scheduled task option to synchronize across time zones. This is enabled + by including the timezone offset in the scheduled task trigger. Defaults to false + which does not include the timezone offset. + .PARAMETER Ensure Present if the task should exist, Absent if it should be removed. @@ -73,7 +78,12 @@ $script:localizedData = Get-LocalizedData ` .PARAMETER ExecuteAsCredential The credential this task should execute as. If not specified defaults to running - as the local system account. + as the local system account. Cannot be used in combination with ExecuteAsGMSA. + Not used in Get-TargetResource. + + .PARAMETER ExecuteAsGMSA + The gMSA (Group Managed Service Account) this task should execute as. Cannot be + used in combination with ExecuteAsCredential. Not used in Get-TargetResource. .PARAMETER DaysInterval @@ -254,6 +264,10 @@ function Get-TargetResource [System.DateTime] $StartTime = [System.DateTime]::Today, + [Parameter()] + [System.Boolean] + $SynchronizeAcrossTimeZone = $false, + [Parameter()] [System.String] [ValidateSet('Present', 'Absent')] @@ -267,6 +281,10 @@ function Get-TargetResource [System.Management.Automation.PSCredential] $ExecuteAsCredential, + [Parameter()] + [System.String] + $ExecuteAsGMSA, + [Parameter()] [System.UInt32] $DaysInterval = 1, @@ -484,17 +502,20 @@ function Get-TargetResource if ($startAt) { - $startAt = [System.DateTime] $startAt + $stringSynchronizeAcrossTimeZone = Get-DateTimeString -Date $startAt -SynchronizeAcrossTimeZone $true + $returnSynchronizeAcrossTimeZone = $startAt -eq $stringSynchronizeAcrossTimeZone } else { $startAt = $StartTime + $returnSynchronizeAcrossTimeZone = $false } return @{ TaskName = $task.TaskName TaskPath = $task.TaskPath StartTime = $startAt + SynchronizeAcrossTimeZone = $returnSynchronizeAcrossTimeZone Ensure = 'Present' Description = $task.Description ActionExecutable = $action.Execute @@ -503,6 +524,7 @@ function Get-TargetResource ScheduleType = $returnScheduleType RepeatInterval = ConvertTo-TimeSpanStringFromScheduledTaskString -TimeSpan $trigger.Repetition.Interval ExecuteAsCredential = $task.Principal.UserId + ExecuteAsGMSA = $task.Principal.UserId -replace '^.+\\|@.+', $null Enable = $settings.Enabled DaysInterval = $trigger.DaysInterval RandomDelay = ConvertTo-TimeSpanStringFromScheduledTaskString -TimeSpan $trigger.RandomDelay @@ -571,6 +593,11 @@ function Get-TargetResource The time of day this task should start at - defaults to 12:00 AM. Not valid for AtLogon and AtStartup tasks. + .PARAMETER SynchronizeAcrossTimeZone + Enable the scheduled task option to synchronize across time zones. This is enabled + by including the timezone offset in the scheduled task trigger. Defaults to false + which does not include the timezone offset. + .PARAMETER Ensure Present if the task should exist, Absent if it should be removed. @@ -579,7 +606,11 @@ function Get-TargetResource .PARAMETER ExecuteAsCredential The credential this task should execute as. If not specified defaults to running - as the local system account. + as the local system account. Cannot be used in combination with ExecuteAsGMSA. + + .PARAMETER ExecuteAsGMSA + The gMSA (Group Managed Service Account) this task should execute as. Cannot be + used in combination with ExecuteAsCredential. .PARAMETER DaysInterval Specifies the interval between the days in the schedule. An interval of 1 produces @@ -742,6 +773,10 @@ function Set-TargetResource [System.DateTime] $StartTime = [System.DateTime]::Today, + [Parameter()] + [System.Boolean] + $SynchronizeAcrossTimeZone = $false, + [Parameter()] [System.String] [ValidateSet('Present', 'Absent')] @@ -755,6 +790,10 @@ function Set-TargetResource [System.Management.Automation.PSCredential] $ExecuteAsCredential, + [Parameter()] + [System.String] + $ExecuteAsGMSA, + [Parameter()] [System.UInt32] $DaysInterval = 1, @@ -951,6 +990,19 @@ function Set-TargetResource -ArgumentName EventSubscription } + if ($ExecuteAsCredential -and $ExecuteAsGMSA) + { + New-InvalidArgumentException ` + -Message ($script:localizedData.gMSAandCredentialError) ` + -ArgumentName ExecuteAsGMSA + } + + if($SynchronizeAcrossTimeZone -and ($ScheduleType -notin @('Once', 'Daily', 'Weekly'))) { + New-InvalidArgumentException ` + -Message ($script:localizedData.SynchronizeAcrossTimeZoneInvalidScheduleType) ` + -ArgumentName SynchronizeAcrossTimeZone + } + # Configure the action $actionParameters = @{ Execute = $ActionExecutable @@ -1193,7 +1245,12 @@ function Set-TargetResource # Prepare the register arguments $registerArguments = @{} - if ($PSBoundParameters.ContainsKey('ExecuteAsCredential')) + if ($PSBoundParameters.ContainsKey('ExecuteAsGMSA')) + { + $username = $ExecuteAsGMSA + $LogonType = 'Password' + } + elseif ($PSBoundParameters.ContainsKey('ExecuteAsCredential')) { $username = $ExecuteAsCredential.UserName $registerArguments.Add('User', $username) @@ -1274,6 +1331,29 @@ function Set-TargetResource $scheduledTask.Description = $Description } + if($scheduledTask.Triggers[0].StartBoundary) + { + <# + The way New-ScheduledTaskTrigger writes the StartBoundary has issues because it does not take + the setting "Synchronize across time zones" in consideration. What happens if synchronize across + time zone is enabled in the scheduled task GUI is that the time is written like this: + + 2018-09-27T18:45:08+02:00 + + When the setting synchronize across time zones is disabled, the time is written as: + + 2018-09-27T18:45:08 + + The problem in New-ScheduledTaskTrigger is that it always writes the time the format that + includes the full timezone offset (W2016 behaviour, W2012R2 does it the other way around). + Which means "Synchronize across time zones" is enabled by default on W2016 and disabled by + default on W2012R2. To prevent that, we are overwriting the StartBoundary here to insert + the time in the format we want it, so we can enable or disable "Synchronize across time zones". + #> + + $scheduledTask.Triggers[0].StartBoundary = Get-DateTimeString -Date $StartTime -SynchronizeAcrossTimeZone $SynchronizeAcrossTimeZone + } + if ($currentValues.Ensure -eq 'Present') { # Updating the scheduled task @@ -1335,6 +1415,11 @@ function Set-TargetResource The time of day this task should start at - defaults to 12:00 AM. Not valid for AtLogon and AtStartup tasks. + .PARAMETER SynchronizeAcrossTimeZone + Enable the scheduled task option to synchronize across time zones. This is enabled + by including the timezone offset in the scheduled task trigger. Defaults to false + which does not include the timezone offset. + .PARAMETER Ensure Present if the task should exist, Absent if it should be removed. @@ -1343,7 +1428,11 @@ function Set-TargetResource .PARAMETER ExecuteAsCredential The credential this task should execute as. If not specified defaults to running - as the local system account. + as the local system account. Cannot be used in combination with ExecuteAsGMSA. + + .PARAMETER ExecuteAsGMSA + The gMSA (Group Managed Service Account) this task should execute as. Cannot be + used in combination with ExecuteAsCredential. .PARAMETER DaysInterval Specifies the interval between the days in the schedule. An interval of 1 produces @@ -1507,6 +1596,10 @@ function Test-TargetResource [System.DateTime] $StartTime = [System.DateTime]::Today, + [Parameter()] + [System.Boolean] + $SynchronizeAcrossTimeZone = $false, + [Parameter()] [System.String] [ValidateSet('Present', 'Absent')] @@ -1520,6 +1613,10 @@ function Test-TargetResource [System.Management.Automation.PSCredential] $ExecuteAsCredential, + [Parameter()] + [System.String] + $ExecuteAsGMSA, + [Parameter()] [System.UInt32] $DaysInterval = 1, @@ -1706,6 +1803,11 @@ function Test-TargetResource $PSBoundParameters['RestartInterval'] = (ConvertTo-TimeSpanFromTimeSpanString -TimeSpanString $RestartInterval).ToString() } + if ($ScheduleType -in @('Once', 'Daily', 'Weekly')) + { + $PSBoundParameters['StartTime'] = Get-DateTimeString -Date $StartTime -SynchronizeAcrossTimeZone $SynchronizeAcrossTimeZone + } + $currentValues = Get-TargetResource @PSBoundParameters Write-Verbose -Message ($script:localizedData.GetCurrentTaskValuesMessage) @@ -1729,6 +1831,21 @@ function Test-TargetResource $PSBoundParameters['ExecuteAsCredential'] = $username } + if ($PSBoundParameters.ContainsKey('ExecuteAsGMSA')) + { + <# + There is a difference in W2012R2 and W2016 behaviour, + W2012R2 returns the gMSA including the DOMAIN prefix, + W2016 returns this without. So to be sure strip off the + domain part in Get & Test. This means we either need to + remove everything before \ in the case of the DOMAIN\User + format, or we need to remove everything after @ in case + when the UPN format (User@domain.fqdn) is used. + #> + + $PSBoundParameters['ExecuteAsGMSA'] = $PSBoundParameters.ExecuteAsGMSA -replace '^.+\\|@.+', $null + } + $desiredValues = $PSBoundParameters $desiredValues.TaskPath = $TaskPath @@ -1891,3 +2008,42 @@ function Disable-ScheduledTask $existingTask.Settings.Enabled = $false $null = $existingTask | Register-ScheduledTask @PSBoundParameters -Force } + +<# + .SYNOPSIS + Returns a formatted datetime string for use in ScheduledTask resource. + + .PARAMETER Date + The date to format. + + .PARAMETER SynchronizeAcrossTimeZone + Boolean to specifiy if the returned string is formatted in synchronize + across time zone format. +#> +Function Get-DateTimeString +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.DateTime] + $Date, + + [Parameter(Mandatory = $true)] + [System.Boolean] + $SynchronizeAcrossTimeZone + ) + + $format = (Get-Culture).DateTimeFormat.SortableDateTimePattern + + if($SynchronizeAcrossTimeZone) + { + $returnDate = (Get-Date -Date $Date -Format $format) + (Get-Date -Format 'zzz') + } + else + { + $returnDate = Get-Date -Date $Date -Format $format + } + + return $returnDate +} diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.schema.mof b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.schema.mof index 4d40f88a..65255166 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.schema.mof +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.schema.mof @@ -10,9 +10,11 @@ class MSFT_ScheduledTask : OMI_BaseResource [Write, Description("When should the task be executed"), ValueMap{"Once", "Daily", "Weekly", "AtStartup", "AtLogOn", "OnEvent"}, Values{"Once", "Daily", "Weekly", "AtStartup", "AtLogOn", "OnEvent"}] string ScheduleType; [Write, Description("How many units (minutes, hours, days) between each run of this task?")] String RepeatInterval; [Write, Description("The time of day this task should start at - defaults to 12:00 AM. Not valid for AtLogon and AtStartup tasks")] DateTime StartTime; + [Write, Description("Enable the scheduled task option to synchronize across time zones. This is enabled by including the timezone offset in the scheduled task trigger. Defaults to false which does not include the timezone offset.")] boolean SynchronizeAcrossTimeZone; [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("The gMSA (Group Managed Service Account) this task should execute as. Cannot be used in combination with ExecuteAsCredential.")] string ExecuteAsGMSA; [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.")] String RandomDelay; [Write, Description("Specifies how long the repetition pattern repeats after the task starts. May be set to `Indefinitely` to specify an indefinite duration.")] String RepetitionDuration; diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/en-US/MSFT_ScheduledTask.strings.psd1 b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/en-US/MSFT_ScheduledTask.strings.psd1 index d811266f..9d3b4309 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/en-US/MSFT_ScheduledTask.strings.psd1 +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/en-US/MSFT_ScheduledTask.strings.psd1 @@ -11,6 +11,8 @@ ConvertFrom-StringData @' WeeksIntervalError = WeeksInterval must be greater than zero (0) for Weekly schedules. WeeksInterval specified is '{0}'. WeekDayMissingError = At least one weekday must be selected for Weekly schedule. OnEventSubscriptionError = No (valid) XML Event Subscription was provided. This is required when the scheduletype is OnEvent. + gMSAandCredentialError = Both ExecuteAsGMSA and ExecuteAsCredential parameters have been specified. A task can either run as a gMSA (Group Managed Service Account) or as a custom credential, not both. Please modify your configuration to include just one of the two. + SynchronizeAcrossTimeZoneInvalidScheduleType = Setting SynchronizeAcrossTimeZone to true when the ScheduleType is not Once, Daily or Weekly is not a valid configuration. Please keep the default value of false when using other schedule types. TriggerCreationError = Error creating new scheduled task trigger. ConfigureTriggerRepetitionMessage = Configuring trigger repetition. RepetitionIntervalError = Repetition interval is set to '{0}' but repetition duration is '{1}'. diff --git a/Modules/ComputerManagementDsc/Examples/Resources/ScheduledTask/14-RunPowerShellTaskOnceAsGroupManagedServiceAccount.ps1 b/Modules/ComputerManagementDsc/Examples/Resources/ScheduledTask/14-RunPowerShellTaskOnceAsGroupManagedServiceAccount.ps1 new file mode 100644 index 00000000..33061ff0 --- /dev/null +++ b/Modules/ComputerManagementDsc/Examples/Resources/ScheduledTask/14-RunPowerShellTaskOnceAsGroupManagedServiceAccount.ps1 @@ -0,0 +1,37 @@ +<# + .EXAMPLE + This example creates a scheduled task called 'Test task Run As gMSA' + in the folder task folder 'MyTasks' that starts a new powershell process once. + The task will run as the user passed into the ExecuteAsGMSA parameter. +#> +Configuration Example +{ + param + ( + [Parameter()] + [System.String[]] + $NodeName = 'localhost', + + # Group Managed Service Account must be in the form of DOMAIN\gMSA$ or user@domain.fqdn (UPN) + [Parameter()] + [ValidatePattern('^\w+\\\w+\$$|\w+@\w+\.\w+')] + [System.String] + $GroupManagedServiceAccount = 'DOMAIN\gMSA$' + ) + + Import-DscResource -ModuleName ComputerManagementDsc + + Node $NodeName + { + ScheduledTask MaintenanceScriptExample + { + TaskName = 'Test task Run As gMSA' + TaskPath = '\MyTasks' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + ActionWorkingPath = (Get-Location).Path + Enable = $true + ExecuteAsGMSA = $GroupManagedServiceAccount + } + } +} diff --git a/Modules/ComputerManagementDsc/Examples/Resources/ScheduledTask/15-CreateScheduledTaskOnceSynchronizeAcrossTimeZoneEnabled.ps1 b/Modules/ComputerManagementDsc/Examples/Resources/ScheduledTask/15-CreateScheduledTaskOnceSynchronizeAcrossTimeZoneEnabled.ps1 new file mode 100644 index 00000000..263c901f --- /dev/null +++ b/Modules/ComputerManagementDsc/Examples/Resources/ScheduledTask/15-CreateScheduledTaskOnceSynchronizeAcrossTimeZoneEnabled.ps1 @@ -0,0 +1,32 @@ +<# + .EXAMPLE + This example creates a scheduled task called 'Test task sync across time zone enabled' + in the folder 'MyTasks' that starts a new powershell process once 2018-10-01 01:00 + The task will have the option Synchronize across time zone enabled. +#> +Configuration Example +{ + param + ( + [Parameter()] + [System.String[]] + $NodeName = 'localhost' + ) + + Import-DscResource -ModuleName ComputerManagementDsc + + Node $NodeName + { + ScheduledTask ScheduledTaskOnceSynchronizeAcrossTimeZoneEnabled + { + TaskName = 'Test task sync across time zone enabled' + TaskPath = '\MyTasks\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + StartTime = '2018-10-01T01:00:00' + SynchronizeAcrossTimeZone = $true + ActionWorkingPath = (Get-Location).Path + Enable = $true + } + } +} diff --git a/Tests/Integration/MSFT_ScheduledTask.Config.ps1 b/Tests/Integration/MSFT_ScheduledTask.Config.ps1 index 8195dff4..728e9a72 100644 --- a/Tests/Integration/MSFT_ScheduledTask.Config.ps1 +++ b/Tests/Integration/MSFT_ScheduledTask.Config.ps1 @@ -22,6 +22,44 @@ Configuration ScheduledTaskOnceCrossTimezone } } +Configuration ScheduledTaskOnceSynchronizeAcrossTimeZoneDisabled +{ + Import-DscResource -ModuleName ComputerManagementDsc + node 'localhost' + { + ScheduledTask ScheduledTaskOnceSynchronizeAcrossTimeZoneDisabled + { + TaskName = 'Test task sync across time zone disabled' + TaskPath = '\ComputerManagementDsc\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + StartTime = '2018-10-01T01:00:00' + SynchronizeAcrossTimeZone = $false + ActionWorkingPath = (Get-Location).Path + Enable = $true + } + } +} + +Configuration ScheduledTaskOnceSynchronizeAcrossTimeZoneEnabled +{ + Import-DscResource -ModuleName ComputerManagementDsc + node 'localhost' + { + ScheduledTask ScheduledTaskOnceSynchronizeAcrossTimeZoneEnabled + { + TaskName = 'Test task sync across time zone enabled' + TaskPath = '\ComputerManagementDsc\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + StartTime = '2018-10-01T01:00:00' + SynchronizeAcrossTimeZone = $true + ActionWorkingPath = (Get-Location).Path + Enable = $true + } + } +} + Configuration ScheduledTaskOnceAdd { Import-DscResource -ModuleName ComputerManagementDsc diff --git a/Tests/Integration/MSFT_ScheduledTask.Integration.Tests.ps1 b/Tests/Integration/MSFT_ScheduledTask.Integration.Tests.ps1 index 38ae8f2f..53fc756c 100644 --- a/Tests/Integration/MSFT_ScheduledTask.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_ScheduledTask.Integration.Tests.ps1 @@ -195,6 +195,100 @@ try } } + Context "When a scheduled task is created and synchronize across time zone is disabled" { + $currentConfig = 'ScheduledTaskOnceSynchronizeAcrossTimeZoneDisabled' + $configDir = (Join-Path -Path $TestDrive -ChildPath $currentConfig) + $configMof = (Join-Path -Path $configDir -ChildPath 'localhost.mof') + + It 'Should compile the MOF without throwing' { + { + . $currentConfig ` + -OutputPath $configDir + } | Should -Not -Throw + } + + It 'Should apply the MOF correctly' { + { + Start-DscConfiguration ` + -Path $configDir ` + -Wait ` + -Force ` + -Verbose ` + -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should return a compliant state after being applied' { + (Test-DscConfiguration -ReferenceConfiguration $configMof -Verbose).InDesiredState | Should -Be $true + } + + $expectedStartTime = '2018-10-01T01:00:00' + + It 'Should have set the resource and all the parameters should match' { + $current = Get-DscConfiguration | Where-Object {$_.ConfigurationName -eq $currentConfig} + $current.TaskName | Should -Be 'Test task sync across time zone disabled' + $current.TaskPath | Should -Be '\ComputerManagementDsc\' + $current.ActionExecutable | Should -Be 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + $current.ScheduleType | Should -Be 'Once' + $current.StartTime | Should -Be (Get-Date -Date $expectedStartTime) + $current.SynchronizeAcrossTimeZone | Should -Be $false + $current.ActionWorkingPath | Should -Be (Get-Location).Path + $current.Enable | Should -Be $true + } + + It "Should have the trigger startBoundary set to $expectedStartTime" { + $task = (Get-ScheduledTask -TaskName 'Test task sync across time zone disabled') + $task.Triggers[0].StartBoundary | Should -Be $expectedStartTime + } + } + + Context "When a scheduled task is created and synchronize across time zone is enabled" { + $currentConfig = 'ScheduledTaskOnceSynchronizeAcrossTimeZoneEnabled' + $configDir = (Join-Path -Path $TestDrive -ChildPath $currentConfig) + $configMof = (Join-Path -Path $configDir -ChildPath 'localhost.mof') + + It 'Should compile the MOF without throwing' { + { + . $currentConfig ` + -OutputPath $configDir + } | Should -Not -Throw + } + + It 'Should apply the MOF correctly' { + { + Start-DscConfiguration ` + -Path $configDir ` + -Wait ` + -Force ` + -Verbose ` + -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should return a compliant state after being applied' { + (Test-DscConfiguration -ReferenceConfiguration $configMof -Verbose).InDesiredState | Should -Be $true + } + + $expectedStartTime = '2018-10-01T01:00:00' + (Get-Date -Format 'zzz') + + It 'Should have set the resource and all the parameters should match' { + $current = Get-DscConfiguration | Where-Object {$_.ConfigurationName -eq $currentConfig} + $current.TaskName | Should -Be 'Test task sync across time zone enabled' + $current.TaskPath | Should -Be '\ComputerManagementDsc\' + $current.ActionExecutable | Should -Be 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + $current.ScheduleType | Should -Be 'Once' + $current.StartTime | Should -Be (Get-Date -Date $expectedStartTime) + $current.SynchronizeAcrossTimeZone | Should -Be $true + $current.ActionWorkingPath | Should -Be (Get-Location).Path + $current.Enable | Should -Be $true + } + + It "Should have the trigger startBoundary set to $expectedStartTime" { + $task = (Get-ScheduledTask -TaskName 'Test task sync across time zone enabled') + $task.Triggers[0].StartBoundary | Should -Be $expectedStartTime + } + } + # Simulate a "built-in" scheduled task $action = New-ScheduledTaskAction -Execute 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) diff --git a/Tests/Unit/MSFT_PowerPlan.Tests.ps1 b/Tests/Unit/MSFT_PowerPlan.Tests.ps1 index 5eafc68c..b40b2b96 100644 --- a/Tests/Unit/MSFT_PowerPlan.Tests.ps1 +++ b/Tests/Unit/MSFT_PowerPlan.Tests.ps1 @@ -57,6 +57,7 @@ try $result = Get-TargetResource @testParameters $result.IsSingleInstance | Should -Be 'Yes' $result.Name | Should -Be $testParameters.Name + $result.IsActive | Should -Be $true } } @@ -72,10 +73,11 @@ try -Verifiable } - It 'Should not return any plan name' { + It 'Should return an inactive plan' { $result = Get-TargetResource @testParameters $result.IsSingleInstance | Should -Be 'Yes' - $result.Name | Should -Be $null + $result.Name | Should -Be $testParameters.Name + $result.IsActive | Should -Be $false } } diff --git a/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 b/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 index e3943a7a..6622572e 100644 --- a/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 +++ b/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 @@ -1528,7 +1528,7 @@ try Settings = [pscustomobject] @{ Enabled = $true } - } + } } It 'Should return the correct values from Get-TargetResource' { @@ -1577,7 +1577,7 @@ try Settings = [pscustomobject] @{ Enabled = $true } - } + } } It 'Should return the correct values from Get-TargetResource' { @@ -1652,7 +1652,7 @@ try Settings = [pscustomobject] @{ Enabled = $true } - } + } } It 'Should return the correct values from Get-TargetResource' { @@ -1707,7 +1707,7 @@ try Settings = [pscustomobject] @{ Enabled = $true } - } + } } It 'Should return the correct values from Get-TargetResource' { @@ -1728,6 +1728,313 @@ try { Set-TargetResource @testParameters } | Should throw } } + + Context 'When a scheduled task is created using a Group Managed Service Account' { + $testParameters = @{ + TaskName = 'Test task' + TaskPath = '\Test\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + RepeatInterval = (New-TimeSpan -Minutes 15).ToString() + RepetitionDuration = (New-TimeSpan -Hours 8).ToString() + ExecuteAsGMSA = 'DOMAIN\gMSA$' + ExecuteAsCredential = [pscredential]::new('DEMO\RightUser', (ConvertTo-SecureString 'ExamplePassword' -AsPlainText -Force)) + Verbose = $true + } + + It 'Should return an error when both the ExecuteAsGMSA an ExecuteAsCredential ar specified' { + try + { + Set-TargetResource @testParameters -ErrorVariable duplicateCredential + } + catch + { + # Error from Set-TargetResource expected + } + finally + { + $duplicateCredential.Message | Should -Be "Both ExecuteAsGMSA and ExecuteAsCredential parameters have been specified. A task can either run as a gMSA (Group Managed Service Account) or as a custom credential, not both. Please modify your configuration to include just one of the two.`r`nParameter name: ExecuteAsGMSA" + } + } + + $testParameters.Remove('ExecuteAsCredential') + + It 'Should call Register-ScheduledTask with the name of the Group Managed Service Account' { + Set-TargetResource @testParameters + Assert-MockCalled -CommandName Register-ScheduledTask -Times 1 -Scope It -ParameterFilter { + $User -eq $null -and $Inputobject.Principal.UserId -eq $testParameters.ExecuteAsGMSA + } + } + + It 'Should set the LogonType to Password when a Group Managed Service Account is used' { + Set-TargetResource @testParameters + Assert-MockCalled -CommandName Register-ScheduledTask -Times 1 -Scope It -ParameterFilter { + $Inputobject.Principal.Logontype -eq 'Password' + } + } + + Mock -CommandName Get-ScheduledTask -MockWith { + @{ + TaskName = $testParameters.TaskName + TaskPath = $testParameters.TaskPath + Actions = @( + [pscustomobject] @{ + Execute = $testParameters.ActionExecutable + } + ) + Triggers = @( + [pscustomobject] @{ + Repetition = @{ + Duration = "PT$([System.TimeSpan]::Parse($testParameters.RepetitionDuration).TotalHours)H" + Interval = "PT$([System.TimeSpan]::Parse($testParameters.RepeatInterval).TotalMinutes)M" + } + CimClass = @{ + CimClassName = 'MSFT_TaskTimeTrigger' + } + } + ) + Principal = [pscustomobject] @{ + UserId = 'gMSA$' + } + } + } + + It 'Should return true if the task is in desired state and given gMSA user in DOMAIN\User$ format' { + Test-TargetResource @testParameters | Should -Be $true + } + + $testParameters.ExecuteAsGMSA = 'gMSA$@domain.fqdn' + + It 'Should return true if the task is in desired state and given gMSA user in UPN format' { + Test-TargetResource @testParameters | Should -Be $true + } + } + + Context 'When a scheduled task Group Managed Service Account is changed' { + $testParameters = @{ + TaskName = 'Test task' + TaskPath = '\Test\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ScheduleType = 'Once' + RepeatInterval = (New-TimeSpan -Minutes 15).ToString() + RepetitionDuration = (New-TimeSpan -Hours 8).ToString() + ExecuteAsGMSA = 'DOMAIN\gMSA$' + Verbose = $true + } + + Mock -CommandName Get-ScheduledTask -MockWith { + @{ + TaskName = $testParameters.TaskName + TaskPath = $testParameters.TaskPath + Actions = @( + [pscustomobject] @{ + Execute = $testParameters.ActionExecutable + } + ) + Triggers = @( + [pscustomobject] @{ + Repetition = @{ + Duration = "PT$([System.TimeSpan]::Parse($testParameters.RepetitionDuration).TotalHours)H" + Interval = "PT$([System.TimeSpan]::Parse($testParameters.RepeatInterval).TotalMinutes)M" + } + CimClass = @{ + CimClassName = 'MSFT_TaskTimeTrigger' + } + } + ) + Principal = [pscustomobject] @{ + UserId = 'update_gMSA$' + } + } + } + + It 'Should return false on Test-TargetResource if the task is not in desired state and given gMSA user in DOMAIN\User$ format' { + Test-TargetResource @testParameters | Should -Be $false + } + + It 'Should call Set-ScheduledTask using the new Group Managed Service Account' { + Set-TargetResource @testParameters + Assert-MockCalled -CommandName Set-ScheduledTask -Times 1 -Scope It -ParameterFilter { + $Inputobject.Principal.UserId -eq $testParameters.ExecuteAsGMSA + } + } + + It 'Should set the LogonType to Password when a Group Managed Service Account is used' { + Set-TargetResource @testParameters + Assert-MockCalled -CommandName Set-ScheduledTask -Times 1 -Scope It -ParameterFilter { + $Inputobject.Principal.Logontype -eq 'Password' + } + } + } + + Context 'When a scheduled task is created and synchronize across time zone is disabled' { + $startTimeString = '2018-10-01T01:00:00' + $startTimeStringWithOffset = '2018-10-01T01:00:00' + (Get-Date -Format 'zzz') + $testParameters = @{ + TaskName = 'Test task' + TaskPath = '\Test\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + StartTime = Get-Date -Date $startTimeString + SynchronizeAcrossTimeZone = $false + ScheduleType = 'Once' + Verbose = $true + } + + Mock -CommandName Get-ScheduledTask -MockWith { + @{ + TaskName = $testParameters.TaskName + TaskPath = $testParameters.TaskPath + Actions = @( + [pscustomobject] @{ + Execute = $testParameters.ActionExecutable + } + ) + Triggers = @( + [pscustomobject] @{ + StartBoundary = $startTimeString + CimClass = @{ + CimClassName = 'MSFT_TaskTimeTrigger' + } + } + ) + } + } + + It 'Should return the time in string format and SynchronizeAcrossTimeZone with value false' { + $result = Get-TargetResource @testParameters + $result.StartTime | Should -Be $startTimeString + $result.SynchronizeAcrossTimeZone | Should -Be $false + } + + It 'Should return true given that startTime is set correctly' { + Test-TargetResource @testParameters | Should -Be $true + } + + Mock -CommandName Get-ScheduledTask -MockWith { + @{ + TaskName = $testParameters.TaskName + TaskPath = $testParameters.TaskPath + Actions = @( + [pscustomobject] @{ + Execute = $testParameters.ActionExecutable + } + ) + Triggers = @( + [pscustomobject] @{ + StartBoundary = $startTimeStringWithOffset + CimClass = @{ + CimClassName = 'MSFT_TaskTimeTrigger' + } + } + ) + } + } + + It 'Should return false given that the task is configured with synchronize across time zone' { + Test-TargetResource @testParameters | Should -Be $false + } + + Set-TargetResource @testParameters + + It "Should set task trigger StartBoundary to $startTimeString" { + Assert-MockCalled -CommandName Set-ScheduledTask -ParameterFilter { + $InputObject.Triggers[0].StartBoundary -eq $startTimeString + } + } + } + + Context 'When a scheduled task is created and synchronize across time zone is enabled' { + $startTimeString = '2018-10-01T01:00:00' + $startTimeStringWithOffset = '2018-10-01T01:00:00' + (Get-Date -Format 'zzz') + $testParameters = @{ + TaskName = 'Test task' + TaskPath = '\Test\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + StartTime = Get-Date -Date $startTimeString + SynchronizeAcrossTimeZone = $true + ScheduleType = 'Once' + Verbose = $true + } + + Mock -CommandName Get-ScheduledTask -MockWith { + @{ + TaskName = $testParameters.TaskName + TaskPath = $testParameters.TaskPath + Actions = @( + [pscustomobject] @{ + Execute = $testParameters.ActionExecutable + } + ) + Triggers = @( + [pscustomobject] @{ + StartBoundary = $startTimeStringWithOffset + CimClass = @{ + CimClassName = 'MSFT_TaskTimeTrigger' + } + } + ) + } + } + + It 'Should return the time in string format and SynchronizeAcrossTimeZone with value true' { + $result = Get-TargetResource @testParameters + $result.StartTime | Should -Be $startTimeStringWithOffset + $result.SynchronizeAcrossTimeZone | Should -Be $true + } + + It 'Should return true given that startTime is set correctly' { + Test-TargetResource @testParameters | Should -Be $true + } + + Mock -CommandName Get-ScheduledTask -MockWith { + @{ + TaskName = $testParameters.TaskName + TaskPath = $testParameters.TaskPath + Actions = @( + [pscustomobject] @{ + Execute = $testParameters.ActionExecutable + } + ) + Triggers = @( + [pscustomobject] @{ + StartBoundary = $startTimeString + CimClass = @{ + CimClassName = 'MSFT_TaskTimeTrigger' + } + } + ) + } + } + + It 'Should return false given that the task is configured with synchronize across time zone disabled' { + Test-TargetResource @testParameters | Should -Be $false + } + + Set-TargetResource @testParameters + + It "Should set task trigger StartBoundary to $startTimeStringWithOffset" { + Assert-MockCalled -CommandName Set-ScheduledTask -ParameterFilter { + $InputObject.Triggers[0].StartBoundary -eq $startTimeStringWithOffset + } + } + } + + Context 'When a scheduled task is configured to SynchronizeAcrossTimeZone and the ScheduleType is not Once, Daily or Weekly' { + $startTimeString = '2018-10-01T01:00:00' + $testParameters = @{ + TaskName = 'Test task' + TaskPath = '\Test\' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + StartTime = Get-Date -Date $startTimeString + SynchronizeAcrossTimeZone = $true + ScheduleType = 'AtLogon' + Verbose = $true + } + + It 'Should throw when Set-TargetResource is called and SynchronizeAcrossTimeZone is used in combination with an unsupported trigger type' { + { Set-TargetResource @testParamers } | Should Throw + } + } } } #endregion