Certs & Creds & Gnashing of Teeth

I’m trying to use certificates to embed credentials into a Service resource. I’ve got PKI in the infrastructure and all my test servers are auto-enrolled. I exported their certs locally to work with and have them in my ConfigData as follows:

@{
	AllNodes = @(

	@{
		NodeName = "*"
		NeoConfigDestinationPath = "D:\ServerBox\Servers\JRun4\_build\shared\config"
	}

	@{
		NodeName = 'DEVOPS'
		Role = @('DSCPullServer')
		CertificateFile = "D:\EQ_DSCModule\Certs\DevOps1.cer"
		Thumbprint = "AE4F10AE4141C8726EEEBE888C69FE7ABB3099A8"
	}
	
	@{
		NodeName = 'Server1'
		Role = @('IIS', 'ServerBox', 'DevInt')
		CFServices = @("Adobe CF9 1", "Adobe CF9 2", "Adobe CF9 3", "Adobe CF9 4")
		CertificateFile = "D:\EQ_DSCModule\Certs\Client1.cer"
		Thumbprint = "4FA343A76AEA2B805850190E9C04AA9E2A82A162"
	}

	@{
		NodeName = 'Server2'
		Role = @('IIS', 'ServerBox', 'DevInt')
		CFServices = @("Adobe CF9 1")
		CertificateFile = "D:\EQ_DSCModule\Certs\Client2.cer"
		Thumbprint = "0FCB76684F0C74495DEB54F637B50BDA7182483D"
	}

	)
	
	ServerBoxConfig = @{
		SourcePath = "\\Share\Path\DevOps\ServerBox"
		DestinationPath = "D:\ServerBox"
	}
	
	DevIntConfig = @{
		SourcePath = "\\Share\Path\DevOps\DevInt"
		DestinationPath = "D:\ServerBox\IIS\wwwroot"
		NeoConfigSourcePath = "\\Share\Path\DevOps\ServerConfig\Environments\DevInt\NeoConfig"
	}
}

This this is the config script that I’m running:

$webCFDevCred = Get-Credential -Credential "svc-webcfdev@domain.com"

Configuration EqConfig
{
	
	Import-DSCResource -Module xPSDesiredStateConfiguration
	Import-DSCResource -Module cChoco

    Node $AllNodes.NodeName {

		cChocoInstaller installChoco {
			InstallDir = "C:\ProgramData\Chocolatey"
		}

    }
	
	Node $AllNodes.Where({ $_.role -eq 'DSCPullServer' }).NodeName { ... } #DSCPullServer
	
	Node $AllNodes.Where({ $_.role -eq 'IIS' }).NodeName { ... } #IIS
	
	Node $AllNodes.Where({ $_.role -eq 'ServerBox' }).NodeName {
		
		File ServerBox
		{
			Ensure = "Present"
			Type = "Directory"
			Recurse = $true
			MatchSource = $true
			Force = $true
			Checksum = "modifiedDate"
			SourcePath = $ConfigurationData.ServerBoxConfig.SourcePath
			DestinationPath = $ConfigurationData.ServerBoxConfig.DestinationPath
		}
		
	} #ServerBox
	
	Node $AllNodes.Where({ $_.role -eq 'DevInt' }).NodeName {
		
		File DevInt
		{
			Ensure = "Present"
			Type = "Directory"
			Recurse = $true
			MatchSource = $true
			Force = $true
			Checksum = "modifiedDate"
			SourcePath = $ConfigurationData.DevIntConfig.SourcePath
			DestinationPath = $ConfigurationData.DevIntConfig.DestinationPath
			DependsOn = "[File]ServerBox"
		}
		
		File DevInt_Config
		{
			Ensure = "Present"
			Type = "Directory"
			MatchSource = $true
			Force = $true
			Checksum = "modifiedDate"
			SourcePath = $ConfigurationData.DevIntConfig.NeoConfigSourcePath
			DestinationPath = $Node.NeoConfigDestinationPath
			DependsOn = "[File]ServerBox"
		}
		
        #This runs a script to build out the ColdFusion cluster/servers
        #Uses the number of services as the param for serverCount
		cChocoPackageInstaller installServerBox {
			Name = "ServerBox.DevInt -params $($Node.CFServices.Length)"
			DependsOn = @("[cChocoInstaller]installChoco", "[File]DevInt_Config")
		}
		
        #Sets the services generated by the ServerBox script
		$Node.CFServices.ForEach({
			Service "Service-$_" {
				Name = $_
				State = 'Running'
				Credential = $webCFDevCred
				DependsOn = "[cChocoPackageInstaller]installServerBox"
			}
		})
		
	} #DevInt
	
} #Configuration

EqConfig -ConfigurationData .\EQConfigData.psd1 -Output .\EqConfig -Verbose

