From 02cd71dfb17b3439d1b5c42313b7bdb5d12db4a2 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Wed, 11 Feb 2026 16:33:05 -0500 Subject: [PATCH 1/4] update registry manifest --- resources/registry/registry.dsc.resource.json | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/resources/registry/registry.dsc.resource.json b/resources/registry/registry.dsc.resource.json index 8a5490869..e92d55d88 100644 --- a/resources/registry/registry.dsc.resource.json +++ b/resources/registry/registry.dsc.resource.json @@ -25,6 +25,9 @@ { "jsonInputArg": "--input", "mandatory": true + }, + { + "whatIfArg": "-w" } ] }, @@ -36,21 +39,11 @@ { "jsonInputArg": "--input", "mandatory": true - } - ] - }, - "whatIf": { - "executable": "registry", - "args": [ - "config", - "set", - "-w", + }, { - "jsonInputArg": "--input", - "mandatory": true + "whatIfArg": "-w" } - ], - "return": "state" + ] }, "exitCodes": { "0": "Success", From d37345b5450879cbd18137f6b22c6ae4fbcdc43c Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Thu, 26 Feb 2026 10:57:54 -0500 Subject: [PATCH 2/4] add tests for whatIfReturns behavior --- dsc/tests/dsc_whatif.tests.ps1 | 42 +++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/dsc/tests/dsc_whatif.tests.ps1 b/dsc/tests/dsc_whatif.tests.ps1 index ba838f8b6..83e9abae6 100644 --- a/dsc/tests/dsc_whatif.tests.ps1 +++ b/dsc/tests/dsc_whatif.tests.ps1 @@ -94,7 +94,7 @@ Describe 'whatif tests' { $LASTEXITCODE | Should -Be 0 $result.metadata.'Microsoft.DSC'.executionType | Should -BeExactly 'whatIf' $result.results.result.afterState.executionType | Should -BeExactly 'WhatIf' - $result.results.result.changedProperties | Should -BeExactly 'executionType' + $result.results.result.changedProperties | Should -Contain 'executionType' $result.hadErrors | Should -BeFalse $result.results.Count | Should -Be 1 } @@ -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 -Contain '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 + } } From 279fa28eec8ebab7ce86b8f73dda502222f816a7 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Thu, 26 Feb 2026 10:58:20 -0500 Subject: [PATCH 3/4] add whatIfReturns field to manifest --- .../src/dscresources/command_resource.rs | 8 ++++- .../src/dscresources/resource_manifest.rs | 3 ++ resources/registry/registry.dsc.resource.json | 3 +- tools/dsctest/dsctest.dsc.manifests.json | 34 +++++++++++++++++++ tools/dsctest/src/args.rs | 2 ++ tools/dsctest/src/main.rs | 29 +++++++++++++--- tools/dsctest/src/whatif.rs | 2 ++ 7 files changed, 75 insertions(+), 6 deletions(-) diff --git a/lib/dsc-lib/src/dscresources/command_resource.rs b/lib/dsc-lib/src/dscresources/command_resource.rs index b2906c5bc..9825415d5 100644 --- a/lib/dsc-lib/src/dscresources/command_resource.rs +++ b/lib/dsc-lib/src/dscresources/command_resource.rs @@ -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 { diff --git a/lib/dsc-lib/src/dscresources/resource_manifest.rs b/lib/dsc-lib/src/dscresources/resource_manifest.rs index 886c789f0..57433da29 100644 --- a/lib/dsc-lib/src/dscresources/resource_manifest.rs +++ b/lib/dsc-lib/src/dscresources/resource_manifest.rs @@ -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, + /// 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, } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] diff --git a/resources/registry/registry.dsc.resource.json b/resources/registry/registry.dsc.resource.json index e92d55d88..40f416f61 100644 --- a/resources/registry/registry.dsc.resource.json +++ b/resources/registry/registry.dsc.resource.json @@ -29,7 +29,8 @@ { "whatIfArg": "-w" } - ] + ], + "whatIfReturns": "state" }, "delete": { "executable": "registry", diff --git a/tools/dsctest/dsctest.dsc.manifests.json b/tools/dsctest/dsctest.dsc.manifests.json index dd3b5a386..aaeff203c 100644 --- a/tools/dsctest/dsctest.dsc.manifests.json +++ b/tools/dsctest/dsctest.dsc.manifests.json @@ -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" + ] + } + } } ] } diff --git a/tools/dsctest/src/args.rs b/tools/dsctest/src/args.rs index fd7b7d56e..b266ca6f4 100644 --- a/tools/dsctest/src/args.rs +++ b/tools/dsctest/src/args.rs @@ -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")] diff --git a/tools/dsctest/src/main.rs b/tools/dsctest/src/main.rs index 5da8dc48a..ae0bbe503 100644 --- a/tools/dsctest/src/main.rs +++ b/tools/dsctest/src/main.rs @@ -325,13 +325,34 @@ 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 } + WhatIf { + execution_type: "WhatIf".to_string(), + exist: None, + from_resource: Some("ResourceProvidedDiff".to_string()) + } } 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 { diff --git a/tools/dsctest/src/whatif.rs b/tools/dsctest/src/whatif.rs index 06850d4b1..586753089 100644 --- a/tools/dsctest/src/whatif.rs +++ b/tools/dsctest/src/whatif.rs @@ -11,4 +11,6 @@ pub struct WhatIf { pub execution_type: String, #[serde(rename = "_exist", skip_serializing_if = "Option::is_none")] pub exist: Option, + #[serde(rename = "fromResource", skip_serializing_if = "Option::is_none")] + pub from_resource: Option, } From 24783afbbcf17ab6eab8569bcae68253c3bb4cea Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Thu, 26 Feb 2026 11:45:08 -0500 Subject: [PATCH 4/4] fix test --- dsc/tests/dsc_resource_set.tests.ps1 | 2 +- dsc/tests/dsc_whatif.tests.ps1 | 4 ++-- tools/dsctest/src/main.rs | 16 ++++++++++++---- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/dsc/tests/dsc_resource_set.tests.ps1 b/dsc/tests/dsc_resource_set.tests.ps1 index dbf7887c2..4f3503a20 100644 --- a/dsc/tests/dsc_resource_set.tests.ps1 +++ b/dsc/tests/dsc_resource_set.tests.ps1 @@ -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' { diff --git a/dsc/tests/dsc_whatif.tests.ps1 b/dsc/tests/dsc_whatif.tests.ps1 index 83e9abae6..554ab934d 100644 --- a/dsc/tests/dsc_whatif.tests.ps1 +++ b/dsc/tests/dsc_whatif.tests.ps1 @@ -94,7 +94,7 @@ Describe 'whatif tests' { $LASTEXITCODE | Should -Be 0 $result.metadata.'Microsoft.DSC'.executionType | Should -BeExactly 'whatIf' $result.results.result.afterState.executionType | Should -BeExactly 'WhatIf' - $result.results.result.changedProperties | Should -Contain 'executionType' + $result.results.result.changedProperties | Should -BeExactly 'executionType' $result.hadErrors | Should -BeFalse $result.results.Count | Should -Be 1 } @@ -198,7 +198,7 @@ Describe 'whatif tests' { $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 -Contain 'fromResource' + $what_if_result.results[0].result.changedProperties | Should -BeExactly 'fromResource' $what_if_result.results.Count | Should -Be 1 } diff --git a/tools/dsctest/src/main.rs b/tools/dsctest/src/main.rs index ae0bbe503..0c9526631 100644 --- a/tools/dsctest/src/main.rs +++ b/tools/dsctest/src/main.rs @@ -327,10 +327,18 @@ fn main() { }, SubCommand::WhatIf { what_if, state_and_diff } => { let result: WhatIf = if what_if { - WhatIf { - execution_type: "WhatIf".to_string(), - exist: None, - from_resource: Some("ResourceProvidedDiff".to_string()) + 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 {