Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xService: Adding support for Group Managed Service Account #441

Merged
merged 6 commits into from
Sep 10, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 86 additions & 24 deletions DSCResources/MSFT_xServiceResource/MSFT_xServiceResource.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ function Get-TargetResource
'NT Authority\LocalService' { 'LocalService'; break }
default { $serviceCimInstance.StartName }
}

$serviceResource = @{
Name = $Name
Ensure = 'Present'
Expand All @@ -71,7 +71,7 @@ function Get-TargetResource
DisplayName = $service.DisplayName
Description = $serviceCimInstance.Description
DesktopInteract = $serviceCimInstance.DesktopInteract
Dependencies = $dependencies
Dependencies = $dependencies
}
}
else
Expand Down Expand Up @@ -99,7 +99,7 @@ function Get-TargetResource

.PARAMETER Ensure
Specifies whether the service should exist or not.

Set this property to Present to create or modify a service.
Set this property to Absent to delete a service.

Expand All @@ -118,11 +118,17 @@ function Get-TargetResource
.PARAMETER BuiltInAccount
The built-in account the service should start under.

Cannot be specified at the same time as Credential.
Cannot be specified at the same time as Credential or GroupManagedServiceAccount.

The user account specified by this property must have access to the service executable path
defined by Path in order to start the service.

.PARAMETER GroupManagedServiceAccount
The Group Managed Service Account the service should start under. The GMSA
must be provided in DOMAIN\gMSA$ format or UPN format gMSA$@domain.fqdn.

Cannot be specified at the same time as BuilInAccount or Credential.

.PARAMETER DesktopInteract
Indicates whether or not the service should be able to communicate with a window on the
desktop.
Expand Down Expand Up @@ -198,6 +204,9 @@ function Set-TargetResource
[String]
$BuiltInAccount,

[String]
$GroupManagedServiceAccount,

[ValidateSet('Running', 'Stopped', 'Ignore')]
[String]
$State = 'Running',
Expand Down Expand Up @@ -234,10 +243,13 @@ function Set-TargetResource
Assert-NoStartupTypeStateConflict -ServiceName $Name -StartupType $StartupType -State $State
}

if ($PSBoundParameters.ContainsKey('BuiltInAccount') -and $PSBoundParameters.ContainsKey('Credential'))
if (($PSBoundParameters.ContainsKey('BuiltInAccount') -and $PSBoundParameters.ContainsKey('Credential')) -or
($PSBoundParameters.ContainsKey('BuiltInAccount') -and $PSBoundParameters.ContainsKey('GroupManagedServiceAccount')) -or
($PSBoundParameters.ContainsKey('GroupManagedServiceAccount') -and $PSBoundParameters.ContainsKey('Credential'))
)
{
$errorMessage = $script:localizedData.BuiltInAccountAndCredentialSpecified -f $Name
New-InvalidArgumentException -ArgumentName 'BuiltInAccount & Credential' -Message $errorMessage
$errorMessage = $script:localizedData.CredentialParametersAreMutallyExclusive -f $Name
New-InvalidArgumentException -ArgumentName 'BuiltInAccount / Credential / GroupManagedServiceAccount' -Message $errorMessage
}

$service = Get-Service -Name $Name -ErrorAction 'SilentlyContinue'
Expand Down Expand Up @@ -285,7 +297,7 @@ function Set-TargetResource
# Update the properties of the service if needed
$setServicePropertyParameters = @{}

$servicePropertyParameterNames = @( 'StartupType', 'BuiltInAccount', 'Credential', 'DesktopInteract', 'DisplayName', 'Description', 'Dependencies' )
$servicePropertyParameterNames = @( 'StartupType', 'BuiltInAccount', 'Credential', 'GroupManagedServiceAccount', 'DesktopInteract', 'DisplayName', 'Description', 'Dependencies' )

