From eff69ff2132d3dff32d30a78bb790bb4699289ac Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Thu, 26 Feb 2026 12:32:26 -0500 Subject: [PATCH 1/7] add resource delete what-if plumbing --- dsc/src/args.rs | 4 ++++ dsc/src/resource_command.rs | 34 ++++++++++++++++++++++++++++++---- dsc/src/subcommand.rs | 4 ++-- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/dsc/src/args.rs b/dsc/src/args.rs index dd331a516..add67b0bb 100644 --- a/dsc/src/args.rs +++ b/dsc/src/args.rs @@ -264,6 +264,10 @@ pub enum ResourceSubCommand { input: Option, #[clap(short = 'f', long, help = t!("args.file").to_string(), conflicts_with = "input")] file: Option, + #[clap(short = 'o', long, help = t!("args.outputFormat").to_string())] + output_format: Option, + #[clap(short = 'w', long, visible_aliases = ["dry-run", "noop"], help = t!("args.whatIf").to_string())] + what_if: bool, }, #[clap(name = "schema", about = "Get the JSON schema for a resource", arg_required_else_help = true)] Schema { diff --git a/dsc/src/resource_command.rs b/dsc/src/resource_command.rs index 54a20283a..600e26910 100644 --- a/dsc/src/resource_command.rs +++ b/dsc/src/resource_command.rs @@ -6,7 +6,7 @@ use crate::util::{EXIT_DSC_ERROR, EXIT_INVALID_ARGS, EXIT_JSON_ERROR, EXIT_DSC_R use dsc_lib::configure::config_doc::{Configuration, ExecutionKind}; use dsc_lib::configure::add_resource_export_results_to_configuration; use dsc_lib::discovery::discovery_trait::DiscoveryFilter; -use dsc_lib::dscresources::{resource_manifest::Kind, invoke_result::{GetResult, ResourceGetResponse, ResourceSetResponse, SetResult}}; +use dsc_lib::dscresources::{resource_manifest::Kind, invoke_result::{DeleteResultKind, GetResult, ResourceGetResponse, ResourceSetResponse, SetResult}}; use dsc_lib::dscresources::dscresource::{Capability, get_diff}; use dsc_lib::dscerror::DscError; use rust_i18n::t; @@ -258,7 +258,7 @@ pub fn test(dsc: &mut DscManager, resource_type: &str, version: Option<&str>, in } } -pub fn delete(dsc: &mut DscManager, resource_type: &str, version: Option<&str>, input: &str) { +pub fn delete(dsc: &mut DscManager, resource_type: &str, version: Option<&str>, input: &str, format: Option<&OutputFormat>, what_if: bool) { let Some(resource) = get_resource(dsc, resource_type, version) else { error!("{}", DscError::ResourceNotFound(resource_type.to_string(), version.unwrap_or("").to_string()).to_string()); exit(EXIT_DSC_RESOURCE_NOT_FOUND); @@ -270,8 +270,34 @@ pub fn delete(dsc: &mut DscManager, resource_type: &str, version: Option<&str>, exit(EXIT_DSC_ERROR); } - match resource.delete(input, &ExecutionKind::Actual) { - Ok(_) => {} + let execution_kind = if what_if { ExecutionKind::WhatIf } else { ExecutionKind::Actual }; + + match resource.delete(input, &execution_kind) { + Ok(result) => { + match result { + DeleteResultKind::ResourceActual => { + }, + DeleteResultKind::ResourceWhatIf(delete_result) => { + match serde_json::to_string(&delete_result) { + Ok(json) => write_object(&json, format, false), + Err(err) => { + error!("JSON: {err}"); + exit(EXIT_JSON_ERROR); + } + } + }, + DeleteResultKind::SyntheticWhatIf(test_result) => { + match serde_json::to_string(&test_result) { + Ok(json) => write_object(&json, format, false), + Err(err) => { + error!("JSON: {err}"); + exit(EXIT_JSON_ERROR); + } + } + } + } + + }, Err(err) => { error!("{err}"); exit(EXIT_DSC_ERROR); diff --git a/dsc/src/subcommand.rs b/dsc/src/subcommand.rs index e5decc185..fe683b5f8 100644 --- a/dsc/src/subcommand.rs +++ b/dsc/src/subcommand.rs @@ -595,13 +595,13 @@ pub fn resource(subcommand: &ResourceSubCommand, progress_format: ProgressFormat let parsed_input = get_input(input.as_ref(), path.as_ref()); resource_command::test(&mut dsc, resource, version.as_deref(), &parsed_input, output_format.as_ref()); }, - ResourceSubCommand::Delete { resource, version, input, file: path } => { + ResourceSubCommand::Delete { resource, version, input, file: path, output_format, what_if } => { if let Err(err) = dsc.find_resources(&[DiscoveryFilter::new(resource, version.as_deref(), None)], progress_format) { error!("{}: {err}", t!("subcommand.failedDiscoverResource")); exit(EXIT_DSC_ERROR); } let parsed_input = get_input(input.as_ref(), path.as_ref()); - resource_command::delete(&mut dsc, resource, version.as_deref(), &parsed_input); + resource_command::delete(&mut dsc, resource, version.as_deref(), &parsed_input, output_format.as_ref(), *what_if); }, } } From de04ebacc79a019591c133f4e64d854fe8a184d6 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Thu, 26 Feb 2026 12:32:36 -0500 Subject: [PATCH 2/7] add test --- dsc/tests/dsc_whatif.tests.ps1 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dsc/tests/dsc_whatif.tests.ps1 b/dsc/tests/dsc_whatif.tests.ps1 index ba838f8b6..c05b3d21d 100644 --- a/dsc/tests/dsc_whatif.tests.ps1 +++ b/dsc/tests/dsc_whatif.tests.ps1 @@ -181,4 +181,12 @@ Describe 'whatif tests' { $out.results[0].result.afterState._exist | Should -BeFalse $out.metadata.'Microsoft.DSC'.executionType | Should -BeExactly 'whatIf' } + + It 'dsc resource delete supports what-if flag' { + $result = dsc resource delete -r Test/WhatIfDelete -i '{"_exist": false}' --what-if | ConvertFrom-Json + $LASTEXITCODE | Should -Be 0 + $result._metadata.whatIf | Should -Not -BeNullOrEmpty + $result._metadata.whatIf | Should -Contain 'Delete what-if message 1' + $result._metadata.whatIf | Should -Contain 'Delete what-if message 2' + } } From 490c64ef00cbc5ccf75b788d9d08b122ef600e0c Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Thu, 26 Feb 2026 12:36:55 -0500 Subject: [PATCH 3/7] fix typo --- dsc/src/resource_command.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/dsc/src/resource_command.rs b/dsc/src/resource_command.rs index 600e26910..0bec12cc2 100644 --- a/dsc/src/resource_command.rs +++ b/dsc/src/resource_command.rs @@ -296,7 +296,6 @@ pub fn delete(dsc: &mut DscManager, resource_type: &str, version: Option<&str>, } } } - }, Err(err) => { error!("{err}"); From 2dbad565e0bb2a3b8b99100a23d6a9fa2c179a0f Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Wed, 11 Mar 2026 14:12:37 -0400 Subject: [PATCH 4/7] standardize resource delete output --- dsc/locales/en-us.toml | 1 + dsc/src/resource_command.rs | 12 +++--------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/dsc/locales/en-us.toml b/dsc/locales/en-us.toml index 62d3525b2..ef0c1a7d9 100644 --- a/dsc/locales/en-us.toml +++ b/dsc/locales/en-us.toml @@ -109,6 +109,7 @@ setInputEmpty = "Desired input is empty" testInputEmpty = "Expected input is required" jsonError = "JSON: %{err}" routingToDelete = "Routing to delete operation because _exist is false" +syntheticWhatIf = "Resource does not natively support what-if, engine would generate synthetic what-if" [subcommand] actualStateNotObject = "actual_state is not an object" diff --git a/dsc/src/resource_command.rs b/dsc/src/resource_command.rs index 0bec12cc2..cc4c0216a 100644 --- a/dsc/src/resource_command.rs +++ b/dsc/src/resource_command.rs @@ -11,7 +11,7 @@ use dsc_lib::dscresources::dscresource::{Capability, get_diff}; use dsc_lib::dscerror::DscError; use rust_i18n::t; use serde_json::Value; -use tracing::{error, debug}; +use tracing::{debug, error, info}; use dsc_lib::{ dscresources::dscresource::{Invoke, DscResource}, @@ -286,14 +286,8 @@ pub fn delete(dsc: &mut DscManager, resource_type: &str, version: Option<&str>, } } }, - DeleteResultKind::SyntheticWhatIf(test_result) => { - match serde_json::to_string(&test_result) { - Ok(json) => write_object(&json, format, false), - Err(err) => { - error!("JSON: {err}"); - exit(EXIT_JSON_ERROR); - } - } + DeleteResultKind::SyntheticWhatIf(_) => { + info!("{} {}", resource.type_name, t!("resource_command.syntheticWhatIf")); } } }, From a60bd80ea4d112184670951e1f29341e17c69f8e Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Wed, 11 Mar 2026 14:29:41 -0400 Subject: [PATCH 5/7] add test for synthetic scenario --- dsc/tests/dsc_whatif.tests.ps1 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dsc/tests/dsc_whatif.tests.ps1 b/dsc/tests/dsc_whatif.tests.ps1 index c05b3d21d..251eefc80 100644 --- a/dsc/tests/dsc_whatif.tests.ps1 +++ b/dsc/tests/dsc_whatif.tests.ps1 @@ -189,4 +189,11 @@ Describe 'whatif tests' { $result._metadata.whatIf | Should -Contain 'Delete what-if message 1' $result._metadata.whatIf | Should -Contain 'Delete what-if message 2' } + + It 'dsc resource delete synthetic what-if logs info message and produces no output' { + $result = dsc -l info resource delete -r Test/Delete -i '{"_exist": false}' --what-if 2>&1 + $LASTEXITCODE | Should -Be 0 + "$result" | Should -Match 'generate synthetic what-if' + "$result" | Should -Not -Match '^\s*\{' + } } From b9a487727bf60e01d7fce39030ccdb557544dc07 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Wed, 11 Mar 2026 16:24:22 -0400 Subject: [PATCH 6/7] Update dsc/locales/en-us.toml Co-authored-by: Steve Lee --- dsc/locales/en-us.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsc/locales/en-us.toml b/dsc/locales/en-us.toml index ef0c1a7d9..c4b747daa 100644 --- a/dsc/locales/en-us.toml +++ b/dsc/locales/en-us.toml @@ -109,7 +109,7 @@ setInputEmpty = "Desired input is empty" testInputEmpty = "Expected input is required" jsonError = "JSON: %{err}" routingToDelete = "Routing to delete operation because _exist is false" -syntheticWhatIf = "Resource does not natively support what-if, engine would generate synthetic what-if" +syntheticWhatIf = "Resource does not natively support what-if, engine will generate synthetic what-if" [subcommand] actualStateNotObject = "actual_state is not an object" From 8c6a0a507c941d459a02ec5cb634d1f9e9a7d38f Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Thu, 12 Mar 2026 10:31:30 -0400 Subject: [PATCH 7/7] fix bad merge --- dsc/tests/dsc_whatif.tests.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dsc/tests/dsc_whatif.tests.ps1 b/dsc/tests/dsc_whatif.tests.ps1 index 2accf8ad4..19b20b96d 100644 --- a/dsc/tests/dsc_whatif.tests.ps1 +++ b/dsc/tests/dsc_whatif.tests.ps1 @@ -198,6 +198,8 @@ Describe 'whatif tests' { $LASTEXITCODE | Should -Be 0 "$result" | Should -Match 'generate synthetic what-if' "$result" | Should -Not -Match '^\s*\{' + } + 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