Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

PowerShell Desired State Configuration

By Kokil Thapa | Last reviewed: September 2026

PowerShell Desired State Configuration (DSC) is Microsoft's declarative configuration engine for Windows. You describe how a node should look — roles installed, registry keys set, services running — and the Local Configuration Manager (LCM) applies that state repeatedly until reality matches the declaration. If you already automate Linux with Ansible or manage idempotent configuration management elsewhere, DSC is the native Windows equivalent baked into the PowerShell ecosystem. This guide walks through classic MOF-based DSC, push and pull delivery, Azure Automation, the newer DSC v3 direction, and the production mistakes I've seen on mixed Windows/Linux estates.

What is PowerShell Desired State Configuration and how does it work?

DSC separates what you want from how to get there. You write a configuration block. PowerShell compiles it into a Managed Object Format (MOF) document. The LCM on the target machine reads the MOF and invokes DSC resources — built-in or custom — until the node reports compliance.

That loop is the same idea behind Puppet manifests or Ansible playbooks. The difference is the runtime: DSC ships with Windows Management Framework and uses WMI/CIM under the hood on classic Windows nodes. On a production Windows Server you might run IIS, SQL, and file shares on the same box. DSC gives you one auditable declaration instead of a pile of one-off scripts.

The main moving parts are:

  • Configuration author — a `.ps1` file with a Configuration { } block and resource stanzas inside Node { }.
  • MOF output — compiled per-node documents the LCM consumes.
  • Resources — small providers with Get, Set, and Test semantics (Script, Registry, WindowsFeature, File, etc.).
  • LCM — the agent on each node that schedules and applies configurations.
PowerShell DSC ArchitectureAuthorConfiguration.ps1CompileMOF filesDeliverPush or PullLCMOn nodeDSC ResourcesTest → Set → Get loop until compliantCompliant Node StateDrift corrected on next consistency check
PowerShell Desired State Configuration flow: author a configuration, compile MOF, deliver to the LCM, and enforce compliance through resource Test/Set cycles.

Classic DSC runs on Windows PowerShell 5.1 (WMF 5.1). That version still powers a large share of on-prem Windows Server estates in 2026. Microsoft also ships DSC v3 as a cross-platform direction without the traditional MOF/LCM pipeline — covered later in this article.

If your team already uses PowerShell automation for Windows servers, DSC is the natural upgrade path when scripts become hard to reason about. Scripts run once. DSC runs on a schedule and fights drift.

How do you write a basic PowerShell DSC configuration?

Start with a single-node push configuration. The pattern is always the same: import the module, define resources inside a Node block, compile, then apply.

Step 1: Author the configuration

# File: C:\DSC\WebServer.ps1
Configuration WebServer {
    Import-DscResource -ModuleName PsDesiredStateConfiguration

    Node 'localhost' {
        WindowsFeature WebServerRole {
            Name   = 'Web-Server'
            Ensure = 'Present'
        }

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

        File SiteRoot {
            DestinationPath = 'C:\inetpub\wwwroot\index.html'
            Contents        = '<h1>Hello from DSC</h1>'
            Type            = 'File'
            Ensure          = 'Present'
            DependsOn       = '[WindowsFeature]WebServerRole'
        }
    }
}

Step 2: Compile to MOF

PS C:\DSC> . .\WebServer.ps1
PS C:\DSC> WebServer -OutputPath C:\DSC\WebServer

Compilation creates a folder with a localhost.mof file. That MOF is the contract the LCM enforces.

Step 3: Apply with push mode

PS C:\DSC> Start-DscConfiguration -Path C:\DSC\WebServer -Wait -Verbose -Force
PS C:\DSC> Test-DscConfiguration
PS C:\DSC> Get-DscConfiguration

Test-DscConfiguration returns True when the node matches the MOF. Get-DscConfiguration shows the actual values the resources read back. Use both during troubleshooting — a failed test with verbose logging usually points to a missing Windows feature or a permission problem on the destination path.

Step 4: Parameterise for multiple nodes

For a small fleet, pass a node list at compile time:

Configuration WebServer {
    param(
        [Parameter(Mandatory)]
        [string[]]$NodeName
    )

    Import-DscResource -ModuleName PsDesiredStateConfiguration

    Node $NodeName {
        WindowsFeature WebServerRole {
            Name   = 'Web-Server'
            Ensure = 'Present'
        }
    }
}

WebServer -NodeName 'WEB01','WEB02' -OutputPath C:\DSC\WebServer

