diff --git a/CHANGELOG.md b/CHANGELOG.md index a06c0cb8..ff2e64c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,13 @@ ## Unreleased - ScheduledTask: + - IdleWaitTimeout returned from Get-TargetResource always null - Fixes [Issue #186](https://github.com/PowerShell/ComputerManagementDsc/issues/186). + - Added BuiltInAccount Property to allow running task as one of the build in + service accounts - Fixes [Issue #130](https://github.com/PowerShell/ComputerManagementDsc/issues/130). - 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). + - Added .VSCode settings for applying DSC PSSA rules - fixes [Issue #189](https://github.com/PowerShell/ComputerManagementDsc/issues/189). ## 5.2.0.0 diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 index 53f9d8c3..71c059c2 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 @@ -76,6 +76,10 @@ $script:localizedData = Get-LocalizedData ` True if the task should be enabled, false if it should be disabled. Not used in Get-TargetResource. + .PARAMETER BuiltInAccount + Run the task as one of the built in service accounts. + When set ExecuteAsCredential will be ignored and LogonType will be set to 'ServiceAccount' + .PARAMETER ExecuteAsCredential The credential this task should execute as. If not specified defaults to running as the local system account. Cannot be used in combination with ExecuteAsGMSA. @@ -277,6 +281,10 @@ function Get-TargetResource [System.Boolean] $Enable = $true, + [ValidateSet('SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE')] + [System.String] + $BuiltInAccount, + [Parameter()] [System.Management.Automation.PSCredential] $ExecuteAsCredential, @@ -511,7 +519,7 @@ function Get-TargetResource $returnSynchronizeAcrossTimeZone = $false } - return @{ + $result = @{ TaskName = $task.TaskName TaskPath = $task.TaskPath StartTime = $startAt @@ -538,7 +546,7 @@ function Get-TargetResource AllowStartIfOnBatteries = -not $settings.DisallowStartIfOnBatteries Hidden = $settings.Hidden RunOnlyIfIdle = $settings.RunOnlyIfIdle - IdleWaitTimeout = ConvertTo-TimeSpanStringFromScheduledTaskString -TimeSpan $settings.IdleSettings.IdleWaitTimeout + IdleWaitTimeout = ConvertTo-TimeSpanStringFromScheduledTaskString -TimeSpan $settings.IdleSettings.WaitTimeout NetworkName = $settings.NetworkSettings.Name DisallowStartOnRemoteAppSession = $settings.DisallowStartOnRemoteAppSession StartWhenAvailable = $settings.StartWhenAvailable @@ -558,6 +566,13 @@ function Get-TargetResource EventSubscription = $trigger.Subscription Delay = ConvertTo-TimeSpanStringFromScheduledTaskString -TimeSpan $trigger.Delay } + + if (($result.ContainsKey('LogonType')) -and ($result['LogonType'] -ieq 'ServiceAccount')) + { + $result.Add('BuiltInAccount', $task.Principal.UserId) + } + + return $result } } @@ -604,6 +619,10 @@ function Get-TargetResource .PARAMETER Enable True if the task should be enabled, false if it should be disabled. + .PARAMETER BuiltInAccount + Run the task as one of the built in service accounts. + When set ExecuteAsCredential will be ignored and LogonType will be set to 'ServiceAccount' + .PARAMETER ExecuteAsCredential The credential this task should execute as. If not specified defaults to running as the local system account. Cannot be used in combination with ExecuteAsGMSA. @@ -786,6 +805,10 @@ function Set-TargetResource [System.Boolean] $Enable = $true, + [ValidateSet('SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE')] + [System.String] + $BuiltInAccount, + [Parameter()] [System.Management.Automation.PSCredential] $ExecuteAsCredential, @@ -945,9 +968,9 @@ function Set-TargetResource and the action executable isn't specified then disable the task #> if ($currentValues.Ensure -eq 'Present' ` - -and $currentValues.Enable ` - -and -not $Enable ` - -and -not $PSBoundParameters.ContainsKey('ActionExecutable')) + -and $currentValues.Enable ` + -and -not $Enable ` + -and -not $PSBoundParameters.ContainsKey('ActionExecutable')) { Write-Verbose -Message ($script:localizedData.DisablingExistingScheduledTask -f $TaskName, $TaskPath) Disable-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath @@ -990,14 +1013,15 @@ function Set-TargetResource -ArgumentName EventSubscription } - if ($ExecuteAsCredential -and $ExecuteAsGMSA) + if ($ExecuteAsGMSA -and ($ExecuteAsCredential -or $BuiltInAccount)) { New-InvalidArgumentException ` -Message ($script:localizedData.gMSAandCredentialError) ` -ArgumentName ExecuteAsGMSA } - if($SynchronizeAcrossTimeZone -and ($ScheduleType -notin @('Once', 'Daily', 'Weekly'))) { + if ($SynchronizeAcrossTimeZone -and ($ScheduleType -notin @('Once', 'Daily', 'Weekly'))) + { New-InvalidArgumentException ` -Message ($script:localizedData.SynchronizeAcrossTimeZoneInvalidScheduleType) ` -ArgumentName SynchronizeAcrossTimeZone @@ -1245,7 +1269,19 @@ function Set-TargetResource # Prepare the register arguments $registerArguments = @{} - if ($PSBoundParameters.ContainsKey('ExecuteAsGMSA')) + $username = $null + if ($PSBoundParameters.ContainsKey('BuiltInAccount')) + { + <# + The validateset on BuiltInAccount has already checked the + non-null value to be 'LOCAL SERVICE', 'NETWORK SERVICE' or + 'SYSTEM' + #> + $username = 'NT AUTHORITY\' + $BuiltInAccount + $registerArguments.Add('User', $username) + $LogonType = 'ServiceAccount' + } + elseif ($PSBoundParameters.ContainsKey('ExecuteAsGMSA')) { $username = $ExecuteAsGMSA $LogonType = 'Password' @@ -1269,6 +1305,11 @@ function Set-TargetResource } else { + <# + 'NT AUTHORITY\SYSTEM' basically gives the schedule task admin + privileges, should we default to 'NT AUTHORITY\LOCAL SERVICE' + instead? + #> $username = 'NT AUTHORITY\SYSTEM' $registerArguments.Add('User', $username) $LogonType = 'ServiceAccount' @@ -1331,7 +1372,7 @@ function Set-TargetResource $scheduledTask.Description = $Description } - if($scheduledTask.Triggers[0].StartBoundary) + if ($scheduledTask.Triggers[0].StartBoundary) { <# The way New-ScheduledTaskTrigger writes the StartBoundary has issues because it does not take @@ -1367,8 +1408,8 @@ function Set-TargetResource # Register the scheduled task - $registerArguments.Add('TaskName',$TaskName) - $registerArguments.Add('TaskPath',$TaskPath) + $registerArguments.Add('TaskName', $TaskName) + $registerArguments.Add('TaskPath', $TaskPath) $registerArguments.Add('InputObject', $scheduledTask) $null = Register-ScheduledTask @registerArguments @@ -1426,6 +1467,10 @@ function Set-TargetResource .PARAMETER Enable True if the task should be enabled, false if it should be disabled. + .PARAMETER BuiltInAccount + Run the task as one of the built in service accounts. + When set ExecuteAsCredential will be ignored and LogonType will be set to 'ServiceAccount' + .PARAMETER ExecuteAsCredential The credential this task should execute as. If not specified defaults to running as the local system account. Cannot be used in combination with ExecuteAsGMSA. @@ -1609,6 +1654,10 @@ function Test-TargetResource [System.Boolean] $Enable = $true, + [ValidateSet('SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE')] + [System.String] + $BuiltInAccount, + [Parameter()] [System.Management.Automation.PSCredential] $ExecuteAsCredential, @@ -1824,12 +1873,40 @@ function Test-TargetResource return $false } - if ($PSBoundParameters.ContainsKey('ExecuteAsCredential')) + if ($PSBoundParameters.ContainsKey('BuiltInAccount')) + { + $PSBoundParameters.User = $BuiltInAccount + $currentValues.User = $BuiltInAccount + + $PSBoundParameters.ExecuteAsCredential = $BuiltInAccount + $currentValues.ExecuteAsCredential = $BuiltInAccount + + $PSBoundParameters['LogonType'] = 'ServiceAccount' + $currentValues['LogonType'] = 'ServiceAccount' + } + elseif ($PSBoundParameters.ContainsKey('ExecuteAsCredential')) { # The password of the execution credential can not be compared $username = $ExecuteAsCredential.UserName $PSBoundParameters['ExecuteAsCredential'] = $username } + else + { + # Must be running as System, login type is ServiceAccount + $PSBoundParameters['LogonType'] = 'ServiceAccount' + $currentValues['LogonType'] = 'ServiceAccount' + } + + if ($PSBoundParameters.ContainsKey('WeeksInterval') -and ((-not $currentValues.ContainsKey('WeeksInterval')) -or ($null -eq $currentValues['WeeksInterval']))) + { + <# + The WeeksInterval parameter is defaulted to 1, even when the + property is unset/undefined for the current task returned from + Get-TargetResouce initialise a missing or null WeeksInterval to + spurious calls to Set-TargetResouce + #> + $currentValues.WeeksInterval = $PSBoundParameters['WeeksInterval'] + } if ($PSBoundParameters.ContainsKey('ExecuteAsGMSA')) { @@ -1848,6 +1925,14 @@ function Test-TargetResource $desiredValues = $PSBoundParameters $desiredValues.TaskPath = $TaskPath + if ($desiredValues.ContainsKey('Verbose')) + { + <# + Initialise a missing or null Verbose to avoid spurious + calls to Set-TargetResouce + #> + $currentValues.Add('Verbose', $desiredValues['Verbose']) + } Write-Verbose -Message ($script:localizedData.TestingDscParameterStateMessage) @@ -2036,7 +2121,7 @@ Function Get-DateTimeString $format = (Get-Culture).DateTimeFormat.SortableDateTimePattern - if($SynchronizeAcrossTimeZone) + if ($SynchronizeAcrossTimeZone) { $returnDate = (Get-Date -Date $Date -Format $format) + (Get-Date -Format 'zzz') } diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.schema.mof b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.schema.mof index 65255166..1ae7465f 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.schema.mof +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.schema.mof @@ -1,30 +1,31 @@ [ClassVersion("1.0.0.0"), FriendlyName("ScheduledTask")] class MSFT_ScheduledTask : 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; - [Write, Description("The path to the .exe for this task")] string ActionExecutable; - [Write, Description("The arguments to pass the executable")] string ActionArguments; - [Write, Description("The working path to specify for the executable")] string ActionWorkingPath; - [Write, Description("When should the task be executed"), ValueMap{"Once", "Daily", "Weekly", "AtStartup", "AtLogOn", "OnEvent"}, Values{"Once", "Daily", "Weekly", "AtStartup", "AtLogOn", "OnEvent"}] string ScheduleType; + [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; + [Write, Description("The path to the .exe for this task.")] string ActionExecutable; + [Write, Description("The arguments to pass the executable.")] string ActionArguments; + [Write, Description("The working path to specify for the executable.")] string ActionWorkingPath; + [Write, Description("When should the task be executed."), ValueMap{"Once", "Daily", "Weekly", "AtStartup", "AtLogOn", "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("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("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("Run the task as one of the built in service accounts. When set ExecuteAsCredential will be ignored and LogonType will be set to 'ServiceAccount'."), ValueMap{"SYSTEM", "LOCAL SERVICE", "NETWORK SERVICE"}, Values{"SYSTEM", "LOCAL SERVICE", "NETWORK SERVICE"}] string BuiltInAccount; + [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 or BuiltInAccount.")] 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; [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("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 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.")] String IdleWaitTimeout; @@ -44,6 +45,6 @@ class MSFT_ScheduledTask : OMI_BaseResource [Write, Description("Indicates that Task Scheduler runs the task only when a network is available. Task Scheduler uses the NetworkID parameter and NetworkName parameter that you specify in this cmdlet to determine if the network is available.")] Boolean RunOnlyIfNetworkAvailable; [Write, Description("Specifies the level of user rights that Task Scheduler uses to run the tasks that are associated with the principal. Defaults to 'Limited'."), ValueMap{"Limited","Highest"}, Values{"Limited","Highest"}] String RunLevel; [Write, Description("Specifies the security logon method that Task Scheduler uses to run the tasks that are associated with the principal."), ValueMap{"Group","Interactive","InteractiveOrPassword","None","Password","S4U","ServiceAccount"}, Values{"Group","Interactive","InteractiveOrPassword","None","Password","S4U","ServiceAccount"}] String LogonType; - [Write, Description("Specifies the EventSubscription in XML. This can be easily generated using the Windows Eventlog Viewer. For the query schema please check: https://docs.microsoft.com/en-us/windows/desktop/WES/queryschema-schema. Can only be used in combination with ScheduleType OnEvent")] String EventSubscription; - [Write, Description("Specifies a delay to the start of the trigger. The delay is a static delay before the task is executed. Can only be used in combination with ScheduleType OnEvent")] String Delay; + [Write, Description("Specifies the EventSubscription in XML. This can be easily generated using the Windows Eventlog Viewer. For the query schema please check: https://docs.microsoft.com/en-us/windows/desktop/WES/queryschema-schema. Can only be used in combination with ScheduleType OnEvent.")] String EventSubscription; + [Write, Description("Specifies a delay to the start of the trigger. The delay is a static delay before the task is executed. Can only be used in combination with ScheduleType OnEvent.")] String Delay; }; 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 9d3b4309..5281c977 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,7 +11,7 @@ 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. + gMSAandCredentialError = Both ExecuteAsGMSA and (ExecuteAsCredential or BuiltInAccount) parameters have been specified. A task can run as a gMSA (Group Managed Service Account), a builtin service account or as a custom credential. Please modify your configuration to include just one of the three options. 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. diff --git a/Modules/ComputerManagementDsc/Examples/Resources/ScheduledTask/16-CreateScheduledTasksAsBuiltinServiceAccount.ps1 b/Modules/ComputerManagementDsc/Examples/Resources/ScheduledTask/16-CreateScheduledTasksAsBuiltinServiceAccount.ps1 new file mode 100644 index 00000000..0d33f198 --- /dev/null +++ b/Modules/ComputerManagementDsc/Examples/Resources/ScheduledTask/16-CreateScheduledTasksAsBuiltinServiceAccount.ps1 @@ -0,0 +1,37 @@ +<# + .EXAMPLE + This example creates a scheduled task called 'TaskRunAsNetworkService' in + the folder root folder. The task is set to run every 15 minutes. + When run the task will start a new PowerShell instance running as the + builtin user NETWORK SERVICE. + The PowerShell instance will write the value of $env:USERNAME to the + file c:\temp\seeme.txt. + The contents of c:\temp\seeme.txt should be "NETWORK SERVICE". +#> +Configuration Example +{ + param + ( + [Parameter()] + [System.String[]] + $NodeName = 'localhost' + ) + + Import-DscResource -ModuleName ComputerManagementDsc + + Node $NodeName + { + ScheduledTask ServiceEventManager + { + TaskName = 'TaskRunAsNetworkService' + Ensure = 'Present' + ActionExecutable = 'C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' + ActionArguments = '-Command Set-Content -Path c:\temp\seeme.txt -Value $env:USERNAME -Force' + ScheduleType = 'Once' + RepeatInterval = '00:15:00' + RepetitionDuration = '4.00:00:00' + BuiltInAccount = 'NETWORK SERVICE' + } + } +} + diff --git a/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 b/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 index 6622572e..7bdc3d75 100644 --- a/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 +++ b/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 @@ -1010,7 +1010,7 @@ try Settings = [pscustomobject] @{ Enabled = $true IdleSettings = @{ - IdleWaitTimeout = "PT$([System.TimeSpan]::Parse($testParameters.IdleWaitTimeout).TotalMinutes)M" + WaitTimeout = "PT$([System.TimeSpan]::Parse($testParameters.IdleWaitTimeout).TotalMinutes)M" IdleDuration = "PT$([System.TimeSpan]::Parse($testParameters.IdleDuration).TotalMinutes)M" } ExecutionTimeLimit = "PT$([System.TimeSpan]::Parse($testParameters.ExecutionTimeLimit).TotalMinutes)M" @@ -1254,7 +1254,7 @@ try ) Settings = [pscustomobject] @{ IdleSettings = @{ - IdleWaitTimeout = "PT$([System.TimeSpan]::Parse($testParameters.IdleWaitTimeout).TotalMinutes)M" + WaitTimeout = "PT$([System.TimeSpan]::Parse($testParameters.IdleWaitTimeout).TotalMinutes)M" IdleDuration = "PT$([System.TimeSpan]::Parse($testParameters.IdleDuration).TotalMinutes)M" } ExecutionTimeLimit = "PT$([System.TimeSpan]::Parse($testParameters.ExecutionTimeLimit).TotalMinutes)M" @@ -1320,7 +1320,7 @@ try ) Settings = [pscustomobject] @{ IdleSettings = @{ - IdleWaitTimeout = "PT$([System.TimeSpan]::Parse($testParameters.IdleWaitTimeout).TotalMinutes + 1)M" + WaitTimeout = "PT$([System.TimeSpan]::Parse($testParameters.IdleWaitTimeout).TotalMinutes + 1)M" IdleDuration = "PT$([System.TimeSpan]::Parse($testParameters.IdleDuration).TotalMinutes + 1)M" } ExecutionTimeLimit = "PT$([System.TimeSpan]::Parse($testParameters.ExecutionTimeLimit).TotalMinutes)M" @@ -1729,7 +1729,7 @@ try } } - Context 'When a scheduled task is created using a Group Managed Service Account' { + Context 'When a scheduled task is created using a Built In Service Account' { $testParameters = @{ TaskName = 'Test task' TaskPath = '\Test\' @@ -1737,27 +1737,84 @@ try 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)) + BuiltInAccount = 'NETWORK SERVICE' + ExecuteAsCredential = [pscredential]::new('DEMO\WrongUser', (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 + It 'Should Disregard ExecuteAsCredential and Set User to the BuiltInAccount' { + Set-TargetResource @testParameters + Assert-MockCalled -CommandName Register-ScheduledTask -Times 1 -Scope It -ParameterFilter { + + $User -ieq ('NT AUTHORITY\' + $testParameters['BuiltInAccount']) } - catch - { - # Error from Set-TargetResource expected + } + + $testParameters.Add('LogonType', 'Password') + + It 'Should overwrite LogonType to "ServiceAccount"' { + Set-TargetResource @testParameters + Assert-MockCalled -CommandName Register-ScheduledTask -Times 1 -Scope It -ParameterFilter { + $Inputobject.Principal.LogonType -ieq 'ServiceAccount' } - 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" + } + + 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 = $testParameters.BuiltInAccount + LogonType = 'ServiceAccount' + } } } - $testParameters.Remove('ExecuteAsCredential') + $testParameters.LogonType = 'Password' + It 'Should return true when BuiltInAccount set even if LogonType parameter different' { + Test-TargetResource @testParameters | Should -Be $true + } + } + + 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$' + BuiltInAccount = 'NETWORK SERVICE' + ExecuteAsCredential = [pscredential]::new('DEMO\RightUser', (ConvertTo-SecureString 'ExamplePassword' -AsPlainText -Force)) + Verbose = $true + } + + It 'Should throw expected exception' { + $errorRecord = Get-InvalidArgumentRecord -Message $LocalizedData.gMSAandCredentialError -ArgumentName 'ExecuteAsGMSA' + + { Set-TargetResource @testParameters -ErrorVariable duplicateCredential } | Should -Throw $errorRecord + $testParameters.Remove('ExecuteAsCredential') + { Set-TargetResource @testParameters -ErrorVariable duplicateCredential } | Should -Throw $errorRecord + } + + $testParameters.Remove('BuiltInAccount') It 'Should call Register-ScheduledTask with the name of the Group Managed Service Account' { Set-TargetResource @testParameters