- A DSC Configuration block does not apply anything itself — running it compiles a .mof file, and the MOF is what actually gets applied
- Every DSC resource implements Get, Test, and Set logic, which is what makes configurations idempotent instead of just re-runnable
- The Local Configuration Manager (LCM) is the engine on each node that applies a MOF and, depending on its ConfigurationMode, can detect and auto-correct drift
- Windows PowerShell 5.1 has DSC built in; PowerShell 7 requires the separate PSDesiredStateConfiguration module and handles the engine differently
Declarative vs. Imperative Configuration
Every PowerShell script you've written so far is imperative: it lists a sequence of steps and runs them in order, regardless of whether the target already satisfies the outcome you want. Install-WindowsFeature runs whether or not the feature is already installed; New-Item throws if the file is already there unless you add a check. The script describes a process.
Desired State Configuration inverts that. A DSC configuration describes an end state — "this Windows feature is present," "this file exists with this content," "this service is running" — and leaves the mechanics of getting there to the DSC engine. You don't write the conditional logic that checks whether the feature is already installed; the resource that manages Windows features already knows how to check, and only acts when the current state doesn't match the desired one.
This makes DSC configurations idempotent by design. Applying the same configuration once, ten times, or on a schedule every fifteen minutes produces the same result: a node either already matches the desired state and nothing happens, or it doesn't and the necessary corrective action is taken. That property is what makes DSC suitable for continuous enforcement rather than one-time provisioning — you can point it at a fleet of servers repeatedly and trust that it will never re-run destructive or redundant steps.
The practical shift for an admin moving from scripting to DSC is letting go of sequencing as the unit of thought. Instead of "first do this, then do that," you enumerate the properties a node should have. Ordering still matters in places — DSC supports dependencies between resources via DependsOn — but the default mental model is a set of independent assertions about the end state, not a procedure.
Configurations and Resources
A DSC configuration is a specially parsed PowerShell block introduced by the Configuration keyword rather than function. Inside it, you declare one or more nodes (the machines the configuration targets) and, within each node block, one or more resource blocks. A resource block names a DSC resource, gives it a unique label, and sets its properties.
A DSC resource is the unit of idempotent logic underneath a configuration — conceptually similar to a cmdlet, but built around three operations: Get (report current state), Test (compare current state to desired state and return a boolean), and Set (make the change if Test returns false). The LCM calls Test before Set on every application, which is exactly what produces idempotency — Set only ever runs when something is actually out of compliance.
Windows PowerShell ships a baseline set of resources in the PSDesiredStateConfiguration module: File (manage files and directories), Service (service state and startup type), WindowsFeature (roles and features via Server Manager), Registry, Environment, Group, User, Script (an escape hatch for arbitrary Get/Test/Set logic), and a handful of others. These cover a large share of routine server configuration without installing anything extra.
Beyond the built-ins, a resource module is just a PowerShell module that packages one or more additional DSC resources — for example ComputerManagementDsc for computer name and time zone, NetworkingDsc for IP configuration and firewall rules, or SqlServerDsc for SQL Server instances. These are published to the PowerShell Gallery like any other module and installed with Install-Module. A configuration references a resource module with an Import-DscResource statement inside the configuration block, which is how the compiler knows where to find the resource's implementation.
Writing a DSC Configuration
The syntax below combines two built-in resources — WindowsFeature and File — to express a simple end state: IIS installed, with a specific default document in place.
Configuration WebServerBaseline
{
param(
[string[]]$NodeName = 'localhost'
)
Import-DscResource -ModuleName PSDesiredStateConfiguration
Node $NodeName
{
WindowsFeature IIS
{
Name = 'Web-Server'
Ensure = 'Present'
}
WindowsFeature IISManagementConsole
{
Name = 'Web-Mgmt-Console'
Ensure = 'Present'
DependsOn = '[WindowsFeature]IIS'
}
File DefaultPage
{
DestinationPath = 'C:\inetpub\wwwroot\index.html'
Contents = '<html><body>Managed by DSC</body></html>'
Type = 'File'
Ensure = 'Present'
DependsOn = '[WindowsFeature]IIS'
}
}
}
WebServerBaseline -NodeName 'WEB01' -OutputPath 'C:\DscOutput'
Each resource block has a label (IIS, IISManagementConsole, DefaultPage) that must be unique within the node, and a type (WindowsFeature, File) that must match an imported resource. DependsOn references another resource by [Type]Label syntax and forces ordering — here, the file and the management console both wait until the base IIS feature is confirmed present.
Notice that calling WebServerBaseline -NodeName 'WEB01' -OutputPath 'C:\DscOutput' at the bottom does not touch WEB01 at all. It runs the configuration as a function, which produces output on disk — covered next.
The MOF Compilation Step
Running a Configuration block is a compile step, not an apply step. PowerShell parses the block and generates one .mof file per node listed, named after the node (for example WEB01.mof), inside the -OutputPath directory. MOF stands for Managed Object Format — a CIM (Common Information Model) standard for describing configuration data, unrelated to native PowerShell syntax despite being produced from a PowerShell block.
This matters because the MOF, not your .ps1 file, is the artifact that actually gets applied to a node. Start-DscConfiguration and any pull mechanism both consume MOF files. The PowerShell configuration syntax is purely an authoring convenience — a domain-specific language that compiles down to a vendor-neutral, CIM-based description the LCM's WMI provider can consume regardless of how it was generated. In principle, MOF describing a desired state could be produced by tools other than PowerShell; DSC's engine doesn't know or care that yours came from a Configuration block.
There's a second, separate category of MOF worth knowing about: the meta-configuration MOF, which configures the LCM itself (its refresh mode, refresh interval, and so on) rather than describing resources on the node. Meta-configuration is authored with a special [DscLocalConfigurationManager()] attribute on a configuration block and applied with Set-DscLocalConfigurationManager, distinct from the regular resource MOF applied with Start-DscConfiguration. Confusing the two is a common early mistake — one configures what the node should look like, the other configures how the node checks and enforces that.
The Local Configuration Manager (LCM)
The Local Configuration Manager is the engine present on every DSC-capable node that actually does the work: it reads a MOF, invokes each resource's Test method, invokes Set where needed, and — depending on configuration — repeats that cycle on a schedule to catch drift that happens after the initial apply. Every Windows PowerShell 5.1 installation includes the LCM as a WMI component; there is nothing extra to install to get basic DSC functionality on a Windows Server or Windows 10/11 box running Windows PowerShell.
The LCM's own behavior is governed by a handful of settings you inspect with Get-DscLocalConfigurationManager:
ConfigurationMode—ApplyOnly(apply once, never revisit),ApplyAndMonitor(reapply detection only, logs drift but doesn't fix it), orApplyAndAutoCorrect(detect drift and reapply Set automatically).ConfigurationModeFrequencyMins— how often the LCM re-evaluates the current MOF against actual state.RefreshMode—PushorPull, covered next.RefreshFrequencyMins— in pull mode, how often the node checks the pull source for a new or changed configuration.
Setting ConfigurationMode to ApplyAndAutoCorrect is what turns DSC from a one-time provisioning tool into continuous enforcement: a manually-tweaked registry value or a stopped service that should be running gets reverted on the next LCM cycle without anyone re-running a script. That self-healing behavior is DSC's core value proposition over ad hoc scripting, and it's entirely a function of LCM settings, not the configuration content itself.
Push Mode vs. Pull Mode
Push mode is the direct route: you have a compiled MOF, and you run Start-DscConfiguration -Path C:\DscOutput -Wait -Verbose against one or more target nodes over WinRM. The configuration is sent immediately, applied immediately, and the LCM does not go looking for further instructions unless you've also set ConfigurationModeFrequencyMins for periodic re-evaluation of that same MOF. Push is well suited to ad hoc changes, initial builds, and testing — it's the mode you reach for while iterating on a configuration, since there's no server infrastructure to keep in sync.
Pull mode flips the direction of initiative. Each node's LCM is configured (via meta-configuration) with the address of a pull source and a configuration ID or name, and the node itself polls that source on its RefreshFrequencyMins interval, downloads its assigned MOF if it has changed, and applies it. Because the node is asking rather than being told, pull mode scales to large fleets without an admin having to push to every machine individually, and it naturally re-establishes the correct configuration on a node that was rebuilt or reimaged, as long as it's re-registered with the pull source.
The traditional on-premises version of pull mode used a dedicated DSC pull server — an IIS site running the pull server DSC resources, serving MOFs and resource modules over HTTP(S) with nodes authenticating by GUID. That pattern still works, but it's now considered legacy: it requires you to build, patch, and secure another piece of server infrastructure whose only job is distributing configuration, which is a lot of overhead for what it delivers.
The managed alternative that displaced it was Azure Automation State Configuration (the evolution of what used to be called Azure Automation DSC), which hosted the pull server role as an Azure service so nodes could register against it without you running your own IIS-based pull server. Microsoft has since retired that service, which means it's no longer the recommendation for new work even though existing references to it are still common. The current recommended path for ongoing, centrally managed compliance is Azure Policy Guest Configuration: it uses a DSC-like resource model to audit and, optionally, remediate configuration drift on Azure VMs and on-premises or multi-cloud machines connected through Azure Arc, all evaluated and reported through Azure Policy rather than a custom pull endpoint you maintain yourself. If you're designing a new pull-based rollout today, Guest Configuration is the starting point rather than a self-hosted pull server.
DSC Across PowerShell Versions
Everything above describes DSC as it exists in Windows PowerShell 5.1, where the engine, the LCM, and the WMI-based CIM provider are all built into the operating system. This is still the most complete and most commonly deployed version of DSC, and it's Windows-only.
PowerShell 7 does not include DSC out of the box. Because PowerShell 7 is cross-platform and the classic LCM is a Windows component tied to WMI, the engine couldn't simply be carried over unchanged. To author or run DSC configurations in PowerShell 7, you install the PSDesiredStateConfiguration module separately from the PowerShell Gallery (version 2.x), which restores the Configuration keyword and related cmdlets. Its behavior isn't a perfect drop-in for the 5.1 experience, though — some cmdlets behave differently, and applying compiled configurations against the classic Windows LCM from a PowerShell 7 session has edge cases worth testing before you rely on it in production. If your environment is a mix of PowerShell 5.1 and 7 hosts, the safest approach is to compile and apply DSC configurations from Windows PowerShell 5.1 sessions specifically, and treat PowerShell 7 support as something to verify against your actual resource modules rather than assume.
Because of that gap, teams standardizing on DSC today generally keep authoring and applying configurations in Windows PowerShell 5.1 even if the rest of their scripting has moved to PowerShell 7, and they lean on Azure Policy Guest Configuration rather than custom pull infrastructure when they need centralized, ongoing enforcement across many nodes.
Key Takeaways
- Think in end states, not steps — a DSC configuration says what a node should look like, and the LCM figures out what to change
- Built-in resources (File, Service, WindowsFeature, Registry, Script, and others) cover common cases; resource modules from the PowerShell Gallery extend coverage to IIS, networking, SQL, and more
- Compiling a configuration produces one MOF per node in an output folder — that folder is what you hand to Start-DscConfiguration or a pull source
- Push mode is fine for ad hoc changes and testing; pull mode (or a managed equivalent) is what gives you continuous drift correction at scale
- On-premises DSC pull servers are largely legacy now — Azure Automation State Configuration has been retired, and Azure Policy Guest Configuration is the current managed path for ongoing compliance