As discussed in an earlier post, PowerShell desired state configuration (DSC) scripts compile to MOF files (or files with the .mof extension).

When working with DSC configuration files, there are frequent situations where you have to specify user credentials using the PowerShell Get-Credential cmdlet. Since the Get-Credential cmdlet uses a popup to interactively request user credentials, the passwords are not exposed in your PowerShell DSC scripts at all. However, depending on your configuration within the DSC script, the credentials may be saved as plain text and stored as such in the resulting MOF file output. Since you never want to save passwords as clear text, this article explains how you can keep passwords secure and encrypted inside your MOF files.

To get a full understanding of how to secure MOF files, we will first examine the default behavior of PowerShell, and then discuss how to work with both options: plain text and secure passwords.

Default behavior of PowerShell DSC when it encounters the Get-Credential cmdlet

Take the following DSC script for example:

Configuration DefaultConfig { $creds = Get-Credential Node "localhost" { File DirectoryCopy { Ensure = "Present" Type = "Directory" Recurse = $true SourcePath = "C:\MOFDemoFolderOne" DestinationPath = "C:\MOFDemoFolderTwo" Credential = $creds } } } DefaultConfig

Since we don’t plan on actually executing the above desired state – we only want to see how the output MOF file looks like and will not be calling the Start-DscConfiguration cmdlet – you can ignore the actual values entered for SourcePath and DestinationPath. None of the folders need to actually exist.

Now run the script. As expected, you will get a popup requesting your user credentials:

Enter your credentials and press OK. When PowerShell attempts to execute the line that compiles the MOF file (line 19 above), you will hit the following error:

ConvertTo-MOFInstance : System.InvalidOperationException error processing property ‘Credential’ OF TYPE ‘File’:

Converting and storing encrypted passwords as plain text is not recommended. For more information on securing

credentials in MOF file, please refer to MSDN blog: http://go.microsoft.com/fwlink/?LinkId=393729

At line:6 char:1

+ File

At line:190 char:16

+ $aliasId = ConvertTo-MOFInstance $keywordName $canonicalizedValue

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ CategoryInfo : InvalidOperation: (:) [Write-Error], InvalidOperationException

+ FullyQualifiedErrorId : FailToProcessProperty,ConvertTo-MOFInstance

Errors occurred while processing configuration ‘DefaultConfig’.

At

C:\Windows\system32\WindowsPowerShell\v1.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psm1:2223

char:5

+ throw $errorRecord

+ ~~~~~~~~~~~~~~~~~~

+ CategoryInfo : InvalidOperation: (DefaultConfig:String) [], InvalidOperationException

+ FullyQualifiedErrorId : FailToProcessConfiguration

That error is a good thing! It means that if you somehow forget to define encryption (or explicitly allow for plain text passwords), your DSC script will not compile.

The error occurred because when PowerShell sees the Get-Credential cmdlet used in a DSC script, it expects additional information that defines how the credentials should be stored in the MOF file.

Saving passwords as plain text in MOF files

To explicitly instruct PowerShell to store our password in plain text, you need to use the PsDscAllowPlainTextPassword cmdlet. Modify the above DSC script like this:

Configuration PlainTextConfig { $creds = Get-Credential Node "localhost" { File DirectoryCopy { Ensure = "Present" Type = "Directory" Recurse = $true SourcePath = "C:\MOFDemoFolderOne" DestinationPath = "C:\MOFDemoFolderTwo" Credential = $creds } } } $cd = @{ AllNodes = @( @{ NodeName = "localhost" PsDscAllowPlainTextPassword = $true } ) } PlainTextConfig -ConfigurationData $cd

When you run the script this time, after entering your credentials, it executes successfully:

The output (see screenshot above) indicates that out MOF file has been created inside the directory C:\Users\Administrator\PlainTextConfig

Go to that folder and open the MOF file (localhost.mof) in a text editor. The contents look like:

/* @TargetNode='localhost' @GeneratedBy=Administrator @GenerationDate=02/14/2018 11:35:25 @GenerationHost=DEVMACHINE */ instance of MSFT_Credential as $MSFT_Credential1ref { Password = "pass@word1"; UserName = "administrator"; }; instance of MSFT_FileDirectoryConfiguration as $MSFT_FileDirectoryConfiguration1ref { ResourceID = "[File]DirectoryCopy"; Type = "Directory"; Credential = $MSFT_Credential1ref; Ensure = "Present"; DestinationPath = "C:\\MOFDemoFolderTwo"; ModuleName = "PSDesiredStateConfiguration"; SourceInfo = "::6::9::File"; Recurse = True; ModuleVersion = "1.0"; SourcePath = "C:\\MOFDemoFolderOne"; }; instance of OMI_ConfigurationDocument { Version="1.0.0"; Author="Administrator"; GenerationDate="02/14/2018 11:35:25"; GenerationHost="DEVMACHINE"; };

On line 10 above, you will notice that the password I entered for the administrator account has been stored in plain text as pass@word1

This is not best practice. You do not want to leave admin accounts and service accounts passwords in plain text on any server since this would be viewable to anyone with access to that server. So, only use plain text for experimental purposes. In almost all other cases, the recommended approach is to encrypt your passwords using a certificate:

Securing passwords in MOF files

To secure our password, we will need to first create a certificate. For this example, we will be using a self signed certificate. For the DSC configuration data, we will specify the path to the self signed certificate as well as the certificate’s thumbprint.

In the below code, I will be exporting the certificate to the following folder C:\SecureConfig

You may want to first create the respective folder in your case.

