Check if remote node is fully compliant with DSC configuration

I’m creating a script which;

  • Creates a new VM
  • Transfers metaconfig.mof to VM, which;
    – Sets LCM to Pull mode and grabs GUID.mof which;
    — Change hostname
    — Change IP
    — Dcpromo

Afterwards my script will do psremoting to this machine to create OU structure, install software et cetera.

I need to check if the VM is in the desired state before continuing. But when I grab the Node info from the pull server NodeComplaint equals True and Status equals 0 before the actual dcpromo ran. How can I programmatically wait for DSC to actually finish?

Setting the pull mode:

Configuration SetPullMode {
    param(
        [string]$guid,
        [string]$computername
    )

    Node $Computername
    {
        LocalConfigurationManager
        {
            ConfigurationMode = 'ApplyAndAutoCorrect'
            ConfigurationID = $guid
            RefreshMode = 'Pull'
            DownloadManagerName = 'WebDownloadManager'
            DownloadManagerCustomData = @{
                ServerUrl = 'http://172.20.20.1:8080/PSDSCPullServer.svc';
                AllowUnsecureConnection = 'true'
                }
            RebootNodeIfNeeded = $True
        }
    }
}
$Guid = New-Guid
SetPullMode -guid $guid -computername localhost

Generating MOF:

Configuration DC01 {
    Param (
        [PScredential]$Credential
    )

    Import-DscResource -Module xNetworking,xActiveDirectory,PSDesiredStateConfiguration,xComputerManagement

    Node $AllNodes.NodeName {

        xComputer ComputerName {
            Name = 'DC01'
            DependsOn = "[xIPAddress]IPAddress"
        }

        xIPAddress IPAddress {
            InterfaceAlias = "Ethernet"
            IPAddress = $AllNodes.IP
            AddressFamily = "IPV4"
            SubnetMask = $AllNodes.Mask 
        }
        xDefaultGatewayAddress Gateway {
            InterfaceAlias = "Ethernet"
            AddressFamily = "IPV4"
            Address = $AllNodes.Gateway
            DependsOn = "[xIPAddress]IPAddress"
        }
        xDNSServerAddress DNSServer {
            InterfaceAlias = "Ethernet"
            AddressFamily = "IPV4"
            Address = $AllNodes.IP
            DependsOn = "[xIPAddress]IPAddress"
        }


        File ADFiles            
        {            
            DestinationPath = 'C:\NTDS'            
            Type = 'Directory'            
            Ensure = 'Present'            
        }
        
        File Unattend
        {            
            DestinationPath = "C:\unattend.xml"
            Type = 'File'            
            Ensure = 'Absent'            
        } 
        
        File WindowsImage
        {            
            DestinationPath = "C:\Convert-WindowsImageInfo.txt"
            Type = 'File'            
            Ensure = 'Absent'            
        }      
                    
        WindowsFeature ADDSInstall             
        {             
            Ensure = "Present"             
            Name = "AD-Domain-Services"             
        }            
            
        # Optional GUI tools            
        WindowsFeature ADDSTools            
        {             
            Ensure = "Present"             
            Name = "RSAT-ADDS"             
        }  
        
        WindowsFeature DotNet35
        {
            Ensure = "Present"
            Name = "Net-Framework-Core"
        }          
            
        # No slash at end of folder paths            
        xADDomain FirstDS             
        {             
            DomainName = $AllNodes.DomainName            
            DomainAdministratorCredential = $Credential             
            SafemodeAdministratorPassword = $Credential          
            DatabasePath = 'C:\NTDS'            
            LogPath = 'C:\NTDS'            
            DependsOn = "[WindowsFeature]ADDSInstall","[File]ADFiles","[xComputer]ComputerName"
                       
        }
            
        File Done
        {
            DestinationPath = "C:\Temp"
            Type = 'Directory'            
            Ensure = 'Present'
            DependsOn = "[xADDomain]FirstDS","[WindowsFeature]DotNet35"
        }
                    
    }
} 

Prepare .mof for machine to pull:

$ConfigData = @{             
    AllNodes = @(             
        @{  
            NodeName = "localhost"
            DomainName = "$DomainName"
            RetryCount = 20      
            RetryIntervalSec = 30
            PsDscAllowPlainTextPassword = $true
            PSDscAllowDomainUser = $True
            IP=$DC01
            Mask=$MaskBits
            Gateway=$Gateway
        }            
    )             
} 

DC01 -ConfigurationData $ConfigData -Credential $DomainCredentials -Verbose 

Rename-Item .\DC01\localhost.mof -NewName "$guid.mof"
New-DscChecksum .\DC01

Get-ChildItem .\DC01 | Move-Item -Destination "$env:ProgramFiles\WindowsPowerShell\DscService\Configuration"

After IP and hostname is set the machine reboots; the Pull server tells me:

ConfigurationId NodeCompliant LastComplianceTime StatusCode


372c6f01-c8df-4473-ac9e-c8de841a22d5 True 2016-01-23T12:51:35.3841422Z 0

But LCM is not done yet.

This all works fine if I wait a while but I need some way to check if this process is done before continuing.
I tried this, but that fails because the result looks like completion before the reboot after Dcpromo;

While ($nodeDone -eq $False) {
    Write-Verbose "Testing Node compliance..."
    $TestNode = (QueryNodeInformation).Value | 
        Where-Object -Property ConfigurationId -eq $Guid | 
        Where-Object -Property StatusCode -eq 0 | 
        Where-Object -Property NodeCompliant -eq $True
    Start-Sleep -Seconds 5
    If ($TestNode) {$NodeDone = $True}
}

Should I just:

Invoke-Command -ComputerName $DC -Credential $Cred -ScriptBlock {
Get-DscLocalConfigurationManager
}

… and take the errors for granted until .LCMState EQ ‘Idle’

Or am I missing something else?

Chuck a wait for domain resource in there. Then, add a logging resource that logs a message, and which depends on the domain wait having completed. That’ll keep the LCM in Pending (or should) until it all unravels.

Yup, that works.

        xWaitForADDomain WaitForAD
        {
            DomainName = $AllNodes.DomainName
            RetryIntervalSec = 30
            RetryCount = 999
            DomainUserCredential = $Credential
        }

Now I can check the compliance server and see if for this guid Nodecompliant -eq $True, Status -eq 0 and Dirty -eq $False.