Rich Hopkins
Rich Hopkins

Reputation: 23

PowerShell DSC: Error 22 using certs to encrypt creds in a Service Resource

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\DEVOPS.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 "[email protected]"

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,'[email protected]','password',$null,$null,$null)
}

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

Ideas?

Upvotes: 0

Views: 694

Answers (1)

Ray Hayes
Ray Hayes

Reputation: 11

When you use both certificate files and thumbprint (certificateid) the encryption will happen with the certificate file but only the thumbprint (certificateid) you entered gets written to the .mof file. They can easily get out of sync. As a test try adding the certificates to the local machine store and then remove the reference to the certificate files from the script. Regenerate and fix any issues if a certificate can't be found. See if that fixes the issue.

Upvotes: 1

Related Questions