PowerShell DSC: Desired State Configuration for Windows Server
Introduction
[Explain infrastructure-as-code benefits; DSC enforces desired state continuously, preventing configuration drift.]
Prerequisites
- Windows Server 2019+
- PowerShell 5.1 or PowerShell 7+
- Administrative access
DSC Architecture
flowchart TB
CONFIG[DSC Configuration Script] --> MOF[MOF Document]
MOF --> LCM[Local Configuration Manager]
LCM -->|Pull Mode| PULL[Pull Server / Azure Automation]
LCM -->|Push Mode| TARGET[Target Node]
LCM --> RESOURCES[DSC Resources]
RESOURCES --> SYSTEM[System State]
LCM -->|Monitor| DRIFT{Drift Detected?}
DRIFT -->|Yes| REMEDIATE[Apply Configuration]
DRIFT -->|No| COMPLIANT[Compliant]
Core Concepts
| Component | Purpose | Example |
|---|---|---|
| Configuration | Declarative script | Install IIS, configure firewall |
| MOF | Compiled config document | Binary representation |
| Resource | Unit of configuration | File, Registry, WindowsFeature |
| LCM | DSC engine on node | Pull/Push mode, frequency |
Step-by-Step Guide
Step 1: Simple Configuration
Configuration WebServerConfig
{
Node "WebServer01"
{
WindowsFeature IIS
{
Ensure = "Present"
Name = "Web-Server"
}
WindowsFeature ASP
{
Ensure = "Present"
Name = "Web-Asp-Net45"
DependsOn = "[WindowsFeature]IIS"
}
File WebContent
{
Ensure = "Present"
DestinationPath = "C:\inetpub\wwwroot\index.html"
Contents = "<h1>Hello from DSC</h1>"
Type = "File"
DependsOn = "[WindowsFeature]IIS"
}
}
}
Step 2: Compile Configuration
# Generate MOF file
WebServerConfig -OutputPath C:\DSC\Configs
# Apply configuration
Start-DscConfiguration -Path C:\DSC\Configs -Wait -Verbose -Force
Step 3: Configure LCM
[DSCLocalConfigurationManager()]
Configuration LCMConfig
{
Node "WebServer01"
{
Settings
{
RefreshMode = 'Push'
ConfigurationMode = 'ApplyAndAutoCorrect'
RebootNodeIfNeeded = $true
RefreshFrequencyMins = 30
}
}
}
LCMConfig -OutputPath C:\DSC\LCM
Set-DscLocalConfigurationManager -Path C:\DSC\LCM -Verbose
Step 4: Using Configuration Data
$ConfigData = @{
AllNodes = @(
@{
NodeName = "WebServer01"
Role = "WebServer"
}
@{
NodeName = "DBServer01"
Role = "DatabaseServer"
}
)
}
Configuration MultiNodeConfig
{
Node $AllNodes.Where{$_.Role -eq "WebServer"}.NodeName
{
WindowsFeature IIS
{
Ensure = "Present"
Name = "Web-Server"
}
}
Node $AllNodes.Where{$_.Role -eq "DatabaseServer"}.NodeName
{
WindowsFeature SQLServer
{
Ensure = "Present"
Name = "SQL-Server-Full"
}
}
}
MultiNodeConfig -ConfigurationData $ConfigData -OutputPath C:\DSC\Configs
Step 5: Custom DSC Resources
# Install from PowerShell Gallery
Install-Module -Name xWebAdministration -Force
Configuration AdvancedIIS
{
Import-DscResource -ModuleName xWebAdministration
Node "WebServer01"
{
xWebAppPool AppPool
{
Name = "MyAppPool"
Ensure = "Present"
managedRuntimeVersion = "v4.0"
}
xWebsite Website
{
Name = "MyWebsite"
State = "Started"
PhysicalPath = "C:\inetpub\mysite"
ApplicationPool = "MyAppPool"
BindingInfo = @(
MSFT_xWebBindingInformation {
Protocol = "HTTP"
Port = 8080
}
)
DependsOn = "[xWebAppPool]AppPool"
}
}
}
Step 6: Pull Server Setup (Azure Automation)
[DSCLocalConfigurationManager()]
Configuration PullClientConfig
{
Node "WebServer01"
{
Settings
{
RefreshMode = 'Pull'
ConfigurationMode = 'ApplyAndAutoCorrect'
RebootNodeIfNeeded = $true
}
ConfigurationRepositoryWeb AzureAutomation
{
ServerURL = 'https://<region>.agentsvc.azure-automation.net/accounts/<guid>'
RegistrationKey = '<registration-key>'
ConfigurationNames = @('WebServerConfig')
}
}
}
Step 7: Compliance Checking
# Test configuration compliance
Test-DscConfiguration -Verbose
# Get current configuration status
Get-DscConfiguration
# Get LCM state
Get-DscLocalConfigurationManager
Advanced Scenarios
Partial Configurations
[DSCLocalConfigurationManager()]
Configuration PartialConfig
{
Node "WebServer01"
{
Settings
{
RefreshMode = 'Pull'
ConfigurationMode = 'ApplyAndAutoCorrect'
}
PartialConfiguration BaseOS
{
Description = 'Base OS Configuration'
ConfigurationSource = '[ConfigurationRepositoryWeb]AzureAutomation'
}
PartialConfiguration AppConfig
{
Description = 'Application Configuration'
ConfigurationSource = '[ConfigurationRepositoryWeb]AzureAutomation'
DependsOn = '[PartialConfiguration]BaseOS'
}
}
}
Composite Resources
Configuration WebServerComposite
{
Import-DscResource -ModuleName PSDesiredStateConfiguration
WindowsFeature IIS
{
Ensure = "Present"
Name = "Web-Server"
}
WindowsFeature ASPNET
{
Ensure = "Present"
Name = "Web-Asp-Net45"
DependsOn = "[WindowsFeature]IIS"
}
}
Monitoring & Reporting
# Get DSC events
Get-WinEvent -LogName "Microsoft-Windows-Dsc/Operational" | Select-Object -First 10
# Custom compliance report
$nodes = @("WebServer01", "WebServer02")
foreach ($node in $nodes) {
$result = Test-DscConfiguration -ComputerName $node
[PSCustomObject]@{
Node = $node
InDesiredState = $result.InDesiredState
Timestamp = Get-Date
}
}
Integration with Azure Automation
- Import configurations to Azure Automation account
- Compile configurations in Azure
- Onboard VMs to Azure Automation DSC
- Monitor compliance via portal
Troubleshooting
Issue: Configuration not applying
Solution: Check LCM mode; verify MOF syntax; review DSC event logs
Issue: Resource dependency loop
Solution: Review DependsOn chains; ensure no circular references
Issue: Credential handling errors
Solution: Use PSDscAllowPlainTextPassword or certificate encryption
Best Practices
- Use configuration data for environment-specific values
- Leverage composite resources for reusability
- Implement pull mode for scale and centralized management
- Monitor drift detection with scheduled compliance checks
- Version control DSC configurations in Git
Key Takeaways
- DSC enforces desired state declaratively.
- LCM monitors and auto-corrects drift.
- Pull mode centralizes configuration management.
- Azure Automation DSC simplifies enterprise deployment.
Next Steps
- Migrate manual server configs to DSC
- Set up Azure Automation DSC pull server
- Build custom DSC resources for org-specific needs
Additional Resources
Which server configuration will you automate first?