How to create a self-signed certificate for Windows Desired State Configuration

‌‌

Learning how to set up a self-signed certificate for Windows Desired State Configuration (DSC) is a must for admins. And, thankfully, it can be done in just a few steps—here’s how.

Local Configuration Manager (LCM)

The continual use of PowerShell DSC means that you’ll eventually need to use a different username and password inside of a configuration. When the Local Configuration Manager (LCM) commits a configuration to a computer it always runs as the local SYSTEM account. Although the SYSTEM account has full access to the computer it's running on, it has no rights at all to other network resources.

To solve the local SYSTEM rights problem, you can use a PSCredential object representing a different username and password to use instead. This is done by either using a Credential property (if one has been defined for the resource) or by using the PowerShell v5 PsDscRunAsCredential, which changes the user context that the LCM executes the configuration as. Either way, a separate credential is necessary.

To demonstrate this, let's use the built-in File resource and the Credential property. Our demonstration configuration looks like this:

Configuration CopyFile
{
	param(
		[Parameter(Mandatory)]
		[ValidateNotNullOrEmpty()]
		[pscredential]$Credential
	)
	
	Import-DscResource -Module PSDesiredStateConfiguration
	
	Node $env:COMPUTERNAME
	{
		File 'FileCopy'
		{
			Ensure = 'Present'
			SourcePath = '\\MEMBERSRV1\c$\RemoteFile.txt'
			DestinationPath = 'C:\RemoteFile.txt'
			Credential = $Credential
		}
	}
}

CopyFile -OutputPath $PSScriptRoot -Credential (Get-Credential)

When CopyFile is attempted to run, you’ll soon find this error message waiting for you:

The reason for this is that when a configuration is compiled into a MOF, the credential has to be stored inside of the MOF. By default, the LCM is configured not to accept this. Some lazy admins may choose to ignore this helpful error message and simply put the equivalent of "Whatever. Just do it" with the configuration, by allowing the plain text password to be stored inside of the MOF file. 

Instead, let's do this the right way by encrypting the credential inside of the MOF file with a certificate. To do this, you'll need a certificate installed on the computer where you're generating the MOF and the DSC node that will be consuming the MOF. This will allow the configuration generating computer to encrypt the credential and the DSC node to decrypt it inside of the MOF. It doesn't matter how you create a certificate. All that really matters is that you're using a certificate.

Generating a self-signed certificate

One of the easiest methods (with the most management burden) is to generate a self-signed certificate and use this. Although using a PKI like Active Directory Certificate Services is better in the long run, self-signed certificates allow you to get up and running quickly in a test environment. Using the technique I'm about to show you will allow you to create a certificate quickly and get it installed on both the DSC node and the computer generating the MOF file.

The first task is creating the certificate and ensuring both the private and public keys exist on the DSC node. The easiest way to make this happen is to build the certificate directly on the DSC node. Otherwise, you'll have to add an extra step of exporting the private key from your admin workstation to the DSC node itself. This requires the help of a pre-built function called New-SelfSignedCertificateEx. This function allows us to create a particular certificate with all of the necessary attributes needed for a DSC certificate, and it works great on local computers. But, keep in mind that our DSC node is remote, which means we'll need to create some magic to make that happen using PowerShell remoting.

We'll use Invoke-Command here, so we'll need to build a scriptblock to pass to the remote computer. But first, we'll need to create a function definition. We'll do this by assigning it to the $functionDef variable.

$functionDef = "function New-SelfSignedCertificateEx { ${function:New-SelfSignedCertificateEx}}"

Once this happens we can build the script block with a single parameter to pass the function definition to, and a line to create the New-SelfSignedCertificateEx function inside of the remote session. The function can then be called as normal.

$ScriptBlock = {
	param ($functionDef)
	
	. ([ScriptBlock]::Create($functionDef))
	
	$signedCertParams = @{
		'Subject' = "CN=$env:COMPUTERNAME"
		'SAN' = $env:COMPUTERNAME
		'EnhancedKeyUsage' = 'Document Encryption'
		'KeyUsage' = 'KeyEncipherment', 'DataEncipherment'
		'FriendlyName' = 'DSC Encryption Certifificate'
		'StoreLocation' = 'LocalMachine'
		'StoreName' = 'My'
		'ProviderName' = 'Microsoft Enhanced Cryptographic Provider v1.0'
		'PassThru' = $true
		'KeyLength' = 2048
		'AlgorithmName' = 'RSA'
		'SignatureAlgorithm' = 'SHA256'
	}
	New-SelfSignedCertificateEx @signedCertParams
}

Looking at the $signedCertParams parameters above, you can see the many options needed to create a DSC certificate successfully.

Once the scriptblock is created, it can now be passed to Invoke-Command along with the ArgumentList parameter, which contains the function definition created earlier.

Invoke-Command –ComputerName RemoteComputer –ScriptBlock $ScriptBlock –ArgumentList $functionDef

Confirming the self-signed certificate

At this point, the self-signed cert has been created. We can confirm it has been created successfully by enumerating all of the certificates in the local computer certificate store looking for the subject name of the cert that was just created.

$cert = Invoke-Command –ComputerName REMOTECOMPUTER –ScriptBlock {Get-Childitem –Path Cert:\LocalMachine\My | where {$_.Subject –eq 'CN=REMOTECOMPUTER'}}

If successful, you should now see the certificate.

Importing the certificate

The final step is to import the certificate onto the computer that will generate the MOF file. First, we'll have to get the certificate into a file. To do that, let’s use the Export-Certificate cmdlet.

$cert | Export-Certificate –FilePath 'C:\DscCert.cer'

Since the certificate that was created on the DSC node is now stored in a file on our local computer, I can now import the certificate to my local machine's certificate store. This will allow me to encrypt the credential inside of the MOF file.

Import-Certificate –FilePath C:\DscCert.cer –CertStoreLocation 'Cert:\LocalMachine\My'

Success! We've finally created a self-signed certificate on a remote DSC node, exported that certificate to our local computer as a file and imported the certificate to the appropriate local certificate store. At this point, if you decide to run the configuration previously discussed, it will now create the MOF file without a complaint. Go ahead, give it a shot and let us know what you think.

Once you’ve got a handle on this, check out my other PowerShell tips and tutorials. Just getting started with PowerShell? Try starting with this path

Contributor

Adam Bertram

is an independent consultant, technical writer, trainer and presenter. Adam specializes in consulting and evangelizing all things IT automation mainly focused around Windows PowerShell. Adam is a Microsoft Windows PowerShell MVP, 2015 powershell.org PowerShell hero and has numerous Microsoft IT pro certifications. He is a writer, trainer and presenter and authors IT pro course content for Pluralsight. He is also a regular contributor to numerous print and online publications and presents at various user groups and conferences. You can find Adam at his site listed below or on Twitter at @adbertram.