How to Configure Desired State Configuration (DSC) Pull Server on Windows Server 2025
Desired State Configuration is PowerShell’s declarative system configuration framework. Rather than writing imperative scripts that install software and set registry keys step by step, you describe the end state you want — which Windows features should be installed, which services should be running, which files should exist — and DSC’s Local Configuration Manager (LCM) enforces that state on every managed node. A Pull Server extends this model to a fleet: nodes periodically check a central server for updated configurations and apply them automatically, with no administrator involvement after initial setup. This guide walks through building a complete DSC Pull Server on Windows Server 2025, publishing configurations, and configuring target nodes to pull automatically.
Prerequisites
- Windows Server 2025 for both the pull server and target nodes
- PowerShell 5.1 (DSC v1) — the built-in engine on Windows Server 2025
- Administrator access on all servers
- IIS available for HTTP/HTTPS pull mode, or a shared SMB path for file-based pull
- Network connectivity between nodes and the pull server on port 8080 (HTTP) or 443 (HTTPS)
- A valid SSL certificate for HTTPS deployments (strongly recommended for production)
Step 1: Install the DSC Service Role
The HTTP DSC Pull Server runs as an IIS web application powered by the DSC-Service Windows feature. Install it along with IIS on the pull server.
# On the pull server
Install-WindowsFeature -Name DSC-Service, Web-Server, Web-Mgmt-Tools -IncludeManagementTools
# Verify installation
Get-WindowsFeature DSC-Service, Web-Server | Format-Table Name, Installed, InstallState
Step 2: Configure the DSC Pull Server
DSC provides a built-in DSC configuration named xDscWebService (from the xPSDesiredStateConfiguration module) for setting up the pull server. This is meta-configuration: using DSC to configure the DSC infrastructure itself.
# Install the required module on the pull server
Install-Module xPSDesiredStateConfiguration -Force -AllowClobber
Import-DscResource -ModuleName xPSDesiredStateConfiguration
# DSC Configuration to set up the pull server
configuration PullServerSetup {
param (
[string]$NodeName = 'localhost',
[string]$CertThumbprint = 'AllowUnencryptedTraffic', # replace with real cert thumbprint
[string]$RegistrationKey = (New-Guid).Guid
)
Import-DscResource -ModuleName xPSDesiredStateConfiguration
Import-DscResource -ModuleName PSDesiredStateConfiguration
Node $NodeName {
WindowsFeature DSCServiceFeature {
Ensure = 'Present'
Name = 'DSC-Service'
}
xDscWebService PSDSCPullServer {
Ensure = 'Present'
EndpointName = 'PSDSCPullServer'
Port = 8080
PhysicalPath = "$env:SystemDriveinetpubPSDSCPullServer"
CertificateThumbPrint = $CertThumbprint
ModulePath = "$env:PROGRAMFILESWindowsPowerShellDscServiceModules"
ConfigurationPath = "$env:PROGRAMFILESWindowsPowerShellDscServiceConfiguration"
State = 'Started'
RegistrationKeyPath = "$env:PROGRAMFILESWindowsPowerShellDscService"
AcceptSelfSignedCertificates = $true
UseSecurityBestPractices = $false # set true with valid cert
DependsOn = '[WindowsFeature]DSCServiceFeature'
}
File RegistrationKeyFile {
Ensure = 'Present'
Type = 'File'
DestinationPath = "$env:PROGRAMFILESWindowsPowerShellDscServiceRegistrationKeys.txt"
Contents = $RegistrationKey
}
}
}
# Compile the configuration (creates localhost.mof)
PullServerSetup -OutputPath 'C:DSCPullServerMof'
# Note the registration key — target nodes need this
Write-Host "Registration Key: "
# Apply the configuration
Start-DscConfiguration -Path 'C:DSCPullServerMof' -Wait -Verbose -Force
After applying this configuration, IIS will host the DSC pull endpoint at http://pullserver:8080/PSDSCPullServer.svc.
Step 3: Write a Node Configuration
Create a DSC configuration that describes the desired state for a managed server. This example ensures IIS is installed, a specific service is running, and a configuration file exists.
# File: C:DSCConfigsWebServerConfig.ps1
configuration WebServerConfig {
param (
[string[]]$NodeName = 'WebServer01'
)
Import-DscResource -ModuleName PSDesiredStateConfiguration
Node $NodeName {
WindowsFeature IIS {
Ensure = 'Present'
Name = 'Web-Server'
}
WindowsFeature IISManagement {
Ensure = 'Present'
Name = 'Web-Mgmt-Tools'
DependsOn = '[WindowsFeature]IIS'
}
Service W3SVC {
Name = 'W3SVC'
State = 'Running'
StartupType = 'Automatic'
DependsOn = '[WindowsFeature]IIS'
}
File DefaultSitePage {
Ensure = 'Present'
Type = 'File'
DestinationPath = 'C:inetpubwwwrootindex.html'
Contents = 'Managed by DSC
'
DependsOn = '[WindowsFeature]IIS'
}
Registry MaxConnections {
Ensure = 'Present'
Key = 'HKLM:SYSTEMCurrentControlSetServicesHTTPParameters'
ValueName = 'MaxConnections'
ValueData = '16384'
ValueType = 'DWord'
}
}
}
# Compile — produces WebServer01.mof
WebServerConfig -OutputPath 'C:DSCCompiled'
Step 4: Generate Checksums and Publish to Pull Server
The pull server uses checksums to detect when a node’s configuration has changed. Every .mof file needs a matching .mof.checksum file. DSC resource modules published to the pull server need the same treatment.
$configRepo = "$env:PROGRAMFILESWindowsPowerShellDscServiceConfiguration"
$moduleRepo = "$env:PROGRAMFILESWindowsPowerShellDscServiceModules"
# Publish the compiled MOF
Copy-Item 'C:DSCCompiledWebServer01.mof' -Destination $configRepo
# Generate the checksum
New-DscChecksum -Path "$configRepoWebServer01.mof" -Force
# Verify both files exist
Get-ChildItem $configRepo | Format-Table Name, Length, LastWriteTime
# Publish a DSC resource module to the pull server
# Modules must be zipped with the correct naming: ModuleName_Version.zip
$moduleName = 'xWebAdministration'
$moduleVersion = (Get-Module xWebAdministration -ListAvailable).Version.ToString()
$moduleSource = "$env:PROGRAMFILESWindowsPowerShellModules$moduleName"
$zipName = "${moduleName}_${moduleVersion}.zip"
Compress-Archive -Path $moduleSource -DestinationPath "$moduleRepo$zipName" -Force
New-DscChecksum -Path "$moduleRepo$zipName" -Force
Write-Host "Published $zipName to pull server module repository"
Step 5: Configure the LCM on Target Nodes
Each target node needs its LCM configured to pull from the pull server. The LCM configuration is itself a special DSC metaconfiguration compiled with the [DscLocalConfigurationManager()] attribute and applied with Set-DscLocalConfigurationManager.
# Run this on the target node or push via Invoke-Command
# ConfigurationID must match the MOF filename on the pull server (without .mof)
[DSCLocalConfigurationManager()]
configuration LCMPullConfig {
param(
[string]$NodeName = 'localhost',
[string]$PullServerURL = 'http://PULLSERVER01:8080/PSDSCPullServer.svc',
[string]$RegistrationKey = 'YOUR-REGISTRATION-KEY-GUID',
[string]$ConfigurationID = 'WebServer01'
)
Node $NodeName {
Settings {
RefreshMode = 'Pull'
ConfigurationMode = 'ApplyAndAutoCorrect' # drift detection enabled
RefreshFrequencyMins = 30
RebootNodeIfNeeded = $true
AllowModuleOverwrite = $true
ConfigurationID = $ConfigurationID
}
ConfigurationRepositoryWeb PullServerWeb {
ServerURL = $PullServerURL
RegistrationKey = $RegistrationKey
ConfigurationNames = @($ConfigurationID)
}
ResourceRepositoryWeb PullServerModules {
ServerURL = $PullServerURL
RegistrationKey = $RegistrationKey
}
ReportServerWeb PullServerReporting {
ServerURL = $PullServerURL
RegistrationKey = $RegistrationKey
}
}
}
# Compile and apply the LCM configuration
LCMPullConfig -OutputPath 'C:DSCLCMConfig'
Set-DscLocalConfigurationManager -Path 'C:DSCLCMConfig' -Verbose -Force
# Verify LCM settings
Get-DscLocalConfigurationManager | Format-List RefreshMode, ConfigurationMode, ConfigurationID
Step 6: Force and Monitor Configuration Application
# Trigger an immediate pull and apply cycle (don't wait for the 30-minute interval)
Update-DscConfiguration -Wait -Verbose
# Check current DSC status
Get-DscConfigurationStatus | Format-List
# Check for configuration drift
Test-DscConfiguration -Detailed
# View the last DSC event log entries
Get-WinEvent -LogName 'Microsoft-Windows-DSC/Operational' -MaxEvents 20 |
Select-Object TimeCreated, Id, Message | Format-Table -Wrap -AutoSize
# On the pull server — view compliance reports submitted by nodes
# Reports are stored in the pull server's database
$reportPath = "$env:PROGRAMFILESWindowsPowerShellDscService"
Get-ChildItem $reportPath -Filter '*.edb'
Step 7: Azure Automation DSC as a Fully Managed Alternative
Running your own pull server means managing IIS, the database, and the server’s availability. Azure Automation DSC provides the same pull semantics as a fully managed service — no infrastructure to operate.
# Import a DSC configuration into Azure Automation
Import-AzAutomationDscConfiguration `
-AutomationAccountName 'aa-production' `
-ResourceGroupName 'rg-automation' `
-SourcePath 'C:DSCConfigsWebServerConfig.ps1' `
-Published `
-Force
# Compile the configuration for a specific node
$params = @{ NodeName = 'WebServer01' }
Start-AzAutomationDscCompilationJob `
-AutomationAccountName 'aa-production' `
-ResourceGroupName 'rg-automation' `
-ConfigurationName 'WebServerConfig' `
-Parameters $params
# Register an Azure VM or Arc server as a DSC node
Register-AzAutomationDscNode `
-AutomationAccountName 'aa-production' `
-ResourceGroupName 'rg-automation' `
-AzureVMName 'WS2025-WEB01' `
-AzureVMResourceGroup 'rg-production' `
-NodeConfigurationName 'WebServerConfig.WebServer01' `
-ConfigurationMode 'ApplyAndAutoCorrect' `
-RebootNodeIfNeeded $true
# Check node compliance status
Get-AzAutomationDscNode `
-AutomationAccountName 'aa-production' `
-ResourceGroupName 'rg-automation' |
Select-Object Name, Status, LastSeen | Format-Table -AutoSize
Conclusion
A DSC Pull Server on Windows Server 2025 gives your organization an automated configuration enforcement layer that works continuously, not just at initial deployment. Nodes check in every 30 minutes by default, detect drift, and correct it without human intervention. The combination of compiled MOF files, published checksums, and LCM pull mode scales from a handful of servers to hundreds. For teams who prefer to avoid managing pull server infrastructure, Azure Automation DSC delivers the same compliance-driven model as a fully managed service with built-in reporting and integration with Azure Monitor. Either way, moving from imperative configuration scripts to DSC declarative configurations means your server states are documented, testable, version-controlled, and self-healing.