How to Configure a DSC Pull Server on Windows Server 2012 R2
Desired State Configuration (DSC) is a PowerShell-based configuration management platform built into Windows Server 2012 R2. In push mode, an administrator pushes configurations directly to individual nodes. In pull mode, nodes periodically contact a central Pull Server to retrieve their configuration and report compliance status. A Pull Server enables at-scale configuration management across hundreds of servers without requiring administrative access to each node at configuration time. This guide walks through deploying a DSC Pull Server with HTTP and compliance reporting.
Prerequisites
– Windows Server 2012 R2 with PowerShell 4.0 or higher
– IIS role installed (Pull Server uses IIS or SMB file share)
– xPSDesiredStateConfiguration DSC resource module
– Certificates for HTTPS (recommended for production) or HTTP for lab
– Network access from all managed nodes to the Pull Server on port 8080 (HTTP) or 443 (HTTPS)
– Administrative credentials on the Pull Server
Step 1: Install Required DSC Resource Modules
The Pull Server configuration requires the xPSDesiredStateConfiguration module from the PowerShell Gallery or DSC Resource Kit:
# Install NuGet provider first
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
# Install the xPSDesiredStateConfiguration module
Install-Module -Name xPSDesiredStateConfiguration -Force
Get-DSCResource -Module xPSDesiredStateConfiguration | Select-Object Name
# Also install the IIS role on the future Pull Server
Install-WindowsFeature -Name Web-Server, Web-Mgmt-Tools, Web-Default-Doc,
Web-Dir-Browsing, Web-Http-Errors, Web-Static-Content,
Web-Windows-Auth, Web-ISAPI-Ext, Web-ISAPI-Filter,
Web-Net-Ext45, Web-ASP-Net45, Web-Log-Libraries -IncludeManagementTools
Step 2: Create the DSC Pull Server Configuration
Use a DSC configuration to bootstrap the Pull Server itself. This is a meta-bootstrap: you apply DSC to configure DSC:
Configuration DSCPullServer {
param(
[string]$NodeName = 'localhost',
[string]$CertThumbprint = 'AllowUnencryptedTraffic', # Replace with cert thumbprint in prod
[int]$Port = 8080
)
Import-DscResource -ModuleName PSDesiredStateConfiguration
Import-DscResource -ModuleName xPSDesiredStateConfiguration
Node $NodeName {
WindowsFeature DSCServiceFeature {
Ensure = "Present"
Name = "DSC-Service"
}
WindowsFeature WebServer {
Ensure = "Present"
Name = "Web-Server"
IncludeAllSubFeature = $false
}
xDscWebService PullServerHTTP {
Ensure = "Present"
EndpointName = "DSCPullServer"
Port = $Port
PhysicalPath = "$env:SystemDriveinetpubPSDSCPullServer"
CertificateThumbPrint = $CertThumbprint
ModulePath = "$env:PROGRAMFILESWindowsPowerShellDscServiceModules"
ConfigurationPath = "$env:PROGRAMFILESWindowsPowerShellDscServiceConfiguration"
State = "Started"
DatabasePath = "$env:PROGRAMFILESWindowsPowerShellDscService"
RegistrationKeyPath = "$env:PROGRAMFILESWindowsPowerShellDscService"
AcceptSelfSignedCertificates = $true
UseSecurityBestPractices = $false # Set to $true with proper TLS cert
DependsOn = "[WindowsFeature]DSCServiceFeature","[WindowsFeature]WebServer"
}
xDscWebService ComplianceServer {
Ensure = "Present"
EndpointName = "DSCComplianceServer"
Port = 9080
PhysicalPath = "$env:SystemDriveinetpubPSDSCComplianceServer"
CertificateThumbPrint = $CertThumbprint
IsComplianceServer = $true
State = "Started"
DependsOn = "[xDscWebService]PullServerHTTP"
}
}
}
# Compile the MOF
DSCPullServer -NodeName "dscpull01" -OutputPath "C:DSCPullServerMOF"
# Apply it
Start-DscConfiguration -Path "C:DSCPullServerMOF" -Wait -Verbose -Force
Step 3: Create a Registration Key
Nodes authenticate to the Pull Server using a shared registration key (GUID). Create this file on the Pull Server:
# Generate a GUID to use as the registration key
$registrationKey = [System.Guid]::NewGuid().ToString()
Write-Host "Registration Key: $registrationKey"
# Save to the required location
$keyPath = "$env:PROGRAMFILESWindowsPowerShellDscServiceRegistrationKeys.txt"
$registrationKey | Out-File $keyPath -Encoding ASCII -Force
# Record this key — nodes need it during LCM configuration
# Example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Step 4: Publish a Node Configuration to the Pull Server
Write a DSC configuration for a node, compile it to MOF, generate a checksum, and copy it to the Pull Server’s configuration repository:
# Example node configuration: enforce that IIS is installed
Configuration WebServerBaseline {
param([string[]]$NodeName)
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"
}
}
}
# Compile — the output MOF file will be named after the NodeName
# For Pull mode, the MOF must be renamed to the node's ConfigurationID (a GUID)
WebServerBaseline -NodeName "web01.corp.local" -OutputPath "C:DSCConfigs"
# Rename to the node's ConfigurationID
$nodeConfigID = "5cf20a8b-1234-5678-abcd-ef0123456789" # Must match LCM setting on node
Copy-Item "C:DSCConfigsweb01.corp.local.mof" `
"C:DSCConfigs$nodeConfigID.mof"
# Generate checksum
New-DscChecksum -Path "C:DSCConfigs$nodeConfigID.mof" -Force
# Copy to Pull Server configuration store
$pullConfigPath = "$env:PROGRAMFILESWindowsPowerShellDscServiceConfiguration"
Copy-Item "C:DSCConfigs$nodeConfigID.mof" $pullConfigPath
Copy-Item "C:DSCConfigs$nodeConfigID.mof.checksum" $pullConfigPath
Step 5: Publish DSC Resource Modules to Pull Server
Nodes require any DSC resource modules referenced in configurations to be available on the Pull Server. Package and publish them:
$modulesPath = "$env:PROGRAMFILESWindowsPowerShellDscServiceModules"
# Publish a DSC resource module (xWebAdministration example)
# First, find the module locally
$module = Get-Module xWebAdministration -ListAvailable | Select-Object -First 1
# Package as a zip
$zipName = "$($module.Name)_$($module.Version).zip"
Compress-Archive -Path $module.ModuleBase -DestinationPath "$modulesPath$zipName" -Force
# Generate checksum
New-DscChecksum -Path "$modulesPath$zipName" -Force
# Verify the module store
Get-ChildItem $modulesPath | Format-Table Name, Length -AutoSize
Step 6: Test the Pull Server Endpoint
Verify the Pull Server is responding correctly before pointing nodes at it:
# Test the Pull Server action handler
$pullServerURL = "http://dscpull01.corp.local:8080"
# Check the web service is responding
Invoke-WebRequest -Uri "$pullServerURL/PSDSCPullServer.svc" -UseBasicParsing |
Select-Object StatusCode, StatusDescription
# Verify configuration endpoint
Invoke-WebRequest -Uri "$pullServerURL/PSDSCPullServer.svc/Action(ConfigurationId='$nodeConfigID')/ConfigurationContent" `
-UseBasicParsing -Headers @{"ProtocolVersion"="2.0"} |
Select-Object StatusCode
# Check IIS application pool is running
Get-WebConfiguration "system.applicationHost/applicationPools/add[@name='PSWS']" |
Select-Object name, state
# Check Windows Event Log for DSC errors
Get-WinEvent -LogName "Microsoft-Windows-Desired State Configuration/Operational" -MaxEvents 20 |
Where-Object { $_.Level -le 3 } |
Format-Table TimeCreated, LevelDisplayName, Message -Wrap
Step 7: Firewall Rules for Pull Server
# Allow inbound connections to Pull Server ports
New-NetFirewallRule -DisplayName "DSC Pull Server HTTP" `
-Direction Inbound -Protocol TCP -LocalPort 8080 `
-Action Allow -Profile Domain
New-NetFirewallRule -DisplayName "DSC Compliance Server HTTP" `
-Direction Inbound -Protocol TCP -LocalPort 9080 `
-Action Allow -Profile Domain
# For HTTPS Pull Server (production)
New-NetFirewallRule -DisplayName "DSC Pull Server HTTPS" `
-Direction Inbound -Protocol TCP -LocalPort 443 `
-Action Allow -Profile Domain
Verification and Health Check
# Comprehensive Pull Server health check
function Test-DSCPullServer {
param([string]$PullServerFQDN, [int]$Port = 8080)
$baseURL = "http://${PullServerFQDN}:${Port}"
# Test 1: Web service
try {
$r = Invoke-WebRequest "$baseURL/PSDSCPullServer.svc" -UseBasicParsing
Write-Host "Web service: OK ($($r.StatusCode))" -ForegroundColor Green
} catch {
Write-Host "Web service: FAILED - $_" -ForegroundColor Red
}
# Test 2: Configuration folder
$configPath = "$env:PROGRAMFILESWindowsPowerShellDscServiceConfiguration"
$configCount = (Get-ChildItem $configPath -Filter "*.mof").Count
Write-Host "Published configurations: $configCount" -ForegroundColor Cyan
# Test 3: Module folder
$modulePath = "$env:PROGRAMFILESWindowsPowerShellDscServiceModules"
$moduleCount = (Get-ChildItem $modulePath -Filter "*.zip").Count
Write-Host "Published modules: $moduleCount" -ForegroundColor Cyan
# Test 4: Registration key
$keyFile = "$env:PROGRAMFILESWindowsPowerShellDscServiceRegistrationKeys.txt"
if (Test-Path $keyFile) {
Write-Host "Registration key: Present" -ForegroundColor Green
} else {
Write-Host "Registration key: MISSING" -ForegroundColor Red
}
}
Test-DSCPullServer -PullServerFQDN "dscpull01.corp.local"
Summary
A DSC Pull Server on Windows Server 2012 R2 provides the central configuration management backbone for a Windows infrastructure. By deploying the IIS-based pull service, publishing compiled MOF configurations with checksums, packaging DSC resource modules, and generating registration keys, you create a scalable platform where nodes self-manage their configuration state. Nodes periodically pull their configuration, apply it if drift is detected, and report compliance status to the reporting endpoint — all without requiring an administrator to interact with each machine individually. This approach is the foundation for infrastructure-as-code on Windows Server.