-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #174 from danielboth/ScheduledTask-gMSA-support
ScheduledTask: Add support for Group Managed Service Accounts
- Loading branch information
Showing
6 changed files
with
242 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -73,7 +73,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 | ||
|
@@ -267,6 +272,10 @@ function Get-TargetResource | |
[System.Management.Automation.PSCredential] | ||
$ExecuteAsCredential, | ||
|
||
[Parameter()] | ||
[System.String] | ||
$ExecuteAsGMSA, | ||
|
||
[Parameter()] | ||
[System.UInt32] | ||
$DaysInterval = 1, | ||
|
@@ -503,6 +512,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 | ||
|
@@ -579,7 +589,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 | ||
|
@@ -755,6 +769,10 @@ function Set-TargetResource | |
[System.Management.Automation.PSCredential] | ||
$ExecuteAsCredential, | ||
|
||
[Parameter()] | ||
[System.String] | ||
$ExecuteAsGMSA, | ||
|
||
[Parameter()] | ||
[System.UInt32] | ||
$DaysInterval = 1, | ||
|
@@ -951,6 +969,13 @@ function Set-TargetResource | |
-ArgumentName EventSubscription | ||
} | ||
|
||
if ($ExecuteAsCredential -and $ExecuteAsGMSA) | ||
{ | ||
New-InvalidArgumentException ` | ||
-Message ($script:localizedData.gMSAandCredentialError) ` | ||
-ArgumentName ExecuteAsGMSA | ||
} | ||
|
||
# Configure the action | ||
$actionParameters = @{ | ||
Execute = $ActionExecutable | ||
|
@@ -1193,7 +1218,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) | ||
|
@@ -1343,7 +1373,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 | ||
|
@@ -1520,6 +1554,10 @@ function Test-TargetResource | |
[System.Management.Automation.PSCredential] | ||
$ExecuteAsCredential, | ||
|
||
[Parameter()] | ||
[System.String] | ||
$ExecuteAsGMSA, | ||
|
||
[Parameter()] | ||
[System.UInt32] | ||
$DaysInterval = 1, | ||
|
@@ -1729,6 +1767,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 ([email protected]) is used. | ||
#> | ||
|
||
$PSBoundParameters['ExecuteAsGMSA'] = $PSBoundParameters.ExecuteAsGMSA -replace '^.+\\|@.+', $null | ||
} | ||
|
||
$desiredValues = $PSBoundParameters | ||
$desiredValues.TaskPath = $TaskPath | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
...Examples/Resources/ScheduledTask/14-RunPowerShellTaskOnceAsGroupManagedServiceAccount.ps1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 [email protected] (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 | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,144 @@ 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 = '[email protected]' | ||
|
||
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' | ||
} | ||
} | ||
} | ||
} | ||
} | ||
#endregion | ||
|