Encrypting a password in a DSC Script Resource

I’m writing up DSC script resource that creates a scheduled task that runs under a particular user. Unfortunately the Register-ScheduledTask cmdlet doesn’t accept [System.Management.Automation.PSCredential] in any parameter. It only accepts -User and -Password. So I’m not too sure on how to encrypt the password. I’ve tried using PsDscRunAsCredential = $Credential to run the DSC resource under that particular user but Register-ScheduledTask still requires -User and -Password to be populated if you want the task to be set to “Run whether user is logged on or not”

Does anyone know how would I go about encrypting the password in the MOF file as the standard DSC way isn’t going to work in this situation or is going to be a case of using a combination of…

Get-CmsMessage
Protect-CmsMessage
Unprotect-CmsMessage

As far as I know, a mof file is only designed to run in the system context, plus you always have to be wary of needing multiple credentials for multiple configurations in your MOF so you should pretty much ONLY be signing passwords inside the configuration mof :slight_smile:

The short answer to your question is that it’s fairly easy: just add a param () to you configuration and then make sure the variable is present in your configuration data.

quick example:

Configuration SCCMConfiguration
{ 
    param (
        [Parameter(Mandatory=$true)][PsCredential]$SCCMAdministratorCredential
    )
    
    Import-DSCResource -ModuleName cSCCM
    
    Node $AllNodes.NodeName
    {
        cCMFolder Device_All
        { 
            FolderName = "_All"
            FolderType = "Device Collection"
            ParentFolder = "Root"
            SCCMAdministratorCredential = $SCCMAdministratorCredential
            Ensure = "Present"
        }
}

$Nodes = @{
    AllNodes = @(
	@{
		PSDscAllowPlainTextPassword=$true
        	NodeName = "SCCM01"
        }
	);
   }

Now in this fictitious example, if I run the config ill be prompted for the required creds “SCCMAdministratorCredentials”. Bam, the generated mof will store the account.

Also note the entry “PSDscAllowPlainTextPassword=$true”. Unforuntatley this means that password is in clear text and anyone with vi/notepad/whatever can see that password in the mof. If you want that encrypted, you need a certificate on the target server and a copy of the public cert and it’s thumbprint … and you just add that to the config data so it looks like this:

$Nodes = @{
    AllNodes = @(
	@{
        	NodeName = "SCCM01"
                CertificateFile = "C:\path\cert.cer"
                Thumbprint = "123412412412412"
        }
	);
   }

Now in this case you encrypt the password using the supplied certificate. But there’s a catch: you need to configure the LCM on your node and tell it which cert to use to decrypt that password.

Hope that helps!

Justin, I appreciate your post. Thank you.

I’m already using a certificate to encrypted the $Credential you supply to the MOF file. The problem is I need to encrypt a second set of credentials because Register-ScheduledTask doesn’t accept [PsCredential]. So I’m wondering what other people do in this instance?

You can make your DSC resource accept a [pscredential] argument, and then just extract the plain-text password within the resource’s code. Here’s a quick demonstration of how you can do that:

$cred = Get-Credential

$username = $cred.UserName

$ptr = [System.Runtime.InteropServices.Marshal]::SecureStringToGlobalAllocUnicode($cred.Password)
$password = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($ptr)
[System.Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode($ptr)

Write-Host "Username: $username , Password: $password"

Alternatively, in WMF 5, you don’t really have to worry about it, because now the entire MOF file can be encrypted instead of just the pscredential passwords.

Dave that is exactly what I was looking for, thank you. I’ll give it a try tomorrow.

However…I’m really liking the idea of encrypting the entire MOF file. I had a look at https://msdn.microsoft.com/en-us/powershell/dsc/securemof but it doesn’t mention anything about encrypting the entire MOF. Any hints on how to do it?

Well crap… I was sure I read something about encrypting the whole MOF file, but that may have been in reference to a preview version or something. Looking at my Windows 10 system right now, it looks like that’s only partially implemented. (It adds a “PasswordEncrypted” ContentType header to the MOF, but doesn’t actually encrypt the text, which is kind of silly. It also only does this if the MOF contains a PSCredential object, which is also silly. If I specify a certificate, just encrypt the stupid thing! :slight_smile: )

Just got clarification on this:

"The encryption of the MOF is handled by the LCM once it gets the MOF.

It is not a compile time thing."

That’s too bad; this means the MOF file on the pull server will still be sitting there in plain text, as well as the copy on the machine where you compiled it.

>That’s too bad; this means the MOF file on the pull server will still be sitting there in
> plain text, as well as the copy on the machine where you compiled it.

Oh well. Maybe a feature for WMF 6 then :wink:

Dave, how would you access the credentials object in a script resource? My script resource is just a couple lines of code so I can’t justify creating a proper dsc resource instead.

Script CredExtractTest
{
PsDscRunAsCredential = $Credential
Credential = $Credential

GetScript = {
    $Status = Test-Path -Path C:\Temp\credtest.txt
    @{
        GetScript = $GetScript
        SetScript = $SetScript
        TestScript = $TestScript
        Result = $Status
    }           
}
        
SetScript = {
    $ptr = [System.Runtime.InteropServices.Marshal]::SecureStringToGlobalAllocUnicode($Credential.Password)
    $password = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($ptr)
    [System.Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode($ptr)

    $Credential.UserName | Out-File C:\Temp\credtest.txt
    $password | Out-File C:\Temp\credtest.txt -Append
}

TestScript = {
    $Status = Test-Path -Path C:\Temp\credtest.txt
    $Status -eq $True
}

}

VERBOSE: [TestServer01]: [[Script]CredExtractTest] Performing the operation “Set-TargetResource” on target “Executing the SetScript with the user supplied credential”.
Invoke-CimMethod : Exception calling “SecureStringToGlobalAllocUnicode” with “1” argument(s): “Value cannot be null. Parameter name: s”

Dave any ideas?

I’m not sure if you can. The credential encryption code in DSC only kicks in if you have a DSC resource with a property of type [pscredential]. The Script resource doesn’t.

I understand. Thanks for confirming Dave.