foreach ($servicePropertyParameterName in $servicePropertyParameterNames)
{
Expand All @@ -294,7 +306,7 @@ function Set-TargetResource
$setServicePropertyParameters[$servicePropertyParameterName] = $PSBoundParameters[$servicePropertyParameterName]
}
}

if ($setServicePropertyParameters.Count -gt 0)
{
Write-Verbose -Message ($script:localizedData.EditingServiceProperties -f $Name)
Expand Down Expand Up @@ -325,14 +337,14 @@ function Set-TargetResource

.PARAMETER Name
The name of the service to test.

This may be different from the service's display name.
To retrieve a list of all services with their names and current states, use the Get-Service
cmdlet.

.PARAMETER Ensure
Specifies whether the service should exist or not.

Set this property to Present to test if a service exists.
Set this property to Absent to test if a service does not exist.

Expand All @@ -345,9 +357,15 @@ function Set-TargetResource
The startup type the service should have.

.PARAMETER BuiltInAccount
The account the service should be starting under.
The built-in account the service should start under.

Cannot be specified at the same time as Credential or GroupManagedServiceAccount.

Cannot be specified at the same time as Credential.
.PARAMETER GroupManagedServiceAccount
The Group Managed Service Account the service should start under. The GMSA
must be provided in DOMAIN\gMSA$ format or UPN format gMSA$@domain.fqdn.

Cannot be specified at the same time as BuilInAccount or Credential.

.PARAMETER DesktopInteract
Indicates whether or not the service should be able to communicate with a window on the
Expand Down Expand Up @@ -392,7 +410,7 @@ function Test-TargetResource
[ValidateNotNullOrEmpty()]
[String]
$Name,

[ValidateSet('Present', 'Absent')]
[String]
$Ensure = 'Present',
Expand All @@ -409,6 +427,9 @@ function Test-TargetResource
[String]
$BuiltInAccount,

[String]
$GroupManagedServiceAccount,

[Boolean]
$DesktopInteract = $false,

Expand Down Expand Up @@ -445,10 +466,13 @@ function Test-TargetResource
Assert-NoStartupTypeStateConflict -ServiceName $Name -StartupType $StartupType -State $State
}

if ($PSBoundParameters.ContainsKey('BuiltInAccount') -and $PSBoundParameters.ContainsKey('Credential'))
if (($PSBoundParameters.ContainsKey('BuiltInAccount') -and $PSBoundParameters.ContainsKey('Credential')) -or
($PSBoundParameters.ContainsKey('BuiltInAccount') -and $PSBoundParameters.ContainsKey('GroupManagedServiceAccount')) -or
($PSBoundParameters.ContainsKey('GroupManagedServiceAccount') -and $PSBoundParameters.ContainsKey('Credential'))
)
{
$errorMessage = $script:localizedData.BuiltInAccountAndCredentialSpecified -f $Name
New-InvalidArgumentException -ArgumentName 'BuiltInAccount & Credential' -Message $errorMessage
$errorMessage = $script:localizedData.CredentialParametersAreMutallyExclusive -f $Name
New-InvalidArgumentException -ArgumentName 'BuiltInAccount / Credential / GroupManagedServiceAccount' -Message $errorMessage
}

$serviceResource = Get-TargetResource -Name $Name
Expand Down Expand Up @@ -521,7 +545,7 @@ function Test-TargetResource
return $false
}
}

