Introduction to DSC Pull Server Architecture
Desired State Configuration (DSC) is a PowerShell-based management platform built into Windows Server that enables declarative configuration of servers. Rather than writing imperative scripts that describe how to configure a server step by step, DSC configurations describe what the end state of the server should be. The DSC engine continuously monitors the managed server and corrects configuration drift automatically. This guide covers how to deploy and manage a DSC Pull Server on Windows Server 2022, which serves as a centralized configuration distribution point for your entire server fleet.
DSC Push vs Pull Mode
DSC supports two operational modes for distributing configuration to managed nodes:
Push Mode: An administrator manually pushes configuration MOF files to target nodes using Start-DscConfiguration. Push mode is simple and requires no additional infrastructure. It is appropriate for small environments, testing, and one-off configurations. However, push mode does not provide automatic drift correction — if someone manually changes a server setting, DSC will not automatically revert it until the next push operation.
Pull Mode: Each managed node is configured with a Local Configuration Manager (LCM) that periodically contacts a Pull Server to download its assigned configuration. The LCM checks the Pull Server on a configurable schedule (default every 30 minutes), downloads the MOF if a new version is available, and applies it. Pull mode provides automatic drift remediation and scales to thousands of nodes. The Pull Server acts as a RESTful web service that nodes query for their configuration.
# Check current LCM mode on a node
Get-DscLocalConfigurationManager | Select-Object RefreshMode, ConfigurationMode, RefreshFrequencyMins
# Expected output for pull mode:
# RefreshMode : Pull
# ConfigurationMode : ApplyAndAutoCorrect
# RefreshFrequencyMins : 30
Installing the DSC Pull Server
The DSC Pull Server is implemented as an IIS web application using the OData endpoint. Install the required Windows features on the server that will host the Pull Server:
# Install the DSC Service feature (includes IIS and the pull server endpoint)
Install-WindowsFeature -Name DSC-Service -IncludeManagementTools
# Verify the feature is installed
Get-WindowsFeature DSC-Service
# Install the xPSDesiredStateConfiguration module which provides the Pull Server DSC resource
Install-Module -Name xPSDesiredStateConfiguration -Scope AllUsers -Force
# Also install supporting modules
Install-Module -Name PSDscResources -Scope AllUsers -Force
# Verify module installation
Get-Module -ListAvailable -Name xPSDesiredStateConfiguration
Requesting a Certificate for HTTPS
Pull Server communications should always be encrypted with HTTPS in production. Request a certificate from your enterprise CA or create a self-signed certificate for lab environments:
# Request a certificate from enterprise CA (preferred for production)
$certReq = @{
Subject = "CN=dscpullserver.corp.example.com"
DnsName = @("dscpullserver.corp.example.com","dscpullserver")
CertStoreLocation = "Cert:LocalMachineMy"
KeyUsage = "KeyEncipherment","DigitalSignature"
EnhancedKeyUsage = "Server Authentication"
}
# Use certreq.exe or an enrollment module for production CA requests
# For lab/testing - self-signed certificate
$cert = New-SelfSignedCertificate `
-DnsName "dscpullserver.corp.example.com","dscpullserver" `
-CertStoreLocation "Cert:LocalMachineMy" `
-KeyUsage KeyEncipherment, DigitalSignature `
-Type SSLServerAuthentication
$cert.Thumbprint # Note this for use in the Pull Server configuration
# Export and trust the cert on managed nodes (for self-signed only)
Export-Certificate -Cert $cert -FilePath "C:CertsDSCPullServer.cer"
# Then on each node: Import-Certificate -FilePath \pullsrvshareDSCPullServer.cer -CertStoreLocation Cert:LocalMachineRoot
Configuring the Pull Server with HTTPS
Use a DSC configuration to configure the Pull Server itself. This is a meta-configuration that bootstraps the Pull Server service using the xDscWebService resource from the xPSDesiredStateConfiguration module:
Configuration DSCPullServer {
param(
[string]$CertThumbprint,
[string]$RegistrationKey
)
Import-DscResource -ModuleName xPSDesiredStateConfiguration
Import-DscResource -ModuleName PSDesiredStateConfiguration
Node localhost {
WindowsFeature DSCServiceFeature {
Ensure = 'Present'
Name = 'DSC-Service'
}
xDscWebService PSDSCPullServer {
Ensure = 'Present'
EndpointName = 'PSDSCPullServer'
Port = 8443
PhysicalPath = "$env:SystemDriveinetpubPSDSCPullServer"
CertificateThumbprint = $CertThumbprint
ModulePath = "$env:ProgramFilesWindowsPowerShellDscServiceModules"
ConfigurationPath = "$env:ProgramFilesWindowsPowerShellDscServiceConfiguration"
State = 'Started'
RegistrationKeyPath = "$env:ProgramFilesWindowsPowerShellDscService"
UseSecurityBestPractices = $true
AcceptSelfSignedCertificates = $false
DependsOn = '[WindowsFeature]DSCServiceFeature'
}
File RegistrationKeyFile {
Ensure = 'Present'
Type = 'File'
DestinationPath = "$env:ProgramFilesWindowsPowerShellDscServiceRegistrationKeys.txt"
Contents = $RegistrationKey
}
}
}
# Generate the registration key (a GUID)
$regKey = (New-Guid).Guid
Write-Output "Registration Key: $regKey"
# Compile the Pull Server configuration
$thumbprint = (Get-ChildItem Cert:LocalMachineMy | Where-Object Subject -match 'dscpullserver').Thumbprint
DSCPullServer -CertThumbprint $thumbprint -RegistrationKey $regKey -OutputPath "C:DSCPullServer"
# Apply the configuration to set up the Pull Server
Start-DscConfiguration -Path "C:DSCPullServer" -Wait -Verbose -Force
Creating and Publishing MOF Configurations
Node configurations are compiled to MOF (Managed Object Format) files. Each node gets a MOF file named after its configuration ID or name. Create and publish a sample configuration that manages IIS on web servers:
Configuration WebServerConfig {
Import-DscResource -ModuleName PSDesiredStateConfiguration
Import-DscResource -ModuleName PSDscResources
Node WebServers {
WindowsFeature IIS {
Ensure = 'Present'
Name = 'Web-Server'
}
WindowsFeature IISManagement {
Ensure = 'Present'
Name = 'Web-Mgmt-Console'
DependsOn = '[WindowsFeature]IIS'
}
Service W3SVC {
Name = 'W3SVC'
State = 'Running'
StartupType = 'Automatic'
DependsOn = '[WindowsFeature]IIS'
}
File DefaultContent {
Ensure = 'Present'
Type = 'File'
DestinationPath = "C:inetpubwwwrootindex.html"
Contents = "Managed by DSC
"
}
Registry SecurityHeader {
Ensure = 'Present'
Key = 'HKLM:SYSTEMCurrentControlSetServicesHTTPParameters'
ValueName = 'DisableServerHeader'
ValueData = '2'
ValueType = 'Dword'
}
}
}
# Compile to MOF
WebServerConfig -OutputPath "C:DSCConfigurations"
# The MOF file will be at: C:DSCConfigurationsWebServers.mof
# Copy to Pull Server with a GUID name (using ConfigurationID method)
# Or keep the node name (using ConfigurationName/registration method)
Generating Checksum Files
Pull Server nodes verify configuration integrity by comparing a checksum before applying a MOF. Generate checksum files for every MOF and module published to the Pull Server:
$configPath = "$env:ProgramFilesWindowsPowerShellDscServiceConfiguration"
$modulePath = "$env:ProgramFilesWindowsPowerShellDscServiceModules"
# Copy compiled MOF to Pull Server configuration directory
Copy-Item "C:DSCConfigurationsWebServers.mof" -Destination "$configPathWebServers.mof"
# Generate checksum for the MOF
New-DscChecksum -Path "$configPathWebServers.mof" -Force
# Verify checksum file was created
Get-ChildItem $configPath | Format-Table Name, Length, LastWriteTime
# Publish DSC resource modules to the Pull Server modules directory
# Modules must be zipped in format: ModuleName_Version.zip
$modulesToPublish = @(
@{ Name = 'xWebAdministration'; Version = '3.3.0' },
@{ Name = 'PSDscResources'; Version = '2.12.0' }
)
foreach ($mod in $modulesToPublish) {
$source = "$env:ProgramFilesWindowsPowerShellModules$($mod.Name)"
$zipName = "$($mod.Name)_$($mod.Version).zip"
$zipPath = Join-Path $modulePath $zipName
Compress-Archive -Path $source -DestinationPath $zipPath -Force
New-DscChecksum -Path $zipPath -Force
Write-Output "Published: $zipName"
}
# List all published modules
Get-ChildItem $modulePath | Where-Object Extension -eq '.zip' |
Select-Object Name, Length, LastWriteTime
Registering Nodes with the Pull Server (LCM Configuration)
Each managed node must be configured to use the Pull Server. This is done by creating a meta-configuration that sets the Local Configuration Manager settings on the node. The Registration Key method is recommended over the legacy ConfigurationID method for new deployments:
[DSCLocalConfigurationManager()]
Configuration NodeLCMConfig {
param(
[string]$PullServerURL = 'https://dscpullserver.corp.example.com:8443/PSDSCPullServer.svc',
[string]$RegistrationKey = 'your-guid-registration-key-here',
[string]$ConfigurationName = 'WebServers'
)
Node localhost {
Settings {
RefreshMode = 'Pull'
ConfigurationMode = 'ApplyAndAutoCorrect'
RefreshFrequencyMins = 30
ConfigurationModeFrequencyMins = 15
AllowModuleOverwrite = $true
RebootNodeIfNeeded = $false
}
ConfigurationRepositoryWeb PullServer {
ServerURL = $PullServerURL
RegistrationKey = $RegistrationKey
ConfigurationNames = @($ConfigurationName)
AllowUnsecureConnection = $false
}
ResourceRepositoryWeb PullServerModules {
ServerURL = $PullServerURL
RegistrationKey = $RegistrationKey
}
ReportServerWeb PullServerReports {
ServerURL = $PullServerURL
RegistrationKey = $RegistrationKey
}
}
}
# Compile and apply LCM configuration on the node
NodeLCMConfig -OutputPath "C:DSCLCM"
# Apply the meta-configuration (must run locally or via Invoke-Command on the target node)
Set-DscLocalConfigurationManager -Path "C:DSCLCM" -Verbose -Force
# Verify LCM configuration was applied
Get-DscLocalConfigurationManager |
Select-Object RefreshMode, ConfigurationMode, RefreshFrequencyMins, LCMState |
Format-List
After applying the LCM configuration, force an immediate pull to test connectivity before waiting for the next scheduled refresh:
# Force the node to check in with Pull Server immediately
Update-DscConfiguration -Wait -Verbose
# Or trigger a consistency check (apply current configuration)
Start-DscConfiguration -UseExisting -Wait -Verbose
Monitoring DSC Compliance
Monitor the configuration compliance state of managed nodes to detect and investigate drift:
# Check configuration status on a single node
Get-DscConfigurationStatus | Select-Object Status, StartDate, DurationInSeconds, Type, Error |
Format-Table -AutoSize
# Get detailed resource-level compliance
Test-DscConfiguration -Detailed | Select-Object -ExpandProperty ResourcesNotInDesiredState |
Format-Table ResourceId, InDesiredState, Error -AutoSize
# Check status across multiple nodes using remoting
$nodes = 'WEB01','WEB02','WEB03','WEB04'
Invoke-Command -ComputerName $nodes -ScriptBlock {
$status = Get-DscConfigurationStatus -ErrorAction SilentlyContinue
[PSCustomObject]@{
ComputerName = $env:COMPUTERNAME
Status = if ($status) { $status.Status } else { 'Unknown' }
LastCheck = if ($status) { $status.StartDate } else { 'N/A' }
Error = if ($status) { $status.Error } else { 'N/A' }
}
} | Format-Table -AutoSize
# Get LCM state (Idle = compliant and waiting; Busy = applying config; PendingReboot = needs restart)
Invoke-Command -ComputerName $nodes -ScriptBlock {
(Get-DscLocalConfigurationManager).LCMState
} | Format-Table PSComputerName, * -AutoSize
Azure Automation State Configuration as an Alternative
Azure Automation State Configuration (ASC) provides a cloud-hosted DSC Pull Server service, eliminating the need to maintain on-premises Pull Server infrastructure. For organizations already using Azure, ASC is often the simpler choice:
# Import a DSC configuration into Azure Automation
Import-AzAutomationDscConfiguration `
-AutomationAccountName "myAutomationAccount" `
-ResourceGroupName "myResourceGroup" `
-SourcePath "C:DSCConfigurationsWebServerConfig.ps1" `
-Published `
-Force
# Compile the configuration in Azure
$compiledJob = Start-AzAutomationDscCompilationJob `
-AutomationAccountName "myAutomationAccount" `
-ResourceGroupName "myResourceGroup" `
-ConfigurationName "WebServerConfig"
# Wait for compilation
do {
Start-Sleep -Seconds 10
$compiledJob = $compiledJob | Get-AzAutomationDscCompilationJob
} until ($compiledJob.Status -ne 'Running')
Write-Output "Compilation status: $($compiledJob.Status)"
# Register an Azure VM with Azure Automation State Configuration
Register-AzAutomationDscNode `
-AutomationAccountName "myAutomationAccount" `
-ResourceGroupName "myResourceGroup" `
-AzureVMName "WEB01" `
-AzureVMResourceGroup "webservers-rg" `
-NodeConfigurationName "WebServerConfig.WebServers" `
-RebootNodeIfNeeded $false `
-ConfigurationMode "ApplyAndAutoCorrect" `
-RefreshFrequencyMins 30
Troubleshooting the DSC Pull Server
Common Pull Server issues and how to diagnose them:
# Test Pull Server reachability from a node
$pullServerUrl = 'https://dscpullserver.corp.example.com:8443/PSDSCPullServer.svc'
try {
$response = Invoke-WebRequest -Uri $pullServerUrl -UseDefaultCredentials -ErrorAction Stop
Write-Output "Pull Server reachable: HTTP $($response.StatusCode)"
}
catch {
Write-Warning "Pull Server unreachable: $($_.Exception.Message)"
}
# Check IIS application pool status on Pull Server
Get-WebConfigurationProperty -Filter 'system.applicationHost/applicationPools/add[@name="PSWS"]' `
-Name 'state' -PSPath 'IIS:'
# Restart the Pull Server application pool if needed
Restart-WebAppPool -Name 'PSWS'
# Check DSC event log for errors on a managed node
Get-WinEvent -LogName 'Microsoft-Windows-Desired State Configuration/Operational' `
-MaxEvents 50 |
Where-Object LevelDisplayName -in 'Error','Warning' |
Select-Object TimeCreated, Id, LevelDisplayName, Message |
Format-Table -AutoSize -Wrap
# Check Pull Server IIS logs for 404 errors (missing MOF or checksum)
Get-Content "$env:SystemDriveinetpublogsLogFilesW3SVC1*.log" -Tail 50 |
Where-Object { $_ -match ' 404 ' } |
Select-Object -Last 20
# Manually test if a node can download its MOF
# ConfigurationID or ConfigurationName must match what's published
$nodeId = (Get-DscLocalConfigurationManager).ConfigurationID
Invoke-WebRequest -Uri "$pullServerUrl/Nodes(ConfigurationId='$nodeId')/ConfigurationContent" `
-Certificate (Get-ChildItem Cert:LocalMachineMy | Where-Object Subject -match 'nodeserver') `
-OutFile "C:Temptest.mof"
# Check MOF and checksum file pairing on Pull Server
$configPath = "$env:ProgramFilesWindowsPowerShellDscServiceConfiguration"
$mofs = Get-ChildItem $configPath -Filter *.mof | Select-Object -ExpandProperty Name
$checksums = Get-ChildItem $configPath -Filter *.checksum | Select-Object -ExpandProperty Name
$missing = $mofs | Where-Object { "$_.checksum" -notin $checksums }
if ($missing) { Write-Warning "Missing checksums for: $($missing -join ', ')" }
DSC Pull Server is a powerful infrastructure component that enables consistent, self-healing server configurations across your Windows Server 2022 environment. For new deployments, evaluate whether Azure Automation State Configuration meets your needs before investing in maintaining on-premises Pull Server infrastructure. For air-gapped or compliance-restricted environments where cloud connectivity is not permitted, an on-premises Pull Server with proper certificate-based HTTPS encryption and regular monitoring provides the same configuration management capabilities while keeping all data within your network boundaries.