Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dsc/tests/dsc_resource_set.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Describe 'Invoke a resource set directly' {
$result = dsc resource set $alias -r Test/WhatIf --input '{"executionType":"Actual"}' | ConvertFrom-Json
$LASTEXITCODE | Should -Be 0
$result.afterState.executionType | Should -BeExactly 'WhatIf'
$result.changedProperties | Should -BeExactly 'executionType'
$result.changedProperties | Should -Contain 'executionType'
}

It 'actual execution of WhatIf resource' {
Expand Down
40 changes: 40 additions & 0 deletions dsc/tests/dsc_whatif.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,44 @@ Describe 'whatif tests' {
$out.results[0].result.afterState._exist | Should -BeFalse
$out.metadata.'Microsoft.DSC'.executionType | Should -BeExactly 'whatIf'
}

It 'Test/WhatIfReturnDiff resource returns state and diff in what-if mode' {
$config_yaml = @"
`$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: WhatIfReturn StateAndDiff
type: Test/WhatIfReturnDiff
properties:
executionType: Actual
"@
$what_if_result = $config_yaml | dsc config set -w -f - | ConvertFrom-Json
$LASTEXITCODE | Should -Be 0
$what_if_result.hadErrors | Should -BeFalse
$what_if_result.metadata.'Microsoft.DSC'.executionType | Should -BeExactly 'whatIf'
$what_if_result.results[0].result.beforeState.executionType | Should -BeExactly 'Actual'
$what_if_result.results[0].result.afterState.executionType | Should -BeExactly 'WhatIf'
$what_if_result.results[0].result.afterState.fromResource | Should -BeExactly 'ResourceProvidedDiff'
$what_if_result.results[0].result.changedProperties | Should -BeExactly 'fromResource'
$what_if_result.results.Count | Should -Be 1
}

It 'Test/WhatIfReturnDiff resource returns state only in actual mode' {
$config_yaml = @"
`$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: WhatIfReturn StateAndDiff
type: Test/WhatIfReturnDiff
properties:
executionType: Actual
"@
$actual_result = $config_yaml | dsc config set -f - | ConvertFrom-Json
$LASTEXITCODE | Should -Be 0
$actual_result.hadErrors | Should -BeFalse
$actual_result.metadata.'Microsoft.DSC'.executionType | Should -BeExactly 'actual'
$actual_result.results[0].result.beforeState.executionType | Should -BeExactly 'Actual'
$actual_result.results[0].result.afterState.executionType | Should -BeExactly 'Actual'
$actual_result.results[0].result.afterState.fromResource | Should -BeNullOrEmpty
$actual_result.results[0].result.changedProperties | Should -Be $null
$actual_result.results.Count | Should -Be 1
}
}
8 changes: 7 additions & 1 deletion lib/dsc-lib/src/dscresources/command_resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,13 @@ pub fn invoke_set(resource: &DscResource, desired: &str, skip_test: bool, execut

let (exit_code, stdout, stderr) = invoke_command(&set.executable, args, input_desired, Some(&resource.directory), env, manifest.exit_codes.as_ref())?;

match set.returns {
let return_kind = if execution_type == &ExecutionKind::WhatIf {
set.what_if_returns.as_ref().or(set.returns.as_ref())
} else {
set.returns.as_ref()
};

match return_kind {
Some(ReturnKind::State) => {

if resource.kind == Kind::Resource {
Expand Down
3 changes: 3 additions & 0 deletions lib/dsc-lib/src/dscresources/resource_manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,9 @@ pub struct SetMethod {
/// The type of return value expected from the Set method.
#[serde(rename = "return", skip_serializing_if = "Option::is_none")]
pub returns: Option<ReturnKind>,
/// The type of return value expected from the Set method when running in what-if mode. When specified, this overrides the `return` property during what-if execution.
#[serde(rename = "whatIfReturns", skip_serializing_if = "Option::is_none")]
pub what_if_returns: Option<ReturnKind>,
}

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)]
Expand Down
22 changes: 8 additions & 14 deletions resources/registry/registry.dsc.resource.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@
{
"jsonInputArg": "--input",
"mandatory": true
},
{
"whatIfArg": "-w"
}
]
],
"whatIfReturns": "state"
},
"delete": {
"executable": "registry",
Expand All @@ -36,21 +40,11 @@
{
"jsonInputArg": "--input",
"mandatory": true
}
]
},
"whatIf": {
"executable": "registry",
"args": [
"config",
"set",
"-w",
},
{
"jsonInputArg": "--input",
"mandatory": true
"whatIfArg": "-w"
}
Comment on lines 41 to 46
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete.args now includes a whatIfArg: "-w", but the registry config delete subcommand does not accept -w/--what-if (only registry config set defines that flag). In what-if mode this will cause the delete invocation to fail argument parsing. Remove the whatIfArg from the delete args (so DSC falls back to synthetic what-if delete), or add what-if support to the registry delete subcommand and ensure its stdout matches the expected DeleteResult JSON contract.

Copilot uses AI. Check for mistakes.
],
"return": "state"
]
},
"exitCodes": {
"0": "Success",
Expand Down
34 changes: 34 additions & 0 deletions tools/dsctest/dsctest.dsc.manifests.json
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,40 @@
]
}
}
},
{
"$schema": "https://aka.ms/dsc/schemas/v3/bundled/resource/manifest.json",
"type": "Test/WhatIfReturnDiff",
"version": "0.1.0",
"description": "Test resource for validating whatIfReturn with stateAndDiff return type",
"get": {
"executable": "dsctest",
"args": [
"whatif"
]
},
"set": {
"executable": "dsctest",
"args": [
"whatif",
{
"whatIfArg": "-w"
},
"--state-and-diff"
],
"return": "state",
"whatIfReturns": "stateAndDiff"
},
"schema": {
"command": {
"executable": "dsctest",
"args": [
"schema",
"-s",
"what-if"
]
}
}
}
]
}
2 changes: 2 additions & 0 deletions tools/dsctest/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ pub enum SubCommand {
WhatIf {
#[clap(name = "whatif", short, long, help = "Run as a whatif executionType instead of actual executionType")]
what_if: bool,
#[clap(name = "state-and-diff", short, long, help = "Output stateAndDiff format (state on first line, diff array on second line)")]
state_and_diff: bool,
},

#[clap(name = "whatif-delete", about = "Check if it is a whatif delete operation")]
Expand Down
37 changes: 33 additions & 4 deletions tools/dsctest/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,13 +325,42 @@ fn main() {
};
serde_json::to_string(&version).unwrap()
},
SubCommand::WhatIf { what_if } => {
SubCommand::WhatIf { what_if, state_and_diff } => {
let result: WhatIf = if what_if {
WhatIf { execution_type: "WhatIf".to_string(), exist: None }
if state_and_diff {
WhatIf {
execution_type: "WhatIf".to_string(),
exist: None,
from_resource: Some("ResourceProvidedDiff".to_string())
}
} else {
WhatIf {
execution_type: "WhatIf".to_string(),
exist: None,
from_resource: None
}
}
} else {
WhatIf { execution_type: "Actual".to_string(), exist: None }
WhatIf {
execution_type: "Actual".to_string(),
exist: None,
from_resource: None
}
};
serde_json::to_string(&result).unwrap()

// Output state as first line
let state_json = serde_json::to_string(&result).unwrap();
println!("{state_json}");

// If state_and_diff is requested and it's a what-if operation, output diff on second line
if state_and_diff && what_if {
let diff = vec!["fromResource"];
let diff_json = serde_json::to_string(&diff).unwrap();
println!("{diff_json}");
}

// Return empty string since we already printed
String::new()
},
SubCommand::WhatIfDelete { what_if } => {
let result = if what_if {
Expand Down
2 changes: 2 additions & 0 deletions tools/dsctest/src/whatif.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ pub struct WhatIf {
pub execution_type: String,
#[serde(rename = "_exist", skip_serializing_if = "Option::is_none")]
pub exist: Option<bool>,
#[serde(rename = "fromResource", skip_serializing_if = "Option::is_none")]
pub from_resource: Option<String>,
}