# Check the service desktop interation setting
if ($PSBoundParameters.ContainsKey('DesktopInteract') -and $serviceResource.DesktopInteract -ine $DesktopInteract)
{
Expand All @@ -535,6 +559,16 @@ function Test-TargetResource
Write-Verbose -Message ($script:localizedData.ServicePropertyDoesNotMatch -f 'BuiltInAccount', $Name, $BuiltInAccount, $serviceResource.BuiltInAccount)
return $false
}
elseif ($PSBoundParameters.ContainsKey('GroupManagedServiceAccount'))
{
$expectedStartName = ConvertTo-StartName -Username $GroupManagedServiceAccount

if ($serviceResource.BuiltInAccount -ine $expectedStartName)
{
Write-Verbose -Message ($script:localizedData.GroupManagedServiceCredentialDoesNotMatch -f $Name, $GroupManagedServiceAccount, $serviceResource.BuiltInAccount)
return $false
}
}
elseif ($PSBoundParameters.ContainsKey('Credential'))
{
$expectedStartName = ConvertTo-StartName -Username $Credential.UserName
Expand Down Expand Up @@ -627,7 +661,7 @@ function ConvertTo-StartupTypeString
.PARAMETER State
The service state to check.
#>
function Assert-NoStartupTypeStateConflict
function Assert-NoStartupTypeStateConflict
{
[CmdletBinding()]
param
Expand Down Expand Up @@ -794,7 +828,7 @@ function Set-ServicePath
{
$serviceChangePropertyString = $changeServiceArguments.Keys -join ', '
$errorMessage = $script:localizedData.InvokeCimMethodFailed -f 'Change', $ServiceName, $serviceChangePropertyString, $changeServiceResult.ReturnValue
New-InvalidArgumentException -ArgumentName 'Path' -Message $errorMessage
New-InvalidArgumentException -ArgumentName 'Path' -Message $errorMessage
}

return $true
Expand Down Expand Up @@ -1323,6 +1357,10 @@ function Set-ServiceAccountProperty
[ValidateSet('LocalSystem', 'LocalService', 'NetworkService')]
$BuiltInAccount,

[Parameter()]
[String]
$GroupManagedServiceAccount,

[Parameter()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
Expand All @@ -1347,6 +1385,17 @@ function Set-ServiceAccountProperty
$changeServiceArguments['StartPassword'] = ''
}
}
elseif ($PSBoundParameters.ContainsKey('GroupManagedServiceAccount'))
{
$startName = ConvertTo-StartName -Username $GroupManagedServiceAccount

if ($serviceCimInstance.StartName -ine $startName)
{
Grant-LogOnAsServiceRight -Username $startName

$changeServiceArguments['StartName'] = $startName
}
}
elseif ($PSBoundParameters.ContainsKey('Credential'))
{
$startName = ConvertTo-StartName -Username $Credential.UserName
Expand Down Expand Up @@ -1459,12 +1508,17 @@ function Set-ServiceStartupType
.PARAMETER BuiltInAccount
The built-in account the service should start under.

Cannot be specified at the same time as Credential.
Cannot be specified at the same time as Credential or GroupManagedServiceAccount.

.PARAMETER GroupManagedServiceAccount
The Group Managed Service Account that is used to run the service.

Cannot be specified at the same time as BuiltInAccount or Credential.

.PARAMETER Credential
The credential of the user account the service should start under.

Cannot be specified at the same time as BuiltInAccount.
Cannot be specified at the same time as BuiltInAccount or GroupManagedServiceAccount.
The user specified by this credential will automatically be granted the Log on as a Service
right.

Expand Down Expand Up @@ -1502,6 +1556,10 @@ function Set-ServiceProperty
[String]
$BuiltInAccount,

[Parameter()]
[String]
$GroupManagedServiceAccount,

[Parameter()]
[Boolean]
$DesktopInteract,
Expand Down Expand Up @@ -1530,7 +1588,7 @@ function Set-ServiceProperty

# Update display name and/or description if needed
$serviceCimInstance = Get-ServiceCimInstance -ServiceName $ServiceName

$setServiceParameters = @{}

if ($PSBoundParameters.ContainsKey('DisplayName') -and $serviceCimInstance.DisplayName -ine $DisplayName)
Expand Down Expand Up @@ -1561,6 +1619,10 @@ function Set-ServiceProperty
{
$setServiceAccountPropertyParameters['BuiltInAccount'] = $BuiltInAccount
}
elseif ($PSBoundParameters.ContainsKey('GroupManagedServiceAccount'))
{
$setServiceAccountPropertyParameters['GroupManagedServiceAccount'] = $GroupManagedServiceAccount
}
elseif ($PSBoundParameters.ContainsKey('Credential'))
{
$setServiceAccountPropertyParameters['Credential'] = $Credential
Expand Down Expand Up @@ -1665,7 +1727,7 @@ function Remove-ServiceWithTimeout
.SYNOPSIS
Waits for the service with the given name to reach the given state within the given time
span.

This is a wrapper function for unit testing.

.PARAMETER ServiceName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class MSFT_xServiceResource : OMI_BaseResource
[Write,Description("The path to the service executable file.")] String Path;
[Write,Description("Indicates the startup type for the service."),ValueMap{"Automatic", "Manual", "Disabled"},Values{"Automatic", "Manual", "Disabled"}] String StartupType;
[Write,Description("Indicates the sign-in account to use for the service."),ValueMap{"LocalSystem", "LocalService", "NetworkService"},Values{"LocalSystem", "LocalService", "NetworkService"}] String BuiltInAccount;
[Write,Description("The Group Managed Service Account to run the service under.")] String GroupManagedServiceAccount;
[Write,Description("The credential to run the service under."),EmbeddedInstance("MSFT_Credential")] String Credential;
[Write,Description("The service can create or communicate with a window on the desktop. Must be false for services not running as LocalSystem. Defaults to False.")] Boolean DesktopInteract;
[Write,Description("Indicates the state you want to ensure for the service. Defaults to Running."),ValueMap{"Running", "Stopped", "Ignore"},Values{"Running", "Stopped", "Ignore"}] String State;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class MSFT_xServiceResource : OMI_BaseResource
[Description("An enumerated value that describes if the service is expected to be running on the machine.\nRunning {default} \nStopped \n") : Amended] string State;
[Description("An enumerated value that describes the service start type.\nAutomatic \nManual \nDisabled \n") : Amended] string StartupType;
[Description("An enumerated value that describes the built in account the service runs under.\nLocalSystem \nLocalService \nNetworkService \n") : Amended] string BuiltInAccount;
[Description("The optional GroupManagedServiceAccount the service runs under, GroupManagedServiceAccount, BuiltInAccount and Credential are mutually exclusive") : Amended] string GroupManagedServiceAccount;
[Description("The optional credentials the service runs under") : Amended] string Credential;
[Description("The service status") : Amended] string Status;
[Description("The service display name") : Amended] string DisplayName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
ConvertFrom-StringData @'
ServiceExists = Service {0} exists.
ServiceDoesNotExist = Service {0} does not exist.
BuiltInAccountAndCredentialSpecified = Both BuiltInAccount and Credential cannot be specified. Please remove one for service {0}.
CredentialParametersAreMutallyExclusive = BuiltInAccount, Credential and GroupManagedServiceAccount are mutually exclusive. Please specify only one of these parameters for service {0}.
ServiceAlreadyAbsent = Service {0} is already absent. No change required.
ServiceDoesNotExistPathMissingError = The service '{0}' does not exist, but Path was not specified. Please specify the path to the executable the service should run to create a new service.
CreatingService = Creating new service {0}...
Expand All @@ -21,6 +21,7 @@ ConvertFrom-StringData @'
ServiceStartupTypeDoesNotMatch = The start mode of service {0} does not match the expected start mode.
ServicePropertyDoesNotMatch = The service property {0} of service {1} does not match the expected value. The expected value is {2}. The actual value is {3}.
ServiceCredentialDoesNotMatch = The start name of service {0} does not match the expected username from the given credential. The expected value is {1}. The actual value is {2}.
GroupManagedServiceCredentialDoesNotMatch = The start name of service {0} does not match the expected username from the given Group Managed Service Account. The expected value is {1}. The actual value is {2}.
ServiceDeletionSucceeded = The service {0} has been successfully deleted.
ServiceDeletionFailed = Failed to delete service {0}.
WaitingForServiceDeletion = Waiting for service {0} to be deleted.
Expand Down
Loading