Introduction to PowerShell DSC on Windows Server 2022

PowerShell Desired State Configuration (DSC) is a management platform built into Windows PowerShell that enables you to define the desired state of your servers and continuously enforce that state. On Windows Server 2022, DSC is delivered through WMF 5.1 (Windows Management Framework), which is included in the OS by default. DSC is particularly valuable for automating server configuration, enforcing security baselines, managing roles and features, and ensuring configuration drift does not occur over time. This guide covers DSC architecture, Push and Pull modes, LCM configuration, built-in and community DSC resources, creating practical server hardening configurations, and monitoring compliance.

DSC Architecture and Core Concepts

DSC operates on a declarative model: you describe what a system should look like, not how to get there. A DSC Configuration is a PowerShell script block that uses Resources to describe the desired state. Resources are PowerShell modules that know how to test and set specific aspects of a system — a Windows Feature, a file, a registry key, a running service, or a local user account.

The Local Configuration Manager (LCM) is the engine on each node that applies and monitors DSC configurations. It runs as part of the WMI service and is configured separately from the node configurations themselves. When you compile a DSC Configuration, it produces a .mof (Managed Object Format) file, which is the serialized representation of the desired state. The LCM reads and applies the .mof.

DSC Push Mode: Start-DscConfiguration

In Push mode, you manually or programmatically push a compiled configuration .mof file to one or more target nodes from a management machine. This is the simplest mode to get started with and requires no infrastructure beyond WinRM connectivity to target servers.

First, write a configuration. This example ensures IIS is installed and the W3SVC service is running:

Configuration WebServerBaseline {
    param (
        [string[]]$ComputerName = 'localhost'
    )

    Import-DscResource -ModuleName PSDesiredStateConfiguration

    Node $ComputerName {
        WindowsFeature IIS {
            Name   = 'Web-Server'
            Ensure = 'Present'
        }

        Service W3SVC {
            Name        = 'W3SVC'
            State       = 'Running'
            StartupType = 'Automatic'
            DependsOn   = '[WindowsFeature]IIS'
        }
    }
}

# Compile the configuration to a .mof file
WebServerBaseline -ComputerName 'WebServer01'

Running this script creates a directory called WebServerBaseline containing WebServer01.mof. Push it to the target:

Start-DscConfiguration -Path .WebServerBaseline -ComputerName WebServer01 -Wait -Verbose -Force

The -Wait flag makes the command synchronous. -Verbose outputs detailed progress. -Force overrides any pending configurations on the target node.

LCM Configuration on Windows Server 2022

Before deploying configurations at scale, configure the LCM on each target node. The LCM settings control how often the node checks for new configurations, whether it applies or only reports on drift, and which Pull Server (if any) it contacts.

A common LCM configuration that sets the node to apply configurations and refresh every 30 minutes:

[DSCLocalConfigurationManager()]
Configuration LCMConfig {
    Node 'localhost' {
        Settings {
            RefreshMode          = 'Push'
            ConfigurationMode    = 'ApplyAndAutoCorrect'
            RebootNodeIfNeeded   = $true
            RefreshFrequencyMins = 30
            ActionAfterReboot    = 'ContinueConfiguration'
        }
    }
}

LCMConfig
Set-DscLocalConfigurationManager -Path .LCMConfig -Verbose

The ConfigurationMode can be:

  • ApplyOnly — Apply configuration once and do not re-apply unless pushed again.
  • ApplyAndMonitor — Apply once and report drift without correcting it.
  • ApplyAndAutoCorrect — Apply and continuously correct any drift at each refresh cycle.

DSC Pull Mode with a Pull Server

In Pull mode, each target node polls a central DSC Pull Server for its configuration. This is the scalable approach for managing many servers. The Pull Server is an IIS-hosted OData web service that stores .mof files and DSC resource modules.

Configure the LCM for Pull mode. The node identifies its configuration by a ConfigurationID (a GUID) or a ConfigurationName (when using a Registration Key):

[DSCLocalConfigurationManager()]
Configuration PullClientConfig {
    Node 'localhost' {
        Settings {
            RefreshMode          = 'Pull'
            ConfigurationMode    = 'ApplyAndAutoCorrect'
            RefreshFrequencyMins = 30
            RebootNodeIfNeeded   = $true
        }

        ConfigurationRepositoryWeb PullServer {
            ServerURL          = 'https://dscpull.corp.local:8080/PSDSCPullServer.svc'
            RegistrationKey    = '5acc7bce-9b19-45d3-a6e0-4f24c8e29a11'
            ConfigurationNames = @('WebServerBaseline')
        }

        ResourceRepositoryWeb PullServerResources {
            ServerURL       = 'https://dscpull.corp.local:8080/PSDSCPullServer.svc'
            RegistrationKey = '5acc7bce-9b19-45d3-a6e0-4f24c8e29a11'
        }

        ReportServerWeb ReportServer {
            ServerURL       = 'https://dscpull.corp.local:8080/PSDSCPullServer.svc'
            RegistrationKey = '5acc7bce-9b19-45d3-a6e0-4f24c8e29a11'
        }
    }
}

PullClientConfig
Set-DscLocalConfigurationManager -Path .PullClientConfig -Verbose

Core DSC Resources in PSDesiredStateConfiguration

The PSDesiredStateConfiguration module is built into WMF 5.1 on Windows Server 2022 and provides the following foundational resources:

WindowsFeature — Install or remove Windows roles and features:

WindowsFeature Hyper-V {
    Name   = 'Hyper-V'
    Ensure = 'Present'
    IncludeAllSubFeature = $true
}

WindowsFeature Telnet {
    Name   = 'Telnet-Client'
    Ensure = 'Absent'
}

File — Manage files and directories:

File ScriptsDirectory {
    DestinationPath = 'C:Scripts'
    Type            = 'Directory'
    Ensure          = 'Present'
}

File DeploymentScript {
    DestinationPath = 'C:Scriptsdeploy.ps1'
    SourcePath      = '\fileserverdscscriptsdeploy.ps1'
    Ensure          = 'Present'
    Force           = $true
}

Registry — Manage registry keys and values:

Registry DisableIPv6 {
    Key       = 'HKLM:SYSTEMCurrentControlSetServicesTcpip6Parameters'
    ValueName = 'DisabledComponents'
    ValueData = '255'
    ValueType = 'Dword'
    Ensure    = 'Present'
}

Service — Manage Windows services:

Service RemoteRegistry {
    Name        = 'RemoteRegistry'
    State       = 'Stopped'
    StartupType = 'Disabled'
}

Service WinRM {
    Name        = 'WinRM'
    State       = 'Running'
    StartupType = 'Automatic'
}

User and Group — Manage local accounts:

User LocalAdmin {
    UserName = 'SvcAccount'
    Ensure   = 'Present'
    Password = (New-Object System.Management.Automation.PSCredential('SvcAccount', (ConvertTo-SecureString 'P@ssw0rd!' -AsPlainText -Force)))
    PasswordNeverExpires = $true
    Disabled = $false
}

Group Administrators {
    GroupName        = 'Administrators'
    MembersToInclude = @('SvcAccount')
    DependsOn        = '[User]LocalAdmin'
}

DSC Configuration for Server Hardening

The following configuration applies a basic CIS-inspired hardening baseline to a Windows Server 2022 node. It disables unnecessary services, sets registry values for security, and removes optional features:

Configuration ServerHardening {
    Import-DscResource -ModuleName PSDesiredStateConfiguration

    Node 'localhost' {

        # Disable legacy services
        Service RemoteRegistry { Name = 'RemoteRegistry'; State = 'Stopped'; StartupType = 'Disabled' }
        Service Spooler         { Name = 'Spooler';        State = 'Stopped'; StartupType = 'Disabled' }
        Service TlntSvr         { Name = 'TlntSvr';        Ensure = 'Absent'  }

        # Remove Telnet client
        WindowsFeature TelnetClient {
            Name   = 'Telnet-Client'
            Ensure = 'Absent'
        }

        # Disable SMBv1
        Registry DisableSMBv1 {
            Key       = 'HKLM:SYSTEMCurrentControlSetServicesLanmanServerParameters'
            ValueName = 'SMB1'
            ValueData = '0'
            ValueType = 'Dword'
            Ensure    = 'Present'
        }

        # Require NTLMv2
        Registry NTLMv2 {
            Key       = 'HKLM:SYSTEMCurrentControlSetControlLsa'
            ValueName = 'LmCompatibilityLevel'
            ValueData = '5'
            ValueType = 'Dword'
            Ensure    = 'Present'
        }

        # Enable UAC
        Registry EnableUAC {
            Key       = 'HKLM:SOFTWAREMicrosoftWindowsCurrentVersionPoliciesSystem'
            ValueName = 'EnableLUA'
            ValueData = '1'
            ValueType = 'Dword'
            Ensure    = 'Present'
        }
    }
}

ServerHardening
Start-DscConfiguration -Path .ServerHardening -Wait -Verbose -Force

Community DSC Resource Modules

The PowerShell Gallery hosts many DSC resource modules that extend DSC beyond the built-ins. The most used are those from the xPSDesiredStateConfiguration, xWebAdministration, ComputerManagementDsc, and NetworkingDsc modules. Install them from PowerShell Gallery:

Install-Module -Name xWebAdministration -Force -AllowClobber
Install-Module -Name ComputerManagementDsc -Force
Install-Module -Name NetworkingDsc -Force
Install-Module -Name SecurityPolicyDsc -Force

Using xWebAdministration to create an IIS website:

Import-DscResource -ModuleName xWebAdministration

xWebsite DefaultSite {
    Name         = 'Default Web Site'
    State        = 'Stopped'
    Ensure       = 'Present'
    PhysicalPath = 'C:inetpubwwwroot'
}

xWebsite AppSite {
    Name            = 'MyApp'
    State           = 'Started'
    Ensure          = 'Present'
    PhysicalPath    = 'C:inetpubmyapp'
    BindingInfo     = @(
        MSFT_xWebBindingInformation {
            Protocol = 'https'
            Port     = 443
            CertificateThumbprint = 'ABCDEF1234567890ABCDEF1234567890'
            CertificateStoreName  = 'My'
        }
    )
}

Monitoring DSC Compliance

Once DSC is applied, you can monitor the compliance status of a node using the following commands:

# Get the last configuration status
Get-DscConfigurationStatus

# Get detailed results of the last DSC run
Get-DscConfigurationStatus -All | Select-Object Status, StartDate, DurationInSeconds, Type

# Test current compliance without applying changes
Test-DscConfiguration -Verbose

# Get a detailed compliance report
Test-DscConfiguration -Detailed

Test-DscConfiguration -Detailed returns an object with ResourcesInDesiredState and ResourcesNotInDesiredState properties, identifying exactly which resources have drifted.

Azure Guest Configuration as Cloud-Based DSC

Azure Guest Configuration (now part of Azure Policy) is the cloud-native evolution of DSC for Azure VMs and Arc-enabled servers. It uses DSC under the hood but integrates with Azure Policy for compliance reporting at scale. Assign a Guest Configuration policy to an Azure Policy definition, and Azure will report compliance across all VMs in your subscription.

To check the Guest Configuration extension status on a Windows Server 2022 Azure VM from PowerShell:

Get-AzVMExtension -ResourceGroupName "rg-prod" -VMName "vm-webserver01" -Name "AzurePolicyforWindows"

To create a custom Guest Configuration package, install the GuestConfiguration module:

Install-Module -Name GuestConfiguration -Force
New-GuestConfigurationPackage -Name 'ServerHardening' -Configuration .ServerHardeninglocalhost.mof -Type AuditAndSet -Force

Summary

PowerShell DSC on Windows Server 2022 provides a robust, built-in mechanism for configuration management. Push mode enables immediate, direct deployment from a management workstation. Pull mode scales to hundreds of servers through a central Pull Server. The LCM controls compliance mode, refresh frequency, and reboot behavior. Core resources handle features, files, registry, services, and accounts, while community modules extend coverage to IIS, networking, security policy, and more. The Get-DscConfigurationStatus and Test-DscConfiguration cmdlets provide visibility into compliance state. For cloud-hosted Windows servers, Azure Guest Configuration extends these capabilities to the Azure Policy compliance dashboard.