From 06bccbe00ead2b9a3c461928b40c61fc4d7fa1f1 Mon Sep 17 00:00:00 2001 From: "AEMO\\DWork" Date: Thu, 4 Oct 2018 08:29:05 +1000 Subject: [PATCH 1/5] ScheduledTask: Added BuiltInUser (Issue #130) & Fixed IdleWaitTimeout (Issue #186) --- CHANGELOG.md | 23 ++++- .../MSFT_ScheduledTask.psm1 | 89 ++++++++++++++++--- .../MSFT_ScheduledTask.schema.mof | 3 +- .../en-US/MSFT_ScheduledTask.strings.psd1 | 2 +- ...eScheduledTasksAsBuiltinServiceAccount.ps1 | 34 +++++++ Tests/Unit/MSFT_ScheduledTask.Tests.ps1 | 26 +++++- 6 files changed, 160 insertions(+), 17 deletions(-) create mode 100644 Modules/ComputerManagementDsc/Examples/Resources/ScheduledTask/16-CreateScheduledTasksAsBuiltinServiceAccount.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index a06c0cb8..65512b41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,31 @@ ## Unreleased - ScheduledTask: + - IdleWaitTimeout returned from Get-TargetResource always null - See [Issue #186](https://github.com/PowerShell/ComputerManagementDsc/issues/186). + - Added Property BuiltInAccount - See [Issue #130](https://github.com/PowerShell/ComputerManagementDsc/issues/130). + Used for running the Scheduled Task as one of the built in + service accounts. + Valid Values: 'SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE'. + If set ExecuteAsCredential will be ignored and LogonType will + be overwritten to 'SericeAccount'. + Added Example [16-CreateScheduledTasksAsBuiltinServiceAccount.ps1](Modules/ComputerManagementDsc/Examples/Resources/ScheduledTask/16-CreateScheduledTasksAsBuiltinServiceAccount.ps1). + The name BuiltInAccount and it's pattern of use is based on + the property of the same name in the [Service DSCR](https://github.com/PowerShell/PSDscResources#service). + The reason for defining a new property and not using the + alternative eg: + + `ExecuteAsCredential = ([pscredential]::new( + 'NT AUTHORITY\NETWORK SERVICE', + (ConvertTo-SecureString -String 'TEST' -AsPlainText -Force)))` + + was the above requires either; the configuration to be compiled + with PSDscAllowPlainTextPassword = $true (not secure) or the + resultant MOF file to be encrytpted (additional complexity that + may not otherwise be required for a specific environment) - 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..48ac2971 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 ('SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE'). + When set -ExecuteAsCredential will be ignored and -LogonType will be set to 'SericeAccount' + .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')] + [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,9 @@ function Get-TargetResource AllowStartIfOnBatteries = -not $settings.DisallowStartIfOnBatteries Hidden = $settings.Hidden RunOnlyIfIdle = $settings.RunOnlyIfIdle - IdleWaitTimeout = ConvertTo-TimeSpanStringFromScheduledTaskString -TimeSpan $settings.IdleSettings.IdleWaitTimeout + #$settings.IdleSettings.IdleWaitTimeout is always null, changed to WaitTimeout property to avoid Test-TargetResource returning a spurious value + #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 +568,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 +621,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 ('SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE'). + When set -ExecuteAsCredential will be ignored and -LogonType will be set to 'SericeAccount' + .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 +807,10 @@ function Set-TargetResource [System.Boolean] $Enable = $true, + [ValidateSet('SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE')] + [String] + $BuiltInAccount, + [Parameter()] [System.Management.Automation.PSCredential] $ExecuteAsCredential, @@ -945,9 +970,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,7 +1015,7 @@ function Set-TargetResource -ArgumentName EventSubscription } - if ($ExecuteAsCredential -and $ExecuteAsGMSA) + if ($ExecuteAsGMSA -and ($ExecuteAsCredential -or $BuiltInAccount)) { New-InvalidArgumentException ` -Message ($script:localizedData.gMSAandCredentialError) ` @@ -1245,7 +1270,15 @@ 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 +1302,7 @@ 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' @@ -1367,8 +1401,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 +1460,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 ('SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE'). + When set -ExecuteAsCredential will be ignored and -LogonType will be set to 'SericeAccount' + .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 +1647,10 @@ function Test-TargetResource [System.Boolean] $Enable = $true, + [ValidateSet('SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE')] + [String] + $BuiltInAccount, + [Parameter()] [System.Management.Automation.PSCredential] $ExecuteAsCredential, @@ -1824,12 +1866,34 @@ function Test-TargetResource return $false } - if ($PSBoundParameters.ContainsKey('ExecuteAsCredential')) + if ($PSBoundParameters.ContainsKey('BuiltInAccount')) + { + + $PSBoundParameters.User = $BuiltInAccount + $currentValues.User = $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 +1912,11 @@ function Test-TargetResource $desiredValues = $PSBoundParameters $desiredValues.TaskPath = $TaskPath + if ($desiredValues.ContainsKey('Verbose')) + { + #initialise a missing or null Verbose to spurious calls to Set-TargetResouce + $currentValues.Add('Verbose', $desiredValues['Verbose']) + } Write-Verbose -Message ($script:localizedData.TestingDscParameterStateMessage) diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.schema.mof b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.schema.mof index 65255166..4f6ba7ff 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.schema.mof +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.schema.mof @@ -13,8 +13,9 @@ class MSFT_ScheduledTask : OMI_BaseResource [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("Run the task as one of the built in service accounts ('SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE'). When set -ExecuteAsCredential will be ignored and -LogonType will be set to 'SericeAccount'"), 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.")] string ExecuteAsGMSA; + [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; 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..6c027c24 --- /dev/null +++ b/Modules/ComputerManagementDsc/Examples/Resources/ScheduledTask/16-CreateScheduledTasksAsBuiltinServiceAccount.ps1 @@ -0,0 +1,34 @@ +<# + .EXAMPLE + This example creates a scheduled task called 'TriggerOnServiceFailures' in the folder + root folder. The task is delayed by exactly 30 seconds each time. The task will run when + an error event 7001 of source Service Control Manager is generated in the system log. + When a service crashes, it waits for 30 seconds and then starts a new PowerShell instance, + in which the file c:\temp\seeme.txt get's created with the value 'Worked!' +#> +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 ''Worked!''' + 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..e2c55a0c 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" @@ -1738,6 +1738,7 @@ try 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 } @@ -1753,12 +1754,29 @@ try } 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" + $duplicateCredential.Message | Should -Be "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.`r`nParameter name: ExecuteAsGMSA" } } $testParameters.Remove('ExecuteAsCredential') + 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 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.`r`nParameter name: ExecuteAsGMSA" + } + } + + $testParameters.Remove('BuiltInAccount') + 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 { From c135317cb3873c6a5ba983bf8419a43cfa87b77d Mon Sep 17 00:00:00 2001 From: "AEMO\\DWork" Date: Fri, 5 Oct 2018 07:27:12 +1000 Subject: [PATCH 2/5] Code Review Fixes --- CHANGELOG.md | 24 ++------- .../MSFT_ScheduledTask.psm1 | 53 ++++++++++++------- .../MSFT_ScheduledTask.schema.mof | 34 ++++++------ ...eScheduledTasksAsBuiltinServiceAccount.ps1 | 23 ++++---- Tests/Unit/MSFT_ScheduledTask.Tests.ps1 | 34 ++---------- 5 files changed, 71 insertions(+), 97 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65512b41..ff2e64c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,27 +3,9 @@ ## Unreleased - ScheduledTask: - - IdleWaitTimeout returned from Get-TargetResource always null - See [Issue #186](https://github.com/PowerShell/ComputerManagementDsc/issues/186). - - Added Property BuiltInAccount - See [Issue #130](https://github.com/PowerShell/ComputerManagementDsc/issues/130). - Used for running the Scheduled Task as one of the built in - service accounts. - Valid Values: 'SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE'. - If set ExecuteAsCredential will be ignored and LogonType will - be overwritten to 'SericeAccount'. - Added Example [16-CreateScheduledTasksAsBuiltinServiceAccount.ps1](Modules/ComputerManagementDsc/Examples/Resources/ScheduledTask/16-CreateScheduledTasksAsBuiltinServiceAccount.ps1). - The name BuiltInAccount and it's pattern of use is based on - the property of the same name in the [Service DSCR](https://github.com/PowerShell/PSDscResources#service). - The reason for defining a new property and not using the - alternative eg: - - `ExecuteAsCredential = ([pscredential]::new( - 'NT AUTHORITY\NETWORK SERVICE', - (ConvertTo-SecureString -String 'TEST' -AsPlainText -Force)))` - - was the above requires either; the configuration to be compiled - with PSDscAllowPlainTextPassword = $true (not secure) or the - resultant MOF file to be encrytpted (additional complexity that - may not otherwise be required for a specific environment) + - 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) diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 index 48ac2971..05f77dbd 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 @@ -77,8 +77,8 @@ $script:localizedData = Get-LocalizedData ` Not used in Get-TargetResource. .PARAMETER BuiltInAccount - Run the task as one of the built in service accounts ('SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE'). - When set -ExecuteAsCredential will be ignored and -LogonType will be set to 'SericeAccount' + 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 @@ -282,7 +282,7 @@ function Get-TargetResource $Enable = $true, [ValidateSet('SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE')] - [String] + [System.String] $BuiltInAccount, [Parameter()] @@ -546,8 +546,6 @@ function Get-TargetResource AllowStartIfOnBatteries = -not $settings.DisallowStartIfOnBatteries Hidden = $settings.Hidden RunOnlyIfIdle = $settings.RunOnlyIfIdle - #$settings.IdleSettings.IdleWaitTimeout is always null, changed to WaitTimeout property to avoid Test-TargetResource returning a spurious value - #IdleWaitTimeout = ConvertTo-TimeSpanStringFromScheduledTaskString -TimeSpan $settings.IdleSettings.IdleWaitTimeout IdleWaitTimeout = ConvertTo-TimeSpanStringFromScheduledTaskString -TimeSpan $settings.IdleSettings.WaitTimeout NetworkName = $settings.NetworkSettings.Name DisallowStartOnRemoteAppSession = $settings.DisallowStartOnRemoteAppSession @@ -622,8 +620,8 @@ function Get-TargetResource 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 ('SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE'). - When set -ExecuteAsCredential will be ignored and -LogonType will be set to 'SericeAccount' + 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 @@ -808,7 +806,7 @@ function Set-TargetResource $Enable = $true, [ValidateSet('SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE')] - [String] + [System.String] $BuiltInAccount, [Parameter()] @@ -1022,7 +1020,8 @@ function Set-TargetResource -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 @@ -1273,7 +1272,10 @@ function Set-TargetResource $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' + <# + 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' @@ -1302,7 +1304,11 @@ function Set-TargetResource } else { - #'NT AUTHORITY\SYSTEM' basically gives the schedule task admin privileges, should we default to 'NT AUTHORITY\LOCAL SERVICE' instead? + <# + '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' @@ -1365,7 +1371,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 @@ -1461,8 +1467,8 @@ function Set-TargetResource 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 ('SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE'). - When set -ExecuteAsCredential will be ignored and -LogonType will be set to 'SericeAccount' + 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 @@ -1648,7 +1654,7 @@ function Test-TargetResource $Enable = $true, [ValidateSet('SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE')] - [String] + [System.String] $BuiltInAccount, [Parameter()] @@ -1883,15 +1889,19 @@ function Test-TargetResource } else { - # must be running as System, login type is ServiceAccount + # 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 + <# + 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'] } @@ -1914,7 +1924,10 @@ function Test-TargetResource $desiredValues.TaskPath = $TaskPath if ($desiredValues.ContainsKey('Verbose')) { - #initialise a missing or null Verbose to spurious calls to Set-TargetResouce + <# + Initialise a missing or null Verbose to avoid spurious + calls to Set-TargetResouce + #> $currentValues.Add('Verbose', $desiredValues['Verbose']) } @@ -2105,7 +2118,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 4f6ba7ff..1ae7465f 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.schema.mof +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.schema.mof @@ -1,20 +1,20 @@ [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("Run the task as one of the built in service accounts ('SYSTEM', 'LOCAL SERVICE', 'NETWORK SERVICE'). When set -ExecuteAsCredential will be ignored and -LogonType will be set to 'SericeAccount'"), 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("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; @@ -22,10 +22,10 @@ class MSFT_ScheduledTask : OMI_BaseResource [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; @@ -45,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/Examples/Resources/ScheduledTask/16-CreateScheduledTasksAsBuiltinServiceAccount.ps1 b/Modules/ComputerManagementDsc/Examples/Resources/ScheduledTask/16-CreateScheduledTasksAsBuiltinServiceAccount.ps1 index 6c027c24..0d33f198 100644 --- a/Modules/ComputerManagementDsc/Examples/Resources/ScheduledTask/16-CreateScheduledTasksAsBuiltinServiceAccount.ps1 +++ b/Modules/ComputerManagementDsc/Examples/Resources/ScheduledTask/16-CreateScheduledTasksAsBuiltinServiceAccount.ps1 @@ -1,10 +1,12 @@ <# .EXAMPLE - This example creates a scheduled task called 'TriggerOnServiceFailures' in the folder - root folder. The task is delayed by exactly 30 seconds each time. The task will run when - an error event 7001 of source Service Control Manager is generated in the system log. - When a service crashes, it waits for 30 seconds and then starts a new PowerShell instance, - in which the file c:\temp\seeme.txt get's created with the value 'Worked!' + 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 { @@ -21,14 +23,15 @@ Configuration Example { 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 ''Worked!''' + 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' + BuiltInAccount = 'NETWORK SERVICE' } } } + diff --git a/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 b/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 index e2c55a0c..1fcb12ca 100644 --- a/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 +++ b/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 @@ -1743,36 +1743,12 @@ try 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 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.`r`nParameter name: ExecuteAsGMSA" - } - } + It 'Should throw expected exception' { + $errorRecord = Get-InvalidArgumentRecord -Message $LocalizedData.gMSAandCredentialError -ArgumentName 'ExecuteAsGMSA' - $testParameters.Remove('ExecuteAsCredential') - - 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 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.`r`nParameter name: ExecuteAsGMSA" - } + { Set-TargetResource @testParameters -ErrorVariable duplicateCredential } | Should -Throw $errorRecord + $testParameters.Remove('ExecuteAsCredential') + { Set-TargetResource @testParameters -ErrorVariable duplicateCredential } | Should -Throw $errorRecord } $testParameters.Remove('BuiltInAccount') From c6a684cbf16a1c04b7a06e6c1b2961e7f6c84f9e Mon Sep 17 00:00:00 2001 From: "AEMO\\DWork" Date: Thu, 11 Oct 2018 07:10:54 +1000 Subject: [PATCH 3/5] additional code comment formatting --- .../MSFT_ScheduledTask.psm1 | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 index 05f77dbd..31d86da1 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 @@ -1273,8 +1273,9 @@ function Set-TargetResource if ($PSBoundParameters.ContainsKey('BuiltInAccount')) { <# - The validateset on BuiltInAccount has already checked the non-null - value to be 'LOCAL SERVICE', 'NETWORK SERVICE' or 'SYSTEM' + 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) @@ -1305,9 +1306,9 @@ function Set-TargetResource else { <# - 'NT AUTHORITY\SYSTEM' basically gives the schedule task admin - privileges, should we default to 'NT AUTHORITY\LOCAL SERVICE' - instead? + '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) @@ -1897,10 +1898,10 @@ function Test-TargetResource 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 + 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'] } @@ -1925,8 +1926,8 @@ function Test-TargetResource if ($desiredValues.ContainsKey('Verbose')) { <# - Initialise a missing or null Verbose to avoid spurious - calls to Set-TargetResouce + Initialise a missing or null Verbose to avoid spurious + calls to Set-TargetResouce #> $currentValues.Add('Verbose', $desiredValues['Verbose']) } From 20d65d466c62f725e94babc0ec6de7f19d3ed928 Mon Sep 17 00:00:00 2001 From: "AEMO\\DWork" Date: Fri, 12 Oct 2018 07:25:56 +1000 Subject: [PATCH 4/5] MSFT_ScheduledTask: Unit Test BuiltInUser \ LogonType --- Tests/Unit/MSFT_ScheduledTask.Tests.ps1 | 63 +++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 b/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 index 1fcb12ca..0afd69b2 100644 --- a/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 +++ b/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 @@ -1729,6 +1729,69 @@ try } } + Context 'When a scheduled task is created using a Built In 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() + BuiltInAccount = 'NETWORK SERVICE' + ExecuteAsCredential = [pscredential]::new('DEMO\WrongUser', (ConvertTo-SecureString 'ExamplePassword' -AsPlainText -Force)) + Verbose = $true + } + + 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']) + } + } + + $testParameters.Add('LogonType', 'Password') + + It 'Should overwrite LogonType to "ServiceAccount"' { + Set-TargetResource @testParameters + Assert-MockCalled -CommandName Register-ScheduledTask -Times 1 -Scope It -ParameterFilter { + $LogonType -ieq 'ServiceAccount' + } + } + + 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.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' From e77014b7152ee917259849327aa7bd6f64b492be Mon Sep 17 00:00:00 2001 From: "AEMO\\DWork" Date: Fri, 12 Oct 2018 08:56:48 +1000 Subject: [PATCH 5/5] MSFT_ScheduledTask: Unit Test BuiltInUser \ LogonType --- .../DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 | 4 +++- Tests/Unit/MSFT_ScheduledTask.Tests.ps1 | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 index 31d86da1..71c059c2 100644 --- a/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 +++ b/Modules/ComputerManagementDsc/DSCResources/MSFT_ScheduledTask/MSFT_ScheduledTask.psm1 @@ -1875,10 +1875,12 @@ function Test-TargetResource if ($PSBoundParameters.ContainsKey('BuiltInAccount')) { - $PSBoundParameters.User = $BuiltInAccount $currentValues.User = $BuiltInAccount + $PSBoundParameters.ExecuteAsCredential = $BuiltInAccount + $currentValues.ExecuteAsCredential = $BuiltInAccount + $PSBoundParameters['LogonType'] = 'ServiceAccount' $currentValues['LogonType'] = 'ServiceAccount' } diff --git a/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 b/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 index 0afd69b2..7bdc3d75 100644 --- a/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 +++ b/Tests/Unit/MSFT_ScheduledTask.Tests.ps1 @@ -1755,7 +1755,7 @@ try It 'Should overwrite LogonType to "ServiceAccount"' { Set-TargetResource @testParameters Assert-MockCalled -CommandName Register-ScheduledTask -Times 1 -Scope It -ParameterFilter { - $LogonType -ieq 'ServiceAccount' + $Inputobject.Principal.LogonType -ieq 'ServiceAccount' } }