Then push each MOF remotely with PowerShell remoting over WinRM:

Invoke-Command -ComputerName WEB01,WEB02 -ScriptBlock {
    Start-DscConfiguration -Path \\fileserver\dsc\WebServer -Wait -Verbose
}

Validate JSON payloads or exported settings with a JSON formatter when you integrate DSC output into CI pipelines or Azure Automation.

What is the difference between push and pull mode in DSC?

Push mode is manual or CI-driven. You compile MOF and call Start-DscConfiguration against each node. Pull mode inverts control. Each node's LCM reaches out to a pull server (or Azure Automation) on a schedule, downloads fresh MOF and modules, and applies them without you targeting machines one by one.

Push vs Pull DeliveryPush ModePull ModeAdmin CIStart-DscConfigWindows NodeLCM applies MOFPull ServerAzure or IISWindows NodeLCM polls MOFBest for: small fleets, CI pipelinesBest for: large fleets, auto-registration
Push mode sends MOF from an admin or pipeline; pull mode lets each node's LCM fetch configuration from a central pull server or Azure Automation.

Configure the LCM for pull mode

Each node needs meta-configuration — a special MOF that tells the LCM where to pull from and how often to check consistency:

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

        ConfigurationRepository WebPullServer {
            ServerURL = 'https://pull.example.com/PSDSCPullServer.svc'
            AllowUnsecureConnection = $false
        }
    }
}

LcmPull -OutputPath C:\DSC\LcmPull
Set-DscLocalConfigurationManager -Path C:\DSC\LcmPull

ApplyAndAutoCorrect is the production default. The LCM re-applies desired state when someone drifts a setting manually. ApplyAndMonitor only reports drift — useful during migration when you are not ready for automatic remediation.

Azure Automation State Configuration

For teams without appetite to host their own pull server, Azure Automation State Configuration registers nodes, stores MOF, and reports compliance centrally. You compile locally or in CI, import the MOF to Azure, and register nodes with a registration key. The portal shows compliant vs non-compliant machines — similar to what you'd expect from a Puppet or Ansible Tower dashboard, but native to Azure AD joined or hybrid Windows fleets.

How does PowerShell DSC compare to Ansible, Puppet, and Terraform?

DSC is not a provisioning tool. It does not replace Terraform for creating VMs or networks. It sits in the same lane as Puppet, Chef, or Ansible configuration tasks — enforcing state after infrastructure exists. Many teams use Terraform for provisioning and a config manager for drift control.

ToolPrimary platformModelAgent requiredStrength
PowerShell DSC (classic)WindowsDeclarative MOF + LCMYes (LCM built in)Native Windows resources, deep OS integration
DSC v3Windows, Linux, macOSDeclarative YAML/JSONLightweight (dsc CLI)Cross-platform, no MOF pipeline
AnsibleCross-platformImperative tasks, idempotent modulesNo (WinRM/SSH)One tool for Linux and Windows, huge module library
PuppetCross-platformDeclarative catalogYes (puppet agent)Mature reporting, large enterprise adoption
TerraformCloud APIsDeclarative infrastructureNoCreates resources; does not manage OS drift well alone

On estates I maintain that mix Ubuntu web servers with a handful of Windows boxes, Ansible often wins for uniformity. When the Windows footprint grows — Active Directory, IIS farms, SQL Always On — DSC earns its place because built-in resources like WindowsFeature, xActiveDirectory, and SqlServerDsc community modules map directly to Microsoft primitives.

DSC also aligns with how Linux system administration teams think about desired state. The vocabulary differs. The goal is identical: repeatable, auditable configuration that survives staff turnover.

Choose Your Config LayerTerraformCreate VMs, disks, netsPowerShell DSCOS roles and driftApp DeployCI/CD pipelinesWindows-heavy shop → DSC + Azure AutomationMixed Linux/Windows → Ansible + selective DSCCloud-only IaC → Terraform + cloud-initGreenfield 2026 → evaluate DSC v3 first
PowerShell Desired State Configuration fits between infrastructure provisioning and application deployment — choose based on platform mix and team skills.

For remote state patterns in Terraform — locking, backends, team workflows — see manage Terraform state safely. DSC solves a different problem, but the same discipline applies: version your declarations, review changes, and never apply untested MOF to production blindly.

What are common PowerShell DSC mistakes in production?

DSC looks simple in demos. Production breaks in predictable ways. These are the issues I watch for when onboarding Windows nodes to declarative management.

1. Skipping resource dependencies

