2

I'm trying to create a DSC script that can be run locally on a machine that is to be a Read Only Domain Controller. The xActiveDirectory DSC resource doesn't provide for creating an RODC so I have to use a script resource and use Install-ADDSDomainController.

My problem arises when I have to provide the Safe Mode Administrator Password. The parameter will only accept a SecureString, however I'm having trouble passing through the secure string to the DSC configuration. I can pass through a PSCredential object for the Credential parameter but the Safe Mode parameter won't accept it so I need a separate variable. I am encrypting the credentials with a self signed cert which seems to be working ok at this point.

My DSC code, there are a couple of commented out lines at the bottom where I tested alternate ways of creating the secure string non of which worked:

get-childitem cert:\localmachine\my | where-object {$_.Subject -like "*CN=DscEncryptionCert*"} | remove-item

$cert = New-SelfSignedCertificate -Type DocumentEncryptionCertLegacyCsp -DnsName 'DscEncryptionCert' -HashAlgorithm SHA256
$cert | Export-Certificate -FilePath "c:\RODC\DscPublicKey.cer" -Force

$thumbprint = (get-childitem cert:\localmachine\my | where-object {$_.Subject -like "*CN=DscEncryptionCert*"}).Thumbprint


$ConfigData= @{ 
    AllNodes = @(     
            @{   
                NodeName = "localhost" 

                CertificateFile = "C:\RODC\localhost.cer" 

                Thumbprint = $thumbprint 
            }; 
        );    
    }




configuration RODC

