Introduction to Desired State Configuration (DSC)

PowerShell Intermediate/Advanced

Chapter 9 · Introduction to Desired State Configuration (DSC)

Every script written since Fundamentals 7 has been imperative: a sequence of steps, executed once, that you're responsible for making safe to re-run yourself — exactly the discipline the Fundamentals capstone's own try/catch-wrapped Remove-Item was built around. DSC is a genuine departure from that whole model: instead of writing steps, you declare the end state you want, and let DSC's own engine figure out — and keep checking — whether reality matches it.

The Configuration Keyword & Resources

Configuration MyWebServerConfig { Node "localhost" { WindowsFeature IIS { Ensure = "Present" Name = "Web-Server" } Service W3SVC { Name = "W3SVC" State = "Running" DependsOn = "[WindowsFeature]IIS" } } }

Configuration is its own distinct keyword, not a function — inside it, Node blocks target specific machines, and inside those, each named block (WindowsFeature, Service, File, Registry, and more) is a resource declaring a piece of desired state, almost always including an Ensure = "Present"/"Absent" property. DependsOn makes ordering explicit where it genuinely matters — here, IIS has to actually be installed before its own W3SVC service can be started.

Compiling a Configuration: .mof Files

Writing the block above doesn't apply anything by itself — Configuration defines a template that has to be compiled first, by calling it like a function:

MyWebServerConfig -OutputPath .\MyWebServerConfig # Produces .\MyWebServerConfig\localhost.mof — one .mof file per targeted Node, nothing applied yet

A .mof (Managed Object Format) file is the actual, portable artifact DSC works from — a real, easy-to-miss two-step process: define the configuration, then compile it, and only after that, apply it.

Applying It: Start-DscConfiguration

Start-DscConfiguration -Path .\MyWebServerConfig -Wait -Verbose

Only this step actually does anything to the target machine — checking each resource's current state and taking action only where it doesn't already match.

The Real Payoff: Idempotency, Built In

The central fact this chapter is built on
Run Start-DscConfiguration a second time against a node that's already fully compliant, and nothing happens — no error, no duplicate install, no second attempt to start an already-running service. This isn't something you had to code yourself the way the Fundamentals capstone's own Remove-Item needed an explicit try/catch and careful "does this still exist" thinking — every DSC resource builds a "check the current state, only act if it's actually different" test into itself as a fundamental design principle. Declarative configuration means idempotency is the resource's own job, not yours.

Detecting and Correcting Drift

Test-DscConfiguration -Detailed # Reports whether the LIVE system still matches the compiled configuration — no changes applied, just a check

Test-DscConfiguration answers "has anything drifted?" without touching anything — a service someone stopped manually, a file someone edited by hand. A node's Local Configuration Manager (LCM) can even be set to periodically re-check and auto-correct drift on its own, turning a one-time configuration into an ongoing, self-healing guarantee — the real distinguishing value DSC has over a script that only ever runs once.

The Escape Hatch: The Script Resource

Script CustomCheck { GetScript = { @{ Result = "n/a" } } TestScript = { Test-Path "C:\Reports\marker.txt" } SetScript = { New-Item -Path "C:\Reports\marker.txt" -ItemType File } }

For anything not covered by a built-in resource, Script lets you hand-write the three pieces DSC's own idempotency model needs: TestScript (is this already true?), SetScript (make it true, only called if TestScript says no), and GetScript (report the current state) — the same idempotent-by-design contract every built-in resource already follows, just written by hand.

Honest scope note
This is genuinely an introduction. Real production DSC usage — class-based custom resources, pull servers, Azure Automation DSC, and the newer cross-platform DSC v3 rewrite — is a substantially larger topic than a single chapter can responsibly cover, matching the same honest boundary this course already drew around raw Runspaces in Chapter 4.
A first practical habit
Before writing a custom Script resource, check whether a built-in resource already covers what you need — WindowsFeature, Service, File, and Registry alone cover a large share of real configuration tasks, already idempotent, with no hand-written TestScript logic to get right yourself.

Hands-On Exercises

Exercise 1

Explain the core difference between a DSC Configuration block and an ordinary PowerShell function, using this chapter's own declarative-vs-imperative framing.

📄 View solution
Exercise 2

Explain what actually happens when you call MyWebServerConfig -OutputPath .\MyWebServerConfig. What gets produced, and what hasn't happened yet at that point?

📄 View solution
Exercise 3

Explain why running Start-DscConfiguration a second time against an already-compliant node does nothing, contrasting this directly with how the Fundamentals capstone had to manually check state before calling Remove-Item.

📄 View solution

Chapter 9 Quick Reference

  • Declarative, not imperative — describe the desired end state; DSC's engine figures out how to get there
  • Configuration / Node / resources — a distinct keyword; Node targets a machine; each resource block (WindowsFeature, Service, File, Registry) declares one piece of desired state
  • Compiling — calling the configuration like a function produces a .mof file per node; nothing is applied yet at this step
  • Start-DscConfiguration — the step that actually checks and applies state
  • Idempotency is built into the resource — re-running against a compliant node does nothing; you never hand-code the "does this already exist" check yourself
  • Test-DscConfiguration — checks for drift without applying anything
  • Script resource — the escape hatch; TestScript/SetScript/GetScript reproduce the same idempotent contract by hand
  • Honest scope note: class-based resources, pull servers, Azure Automation DSC, and DSC v3 are all genuinely beyond this introduction
  • Next chapter: Testing PowerShell Code with Pester