DSC parallelises resource application where possible. Without DependsOn, a Service resource may run before the feature that installs it. Always chain dependent resources explicitly, as in the IIS example above.

2. Wrong LCM configuration mode during migration

Teams sometimes leave ApplyAndMonitor in production and wonder why manual changes persist. Flip to ApplyAndAutoCorrect only after a successful test window. Schedule reboots with RebootNodeIfNeeded = $true when installing roles that require it.

3. Module version drift on pull servers

Pull mode serves both MOF and zip archives of resource modules. If WEB01 pulls NetworkingDsc 8.x and WEB02 still caches 7.x, you get silent inconsistency. Pin module versions in your pull server repository and bump them deliberately — the same discipline as locking Composer or npm versions on a web development project.

4. Secrets in plain MOF

MOF files are text. Never embed passwords directly. Use PSCredential with certificates:

Configuration SqlWithCredential {
    param(
        [Parameter(Mandatory)]
        [PSCredential]$SqlCredential
    )

    Node 'SQL01' {
        # resources referencing $SqlCredential
    }
}

# Compile with encrypted credentials for target node cert thumbprint
SqlWithCredential -SqlCredential (Get-Credential) `
    -OutputPath C:\DSC\Sql `
    -ConfigurationData @{
        AllNodes = @(
            @{
                NodeName                    = 'SQL01'
                PSDscAllowPlainTextPassword = $false
                CertificateFile             = 'C:\Certs\SQL01.cer'
                Thumbprint                  = 'ABC123...'
            }
        )
    }

Store the decryption certificate only on the target node. This pattern mirrors how you'd handle secrets in multi-server application configuration — never commit credentials to the artifact repository.

5. Treating DSC as a one-time installer

DSC is a continuous compliance engine. If you disable the consistency task or never monitor reports, drift returns within weeks. Wire compliance output into logging or Azure Monitor the same way you'd monitor fail2ban rules on Linux or disk alerts — configuration management without observability is half a solution.

Production DSC GotchasMissing DependsOnRace on feature installPlaintext secretsMOF exposes credsModule driftUnpinned pull modulesNo monitoringDrift goes unnoticedFix: CI compile, cert secrets, pin modulesApplyAndAutoCorrect + Azure reportsTreat MOF like application code in Git
Common PowerShell Desired State Configuration production failures — dependency races, credential handling, module pinning, and missing compliance monitoring.

Document every custom resource and third-party module. On an infrastructure project like SRP Infrastructure Development Nepal, the next engineer should open one repo and understand the full desired state — not reverse-engineer a pull server by hand.

When should you use DSC v3 instead of classic DSC?

Microsoft's classic DSC (WMF 5.1, MOF, LCM) remains valid for existing Windows Server fleets. DSC v3 is the forward path for new work in 2026. It drops the MOF/LCM pipeline in favour of a standalone dsc command-line tool, YAML or JSON configuration files, and cross-platform resources that run on Windows, Linux, and macOS through the same interface.

Choose classic DSC when:

  1. You maintain a large installed base already reporting to Azure Automation State Configuration.
  2. You depend on mature community resources like ComputerManagementDsc, ActiveDirectoryDsc, or SqlServerDsc that target the MOF model.
  3. Your runbooks and compliance tooling already ingest classic DSC events.

Choose DSC v3 when:

  1. You start a greenfield project and want one configuration language across Windows and Linux nodes.
  2. You prefer Git-tracked YAML over compiled MOF artifacts in CI.
  3. You want to invoke configuration from any shell or pipeline without installing the full LCM stack.

A minimal DSC v3 resource manifest is a JSON document describing parameters and the script that enforces state. Configurations reference resources by name. The exact schema evolves — always check the official DSC v3 overview on Microsoft Learn before pinning versions in CI.

DSC v3 does not magically replace Ansible on a mixed fleet overnight. It does give Microsoft-centric shops a credible exit from MOF complexity while keeping declarative semantics. Pair it with CI pipeline reviews so configuration changes get the same scrutiny as application code.

Custom resources in classic DSC

When built-in resources are not enough, author a class-based or MOF-based resource module with three functions: Get-TargetResource, Set-TargetResource, and Test-TargetResource. Test must be fast and side-effect free. Set makes changes. Get returns current state for reporting. That trio is the same contract Puppet types and providers use — once you internalise it, custom resources are straightforward.

# Simplified pattern inside a custom resource module
function Test-TargetResource {
    param([string]$Path, [Ensure]$Ensure)
    $exists = Test-Path $Path
    if ($Ensure -eq 'Present') { return $exists }
    return -not $exists
}

