How to Configure Desired State Configuration (DSC) on Windows Server 2012 R2

PowerShell Desired State Configuration (DSC) is a management platform introduced in PowerShell 4.0 that allows administrators to define the intended state of a server — which Windows features are installed, which services are running, which files exist, and how the system is configured — in a declarative configuration document. The Local Configuration Manager (LCM) on each server then enforces this desired state, either continuously monitoring for drift and correcting it automatically, or applying the configuration on demand.

DSC fundamentally changes how Windows Server 2012 R2 configurations are managed. Instead of writing procedural scripts that execute a sequence of steps, you write a configuration document describing what the end state should look like, and DSC handles the “how.” This approach makes server configurations repeatable, auditable, and version-controllable. This guide covers configuring the LCM, writing basic configuration documents, using built-in resources, and applying configurations in both push and pull modes.

Prerequisites

– Windows Server 2012 R2 with PowerShell 4.0 (included by default)
– Administrative access on target nodes
– Basic PowerShell scripting knowledge
– For Pull mode: a DSC Pull Server configured on the network (also WS2012 R2)
– Network connectivity between the pull server and managed nodes
– Understanding of MOF (Managed Object Format) files

Step 1: Verify DSC is Available

Confirm DSC and the Local Configuration Manager are available on the target server:

Get-DscLocalConfigurationManager

This returns the current LCM settings including ConfigurationMode, RefreshMode, and RebootNodeIfNeeded. On a fresh WS2012 R2 installation, the default mode is ApplyAndMonitor.

List all built-in DSC resources available:

Get-DscResource | Select-Object Name, Module, Version | Sort-Object Name

Step 2: Write Your First DSC Configuration

A DSC configuration is a PowerShell script with a special Configuration keyword block. This example ensures the Web Server role is installed, IIS is running, and a specific directory exists:

Configuration WebServerState {
    param (
        [string[]]$NodeName = 'localhost'
    )

    Node $NodeName {
        # Ensure IIS is installed
        WindowsFeature IIS {
            Ensure = "Present"
            Name   = "Web-Server"
        }

        # Ensure the default app pool is running
        Service W3SVC {
            Name        = "W3SVC"
            State       = "Running"
            StartupType = "Automatic"
            DependsOn   = "[WindowsFeature]IIS"
        }

        # Ensure a web content directory exists
        File WebRoot {
            Ensure          = "Present"
            Type            = "Directory"
            DestinationPath = "C:inetpubwwwrootapp"
        }
    }
}

# Compile the configuration to a MOF file
WebServerState -OutputPath "C:DSCConfigs"

Run this script in PowerShell. It will create a MOF file at C:DSCConfigslocalhost.mof. The MOF file is the compiled, machine-readable version of your configuration that the LCM processes.

Step 3: Apply the Configuration (Push Mode)

In Push mode, the administrator manually pushes configurations to target nodes. This is the simplest mode and suitable for small environments. Apply the compiled MOF to the local machine:

Start-DscConfiguration -Path "C:DSCConfigs" -Wait -Verbose -Force

The -Wait parameter keeps the command running until the configuration is fully applied. -Verbose shows detailed progress. Without -Wait, the configuration runs as a background job.

To push a configuration to a remote server:

# The MOF file must be named after the target computer
WebServerState -NodeName "WebServer01" -OutputPath "C:DSCConfigs"

# Push to the remote server
Start-DscConfiguration -Path "C:DSCConfigs" -ComputerName "WebServer01" -Wait -Verbose -Credential (Get-Credential)

Step 4: Configure the Local Configuration Manager

The LCM controls how DSC behaves on a node. Configure it using a special DSC configuration with the LocalConfigurationManager block:

Configuration LCMSettings {
    Node "localhost" {
        LocalConfigurationManager {
            # How to apply config: ApplyOnly, ApplyAndMonitor, ApplyAndAutoCorrect
            ConfigurationMode              = "ApplyAndAutoCorrect"
            # How often to check for drift (minutes) - requires ApplyAndMonitor or ApplyAndAutoCorrect
            ConfigurationModeFrequencyMins = 30
            # Refresh mode: Push or Pull
            RefreshMode                    = "Push"
            # Automatically reboot if required by a resource
            RebootNodeIfNeeded             = $false
            # Allow the LCM to be told to apply a new configuration
            AllowModuleOverwrite           = $true
        }
    }
}

# Compile the meta-configuration
LCMSettings -OutputPath "C:DSCLCM"

# Apply the meta-configuration
Set-DscLocalConfigurationManager -Path "C:DSCLCM" -Verbose

Verify the LCM settings were applied:

Get-DscLocalConfigurationManager | Select-Object ConfigurationMode, RefreshMode, RebootNodeIfNeeded, ConfigurationModeFrequencyMins

Step 5: Set Up a DSC Pull Server

In Pull mode, nodes periodically contact a central Pull Server to check for new or updated configurations. This is the preferred approach for managing many servers. Install the DSC service on a designated server:

# Install required features on the Pull Server
Install-WindowsFeature DSC-Service, Web-Server -IncludeManagementTools

# Install the xPSDesiredStateConfiguration module (download from PSGallery or copy manually)
# For WS2012 R2 without internet, copy the module to C:Program FilesWindowsPowerShellModules

# Configure the Pull Server
Configuration PullServerConfig {
    param (
        [string]$NodeName = "localhost",
        [string]$RegistrationKey = "5b83440d-8cc4-4e4f-8dfe-e8f23bbc5c39"
    )

    Import-DSCResource -ModuleName xPSDesiredStateConfiguration

    Node $NodeName {
        WindowsFeature DSCService {
            Ensure = "Present"
            Name   = "DSC-Service"
        }

        xDscWebService PullServerEndpoint {
            Ensure                  = "Present"
            EndpointName            = "DSCPullServer"
            Port                    = 8080
            PhysicalPath            = "$env:SystemDriveinetpubDSCPullServer"
            CertificateThumbprint   = "AllowUnencryptedTraffic"
            ModulePath              = "$env:PROGRAMFILESWindowsPowerShellDscServiceModules"
            ConfigurationPath       = "$env:PROGRAMFILESWindowsPowerShellDscServiceConfiguration"
            State                   = "Started"
            RegistrationKeyPath     = "$env:PROGRAMFILESWindowsPowerShellDscService"
            UseSecurityBestPractices = $false
            DependsOn               = "[WindowsFeature]DSCService"
        }
    }
}

PullServerConfig -OutputPath "C:DSCPullServer"
Start-DscConfiguration -Path "C:DSCPullServer" -Wait -Verbose -Force

Step 6: Register Nodes with the Pull Server

Configure a managed node’s LCM to use the Pull Server. Each node needs a configuration ID (GUID) that maps to the MOF file stored on the pull server:

Configuration PullNodeLCM {
    Node "WebServer01" {
        LocalConfigurationManager {
            RefreshMode                    = "Pull"
            ConfigurationMode              = "ApplyAndAutoCorrect"
            ConfigurationModeFrequencyMins = 30
            RefreshFrequencyMins           = 30
            RebootNodeIfNeeded             = $false
            ConfigurationID                = "a3e7c4f1-2b5d-4e6f-9a1b-3c7d8e2f1a4b"
            DownloadManagerName            = "WebDownloadManager"
            DownloadManagerCustomData      = @{
                ServerUrl       = "http://PullServer01:8080/PSDSCPullServer.svc"
                AllowUnsecureConnection = "true"
            }
        }
    }
}

PullNodeLCM -OutputPath "C:DSCNodeLCM"
Set-DscLocalConfigurationManager -Path "C:DSCNodeLCM" -ComputerName "WebServer01" -Verbose

