Encrypting a password in a DSC Script Resource

This topic contains 10 replies, has 3 voices, and was last updated by Profile photo of Zuldan Zuldan 11 months ago.

  • Author
    Posts
  • #33466
    Profile photo of Zuldan
    Zuldan
    Participant

    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

  • #33467
    Profile photo of Justin King
    Justin King
    Participant

    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 🙂

    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!

  • #33471
    Profile photo of Zuldan
    Zuldan
    Participant

    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?

  • #33473
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    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.

  • #33475
    Profile photo of Zuldan
    Zuldan
    Participant

    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?

  • #33477
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    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! 🙂 )

  • #33480
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    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.

  • #33516
    Profile photo of Zuldan
    Zuldan
    Participant

    >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 😉

    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"

  • #33754
    Profile photo of Zuldan
    Zuldan
    Participant

    Dave any ideas?

  • #33768
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    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.

  • #33796
    Profile photo of Zuldan
    Zuldan
    Participant

    I understand. Thanks for confirming Dave.

You must be logged in to reply to this topic.