Function Get-ComputerGuid
{
	param (
		[Parameter(Mandatory = $true)]
		[string]$ComputerName
	)
	process
	{
		([guid]([adsisearcher]"(samaccountname=$ComputerName`$)").FindOne().Properties["objectguid"][0]).Guid
	}
}

$DSCPullFolder = "C:\Program Files\WindowsPowerShell\DscService\Configuration"

Get-ChildItem .\EqConfig\* -Filter *.mof | ForEach-Object {
	$guidMofFile = "$DSCPullFolder\$(Get-ComputerGuid $_.BaseName).mof"
	$newMof = copy $_.FullName $guidMofFile -PassThru -Force
	$newHash = (Get-FileHash $newMof).hash
	[System.IO.File]::WriteAllText("$newMof.checksum", $newHash)
}

Configuration EqLocalConfig
{
	Node $AllNodes.NodeName {
		LocalConfigurationManager {
			AllowModuleOverwrite = 'True'
			CertificateID = $Node.Thumbprint
			ConfigurationID = $(Get-ComputerGuid $nodeName)
			ConfigurationModeFrequencyMins = 15
			ConfigurationMode = 'ApplyAndAutoCorrect'
			RebootNodeIfNeeded = 'True'
			RefreshMode = 'PULL'
			DownloadManagerName = 'WebDownloadManager'
			DownloadManagerCustomData = (@{ ServerUrl = "https://DEVOPS:443/psdscpullserver.svc" })
		}
	}
}

EqLocalConfig -ConfigurationData .\EQConfigData.psd1 -Verbose

Set-DscLocalConfigurationManager -Path .\EqLocalConfig -Verbose

As far as I can tell it should work. My MOFs get generated with encrypted passwords inside, but when the client servers pick up the config and get to the Service step, it errors out. Checking the event viewer this is the details on the event:

“This event indicates that failure happens when LCM is processing the configuration. ErrorId is 0x1. ErrorDetail is The SendConfigurationApply function did not succeed… ResourceId is [Service]Service-Adobe CF9 1 and SourceInfo is D:\EQ_DSCModule\EqConfig.ps1::285::4::Service. ErrorMessage is PowerShell provider MSFT_ServiceResource failed to execute Set-TargetResource functionality with error message: Failed to change ‘Credential’ property. Message: ‘The ‘Change’ method of ‘Win32_Service’ failed with error code: ‘22’.’ .”

According to MSDN (https://msdn.microsoft.com/en-us/library/aa384901(v=vs.85).aspx) error code 22 on the Change method means “The account under which this service runs is either invalid or lacks the permissions to run the service.” I know the service account works fine and I can add it myself using WMI as follows:

For ($i=0; $i -lt $clusterCount; $i++) {
	(Get-WmiObject -Query "SELECT * FROM Win32_Service WHERE Name = 'Adobe CF9 $($i+1)'").Change($null,$null,$null,$null,$null,$null,'svc-webcfdev@domain.com','password',$null,$null,$null)
}

So if I can add the account using WMI, DSC should be able to as well, right? Ugh!

Ideas?

edit: Note to moderators… you guys really need a preview post option. :slight_smile:

Well… at the risk of being annoying, I think it’s worth focusing on the error you’re getting, along with any others you’ve picked up by enabling Diagnostics on the target node.

Can you trim this down to a configuration that does nothing but set this service account, and which does so using clear-text passwords rather than encrypted ones, as a test case? That’ll tell us if DSC can or cannot make the core configuration change. Then we can start adding complexity like encrypted passwords, one at a time, until it breaks again. That’ll better identify where the actual problem lies. I might even taken an interim step of coding a Script resource that literally runs your Get-WmiObject command, again using a clear-text password as a test, to see if DSC is in fact able to run that same command that you know works.

Note from moderators: you code one, we’ll use it!

sigh Crap. Getting the same error with plaintext passwords. Even cracked open MSFT_ServiceResource.psm1 and followed the logic that the cred would go through and it gets to the same type of Change method call I’d use. No clue why mine works and DSC throws error 22 yet.

It might be something to do with the account the LCM runs under. That’s obviously different than what you’re using to run the command.

I thought LCM ran as System? I assumed that wouldn’t be the problem, but I’ll try running the WMI methods in a Scheduled Task that launches as System to verify.

The LCM’s scheduled task does, yes, but System is both all-powerful and powerless, depending on the specific situation. My point being, it’s one difference between YOU running the command and the LCM running the command, so it’s a troubleshooting step to eliminate that as a root cause. Running your own task the same way is a good check. Amping up the Diagnostics on the node would be another check, to see if anything more useful turns up in the log. You can also (it’s slightly tricky) manually load the xService resource module into the console and try manually calling the Set-TargetResource function. Doing that under YOUR credentials would be another check.

Injected my own cred into the LCM to run commands as me and still get the same thing… will test more next week.