Step 7: Using Common DSC Resources

DSC ships with several built-in resources for common configuration scenarios. Here are practical examples using the most important ones:

Configuration ServerBaseline {
    Node "localhost" {

        # Registry resource - configure a registry key
        Registry DisableSMBv1 {
            Ensure    = "Present"
            Key       = "HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesLanmanServerParameters"
            ValueName = "SMB1"
            ValueData = "0"
            ValueType = "Dword"
        }

        # WindowsProcess resource - ensure a process is or isn't running
        # (typically used with Service resource instead)
        Service RemoteRegistry {
            Name        = "RemoteRegistry"
            State       = "Stopped"
            StartupType = "Disabled"
        }

        # Environment resource - set an environment variable
        Environment AppPath {
            Ensure = "Present"
            Name   = "APP_HOME"
            Value  = "C:ApplicationsMyApp"
        }

        # Script resource - run arbitrary PowerShell for complex scenarios
        Script ConfigureAuditPolicy {
            GetScript  = { return @{ Result = (auditpol /get /category:"Logon/Logoff") } }
            TestScript = { (auditpol /get /category:"Logon/Logoff") -match "Success and Failure" }
            SetScript  = { auditpol /set /category:"Logon/Logoff" /success:enable /failure:enable }
        }
    }
}

ServerBaseline -OutputPath "C:DSCBaseline"
Start-DscConfiguration -Path "C:DSCBaseline" -Wait -Verbose

Step 8: Check DSC Configuration Status

After applying configurations, verify the current state and check for any drift:

# Get the current configuration applied to this node
Get-DscConfiguration

# Test whether the node is in the desired state (returns $true if compliant)
Test-DscConfiguration -Verbose

# Get detailed status including each resource's state
Get-DscConfigurationStatus | Select-Object Status, StartDate, DurationInSeconds, NumberOfResources

Force the LCM to immediately apply the stored configuration (useful after changes):

Update-DscConfiguration -Wait -Verbose

Step 9: Managing DSC Configuration History

DSC stores a history of configuration runs on the node. Review this to audit changes and diagnose failures:

# View the last 10 configuration events
Get-DscConfigurationStatus -All | Select-Object -First 10 Status, StartDate, Type, DurationInSeconds, Error

Review DSC-related events in the Windows event log:

Get-WinEvent -LogName "Microsoft-Windows-Desired State Configuration-LCMEventSource/Operational" -MaxEvents 25 | Select-Object TimeCreated, Id, LevelDisplayName, Message

Step 10: Handle Configuration Dependencies

Use DependsOn to define the order in which DSC resources are applied. This prevents a service from starting before its prerequisite feature is installed:

Configuration OrderedConfig {
    Node "localhost" {
        WindowsFeature SMTP {
            Ensure = "Present"
            Name   = "SMTP-Server"
        }

        WindowsFeature SMTPConsole {
            Ensure    = "Present"
            Name      = "Web-Mgmt-Console"
            DependsOn = "[WindowsFeature]SMTP"
        }

        Service SMTPSvc {
            Name      = "SMTPSVC"
            State     = "Running"
            DependsOn = "[WindowsFeature]SMTP","[WindowsFeature]SMTPConsole"
        }
    }
}

OrderedConfig -OutputPath "C:DSCOrdered"
Start-DscConfiguration -Path "C:DSCOrdered" -Wait -Verbose

Summary

PowerShell Desired State Configuration on Windows Server 2012 R2 provides a declarative, repeatable approach to server configuration management. By writing configuration documents that describe the desired state, compiling them to MOF files, and using either Push mode for direct application or Pull mode for scalable centralized management, administrators can enforce consistent configurations across entire server fleets. The LCM’s ApplyAndAutoCorrect mode continuously monitors for configuration drift and automatically remediates deviations, reducing the risk of configuration rot over time. DSC integrates naturally with version control systems, making server configurations as auditable and manageable as application code.