{
    param(
        [Parameter()]$DomainName,
        [Parameter()]$ReplicationSourceDC,
        [Parameter()]$SiteName,
        [Parameter()]$Thumbprint,
        [PSCredential]$PSCredential = $PSCredential,
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [System.Security.SecureString]$safemodepassword = $safemodepassword
        )

    Import-DscResource -module 'PSDesiredStateConfiguration'

    Node localhost

    {

        LocalConfigurationManager 
            { 
            CertificateId = $Thumbprint 
            } 

        WindowsFeature ADDSInstall
            {
            Ensure = 'Present'
            Name = 'AD-Domain-Services'
            IncludeAllSubFeature = $true
            }

        script installRODC
            {
            DependsOn = '[WindowsFeature]ADDSInstall'
            SetScript =
                {
                Import-Module ADDSDeployment
                Install-ADDSDomainController `
                -AllowPasswordReplicationAccountName @("test\Allowed RODC Password Replication Group") `
                -NoGlobalCatalog:$false `
                -Credential:$PSCredential `
                -CriticalReplicationOnly:$false `
                -DenyPasswordReplicationAccountName @("BUILTIN\Administrators", "BUILTIN\Server Operators", "BUILTIN\Backup Operators", "BUILTIN\Account Operators", "test\Denied RODC Password Replication Group") `
                -DomainName:$using:DomainName `
                -InstallDns:$true `
                -NoRebootOnCompletion:$false `
                -ReadOnlyReplica:$true `
                -ReplicationSourceDC:$using:ReplicationSourceDC `
                -SiteName $using:SiteName `
                -Force:$true `
                -SafeModeAdministratorPassword:$safemodepassword
                }
            TestScript =
                {
                if((get-wmiobject win32_computersystem).domainrole -eq 4){$true}else{$false}
                }
            GetScript =
                {
                Return @{result = (get-wmiobject win32_computersystem).domainrole}
                }
            }

    }

}

$PSCredential = Get-Credential
$safemodepassword = Read-Host -assecurestring "Please enter the Safe Mode Administrator password"
#$safemodepassword = ConvertTo-SecureString "P@55word" -AsPlainText -Force
#$safemodepassword = New-Object System.Management.Automation.PSCredential ("Administrator", $password)

RODC -DomainName test.local -ReplicationSourceDC DC1.test.local -Sitename Site11 -PSCredential $PSCredential -safemodepassword $safemodepassword

Set-DscLocalConfigurationManager -path .\RODC -Verbose -Force

Start-DscConfiguration -path .\RODC -Verbose -force

A simple test I wrote to check if the script code itself is working, which it is:

$PSCredential = Get-Credential
$safemodepassword = Read-Host -assecurestring "Please enter the Safe Mode Administrator password"

$DomainName = "test.local"
$ReplicationSourceDC = "DC1.test.local"
$Sitename = "Site11"


Install-ADDSDomainController `
-AllowPasswordReplicationAccountName @("test\Allowed RODC Password Replication Group") `
-NoGlobalCatalog:$false `
-Credential:$PSCredential `
-CriticalReplicationOnly:$false `
-DenyPasswordReplicationAccountName @("BUILTIN\Administrators", "BUILTIN\Server Operators", "BUILTIN\Backup Operators", "BUILTIN\Account Operators", "test\Denied RODC Password Replication Group") `
-DomainName:$DomainName `
-InstallDns:$true `
-NoRebootOnCompletion:$false `
-ReadOnlyReplica:$true `
-ReplicationSourceDC:$ReplicationSourceDC `
-SiteName $SiteName `
-Force:$true `
-SafeModeAdministratorPassword:$safemodepassword

The main error I get is:

PowerShell DSC resource MSFT_ScriptResource failed to execute Set-TargetResource functionality with error message: Cannot bind parameter 'SafeModeAdministratorPassword' to the target. Exception setting "SafeModeAdministratorPassword": "SafeModeAdministratorPassword cannot be null."

Is it NULL because it's not being passed through correctly? If I print out the value of the variable it tells me there is a secure string present but that doesn't seem to be the case in the actual DSC configuration itself.

If I change -SafeModeAdministratorPassword:$safemodepassword to include $using as I have with some of the other variables I get the error:

PowerShell DSC resource MSFT_ScriptResource failed to execute Set-TargetResource functionality with error message: Exception calling "Deserialize" with "1" argument(s): "The system cannot find the path specified.

I'm not sure where I can go from here. Any help would be appreciated. Thanks.

Keiran
  • 21
  • 2
  • easiest workaround would be to construct securestring inside the dsc configuration out of a regular string (just to see if that works). also, debugging DSC is a lot easier in ps 5+, maybe you should use that. https://blogs.technet.microsoft.com/ashleymcglone/2016/10/26/gnarly-innards-how-to-live-debug-powershell-dsc-configurations-without-using-enable-dscdebug/ – 4c74356b41 Jun 16 '17 at 09:36

1 Answers1

0

I think it's not possible to pass SecureString inside Script block.

It's encrypted by key which exists on local PC only.

PS C:\> [System.Management.Automation.PSSerializer]::Deserialize('<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
>>   <Obj RefId="0">
>>     <TN RefId="0">
>>       <T>System.Management.Automation.PSCredential</T>
>>       <T>System.Object</T>
>>     </TN>
>>     <ToString>System.Management.Automation.PSCredential</ToString>
>>     <Props>
>>       <S N="UserName">Hey</S>
>>       <SS N="Password">01000000d08c9ddf0115d1118c7a00c04fc297eb0100000034646a6e6b53d244b223386a302a6fe700000000020000000000106600000001000020000000db75ebc7ae7b02d84ef6cb1161559006bdd81a84ccd5d152f3a6fdfdcf102165000000000e8000000002000020000000f0c4f2676ae5a65d2823ec8d73c352c79a97d7fd3971fd64c084d90c6c94ff7c20000000476fd1bd7f1842fdfb2e2f2fc4fd17ee0d7b41fefb39cda407bd2a6176e7b40e40000000575dac900276dcc550f09fe48b341885431dd8d287a6073ccbbfbc89e2ff8ee9e3158a8d75a52332ab2a60126cbc69232c6d9109d1db17e28535726b5e1ec2b3</SS>
>>     </Props>
>>   </Obj>
>> </Objs>')

UserName                     Password
--------                     --------
Hey      System.Security.SecureString

[WIN-U42BH7N5O4B]: PS C:\> [System.Management.Automation.PSSerializer]::Deserialize('<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
>>   <Obj RefId="0">
>>     <TN RefId="0">
>>       <T>System.Management.Automation.PSCredential</T>
>>       <T>System.Object</T>
>>     </TN>
>>     <ToString>System.Management.Automation.PSCredential</ToString>
>>     <Props>
>>       <S N="UserName">Hey</S>
>>       <SS N="Password">01000000d08c9ddf0115d1118c7a00c04fc297eb0100000034646a6e6b53d244b223386a302a6fe700000000020000000000106600000001000020000000db75ebc7ae7b02d84ef6cb1161559006bdd81a84ccd5d152f3a6fdfdcf102165000000000e8000000002000020000000f0c4f2676ae5a65d2823ec8d73c352c79a97d7fd3971fd64c084d90c6c94ff7c20000000476fd1bd7f1842fdfb2e2f2fc4fd17ee0d7b41fefb39cda407bd2a6176e7b40e40000000575dac900276dcc550f09fe48b341885431dd8d287a6073ccbbfbc89e2ff8ee9e3158a8d75a52332ab2a60126cbc69232c6d9109d1db17e28535726b5e1ec2b3</SS>
>>     </Props>
>>   </Obj>
>> </Objs>')
Exception calling "Deserialize" with "1" argument(s): "Key not valid for use in specified state.
"
At line:1 char:1
+ [System.Management.Automation.PSSerializer]::Deserialize('<Objs Versi ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : CryptographicException

Use Cryptographic Message Syntax encrypted strings.

Configuration WebSitePublishConfig
{
    param
    (
        [Parameter(Mandatory=$true)]
        [PSCredential] $Credential,
        [Parameter(Mandatory=$true)]
        [string] $CertificateFile
    )

    Import-DscResource -ModuleName PSDesiredStateConfiguration

    $UserName = $Credential.UserName
    $EncryptedPassword = $Credential.GetNetworkCredential().Password | Protect-CmsMessage -To $CertificateFile

    Script MyScript
    {
        SetScript = `
            {
                # $using:UserName
                $password = Unprotect-CmsMessage -Content $using:EncryptedPassword
            }
    }
}

If you don't care about security, pass password in plain text:

$UserName = $Credential.UserName
$PasswordPlain = $Credential.GetNetworkCredential().Password

Script MyScript
{
    SetScript = `
        {
            # $using.UserName
            # $using:PasswordPlain
        }
}
Der_Meister
  • 4,771
  • 2
  • 46
  • 53