What Is PowerShell Desired State Configuration?

Desired State Configuration (DSC) is a PowerShell management platform that allows administrators to declaratively define the desired state of servers and continuously enforce that state. Rather than running imperative scripts that might leave servers in inconsistent states, DSC configurations describe what a server should look like—which features are installed, which services are running, which files exist—and the Local Configuration Manager (LCM) on each node pulls or receives configurations to apply them.

A DSC Pull Server is a central repository from which target nodes periodically retrieve their configurations and report compliance back. This is the scalable model for managing dozens or hundreds of servers from a single control point on Windows Server 2019.

Architecture of a DSC Pull Server

The pull server is an OData web service hosted by IIS on Windows Server 2019. Target nodes (managed servers) have their LCM configured to contact the pull server URL every N minutes, download their assigned configuration MOF file, apply it, and then report status back to a compliance database. All communication uses HTTPS.

Prerequisites on the Pull Server


# On the server that will host the pull service (e.g., PULL-SRV01)
# Install IIS and DSC service modules
Install-WindowsFeature -Name Web-Server, Web-Mgmt-Tools -IncludeManagementTools

# Install the xPSDesiredStateConfiguration module from PSGallery
Install-PackageProvider -Name NuGet -Force
Install-Module -Name xPSDesiredStateConfiguration -Force -Scope AllUsers
Install-Module -Name PSDesiredStateConfiguration   -Force -Scope AllUsers

# Verify
Get-Module -ListAvailable xPSDesiredStateConfiguration

Generate a Self-Signed HTTPS Certificate for the Pull Service


$pullFQDN = 'pull-srv01.corp.local'

$cert = New-SelfSignedCertificate `
    -DnsName $pullFQDN `
    -CertStoreLocation 'Cert:LocalMachineMy' `
    -NotAfter (Get-Date).AddYears(3)

$certThumb = $cert.Thumbprint

# Export for import on target nodes (for trust)
$certPwd = ConvertTo-SecureString -String 'CertPa$$w0rd!' -AsPlainText -Force
Export-PfxCertificate -Cert "Cert:LocalMachineMy$certThumb" `
    -FilePath 'C:DSCPullServerCert.pfx' -Password $certPwd
Export-Certificate -Cert "Cert:LocalMachineMy$certThumb" `
    -FilePath 'C:DSCPullServerCert.cer'

Write-Output "Certificate thumbprint: $certThumb"

Configure the DSC Pull Server Using a DSC Configuration

Bootstrapping the pull server itself with DSC is the recommended approach—it is idempotent and self-documenting:


Configuration PullServerSetup {
    Import-DscResource -ModuleName xPSDesiredStateConfiguration
    Import-DscResource -ModuleName PSDesiredStateConfiguration

    Node 'PULL-SRV01' {

        WindowsFeature IIS {
            Ensure = 'Present'
            Name   = 'Web-Server'
        }

        WindowsFeature DSCServiceFeature {
            Ensure    = 'Present'
            Name      = 'DSC-Service'
            DependsOn = '[WindowsFeature]IIS'
        }

        xDscWebService PSDSCPullServer {
            Ensure                  = 'Present'
            EndpointName            = 'PSDSCPullServer'
            Port                    = 8080
            PhysicalPath            = 'C:inetpubPSDSCPullServer'
            CertificateThumbPrint   = $certThumb   # variable from outer scope
            ModulePath              = 'C:Program FilesWindowsPowerShellDscServiceModules'
            ConfigurationPath       = 'C:Program FilesWindowsPowerShellDscServiceConfiguration'
            State                   = 'Started'
            DependsOn               = '[WindowsFeature]DSCServiceFeature'
            UseSecurityBestPractices = $true
        }

        xDscWebService PSDSCComplianceServer {
            Ensure                  = 'Present'
            EndpointName            = 'PSDSCComplianceServer'
            Port                    = 9080
            PhysicalPath            = 'C:inetpubPSDSCComplianceServer'
            CertificateThumbPrint   = $certThumb
            IsComplianceServer      = $true
            State                   = 'Started'
            DependsOn               = '[xDscWebService]PSDSCPullServer'
        }
    }
}

# Compile to MOF and apply
PullServerSetup -OutputPath 'C:DSCPullServerConfig'
Start-DscConfiguration -Path 'C:DSCPullServerConfig' -Wait -Verbose -Force

Create and Publish a Node Configuration

Write a DSC configuration for a managed node, compile it to a MOF, checksum it, and publish it to the pull server’s configuration directory:


Configuration WebServerBaseline {
    Import-DscResource -ModuleName PSDesiredStateConfiguration

    Node 'WEB-SRV01' {

        WindowsFeature IIS {
            Ensure = 'Present'
            Name   = 'Web-Server'
        }

        WindowsFeature AspNet45 {
            Ensure    = 'Present'
            Name      = 'Web-Asp-Net45'
            DependsOn = '[WindowsFeature]IIS'
        }

        Service W3SVC {
            Name        = 'W3SVC'
            State       = 'Running'
            StartupType = 'Automatic'
            DependsOn   = '[WindowsFeature]IIS'
        }

        File DefaultPage {
            Ensure          = 'Present'
            DestinationPath = 'C:inetpubwwwrootindex.html'
            Contents        = 'Managed by DSC'
            Type            = 'File'
        }

        Registry DisableNetBIOS {
            Ensure    = 'Present'
            Key       = 'HKLM:SYSTEMCurrentControlSetservicesNetBTParameters'
            ValueName = 'NetbiosOptions'
            ValueData = '2'
            ValueType = 'Dword'
        }
    }
}

# Compile
WebServerBaseline -OutputPath 'C:DSCConfigurations'

# Create checksum file (required for pull server)
New-DscChecksum -Path 'C:DSCConfigurationsWEB-SRV01.mof' -Force

# Copy to pull server configuration directory
Copy-Item 'C:DSCConfigurationsWEB-SRV01.mof'      `
    -Destination 'C:Program FilesWindowsPowerShellDscServiceConfiguration'
