Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed

- xService
- Fixed user type returned by Get-TargetResource [Issue #759](https://github.com/dsccommunity/xPSDesiredStateConfiguration/issues/759)


## [9.2.1] - 2024-11-11

### Fixed
Expand Down
73 changes: 52 additions & 21 deletions source/DSCResources/DSC_xServiceResource/DSC_xServiceResource.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,11 @@ $script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US'
cmdlet.

.NOTES
BuiltInAccount, Credential and GroupManagedServiceAccount parameters output the user used
to run the service to the BuiltinAccount property, Evaluating if the account is a gMSA would
mean doing a call to active directory to verify, as the property returned by the ciminstance
is just a string. In a production scenario that would mean that every xService test will check
with AD every 15 minutes if the account is a gMSA. That's not desireable, so we output Credential
and GroupManagedServiceAccount without evaluating what kind of user is supplied.
Evaluating if the account is a gMSA would mean doing a call to active directory to verify,
as the property returned by the ciminstance is just a string.
In a production scenario that would mean that every xService test will check
with AD every 15 minutes if the account is a gMSA. That's not desireable, so we evaluate
what kind of user is supplied only based on the format of the string.
#>
function Get-TargetResource
{
Expand Down Expand Up @@ -73,24 +72,47 @@ function Get-TargetResource

$startupType = ConvertTo-StartupTypeString -StartMode $serviceCimInstance.StartMode

$builtInAccount = switch ($serviceCimInstance.StartName)
$serviceAccount = switch ($serviceCimInstance.StartName)
{
'NT Authority\NetworkService' { 'NetworkService'; break }
'NT Authority\LocalService' { 'LocalService'; break }
default { $serviceCimInstance.StartName }
}

# Initialize variables
$builtInAccount = $null
$GroupManagedServiceAccount = $null
$Credential = $null

# Evaluate user type only based on the format of the string (as stated in the note)
if (($serviceAccount -eq 'LocalSystem') -or ($serviceAccount -eq 'LocalService') -or ($serviceAccount -eq 'NetworkService'))
{
$builtInAccount = $serviceAccount
}
elseif ($serviceAccount -match '^[\w-]+\\[\w-]+\$$')
{
$GroupManagedServiceAccount = $serviceAccount
}
else
{
# Password cannot be null
$Password = ConvertTo-SecureString 'DummyPassword' -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential($serviceAccount, $Password)
}

$serviceResource = @{
Name = $Name
Ensure = 'Present'
Path = $serviceCimInstance.PathName
StartupType = $startupType
BuiltInAccount = $builtInAccount
State = $service.Status.ToString()
DisplayName = $service.DisplayName
Description = $serviceCimInstance.Description
DesktopInteract = $serviceCimInstance.DesktopInteract
Dependencies = $dependencies
Name = $Name
Ensure = 'Present'
Path = $serviceCimInstance.PathName
StartupType = $startupType
BuiltInAccount = $builtInAccount
GroupManagedServiceAccount = $GroupManagedServiceAccount
Credential = $Credential
State = $service.Status.ToString()
DisplayName = $service.DisplayName
Description = $serviceCimInstance.Description
DesktopInteract = $serviceCimInstance.DesktopInteract
Dependencies = $dependencies
}
}
else
Expand Down Expand Up @@ -600,20 +622,29 @@ function Test-TargetResource
{
$expectedStartName = ConvertTo-StartName -Username $GroupManagedServiceAccount

if ($serviceResource.BuiltInAccount -ine $expectedStartName)
if ($serviceResource.GroupManagedServiceAccount -ine $expectedStartName)
{
Write-Verbose -Message ($script:localizedData.GroupManagedServiceCredentialDoesNotMatch -f $Name, $GroupManagedServiceAccount, $serviceResource.BuiltInAccount)
Write-Verbose -Message ($script:localizedData.GroupManagedServiceCredentialDoesNotMatch -f $Name, $GroupManagedServiceAccount, $serviceResource.GroupManagedServiceAccount)
return $false
}
}
elseif ($PSBoundParameters.ContainsKey('Credential'))
{
$expectedStartName = ConvertTo-StartName -Username $Credential.UserName

if ($serviceResource.BuiltInAccount -ine $expectedStartName)
# Check that credential resource exist
if ($null -eq $serviceResource.Credential)
{
Write-Verbose -Message ($script:localizedData.ServiceCredentialDoesNotMatch -f $Name, $Credential.UserName, $serviceResource.BuiltInAccount)
Write-Verbose -Message ($script:localizedData.ServiceCredentialIsEmpty -f $Name, $Credential.UserName)
return $false
} else
{
# Check that the username matches the expected start name
if ($serviceResource.Credential.UserName -ine $expectedStartName)
{
Write-Verbose -Message ($script:localizedData.ServiceCredentialDoesNotMatch -f $Name, $Credential.UserName, $serviceResource.Credential.UserName)
return $false
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ ConvertFrom-StringData @'
ServiceStartupTypeMatches = The start mode of service {0} matches the expected start mode.
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}.
ServiceCredentialIsEmpty = 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 empty.
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.
Expand Down
18 changes: 9 additions & 9 deletions tests/Unit/DSC_xServiceResource.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ try
Ensure = 'Present'
Path = $testServiceCimInstance.PathName
StartupType = $convertToStartupTypeStringResult
BuiltInAccount = $testServiceCimInstance.StartName
Credential = New-Object PSCredential $testServiceCimInstance.StartName, (ConvertTo-SecureString 'DummyPassword' -AsPlainText -Force)
State = $testService.Status
DisplayName = $testService.DisplayName
Description = $testServiceCimInstance.Description
Expand Down Expand Up @@ -1446,7 +1446,7 @@ try
Name = $script:testServiceName
Ensure = 'Present'
State = 'Running'
BuiltInAccount = $script:testCredential1.UserName
Credential = $script:testCredential1
DisplayName = 'TestDisplayName'
Description = 'Test service description'
Dependencies = @( 'TestServiceDependency1', 'TestServiceDependency2' )
Expand Down Expand Up @@ -1678,13 +1678,13 @@ try
}

$serviceResourceWithGroupManagedServiceAccount = @{
Name = $script:testServiceName
Ensure = 'Present'
State = 'Running'
BuiltInAccount = $script:gMSAUser1
DisplayName = 'TestDisplayName'
Description = 'Test service description'
Dependencies = @( 'TestServiceDependency1', 'TestServiceDependency2' )
Name = $script:testServiceName
Ensure = 'Present'
State = 'Running'
GroupManagedServiceAccount = $script:gMSAUser1
DisplayName = 'TestDisplayName'
Description = 'Test service description'
Dependencies = @( 'TestServiceDependency1', 'TestServiceDependency2' )
}

Mock -CommandName 'Get-TargetResource' -MockWith { return $serviceResourceWithGroupManagedServiceAccount }
Expand Down