I’ve been trying to add a credential set to a DSC process, and run into a roadblock. The DSC server is a Windows 2012R2 system in Pull mode, and configured using WMF5.1 :
Name Value
---- -----
PSVersion 5.1.14409.1005
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.14409.1005
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
configuration DSCPullServer
{
param
(
[string[]]$NodeName = 'localhost',
[ValidateNotNullOrEmpty()]
[string] $certificateThumbPrint
)
Import-DSCResource -ModuleName xPSDesiredStateConfiguration -ModuleVersion 3.10.0.0
Import-DscResource -ModuleName PSDesiredStateConfiguration
Import-DscResource -ModuleName xWebAdministration
$ensureState = "present"
$PullServerName = "FQDN of pull server"
$RegKey = "--guid for registration--"
Node $NodeName
{
# this all runs on top of IIS
WindowsFeature WebServer
{
Ensure = $ensureState
Name = "Web-WebServer"
}
# Next 2 items provide the IIS UI and depemd on having base IIS component installed
WindowsFeature WebManagement
{
Ensure = $ensureState
name = "Web-Mgmt-Tools"
DependsOn = "[WindowsFeature]WebServer"
}
WindowsFeature IISConsole
{
Ensure = $ensureState
name = "Web-Mgmt-Console"
DependsOn = "[WindowsFeature]WebServer"
}
# obviously the DSC Pull server needs the DSC-Service
WindowsFeature DSCServiceFeature
{
Ensure = $ensureState
Name = 'DSC-Service'
DependsOn = "[WindowsFeature]WebServer"
}
#NetHTTPActivation is requires in order to start a stopped website
WindowsFeature NETHTTPActivation
{
Ensure = $ensureState
Name = 'NET-HTTP-Activation'
DependsOn = "[WindowsFeature]WebServer"
}
# enable32bitAppOnWin64 is required otherwise the app pool crashes on access
xWebAppPool DefaultAppPool
{
Ensure = $ensureState
Name = 'PSWS'
State = 'Started'
enable32BitAppOnWin64 = $True
DependsOn = "[WindowsFeature]WebServer"
}
xDscWebService DSCPullSRV
{
Ensure = $ensureState
EndpointName = 'DSCPullSRV'
Port = 8080
PhysicalPath = "$env:SystemDrive\inetpub\DSCPullSRV"
CertificateThumbPrint = $certificateThumbPrint
ModulePath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules"
ConfigurationPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration"
RegistrationKeyPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService"
AcceptSelfSignedCertificates = $true
State = 'Started'
DependsOn = '[WindowsFeature]DSCServiceFeature'
#UseSecurityBestPractices = $false <-- no longer a valid member
}
xWebsite FileRepo
{
Ensure = $ensureState
Name = "Filerepo"
State = "Started"
PhysicalPath = "$env:SystemDrive\inetpub\wwwroot\filerepo"
BindingInfo =
@( MSFT_xWebBindingInformation
{
Protocol = "HTTP"
Port = 80
}
)
DependsOn = "[WindowsFeature]WebServer"
}
File RegistrationKeyFile
{
Ensure = $ensureState
Type = 'File'
DestinationPath = 'C:\Program Files\WindowsPowerShell\DscService\RegistrationKeys.txt'
Contents = $RegKey
}
}
}
DSCPullServer -certificateThumbPrint '--fully signed cert--'
Start-DscConfiguration -path .\DSCPullServer -verbose -wait -force
I have a PowerShell script that is triggered from TFS Release Management that creates the MOF files for machines based on release requirements. I wont bore you with all the details of the script, but each node does contain envrypted credentials, and the resulting Configuration references a Node block:
Node $AllNodes.Nodename
{
Write-Host "Adding Local Configuration Manager config to MOF"
#Configuration of Local Configuration Manager needed to use Encrypted Credentials to support accessing domain resources
LocalConfigurationManager
{
CertificateId = $node.Thumbprint
}
}
The resulting MOF files contain encrypted credential sets such as
instance of MSFT_Credential as $MSFT_Credential1ref
{
Password = "-----BEGIN CMS-----\nMIIBsgYJKoZIhvcNAQcDoIIBozCCAZ8CAQAxggFa................P2OR3QmDb69\n-----END CMS-----";
UserName = "---username---";
};
for each reference of the credential. All seems good on the server side.
On the Client side I have Windows 2008R2 systems, also using WMF5.1
Name Value
---- -----
PSVersion 5.1.14409.1005
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.14409.1005
CLRVersion 4.0.30319.18063
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
And the client is registered using this script
#Server to configure for
$hostname = $env:COMPUTERNAME + "." + $env:USERDNSDOMAIN
#pull server to configure against
$pullServer = "FQDN of pull server"
#Certificate CN that restricts to the certificate to be used for encryption
$encryCertCN = "CN=$($pullServer)"
#DSC Registration key - without this, the DSC server wont let you in
$regkey = "--reg key that matches pull server--"
# Get the certificate that works for encryption
function Get-LocalEncryptionCertificateThumbprint
{
(dir Cert:\LocalMachine\Root) | %{
# Verify the certificate is for Encryption and valid
If ($_.PrivateKey.KeyExchangeAlgorithm -and ($_.Issuer -eq $encryCertCN ) -and ($_.Subject -eq $encryCertCN ) )
{
Write-Output "Thumbprint $($_.Thumbprint)"
return $_.Thumbprint
}
}
}
[DscLocalConfigurationManager()]
Configuration RegistrationMetaConfig
{
Node localhost
{
Settings
{
ActionafterReboot="ContinueConfiguration";
AllowModuleOverwrite = $true;
CertificateID ='---same thumbprint as pull server--' # Get-LocalEncryptionCertificateThumbprint
ConfigurationMode = "ApplyAndAutocorrect";
ConfigurationModeFrequencyMins = 15;
RebootNodeIfNeeded = $true;
RefreshFrequencyMins = 30;
RefreshMode = "Pull";
}
ConfigurationRepositoryWeb ConfigurationManager
{
ServerURL = "https://$($pullServer):8080/PSDSCPullServer.svc"
RegistrationKey = $regkey
ConfigurationNames = @($hostname)
}
ReportServerWeb DSCPull
{
ServerURL = "https://$($pullServer):8080/PSDSCPullServer.svc"
RegistrationKey = $regkey
}
}
}
$ConfigData= @{
AllNodes = @(
@{
# Common to all nodes
NodeName = "*"
# The path to the .cer file containing the
# public key of the Encryption Certificate
# used to encrypt credentials for this node
CertificateFile = "C:\DSC\DscPublicKey.cer"
# The thumbprint of the Encryption Certificate
# used to decrypt the credentials on target node
Thumbprint ='--same thumbprint as on pull server--'# Get-LocalEncryptionCertificateThumbprint
}
)
}
Write-Host "Disabling DSC Debug"
Disable-DscDebug
Write-Host "Removing current DSC config"
Remove-DscConfigurationDocument -Stage Current, Pending, Previous -Verbose
Write-Host "Creating new DSC Config"
RegistrationMetaConfig -ConfigurationData $ConfigData -OutputPath C:\DSC -Verbose
Write-Host "Configuring LCM to use new config"
Set-DscLocalConfigurationManager -ComputerName localhost -Path C:\DSC -Verbose -Force
Write-Host "Start DSC Configuration"
Start-DSCConfiguration -ComputerName localhost.meta -Path C:\DSC -Verbose
And the client registers fine. It pulls a configuration, and attempts to apply it… however - the client fails to execute the configuration
Status StartDate Type Mode RebootRequested NumberOfResources ------ --------- ---- ---- --------------- ----------------- Failure 2/15/2017 9:30:03 AM Consistency Pull False 5
and when I look into the event viewer I find this error in the DSC event log
Job {3CF0DEDD-F38B-11E6-8A11-0050569272C7} :
MIResult: 1
Error Message: Decryption failed.
Message ID: Windows System Error 13
Error Category: 6
Error Code: 13
Error Type: WIN32
Now, if the certificate which was used for encryption was exported from the pull server, as a pfx, and imported into the client, (and I know it can find the cert, as I’ve seen a different error when the cert can not be found). What is causing the decryption error - and how do I go about fixing it?