diff --git a/CHANGELOG.md b/CHANGELOG.md index e270c642..8778fc12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/source/DSCResources/DSC_xServiceResource/DSC_xServiceResource.psm1 b/source/DSCResources/DSC_xServiceResource/DSC_xServiceResource.psm1 index c3c58082..e9142e9c 100644 --- a/source/DSCResources/DSC_xServiceResource/DSC_xServiceResource.psm1 +++ b/source/DSCResources/DSC_xServiceResource/DSC_xServiceResource.psm1 @@ -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 { @@ -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 @@ -600,9 +622,9 @@ 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 } } @@ -610,10 +632,19 @@ function Test-TargetResource { $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 + } } } diff --git a/source/DSCResources/DSC_xServiceResource/en-US/DSC_xServiceResource.strings.psd1 b/source/DSCResources/DSC_xServiceResource/en-US/DSC_xServiceResource.strings.psd1 index 6bfb21e8..52b5a1e7 100644 --- a/source/DSCResources/DSC_xServiceResource/en-US/DSC_xServiceResource.strings.psd1 +++ b/source/DSCResources/DSC_xServiceResource/en-US/DSC_xServiceResource.strings.psd1 @@ -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. diff --git a/tests/Unit/DSC_xServiceResource.Tests.ps1 b/tests/Unit/DSC_xServiceResource.Tests.ps1 index da6a5642..1769b43b 100644 --- a/tests/Unit/DSC_xServiceResource.Tests.ps1 +++ b/tests/Unit/DSC_xServiceResource.Tests.ps1 @@ -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 @@ -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' ) @@ -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 }