Run the following code. It works on Windows 10 and Windows Server 2016. For earlier operating systems, please see the note below.

$cert = New-SelfSignedCertificate -Type DocumentEncryptionCertLegacyCsp -DnsName 'DscEncryptionCert' -HashAlgorithm SHA256 $thumbprint = $cert.thumbprint $filepath = "C:\SecureConfig\SelfSigned.cer" Export-Certificate -Cert $cert -FilePath $filepath Configuration SecureConfig { $creds = Get-Credential Node "localhost" { File DirectoryCopy { Ensure = "Present" Type = "Directory" Recurse = $true SourcePath = "C:\MOFDemoFolderOne" DestinationPath = "C:\MOFDemoFolderTwo" Credential = $creds } } } $cd = @{ AllNodes = @( @{ NodeName = "localhost" CertificateFile = $filepath Thumbprint = $thumbprint } ) } SecureConfig -ConfigurationData $cd

Note: Depending on your operating system, you may encounter issues executing the New-SelfSignedCertificate cmdlet. On windows server 2012 R2, the error would be something like:

New-SelfSignedCertificate : A parameter cannot be found that matches parameter name ‘Type’.

At line:1 char:35

+ $cert = New-SelfSignedCertificate -Type DocumentEncryptionCertLegacyCsp -DnsName …

+ ~~~~~

+ CategoryInfo : InvalidArgument: (:) [New-SelfSignedCertificate], ParameterBindingException

+ FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.CertificateServices.Commands.NewSelfSignedCertificateCommand

Because the New-SelfSignedCertificate cmdlet on Windows Operating Systems prior to Windows 10 and Windows Server 2016 do not support the Type parameter, an alternate method of creating this certificate is required on these operating systems. In this case you can use makecert.exe or certutil.exe to create the certificate.

An alternate method is to download the New-SelfSignedCertificateEx.ps1 script from Microsoft Script Center and use it to create the certificate instead.

I did not personally try the above alternate methods (yet). While experimenting with this, I first started with Windows Server 2012 R2, had issues with the Type parameter and switched to Windows Server 2016 where everything worked flawlessly.

Assuming you have successfully executed the code, you should get a MOF file created (in my case, the file was put inside C:\Users\ehikioya.SP\SecureConfig):

Open the MOF file in a text editor. It should look like this:

/* @TargetNode='localhost' @GeneratedBy=ehikioya @GenerationDate=02/14/2018 17:44:24 @GenerationHost=spVM */ instance of MSFT_Credential as $MSFT_Credential1ref { Password = "-----BEGIN CMS-----

MIIBpAYJKoZIhvcNAQcDoIIBlTCCAZECAQAxggFMMIIBSAIBADAwMBwxGjAYBgNVBAMMEURzY0Vu

Y3J5cHRpb25DZXJ0AhBf3hfrOYQHnENNTjPQ/Yl/MA0GCSqGSIb3DQEBBzAABIIBAD1DhamFB+pP

7sCB3QvtzEwmJXWPNTYLhtwvTXZjS+IJlwDfiFrMHI9xa33Qg/KKeYvugz68PyJPCTkeO+mnnPEv

6f5NeOxDvUpS8noKSzCUg2TRhOTySZrwwjETrx342Jjwcy1acztRstoYSBxyH3MQ1QhyJ2SYp45c

hAxKafW/JzdSJwfKvkfz9AlGY1B3GpO2ZcfDQSPQfkKrP/arCRTJ/2Nt2JdBI8n6U2VjhtyXxHYc

HVeRhZ84AevPC6OpmsGRpJPUVulxO5mKfAYyj9b+4lJ2B6oi3/waLxNwpxa8btLZsbj9LWhEp0K+

HD7+pSiTQ5oDTVQwuf+vn0Fxnm0wPAYJKoZIhvcNAQcBMB0GCWCGSAFlAwQBKgQQE9cgnRX42rhm

/CR10pNyjIAQaCKOyfS2tjh39xbW9oR8ow==

-----END CMS-----"; UserName = "ehikioya"; }; instance of MSFT_FileDirectoryConfiguration as $MSFT_FileDirectoryConfiguration1ref { ResourceID = "[File]DirectoryCopy"; Type = "Directory"; Credential = $MSFT_Credential1ref; Ensure = "Present"; DestinationPath = "C:\\MOFDemoFolderTwo"; ModuleName = "PSDesiredStateConfiguration"; SourceInfo = "::7::9::File"; Recurse = True; SourcePath = "C:\\MOFDemoFolderOne"; ModuleVersion = "1.0"; ConfigurationName = "SecureConfig"; }; instance of OMI_ConfigurationDocument { Version="2.0.0"; MinimumCompatibleVersion = "1.0.0"; CompatibleVersionAdditionalProperties= {"Omi_BaseResource:ConfigurationName"}; Author="ehikioya"; GenerationDate="02/14/2018 17:44:24"; GenerationHost="spVM"; ContentType="PasswordEncrypted"; Name="SecureConfig"; };

Notice that the password on line 10 is now encrypted. So, there’s no way for users to retrieve the passwords from the MOF files even if they have access to the server.

Found this article valuable? Want to show your appreciation? Here are some options: Spread the word! Use these buttons to share this link on your favorite social media sites. Help me share this on . . . Facebook

Twitter

LinkedIn

Reddit

Tumblr

Pinterest

Pocket

Telegram

WhatsApp

Skype

Sign up to join my audience and receive email notifications when I publish new content. Contribute by adding a comment using the comments section below. Follow me on Twitter, LinkedIn, and Facebook.