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.