PowerShell DSC: Introduction to Desired State Configuration

PowerShell DSC: Introduction to Desired State Configuration

PowerShell Tips Editor 3 min read
PowerShell DSC: Introduction to Desired State Configuration

Configuring servers manually or through one-off scripts means configuration drift — machines diverge from their intended state over time. PowerShell Desired State Configuration (DSC) takes a different approach: you declare what a machine should look like, and PowerShell continuously ensures it stays that way. This post walks you through writing your first configuration, compiling it, applying it, and checking compliance.

Quick Answer / TL;DR

Write a Configuration block, call it to compile a MOF file, then apply with Start-DscConfiguration -Path .\ConfigName -Wait -Verbose. Test compliance with Test-DscConfiguration.

What Is DSC and Why Use It

DSC is a declarative configuration management platform built into PowerShell 5 and available on PowerShell 7 via the PSDesiredStateConfiguration module. You describe the desired state — which Windows features should be installed, what registry keys should exist, which services should be running — and DSC’s Local Configuration Manager (LCM) enforces that state. The two modes are Push (you apply the configuration) and Pull (machines check a central server). This post covers Push mode, which is the simpler starting point.

Write Your First Configuration

A Configuration block looks like a function but compiles to a Managed Object Format (MOF) file. Inside the block, you declare resources — each representing one configurable aspect of the system. The most common built-in resources are File, Registry, Service, and WindowsFeature.

# Define a DSC configuration
Configuration WebServerConfig {
    param([string]$ComputerName = 'localhost')

    Import-DscResource -ModuleName PSDesiredStateConfiguration

    Node $ComputerName {

        # Ensure IIS is installed
        WindowsFeature IISInstall {
            Ensure = 'Present'
            Name   = 'Web-Server'
        }

        # Ensure a directory exists
        File WebRoot {
            Ensure          = 'Present'
            Type            = 'Directory'
            DestinationPath = 'C:\inetpub\wwwroot\app'
        }

        # Ensure W3SVC service is running
        Service W3SVC {
            Name        = 'W3SVC'
            State       = 'Running'
            StartupType = 'Automatic'
            DependsOn   = '[WindowsFeature]IISInstall'
        }
    }
}

Compile to MOF File

Calling the configuration function compiles it into a MOF file in a folder named after the configuration. The MOF file is the actual artifact that the LCM reads — it is a text-based representation of the desired state. Inspect it to verify the resources are configured as intended.

# Compile the configuration to MOF
WebServerConfig -ComputerName 'webserver01' -OutputPath C:\DSC\WebServerConfig

# A folder is created with a .mof file named after the target node
Get-ChildItem C:\DSC\WebServerConfig

# View the MOF content
Get-Content C:\DSC\WebServerConfig\webserver01.mof
    Directory: C:\DSC\WebServerConfig

Mode   LastWriteTime   Length Name
----   -------------   ------ ----
-a---  2024-03-15      1248   webserver01.mof

Apply Configuration with Start-DscConfiguration

Start-DscConfiguration applies the MOF to the target node. Use -Wait to block until the configuration is applied (rather than running as a background job) and -Verbose to see what each resource is doing. For remote targets, the LCM on the remote machine must be running and WinRM must be configured.

# Apply to local machine
Start-DscConfiguration -Path C:\DSC\WebServerConfig -Wait -Verbose -Force

# Apply to remote machine
Start-DscConfiguration -Path C:\DSC\WebServerConfig `
    -ComputerName webserver01 -Credential (Get-Credential) `
    -Wait -Verbose -Force

Built-In DSC Resources

The PSDesiredStateConfiguration module includes these core resources that cover most server configuration scenarios:

  • File — create/manage files and directories
  • Registry — manage registry keys and values
  • Service — control Windows services
  • WindowsFeature — install/remove Windows roles and features (Server only)
  • User and Group — manage local users and groups
  • Package — install/remove software packages
  • Script — run arbitrary PowerShell as a DSC resource
# Registry resource example
Registry AppSettings {
    Ensure    = 'Present'
    Key       = 'HKLM:\SOFTWARE\MyApp'
    ValueName = 'Version'
    ValueData = '2.0'
    ValueType = 'String'
}

# Service resource ensuring a service is stopped and disabled
Service PrintSpooler {
    Name        = 'Spooler'
    State       = 'Stopped'
    StartupType = 'Disabled'
}

Test Configuration Compliance

Test-DscConfiguration checks whether the current system state matches the desired state without making any changes. It returns $true if compliant and $false if any resource is out of compliance. Use this in monitoring scripts to detect drift without risking unintended changes.

# Test compliance without making changes
$compliant = Test-DscConfiguration -Detailed

if ($compliant.InDesiredState) {
    Write-Host 'System is compliant' -ForegroundColor Green
} else {
    Write-Warning 'Configuration drift detected!'
    $compliant.ResourcesNotInDesiredState | ForEach-Object {
        Write-Warning "  Non-compliant: $($_.ResourceId)"
    }
}

# Get current applied configuration
Get-DscConfiguration

Common Errors and Fixes

  • Configuration function name becomes MOF filename. When you compile a Configuration WebServerConfig { Node server01 { } }, the output folder is named WebServerConfig and the MOF file is named server01.mof. The node name (from Node block), not the configuration name, becomes the MOF filename. This matters when targeting multiple machines — each node gets its own MOF.
  • LCM must be configured to apply push mode. The Local Configuration Manager on the target machine controls how DSC configurations are applied and refreshed. Check its current mode with Get-DscLocalConfigurationManager. For push mode, RefreshMode should be Push. Configure it with a LocalConfigurationManager block in a DSCLocalConfigurationManager decorated configuration.

Related Cmdlets / See Also

Wrapping Up

DSC shifts server configuration from imperative “do this” scripts to declarative “it should look like this” configurations. The workflow is always: write the Configuration block, compile to MOF, apply with Start-DscConfiguration, verify with Test-DscConfiguration. Start with the built-in resources and expand to community resources from the PowerShell Gallery as your needs grow.

Send-Item -To