function Set-TargetResource {
    param([string]$Path, [Ensure]$Ensure)
    if ($Ensure -eq 'Present') { New-Item -Path $Path -ItemType Directory -Force }
    else { Remove-Item -Path $Path -Recurse -Force -ErrorAction SilentlyContinue }
}

function Get-TargetResource {
    param([string]$Path, [Ensure]$Ensure)
    return @{ Path = $Path; Ensure = (Test-Path $Path) ? 'Present' : 'Absent' }
}

Publish the module to your pull server or an internal NuGet/Artifactory feed. Version it SemVer-style. Breaking changes in a custom resource can reconfigure production IIS bindings or firewall rules on the next consistency pass — treat releases carefully.

Integrating DSC into a broader ops practice

DSC handles OS-level desired state. It does not replace backup verification, patch scheduling, or application deployment. On projects where I deliver support and maintenance, DSC covers the baseline: roles, security baselines, service states, registry hardening. Application releases still flow through GitLab CI, Deployer, or platform-specific pipelines.

If you also manage Windows DNS or hybrid networking, keep DNS declarations in separate configurations with their own test environments. A single monolithic configuration document becomes slow to compile and risky to change. Split by concern — web tier, data tier, domain controllers — and compose with configuration data files.

For enterprise rollouts, involve stakeholders early. Declarative management removes silent snowflake servers. That is good engineering and occasionally political friction. Document the compliance report dashboard before you enable auto-correct so teams know changes will be reverted.

Key Takeaways

  • PowerShell Desired State Configuration declares Windows (and with v3, cross-platform) server state as code and enforces it through the LCM or the modern dsc CLI.
  • Author configurations in PowerShell, compile to MOF for classic DSC, and apply via push (Start-DscConfiguration) or pull (LCM meta-configuration pointing at Azure or a pull server).
  • Use ApplyAndAutoCorrect, certificate-encrypted credentials, explicit DependsOn chains, and pinned module versions before trusting DSC in production.
  • DSC complements — not replaces — Terraform for infrastructure creation and Ansible for homogeneous cross-platform tasks; pick based on fleet composition.
  • Store configurations in Git, compile in CI, monitor compliance centrally, and evaluate DSC v3 for greenfield 2026 projects that need YAML-based declarative management.
  • Custom resources follow Test/Set/Get semantics — keep Test fast and idempotent to avoid side effects during consistency checks.

People Also Ask

Is PowerShell DSC still supported in 2026?

Yes. Classic DSC ships with Windows Management Framework 5.1 and remains supported on Windows Server through Microsoft's lifecycle for that platform. Azure Automation State Configuration continues to accept MOF-based configurations. Microsoft actively develops DSC v3 as the next-generation cross-platform engine documented on Microsoft Learn.

Do I need a pull server to use DSC?

No. Push mode applies MOF directly with Start-DscConfiguration and suits small fleets or CI-driven workflows. Pull mode — via a self-hosted pull server or Azure Automation — scales better when hundreds of nodes must fetch configuration and modules without manual targeting.

Can PowerShell DSC manage Linux servers?

Classic MOF-based DSC targeted Windows primarily, with limited Linux support through OMI-based setups that never saw wide adoption. DSC v3 is explicitly cross-platform and runs configuration resources against Linux and macOS through the same dsc tool chain. For large Linux estates today, Ansible remains the more common choice.

What is the difference between DSC and Group Policy?

Group Policy pushes Active Directory–linked settings to domain-joined clients on refresh intervals. DSC declares machine state — including roles, files, and services — and can run on workgroup or cloud VMs without AD dependency. Many enterprises use both: Group Policy for desktop baselines, DSC for server roles and Azure-hosted infrastructure.

Build declarative Windows baselines that survive drift

PowerShell Desired State Configuration turns ad-hoc server scripts into versioned, testable declarations. Start with one configuration — IIS, DNS, or a baseline hardening set — compile MOF, validate on a staging node, then expand to pull mode or Azure Automation as your fleet grows. Whether you run classic LCM-based DSC or pilot DSC v3 on new hosts, the payoff is the same: fewer snowflake servers and faster recovery when someone misclicks in Server Manager.

Need help designing configuration management across Windows and Linux estates, or integrating declarative baselines into your CI pipeline? Review our enterprise application development services or custom software development offerings, browse the portfolio for infrastructure work, and contact us to discuss your environment.

Frequently Asked Questions

