PowerShell DSC: Desired State Configuration for Windows Server

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

  1. Import configurations to Azure Automation account
  2. Compile configurations in Azure
  3. Onboard VMs to Azure Automation DSC
  4. 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?