Repository navigation
Engines
An engine is what actually evaluates a resource. The runner's core loop is engine-agnostic: it hands the engine a method, a module, a resource name and a property table, and expects a normalized result back.
Two engines ship with the module, and the seam is open.
| Engine | Backed by | Platform | Default |
|---|---|---|---|
DscV2 |
Invoke-DscResource |
Windows / PowerShell DSC | yes |
DscV3 |
dsc (the DSC v3 CLI) |
Windows, Linux, macOS | no |
First match wins:
-
-EngineAction— an inline scriptblock. -
-Enginepassed explicitly to the cmdlet. -
PipelineRunnerSettings.Engine. -
PipelineRunnerSettings.DSCResourceVersion— major2⇒DscV2, major3⇒DscV3. -
DscV2.
Invoke-DscRunner -ConfigurationSourcePath 'C:\config' -Engine DscV3PipelineRunnerSettings:
Engine: DscV3PipelineRunnerSettings:
DSCResourceVersion: 3.0 # back-compat spelling; selects DscV3Auto is the only value that triggers detection:
Invoke-DscRunner -ConfigurationSourcePath 'C:\config' -Engine Auto- With a version hint (
-EngineVersion, orDSCResourceVersion), the major version decides:>= 3⇒DscV3,2⇒DscV2. - Without a decisive version,
DscV3is chosen only when thedscexecutable is actually resolvable onPATH; otherwiseDscV2. - When the version asks for
DscV3butdscis not onPATH, a warning is emitted andDscV3is still returned — so the engine surfaces the concrete failure rather than selection silently rewriting your intent.
An explicitly named engine is never probed or overridden. Naming an engine gets you that engine.
Selection happens once per file, not per resource, so a dsc.exe probe or an
auto-selection warning appears once rather than repeating for every resource.
The original path, and the default. One Invoke-DscResource call per method:
Invoke-DscResource -Name File -ModuleName PSDscResources -Method Test -Property @{ ... }Resource types are the familiar Module/ResourceName:
- name: Log Directory
type: PSDscResources/File
properties:
DestinationPath: C:\Logs
Type: Directory
Ensure: PresentRemote execution runs the same Invoke-DscResource call on the far side, over the target's
PSSession. It does not add -CimSession: PSDesiredStateConfiguration 2.x, the module
PowerShell 7 uses, has no such parameter, and passing one fails the call outright. A target that
resolves a CimSession and no PSSession still uses -CimSession, where the host's
Invoke-DscResource provides it.
[pscredential] properties marshal natively as MSFT_Credential over the encrypted WinRM
transport, so a credential never needs to be converted to plaintext.
Invoke-DscResource drives the target's CIM/DSC subsystem, so the identity the session was
opened as must be a local administrator on the target — being able to connect is not enough
to configure. With no target.credential that identity is the account the runner itself runs
as; see The identity the runner runs as.
Drives Microsoft's cross-platform DSC v3 CLI. This is what makes hosted Linux and macOS agents
useful, since Invoke-DscResource is Windows-first.
Each method becomes one process invocation:
dsc resource test --resource Microsoft.Windows/Registry --input '{"keyPath":"...","valueName":"..."}'
dsc resource set --resource Microsoft.Windows/Registry --input '{...}'
dsc resource get --resource Microsoft.Windows/Registry --input '{...}'
DSC v3 types are namespaced namespace/name, where each half may be dotted:
- name: App Key
type: Microsoft.Windows/Registry
properties:
keyPath: HKCU\Software\Contoso
valueName: Installed
valueData:
String: 'true'The engine checks the shape before invoking anything, so a configuration written for DscV2
and pointed at DscV3 fails with a message that names the problem rather than an opaque
"resource not found" exit:
[Actions/Engine/DscV3] Resource type 'PSDscResources/File' is not a DSC v3 identifier.
DSC v3 types are 'namespace/name' (for example 'Microsoft.Windows/Registry'); a compiled
configuration targeting the DscV3 engine must use DSC v3 resource types.
The structural check is not an existence check — whether the type really exists is still
confirmed by dsc itself.
JSON has no credential type, so a [pscredential] property is resolved to
{ username, password } and a bare [securestring] to its plaintext value, immediately before
the --input payload is built. That payload goes either to the local dsc process or, for a
remote target, over the already-encrypted Invoke-Command -Session channel. It is never
written to disk via --file.
The verbose log is built from a separately redacted copy: any property that started life as a credential or secure string is force-redacted regardless of its name, and a name-based heuristic catches the rest.
[Actions/Engine/DscV3] dsc resource set --resource Contoso/SqlDatabase --input {"ServerName":"sql01","Credential":"[REDACTED]"}
The engine normalizes the three DSC v3 result shapes:
| Method | DSC v3 returns | Normalized to |
|---|---|---|
test |
desiredState, actualState, inDesiredState, differingProperties
|
InDesiredState, and a Message listing the differing properties |
set |
beforeState, afterState, changedProperties
|
a Message listing the changed properties |
get |
actualState, or the state object directly |
Raw |
A non-zero exit from dsc is thrown, so the resource is recorded FAIL with the CLI's own
output. Output that is not parseable JSON is likewise a thrown error naming the resource.
dsc's exact shape for a reboot-pending signal is not confirmed against a real resource that
sets it, so the engine reads tolerantly from either observed shape: a top-level
rebootRequired boolean, or a nested metadata.'Microsoft.DSC'.rebootRequired. Absent either,
it reports $false.
The runner invokes dsc resource <verb> per resource, per method. It never calls
dsc config, and the decision is forced by the design rather than by convenience: the runner
makes decisions between resources — a preCondition, a postExecutionScript, a
Stop-TaskProcessing, a reboot policy, a notify-forced refresh — based on what the previous
resource returned. Handing a whole document to dsc config would surrender exactly those
decision points.
ConvertTo-DscV3ConfigurationDocument is available if you want a v3 configuration document for
some other purpose. It is an exported helper with no internal caller: it builds a fresh
document containing only name, type and properties per resource, so every pipeline-only
key — dependsOn, notify, preCondition, postCondition, the execution scripts, target,
resourceCredential — is dropped by construction.
$doc = ConvertTo-DscV3ConfigurationDocument -Resource $resources
$doc | ConvertTo-Json -Depth 10Those keys are pipeline-runner concepts, not DSC concepts. DSC receives a flat, compiled artifact; the relationships have already been discharged into the order the runner evaluates things in.
An engine action is a .ps1 under Actions/Engine/ taking a single -Context hashtable:
| Context key | Meaning |
|---|---|
Method |
Test, Set or Get. |
ModuleName |
The left half of the resource's type. |
Name |
The right half of the resource's type. |
Property |
The fully expanded property table. |
Session |
The target session, or $null for local. |
It must return an object exposing:
| Property | Meaning |
|---|---|
InDesiredState |
[bool]. Default $true when the method does not report one. |
RebootRequired |
[bool]. Default $false. |
Message |
A human-readable detail, or $null. |
Raw |
The underlying result, unmodified — this is what reference() and using() expose. |
The runner normalizes that into a [DscMethodResult], so the loop, the report and the exit
code stay engine-agnostic.
Pass a scriptblock, no file required:
Invoke-DscRunner -ConfigurationSourcePath 'C:\config' -EngineAction {
param($Context)
$raw = Invoke-MyPlatformResource -Type "$($Context.ModuleName)/$($Context.Name)" `
-Verb $Context.Method `
-Body $Context.Property
[pscustomobject]@{
InDesiredState = [bool]$raw.converged
RebootRequired = $false
Message = $raw.detail
Raw = $raw
}
}An inline -EngineAction takes precedence over -Engine and bypasses selection entirely.
Alternatively, drop Actions/Engine/MyEngine.ps1 into the module and select it by name:
PipelineRunnerSettings:
Engine: MyEngine-
Windows, existing PSDesiredStateConfiguration resource modules ⇒
DscV2. It is the default for a reason: credentials marshal natively, remoting is CIM, and resource schemas are inspectable by the pre-parse property rule. -
Linux or macOS agents, or DSC v3 resources ⇒
DscV3. Note that SSH targets require it — there is no CIM-over-SSH transport forInvoke-DscResource. -
Migrating ⇒ the two engines need different
typespellings, so a configuration cannot straddle both. Move a whole configuration at a time.
Dsc.PipelineRunner 1.1.0 — generated from
the repository on 2026-09-17.
Conceptual pages are authored in WikiSource/; command pages are generated from
comment-based help. Edit those, not the wiki: the wiki is overwritten on every release.