Certs & Creds & Gnashing of Teeth

This topic contains 6 replies, has 2 voices, and was last updated by Profile photo of Rich Hopkins Rich Hopkins 1 year, 8 months ago.

  • Author
    Posts
  • #23523
    Profile photo of Rich Hopkins
    Rich Hopkins
    Participant

    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%28v=vs.85%29.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. 🙂

  • #23524
    Profile photo of Don Jones
    Don Jones
    Keymaster

    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!

  • #23532
    Profile photo of Rich Hopkins
    Rich Hopkins
    Participant

    *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.

  • #23538
    Profile photo of Don Jones
    Don Jones
    Keymaster

    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.

  • #23539
    Profile photo of Rich Hopkins
    Rich Hopkins
    Participant

    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.

  • #23540
    Profile photo of Don Jones
    Don Jones
    Keymaster

    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.

  • #23542
    Profile photo of Rich Hopkins
    Rich Hopkins
    Participant

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

You must be logged in to reply to this topic.