PowerShell DSC is Microsoft's declarative Windows configuration engine. You declare desired node state and the Local Configuration Manager enforces it idempotently until the node matches.

You author a Configuration block in PowerShell, which compiles to a Managed Object Format document. The LCM on each node reads the MOF and invokes DSC resources with Get, Set, and Test semantics until the node reports compliance. The LCM can re-run on a schedule to fight configuration drift, unlike one-off scripts that execute once and stop.

Author a .ps1 file with a Configuration block and resource stanzas inside Node. Dot-source the file, compile with the configuration name and OutputPath to generate MOF files, then run Start-DscConfiguration with Wait and Force. Confirm compliance with Test-DscConfiguration and inspect actual values using Get-DscConfiguration. Chain dependent resources with DependsOn, as when a Service must wait for WindowsFeature installation.

Push mode means you compile MOF and call Start-DscConfiguration against each node manually or from CI. Pull mode inverts control: each node's LCM reaches out to a pull server or Azure Automation on a schedule, downloads fresh MOF and modules, and applies them without targeting machines individually. Pull requires LCM meta-configuration specifying RefreshMode, server URL, and refresh frequency.

No. DSC enforces OS configuration and drift control after infrastructure exists. Terraform declares cloud resources through APIs and does not manage OS drift alone.

DSC sits in the same lane as Puppet, Chef, or Ansible configuration tasks rather than provisioning. Ansible often wins for mixed Linux and Windows estates needing one tool. DSC earns its place when Windows footprints grow with IIS, Active Directory, or SQL because built-in resources like WindowsFeature and community modules map directly to Microsoft primitives. Many teams pair Terraform for provisioning with a config manager for drift.

MOF is the Managed Object Format document PowerShell compiles from your configuration. The LCM on each target node consumes it as the enforced contract.

The LCM is the agent on each node that schedules and applies configurations. It reads MOF documents, invokes resources until the node matches desired state, and in pull mode fetches updated MOF and modules from a central repository. Configure it through meta-configuration MOF files that set refresh mode, consistency frequency, and whether drift is auto-corrected or only monitored.

Skipping DependsOn causes resource race conditions. Leaving ApplyAndMonitor instead of ApplyAndAutoCorrect lets manual drift persist. Pull servers serving mismatched module versions create silent inconsistency across nodes. Embedding passwords in plain MOF exposes secrets because MOF is text. Treating DSC as a one-time installer instead of continuous compliance allows drift to return within weeks without monitoring wired into logging or Azure Monitor.

Never embed passwords directly in MOF because MOF files are plain text. Use PSCredential with certificates at compile time: set PSDscAllowPlainTextPassword to false, supply CertificateFile and Thumbprint in ConfigurationData, and store the decryption certificate only on the target node. This mirrors multi-server secret handling where credentials never belong in the artifact repository.

Choose classic DSC when you have a large installed base on Azure Automation State Configuration, depend on mature MOF-model community resources like ActiveDirectoryDsc or SqlServerDsc, or existing tooling ingests classic DSC events. Choose DSC v3 for greenfield projects needing YAML or JSON across Windows, Linux, and macOS via the standalone dsc CLI without the MOF and LCM pipeline.

Azure Automation State Configuration hosts MOF centrally for teams that do not want to run their own pull server. You compile locally or in CI, import MOF to Azure, and register nodes with a registration key. The portal displays compliant versus non-compliant machines, similar to Puppet or Ansible Tower dashboards but native to Azure AD joined or hybrid Windows fleets.

ApplyAndAutoCorrect is the production default because the LCM re-applies desired state when someone manually drifts a setting. ApplyAndMonitor only reports drift and is useful during migration when you are not ready for automatic remediation. Enable RebootNodeIfNeeded when installing roles that require restart, and switch to auto-correct only after a successful test window.

Author a meta-configuration with DSCLocalConfigurationManager, set RefreshMode to Pull, specify RefreshFrequencyMins and ConfigurationModeFrequencyMins, choose ApplyAndAutoCorrect, and define a ConfigurationRepository pointing to your pull server URL. Compile and apply with Set-DscLocalConfigurationManager. Each node then pulls MOF and resource module zip archives on schedule without manual Start-DscConfiguration calls per machine.

When built-in resources are insufficient, author a module with Get-TargetResource, Set-TargetResource, and Test-TargetResource following the same contract as Puppet types and providers. Test must be fast and side-effect free, Set makes changes, and Get returns current state for reporting. Publish the module to your pull server or internal repository and pin versions to avoid drift across nodes.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: