Home / Windows Server / PowerShell DSC: Desired State Configuration for Windows
Windows Server

PowerShell DSC: Desired State Configuration for Windows

Automate Windows Server configuration with PowerShell DSC—declarative infrastructure, drift detection, and compliance enforcement.

What you will learn

Practical execution with concise explanations, real implementation patterns, and production-ready recommendations.

PowerShell DSC: Desired State Configuration for Windows Server

Introduction

PowerShell Desired State Configuration (DSC) enables declarative infrastructure management for Windows Server environments. Instead of writing imperative scripts that describe how to configure a server, DSC lets you declare what the configuration should be — and the DSC engine ensures the system matches that desired state, automatically correcting drift.

This guide covers DSC fundamentals, configuration authoring, Pull Server deployment, Azure Automation DSC integration, and best practices for enterprise environments.

Prerequisites

  • Windows Server 2019/2022 with PowerShell 5.1+
  • Local Administrator privileges on target nodes
  • Understanding of PowerShell scripting basics
  • For Azure DSC: Azure subscription with Automation account

DSC Architecture

DSC Architecture

Figure: Enterprise architecture – integrated components with data flow.

Architecture Overview: Configuration Authoring

Writing DSC Configurations

Web Server Configuration

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

    Import-DscResource -ModuleName PSDesiredStateConfiguration
    Import-DscResource -ModuleName xWebAdministration

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

        WindowsFeature IISManagement {
            Ensure    = 'Present'
            Name      = 'Web-Mgmt-Tools'
            DependsOn = '[WindowsFeature]IIS'
        }

        WindowsFeature ASPNet45 {
            Ensure    = 'Present'
            Name      = 'Web-Asp-Net45'
            DependsOn = '[WindowsFeature]IIS'
        }

        # Configure website
        xWebsite DefaultSite {
            Ensure       = 'Present'
            Name         = 'Default Web Site'
            State        = 'Started'
            PhysicalPath = 'C:\inetpub\wwwroot'
            BindingInfo  = @(
                MSFT_xWebBindingInformation {
                    Protocol = 'HTTP'
                    Port     = 80
                }
                MSFT_xWebBindingInformation {
                    Protocol  = 'HTTPS'
                    Port      = 443
                    CertificateThumbprint = $Node.CertThumbprint


                }
            )
            DependsOn    = '[WindowsFeature]IIS'
        }

        # Security: Disable unnecessary services
        Service TelnetService {
            Name   = 'TlntSvr'
            State  = 'Stopped'
            Ensure = 'Present'
        }

        # Firewall rules
        Script ConfigureFirewall {
            SetScript = {
                New-NetFirewallRule -DisplayName "Allow HTTP" -Direction Inbound -Port 80 -Protocol TCP -Action Allow
                New-NetFirewallRule -DisplayName "Allow HTTPS" -Direction Inbound -Port 443 -Protocol TCP -Action Allow
            }
            TestScript = {
                $http = Get-NetFirewallRule -DisplayName "Allow HTTP" -ErrorAction SilentlyContinue
                $https = Get-NetFirewallRule -DisplayName "Allow HTTPS" -ErrorAction SilentlyContinue
                return ($null -ne $http -and $null -ne $https)
            }
            GetScript = { return @{ Result = "Firewall rules configured" } }
        }
    }
}

# Compile and apply
WebServerConfig -NodeName 'WebServer01' -OutputPath './WebConfig'
Start-DscConfiguration -Path './WebConfig' -Wait -Verbose -Force

Security Hardening Configuration

Configuration SecurityBaseline {
    Import-DscResource -ModuleName PSDesiredStateConfiguration
    Import-DscResource -ModuleName SecurityPolicyDsc
    Import-DscResource -ModuleName AuditPolicyDsc

    Node $AllNodes.NodeName {
        # Password policy
        AccountPolicy PasswordPolicy {
            Name                             = 'PasswordPolicy'
            Minimum_Password_Length           = 14
            Password_must_meet_complexity    = 'Enabled'
            Maximum_Password_Age             = 60
            Minimum_Password_Age             = 1
            Enforce_password_history         = 24
        }

        # Audit policy
        AuditPolicySubcategory LogonAudit {
            Name      = 'Logon'
            AuditFlag = 'Success'
            Ensure    = 'Present'
        }

        AuditPolicySubcategory LogonFailureAudit {
            Name      = 'Logon'
            AuditFlag = 'Failure'
            Ensure    = 'Present'
        }

        # Registry settings for security
        Registry DisableRemoteDesktopNLA {
            Key       = 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp'
            ValueName = 'UserAuthentication'
            ValueData = '1'
            ValueType = 'Dword'
            Ensure    = 'Present'
        }
    }
}

Push vs Pull Mode

Aspect Push Mode Pull Mode
Initiation Admin pushes to nodes Nodes pull from server
Scale Small environments (<50) Large environments
Setup complexity Minimal Requires Pull Server
Compliance reporting Manual Automatic
Self-healing On demand Continuous (configurable)

Azure Automation DSC

Azure Automation DSC

Figure: Test Studio – recorded test cases, assertions, and execution results.

# Register a node with Azure Automation DSC
$RegistrationInfo = Get-AzAutomationRegistrationInfo -ResourceGroupName "rg-dsc" -AutomationAccountName "aa-dsc"





# Configure LCM on target node
[DSCLocalConfigurationManager()]
Configuration LCMConfig {
    Node 'localhost' {
        Settings {
            RefreshMode = 'Pull'
            RefreshFrequencyMins = 30
            ConfigurationMode = 'ApplyAndAutoCorrect'
            RebootNodeIfNeeded = $false
        }
        ConfigurationRepositoryWeb AzureAutomation {
            ServerURL = $RegistrationInfo.Endpoint
            RegistrationKey = $RegistrationInfo.PrimaryKey
        }
    }
}

LCMConfig -OutputPath './LCMConfig'
Set-DscLocalConfigurationManager -Path './LCMConfig' -Verbose

Best Practices

  1. Version control configurations: Store DSC configs in Git alongside your infrastructure code
  2. Use configuration data: Separate environment-specific values from configuration logic
  3. Test configurations: Use Pester tests to validate DSC resources before applying
  4. Monitor compliance: Use Azure Automation or custom reporting to detect configuration drift
  5. Modular design: Break large configurations into composites for reusability
  6. Credential management: Use certificates or Azure Key Vault — never embed passwords in configs

Architecture Decision and Tradeoffs

When designing server infrastructure solutions with Windows Server, consider these key architectural trade-offs:

Approach Best For Tradeoff
Managed / platform service Rapid delivery, reduced ops burden Less customisation, potential vendor lock-in
Custom / self-hosted Full control, advanced tuning Higher operational overhead and cost

Recommendation: Start with the managed approach for most workloads and move to custom only when specific requirements demand it.

Validation and Versioning

  • Last validated: April 2026
  • Validate examples against your tenant, region, and SKU constraints before production rollout.
  • Keep module, CLI, and SDK versions pinned in automation pipelines and review quarterly.

Security and Governance Considerations

  • Apply least-privilege access using RBAC roles and just-in-time elevation for admin tasks.
  • Store secrets in managed secret stores and avoid embedding credentials in scripts or source files.
  • Enable audit logging, data protection policies, and periodic access reviews for regulated workloads.

Cost and Performance Notes

  • Define budgets and alerts, then monitor usage and cost trends continuously after go-live.
  • Baseline performance with synthetic and real-user checks before and after major changes.
  • Scale resources with measured thresholds and revisit sizing after usage pattern changes.

Official Microsoft References

  • https://learn.microsoft.com/windows-server/
  • https://learn.microsoft.com/windows/security/
  • https://learn.microsoft.com/azure/azure-arc/

Public Examples from Official Sources

  • These examples are sourced from official public Microsoft documentation and sample repositories.
  • Documentation examples: https://learn.microsoft.com/windows-server/
  • Sample repositories: https://github.com/microsoft/Windows-Containers
  • Prefer adapting these examples to your tenant, subscriptions, and governance requirements before production use.

Key Takeaways

  • ✅ DSC enables declarative, idempotent infrastructure management for Windows Server
  • ✅ Configuration drift is automatically corrected in Pull mode with ApplyAndAutoCorrect
  • ✅ Azure Automation DSC scales management across hybrid cloud environments
  • ✅ Security baselines and compliance policies can be enforced consistently via DSC
  • ✅ Version-controlled configurations provide auditability and reproducibility

Additional Resources

Discussion