From 3beab6ea18c0657177f1b666d947198e47502bf2 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Sun, 31 May 2026 09:20:46 -0700 Subject: [PATCH 1/7] Change PSScript resource to not exit on non-terminating errors --- resources/PSScript/psscript.ps1 | 7 +++---- resources/PSScript/psscript.tests.ps1 | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/resources/PSScript/psscript.ps1 b/resources/PSScript/psscript.ps1 index 2b395dce1..b46dba9e9 100644 --- a/resources/PSScript/psscript.ps1 +++ b/resources/PSScript/psscript.ps1 @@ -138,7 +138,7 @@ try { $asyncResult = $ps.BeginInvoke() while (-not $asyncResult.IsCompleted) { Write-TraceQueue - + Start-Sleep -Milliseconds 100 } $outputCollection = $ps.EndInvoke($asyncResult) @@ -146,9 +146,8 @@ try { if ($ps.HadErrors) { - # If there are any errors, we will exit with an error code - Write-DscTrace -Now -Level Error -Message 'Errors occurred during script execution.' - exit 1 + # Errors can be non-terminating, so we just write a warning and continue + Write-DscTrace -Now -Level Warn -Message 'Non-terminating errors occurred during script execution.' } foreach ($output in $outputCollection) { diff --git a/resources/PSScript/psscript.tests.ps1 b/resources/PSScript/psscript.tests.ps1 index 4406ccae5..3ff19efeb 100644 --- a/resources/PSScript/psscript.tests.ps1 +++ b/resources/PSScript/psscript.tests.ps1 @@ -324,4 +324,18 @@ Describe 'Tests for PSScript resource' { (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*INFO*:*This is a host message*' (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*INFO*:*This is a verbose message*' } + + It 'Non-terminating errors do not cause script to fail' { + $yaml = @' + getScript: | + $ErrorActionPreference = 'Continue' + Write-Error "This is an error" + "This should still be output" +'@ + $result = dsc resource get -r 'Microsoft.DSC.Transitional/PowerShellScript' -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json + $LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) + $result.actualState.output.Count | Should -Be 1 -Because ($result | ConvertTo-Json | Out-String) + $result.actualState.output[0] | Should -BeExactly "This should still be output" + (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*WARN*:*Non-terminating errors occurred during script execution.*' + } } From 91c607c12ea8394bc464f1be1cbe1b2e115da207 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Sun, 31 May 2026 12:46:08 -0700 Subject: [PATCH 2/7] cover both ps7 and winps --- resources/PSScript/psscript.tests.ps1 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/resources/PSScript/psscript.tests.ps1 b/resources/PSScript/psscript.tests.ps1 index 3ff19efeb..905439e4e 100644 --- a/resources/PSScript/psscript.tests.ps1 +++ b/resources/PSScript/psscript.tests.ps1 @@ -325,14 +325,16 @@ Describe 'Tests for PSScript resource' { (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*INFO*:*This is a verbose message*' } - It 'Non-terminating errors do not cause script to fail' { + It 'Non-terminating errors do not cause script to fail' -TestCases $testCases { + param($resourceType) + $yaml = @' getScript: | $ErrorActionPreference = 'Continue' Write-Error "This is an error" "This should still be output" '@ - $result = dsc resource get -r 'Microsoft.DSC.Transitional/PowerShellScript' -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json + $result = dsc resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) $result.actualState.output.Count | Should -Be 1 -Because ($result | ConvertTo-Json | Out-String) $result.actualState.output[0] | Should -BeExactly "This should still be output" From 2f5c7eec4424257cff4bc4f03e98a9fd9b12daa3 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Mon, 1 Jun 2026 11:45:03 -0700 Subject: [PATCH 3/7] Update resources/PSScript/psscript.ps1 Co-authored-by: Mikey Lombardi (He/Him) --- resources/PSScript/psscript.ps1 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/resources/PSScript/psscript.ps1 b/resources/PSScript/psscript.ps1 index b46dba9e9..0a99e7b6b 100644 --- a/resources/PSScript/psscript.ps1 +++ b/resources/PSScript/psscript.ps1 @@ -141,6 +141,18 @@ try { Start-Sleep -Milliseconds 100 } + + if ($ps.InvocationStateInfo.State -eq 'Failed') { + $record = $ps.InvocationStateInfo.Reason.ErrorRecord + $message = "Script failed with terminating error at line {0} for statement ``{1}`` - {2}" -f @( + $record.InvocationInfo.ScriptLineNumber, + $record.InvocationInfo.Statement.Trim(), + $record.Exception.ToString() + ) + Write-DscTrace -Now -Level Error -Message $message + exit 1 + } + $outputCollection = $ps.EndInvoke($asyncResult) Write-TraceQueue From 94295d2d7bd351e2b1e89cd58fff7f004dc49591 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Mon, 1 Jun 2026 11:45:51 -0700 Subject: [PATCH 4/7] add additional test for ErrorAction Stop --- resources/PSScript/psscript.tests.ps1 | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/resources/PSScript/psscript.tests.ps1 b/resources/PSScript/psscript.tests.ps1 index 905439e4e..2dcda1caa 100644 --- a/resources/PSScript/psscript.tests.ps1 +++ b/resources/PSScript/psscript.tests.ps1 @@ -340,4 +340,17 @@ Describe 'Tests for PSScript resource' { $result.actualState.output[0] | Should -BeExactly "This should still be output" (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*WARN*:*Non-terminating errors occurred during script execution.*' } + + It 'Terminating errors result in an error for ' -TestCases $testCases { + param($resourceType) + + $yaml = @' + GetScript: | + Get-Item "ThisFileDoesNotExist.txt" -ErrorAction Stop +'@ + $result = dsc resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json + $LASTEXITCODE | Should -Be 2 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) + $result.actualState.output.Count | Should -Be 0 -Because ($result | ConvertTo-Json | Out-String) + (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*ERROR*:*Cannot find path*' + } } From aab76e74e0df7eeb77884d87818ff1a4b5808bab Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Mon, 1 Jun 2026 12:44:03 -0700 Subject: [PATCH 5/7] change HadErrors trace to debug level --- resources/PSScript/psscript.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/PSScript/psscript.ps1 b/resources/PSScript/psscript.ps1 index 0a99e7b6b..e504dc843 100644 --- a/resources/PSScript/psscript.ps1 +++ b/resources/PSScript/psscript.ps1 @@ -159,7 +159,7 @@ try { if ($ps.HadErrors) { # Errors can be non-terminating, so we just write a warning and continue - Write-DscTrace -Now -Level Warn -Message 'Non-terminating errors occurred during script execution.' + Write-DscTrace -Now -Level Debug -Message 'Non-terminating errors occurred during script execution.' } foreach ($output in $outputCollection) { From a014819a9b9660aacc3edc570764e5ada0526df1 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Mon, 1 Jun 2026 13:43:59 -0700 Subject: [PATCH 6/7] fix test --- resources/PSScript/psscript.tests.ps1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/PSScript/psscript.tests.ps1 b/resources/PSScript/psscript.tests.ps1 index 2dcda1caa..9d7f624ab 100644 --- a/resources/PSScript/psscript.tests.ps1 +++ b/resources/PSScript/psscript.tests.ps1 @@ -330,15 +330,15 @@ Describe 'Tests for PSScript resource' { $yaml = @' getScript: | - $ErrorActionPreference = 'Continue' - Write-Error "This is an error" + Get-Item "ThisFileDoesNotExist.txt" -ErrorAction SilentlyContinue "This should still be output" '@ - $result = dsc resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json + $result = dsc -l debug resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) - $result.actualState.output.Count | Should -Be 1 -Because ($result | ConvertTo-Json | Out-String) + $result.actualState.output.Count | Should -Be 1 -Because ($result | ConvertTo-Json -Depth 10 | Out-String) $result.actualState.output[0] | Should -BeExactly "This should still be output" - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*WARN*:*Non-terminating errors occurred during script execution.*' + $errorLog = Get-Content $TestDrive/error.txt -Raw + $errorLog | Should -BeLike '*DEBUG*:*Non-terminating errors occurred during script execution.*' -Because $errorLog } It 'Terminating errors result in an error for ' -TestCases $testCases { From 62d3be2617a366a29364545a6cd714dfcec0bb91 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Mon, 1 Jun 2026 16:25:40 -0700 Subject: [PATCH 7/7] Fix error handling code --- resources/PSScript/psscript.ps1 | 16 ++++++---- resources/PSScript/psscript.tests.ps1 | 43 +++++++++++++++++---------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/resources/PSScript/psscript.ps1 b/resources/PSScript/psscript.ps1 index e504dc843..e3d562abb 100644 --- a/resources/PSScript/psscript.ps1 +++ b/resources/PSScript/psscript.ps1 @@ -144,11 +144,15 @@ try { if ($ps.InvocationStateInfo.State -eq 'Failed') { $record = $ps.InvocationStateInfo.Reason.ErrorRecord - $message = "Script failed with terminating error at line {0} for statement ``{1}`` - {2}" -f @( - $record.InvocationInfo.ScriptLineNumber, - $record.InvocationInfo.Statement.Trim(), - $record.Exception.ToString() - ) + $message = if ($null -ne $record) { + "Script failed with terminating error at line {0} for statement ``{1}`` - {2}" -f @( + $record.InvocationInfo.ScriptLineNumber, + $record.InvocationInfo.Line, + $record.Exception.ToString() + ) + } else { + "Script failed with terminating error: $($ps.InvocationStateInfo.Reason)" + } Write-DscTrace -Now -Level Error -Message $message exit 1 } @@ -167,7 +171,7 @@ try { } } catch { - Write-DscTrace -Now -Level Error -Message $_ + Write-DscTrace -Now -Level Error -Message ($_ | Format-List -Force * | Out-String) exit 1 } finally { diff --git a/resources/PSScript/psscript.tests.ps1 b/resources/PSScript/psscript.tests.ps1 index 9d7f624ab..b2d91786d 100644 --- a/resources/PSScript/psscript.tests.ps1 +++ b/resources/PSScript/psscript.tests.ps1 @@ -108,7 +108,8 @@ Describe 'Tests for PSScript resource' { $result = dsc resource test -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 2 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) $result | Should -BeNullOrEmpty -Because "Test operation should return an error" - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*Test operation did not return a single boolean value.*' + $errorLog = Get-Content $TestDrive/error.txt -Raw + $errorLog | Should -BeLike '*ERROR*:*Test operation did not return a single boolean value.*' -Because $errorLog } It 'Test operation returns error for multiple boolean results for ' -TestCases $testCases { @@ -122,7 +123,8 @@ Describe 'Tests for PSScript resource' { $result = dsc resource test -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 2 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) $result | Should -BeNullOrEmpty -Because "Test operation should return an error" - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*Test operation did not return a single boolean value.*' + $errorLog = Get-Content $TestDrive/error.txt -Raw + $errorLog | Should -BeLike '*ERROR*:*Test operation did not return a single boolean value.*' -Because $errorLog } It 'Empty SetScript is ignored for ' -TestCases $testCases { @@ -187,8 +189,9 @@ Describe 'Tests for PSScript resource' { $result = dsc resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) $result.actualState.output.Count | Should -Be 0 -Because ($result | ConvertTo-Json | Out-String) - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*WARN*:*This is a warning*' - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*WARN*:*This is second warning*' + $errorLog = Get-Content $TestDrive/error.txt -Raw + $errorLog | Should -BeLike '*WARN*:*This is a warning*' -Because $errorLog + $errorLog | Should -BeLike '*WARN*:*This is second warning*' -Because $errorLog } It 'Write-Error shows up as error traces for ' -TestCases $testCases { @@ -201,8 +204,9 @@ Describe 'Tests for PSScript resource' { $result = dsc resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 2 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) + $errorLog = Get-Content $TestDrive/error.txt -Raw $result.actualState.output.Count | Should -Be 0 -Because ($result | ConvertTo-Json | Out-String) - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*ERROR*:*This is an error*' + $errorLog | Should -BeLike '*ERROR*:*This is an error*' -Because $errorLog } It 'Write-Verbose shows up as info traces for ' -TestCases $testCases { @@ -214,8 +218,9 @@ Describe 'Tests for PSScript resource' { '@ $result = dsc -l info resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) + $errorLog = Get-Content $TestDrive/error.txt -Raw $result.actualState.output.Count | Should -Be 0 -Because ($result | ConvertTo-Json | Out-String) - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*INFO*:*This is a verbose message*' + $errorLog | Should -BeLike '*INFO*:*This is a verbose message*' -Because $errorLog } It 'Write-Debug shows up as debug traces for ' -TestCases $testCases { @@ -227,8 +232,9 @@ Describe 'Tests for PSScript resource' { '@ $result = dsc -l debug resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) + $errorLog = Get-Content $TestDrive/error.txt -Raw $result.actualState.output.Count | Should -Be 0 -Because ($result | ConvertTo-Json | Out-String) - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*DEBUG*:*This is a debug message*' + $errorLog | Should -BeLike '*DEBUG*:*This is a debug message*' -Because $errorLog } It 'Write-Information shows up as trace traces for ' -TestCases $testCases { @@ -241,8 +247,9 @@ Describe 'Tests for PSScript resource' { '@ $result = dsc -l trace resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) + $errorLog = Get-Content $TestDrive/error.txt -Raw $result.actualState.output.Count | Should -Be 0 -Because ($result | ConvertTo-Json | Out-String) - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*TRACE*:*This is an information message*' + $errorLog | Should -BeLike '*TRACE*:*This is an information message*' -Because $errorLog } It 'A thrown exception results in an error for ' -TestCases $testCases { @@ -254,8 +261,9 @@ Describe 'Tests for PSScript resource' { '@ $result = dsc resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 2 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) + $errorLog = Get-Content $TestDrive/error.txt -Raw $result.actualState.output.Count | Should -Be 0 -Because ($result | ConvertTo-Json | Out-String) - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*ERROR*:*This is an exception*' + $errorLog | Should -BeLike '*ERROR*:*This is an exception*' -Because $errorLog } It 'Sample config works' { @@ -291,8 +299,9 @@ Describe 'Tests for PSScript resource' { input: "This is a string" '@ dsc resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json - $LASTEXITCODE | Should -Be 2 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*ERROR*:*Input was provided but script does not have a parameter to accept input.*' + $errorLog = Get-Content $TestDrive/error.txt -Raw + $LASTEXITCODE | Should -Be 2 -Because $errorLog + $errorLog | Should -BeLike '*ERROR*:*Input was provided but script does not have a parameter to accept input.*' -Because $errorLog } It 'Param without input is an error for ' -TestCases $testCases { @@ -304,8 +313,9 @@ Describe 'Tests for PSScript resource' { "This should fail" '@ dsc resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json - $LASTEXITCODE | Should -Be 2 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike "*ERROR*:*Script has a parameter 'inputObj' but no input was provided.*" + $errorLog = Get-Content $TestDrive/error.txt -Raw + $LASTEXITCODE | Should -Be 2 -Because $errorLog + $errorLog | Should -BeLike "*ERROR*:*Script has a parameter 'inputObj' but no input was provided.*" -Because $errorLog } It 'Write-Host results in an info message for ' -TestCases $testCases { @@ -319,10 +329,11 @@ Describe 'Tests for PSScript resource' { '@ $result = dsc -l trace resource get -r $resourceType -i $yaml 2> $TestDrive/error.txt | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.txt -Raw | Out-String) + $errorLog = Get-Content $TestDrive/error.txt -Raw $result.actualState.output.Count | Should -Be 0 -Because ($result | ConvertTo-Json | Out-String) - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*WARN*:*This is a warning*' - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*INFO*:*This is a host message*' - (Get-Content $TestDrive/error.txt -Raw) | Should -BeLike '*INFO*:*This is a verbose message*' + $errorLog | Should -BeLike '*WARN*:*This is a warning*' -Because $errorLog + $errorLog | Should -BeLike '*INFO*:*This is a host message*' -Because $errorLog + $errorLog | Should -BeLike '*INFO*:*This is a verbose message*' -Because $errorLog } It 'Non-terminating errors do not cause script to fail' -TestCases $testCases {