Copy-Item 'C:DSCConfigurationsWEB-SRV01.mof.checksum' `
    -Destination 'C:Program FilesWindowsPowerShellDscServiceConfiguration'

Configure the LCM on Target Nodes

Each managed node needs its Local Configuration Manager pointed at the pull server. Generate a unique configuration ID (GUID) per node or use the node’s name-based ID:


[DSCLocalConfigurationManager()]
Configuration LCMConfig {
    Node 'WEB-SRV01' {
        Settings {
            RefreshMode          = 'Pull'
            ConfigurationMode    = 'ApplyAndAutoCorrect'
            RefreshFrequencyMins = 30
            RebootNodeIfNeeded   = $true
            AllowModuleOverwrite = $true
        }

        ConfigurationRepositoryWeb PullSrv {
            ServerURL          = 'https://pull-srv01.corp.local:8080/PSDSCPullServer.svc'
            AllowUnsecureConnection = $false
            CertificateID      = $certThumb    # pull server's cert thumbprint
            ConfigurationNames = @('WEB-SRV01')
        }

        ReportServerWeb ComplianceSrv {
            ServerURL          = 'https://pull-srv01.corp.local:9080/PSDSCComplianceServer.svc'
            CertificateID      = $certThumb
        }

        ResourceRepositoryWeb PullSrvModules {
            ServerURL          = 'https://pull-srv01.corp.local:8080/PSDSCPullServer.svc'
            CertificateID      = $certThumb
        }
    }
}

# Compile and apply LCM config on the target node
LCMConfig -OutputPath 'C:DSCLCM'
Set-DscLocalConfigurationManager -Path 'C:DSCLCM' -ComputerName 'WEB-SRV01' -Verbose

Monitoring DSC Compliance


# Check current state on target node
Get-DscConfigurationStatus -ComputerName WEB-SRV01
Get-DscLocalConfigurationManager -ComputerName WEB-SRV01

# Force an immediate pull and apply
Update-DscConfiguration -ComputerName WEB-SRV01 -Wait -Verbose

# Test config without applying
Test-DscConfiguration -ComputerName WEB-SRV01 -Detailed

# Get detailed resource-level compliance from compliance server
Invoke-RestMethod -Uri 'https://pull-srv01.corp.local:9080/PSDSCComplianceServer.svc/Nodes' |
    Select-Object NodeName, Status, LastComplianceTime | Format-Table

# View DSC event log for errors
Get-WinEvent -LogName 'Microsoft-Windows-DSC/Operational' -MaxEvents 50 |
    Where-Object { $_.LevelDisplayName -eq 'Error' } |
    Select-Object TimeCreated, Message

Conclusion

A DSC Pull Server on Windows Server 2019 provides centralized, continuous configuration enforcement across an entire server fleet. Nodes automatically correct configuration drift every 30 minutes and report compliance status back to the server. By combining DSC configurations with secure HTTPS communication, certificate-based authentication, and the compliance server, organizations can maintain a proven, auditable baseline across hundreds of servers with minimal ongoing manual effort.