Chicken / Egg Problem with Pull Server Creation

First real crack at DSC… I’ve created an Active Directory server, and a CA using push configurations since those seem to be the requirements for a HTTPS Pull Server. I am running into an issue attempting to request a certificate in the same configuration that creates the pull server.

The CertReq resource works perfectly to request the certificate, but populating the Thumbprint from that certificate to the xDSCWebService resource is very challenging. I’m trying to acquire the thumbprint programatically with the following :

CertificateThumbPrint = (Get-ChildItem 'Cert:\LocalMachine\My\' | Where-Object { $_.Subject -eq 'CN=Pull01'}).Thumbprint.ToString()
It appears to try and calculate that prior to running the preceding resources even though it "depends" on them, which causes the error:
PSDesiredStateConfiguration\Node : You cannot call a method on a null-valued expression.
Presumably because the certificate does not exist yet.

I know I can request the certificate via command line and feed the proper values to the script, but I’d love to be able to do everything directly inside the script, thus creating a single DSC configuration file for each host, and I can recreate my environment.

Is there any way to request the certificate and provide the proper values from that certificate?

Full DSC Configuration file below… Thanks in advance!

Configuration Build-Pull01 {
Param (
    [Parameter(Mandatory)]
    [pscredential]$CredDomain,

    [ValidateNotNullOrEmpty]
    [string]$CertificateFile,

    #[ValidateNotNullOrEmpty]
    #[string]$DSCCertThumbprint,

    [ValidateNotNullOrEmpty]
    [string]$DSCRegistration

)

Import-DscResource -ModuleName xPSDesiredStateConfiguration
Import-DscResource -ModuleName ComputerManagementDsc
Import-DscResource -ModuleName NetworkingDsc
Import-DscResource -ModuleName PSDscResources
Import-DscResource -ModuleName CertificateDsc

Node localhost {

    LocalConfigurationManager {
        ActionAfterReboot = 'ContinueConfiguration'
        ConfigurationMode = 'ApplyOnly'
        RebootNodeIfNeeded = $true
    }

    NetIPInterface EnableDHCP {
        InterfaceAlias = $Node.IPInterfaceAlias
        AddressFamily = $Node.IPAddressFamily
        Dhcp = 'Enabled'
    }

    DnsServerAddress NewDnsAddress {
        InterfaceAlias = $Node.IPInterfaceAlias
        AddressFamily = $Node.IPAddressFamily
        Validate = $true
        DependsOn = '[NetIPInterface]EnableDHCP'
    }

    Computer JoinDomain {
        Name = $Node.ComputerName
        DomainName = $Node.IPDomainName
        Credential = $CredDomain
        DependsOn = '[DnsServerAddress]NewDnsAddress'
    }

    WindowsFeature DSCServiceInstall {
        Ensure = 'Present'
        Name = 'DSC-Service'
        DependsOn = '[Computer]JoinDomain'
    }

    WaitForCertificateServices WaitForADCS {
        CAServerFQDN = 'Cert01.dummydomain.local'
        CARootName = 'dummydomain-CERT01-CA'
        DependsOn = '[WindowsFeature]DSCServiceInstall'
    }

    CertReq PullCert {
        Credential = $CredDomain
        Subject = 'Pull01'#'CN=Pull01, OU=IT, O=dummydomain, L=City, S=CA, C=US'
        KeyLength = 2048
        CAServerFQDN = 'Cert01.dummydomain.local'
        CARootName = 'dummydomain-CERT01-CA'
        ProviderName = '"Microsoft RSA SChannel Cryptographic Provider"'
        AutoRenew = $true
        Exportable = $true
        FriendlyName = 'PullServerCert'
        KeyUsage = '0xa0'
        RequestType = 'PKCS10'
        KeyType = 'RSA'
        CertificateTemplate = 'WebServer'
        DependsOn = '[WaitForCertificateServices]WaitForADCS'
    }

    xDSCWebService PullServer {
        Ensure = 'Present'
        EndpointName = 'Pull01'
        CertificateThumbPrint = (Get-ChildItem 'Cert:\LocalMachine\My\' | Where-Object { $_.Subject -eq 'CN=Pull01'}).Thumbprint.ToString()
        Port = 8080
        State = 'Started'
        PhysicalPath = $Node.DSCPhysicalPath
        ModulePath = $Node.DSCModulePath
        ConfigurationPath = $Node.DSCConfigurationPath
        RegistrationKeyPath = $Node.DSCRegistrationKeyPath
        UseSecurityBestPractices = $true
        DependsOn = '[CertReq]PullCert'
    }

    File RegistrationKeyFile {
        Ensure = 'Present'
        Type = 'File'
        DestinationPath = $Node.FileDSCRegistrationPath
        Contents = $DSCRegistration
        DependsOn = '[xDSCWebService]PullServer'
    }
}

}

$ConfigData = @{
AllNodes = @(
@{
NodeName = ‘localhost’
IPInterfaceAlias = ‘Ethernet0’
IPAddressFamily = ‘IPv4’
IPDomainName = ‘dummydomain.local’
ComputerName = ‘Pull01’
DSCPhysicalPath = “$env:SystemDrive\inetpub\PullServer”
DSCModulePath = “$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules”
DSCConfigurationPath = “$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration”
DSCRegistrationKeyPath = “$env:PROGRAMFILES\WindowsPowerShell\DscService”
FileDSCRegistrationPath = “$env:ProgramFiles\WindowsPowerShell\DscService\RegistrationKeys.txt”
PSDScAllowDomainUser = $true
PSDscAllowPlainTextPassword = $true
}
)
}

$CredDomain = Get-Credential -Message ‘Enter Domain Credentials’ -UserName ‘dummydomain\administrator’

$DSCRegistration = (New-Guid).ToString()

Build-Pull01 -ConfigurationData $ConfigData -Verbose -CredDomain $CredDomain -DSCRegistration $DSCRegistration -CertificateFile $CertificateFile

Start-DscConfiguration -Wait -Force -Path ‘c:\Build-Pull01’ -Verbose


 

DSC is a great technology. In my recently published book (), I devote a chapter to DSC. The supporting scripts are on GItHub at: https://github.com/doctordns/PowerShellCookBook2019/tree/master/Chapter%2010%20-%20Implementing%20Desired%20State%20Configuration)

These show you how I created a pull server and how I careate a pull server using partial configuration (as well as using a push mechanism).

Personally, I use Self-signed certificates for testing - as shown in the scripts I pointed out. You could easily change those out for Public Certs and proceed in thbe same way (a certificate is, after all, just a certificate).

Thanks for the prompt reply. I will have to dig deeper into partial configurations. For now, I split the dsc configuration into two parts. The first part brings the machine to the point where it would have been deployed from a template (name computer, setup network, join domain…). The second configuration takes care of the rest.

I’m sure this is not the “most correct” way to do it, but it works.

Thanks Again!