Scripts showing as "Successful" even when they fail because script execution isn't allowed

I’m realizing that scripts at some of my customers have been silently failing on computers, showing a last run status of “Successful”, even when looking at the output I see this:

error> C:\ProgramData\Syncro\bin\26c34da4-cb56-4650-91f3-26c23bf25178.ps1 cannot be loaded because running scripts is 
error> disabled on this system. For more information, see about_Execution_Policies at 
error> https:/go.microsoft.com/fwlink/?LinkID=135170.
error> At line:1 char:4
error> + & {C:\ProgramData\Syncro\bin\26c34da4-cb56-4650-91f3-26c23bf25178.ps1 ...
error> +    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error>     + CategoryInfo          : SecurityError: (:) [], PSSecurityException
error>     + FullyQualifiedErrorId : UnauthorizedAccess

Success or failure is based on the exit code of the script. If you want scripts to explicitly fail in Syncro, you’d need to trap errors and then gracefully exit the script with an error code. There is a bootstrapped script in all Syncro instances by default that demonstrates how to do this.

I’m not seeing that script in my environment. Someone may have deleted it. Is there an example in documentation somewhere?

This is the script. It uses a runtime variable named “FailThisScript” and expects a “true” or “false” string:

#FailThisScript (boolean) - Determines whether or not the script will show a “Success” or “Error” status in the script history

if ($FailThisScript.toLower() -eq “true”)
{
Write-Host “This is how you exit a script with a error code”
Exit 1
}
else
{
Write-Host “This is how you exit a script with a success code”
Exit 0
}

Is $FailThisScript automatically included in every script by the platform or does it need to be declared somehow?

It’s a runtime script variable. All you really need to do is use “Exit 1” in your script and it will report back a failure. So you do whatever logic you want to do with a try/catch, and if the script bombs you Exit 1 in the catch and it will report back to Syncro as a failure.

The failure code you see in the script output in the script you referenced is just the script output, but the script completed all of its commands, so it wouldn’t report back a failure.

Well here’s an example of a problem with that:

I spent a few hours trying to figure out why a script I was working on the other day wasn’t behaving as expected. If I use Set-Asset-Field and it fails due to a web exception, such as an error 429 due to the API rate limit, a catch block won’t trigger. The script just keeps on running like no error happened. I had to write a whole function to wrap around Set-Asset-Field for that purpose.

Well you could in theory try/catch the entire script and exit that way. My only point here is that if the script runs through the last line of code and completes it, then it doesn’t exit with a failure code meaning the script may not have done what you wanted, but it didn’t fail, either. So the type of failure you’re speaking to needs to be caught and properly exited to report as a failure in Syncro.

OK, two things:

1 - Why should I have to design extra logic to properly throw/catch an error in a function built into the Syncro platform? I’m saying that the Set-Asset-Field function in the Syncro module will not trigger a try/catch block correctly (EDIT: In case of an error 429, for example). as an example I did something like this:

try{
    Set-Asset-Field -Name "TestField" -Value "TestValue"
    Write-Host "If you're seeing this, setting the asset field was successful."
}catch{
    Write-Host "Setting the asset field failed."
    exit 1
}

That script would be listed as successful in the Syncro console and give me output like this (EDIT: If the API write failed due to the rate limit):

error> System.Net.WebException: The remote server returned an error: (429) Too Many Requests.
error> HTTP Status: 429 '429'
error> Content-Type: ''
error> Stack:
error>   at Invoke-WebRequest20, C:\ProgramData\Syncro\bin\module.psm1: line 341
error>   at Call-Api, C:\ProgramData\Syncro\bin\module.psm1: line 268
error>   at Call-SyncroApi, C:\ProgramData\Syncro\bin\module.psm1: line 249
error>   at Set-Asset-Field, C:\ProgramData\Syncro\bin\module.psm1: line 150
error>   at <ScriptBlock>, C:\ProgramData\Syncro\bin\afe37143-4e00-4213-b557-7c405ec29b32.ps1: line 67
error>   at <ScriptBlock>, <No file>: line 1
error>   at <ScriptBlock>, <No file>: line 1
error> Call-SyncroApi: failure
If you're seeing this, setting the asset field was successful.

The catch block wouldn’t trigger despite the error, because the Handle-WebException function in the Syncro PS module isn’t written in a way that throws an error if you get a 429 or other similar web exception. It only writes the error contents to the display and then just keeps going as if nothing happened. That’s why I had to write a function to wrap around it just to read the contents of the $Error variable for a 429 so that I could handle it properly.

2 - I’ve attempted to test a script with $FailThisScript. I have it declared as a runtime variable in a test script, but when I attempt to just write it to the transcript it’s blank. I followed the example here:

Here’s a screenshot of my test:

And here’s the result:

The date and time is: 
Call-SyncroApi: success
FailThisScript value:  

I’m attempting to call a non-existent variable, write to a non-existent custom asset field, and am explicitly throwing an error in that try/catch block, but FailThisScript doesn’t show a value.

I edited my error test script to the following:

Import-Module $env:SyncroModule

$TheDateAndTime = get-date

write-host ("The date and time is: " + $TheDateAndTim)

Set-Asset-Field -Name "NonexistentField" -Value "TestValue"

$i = 0

try {
    do {
        Set-Asset-Field -Name "CrowdstrikeInstalled" -Value "True"
        Set-Asset-Field -Name "CrowdstrikeInstalled" -Value "True"
        Set-Asset-Field -Name "CrowdstrikeInstalled" -Value "True"
        Set-Asset-Field -Name "CrowdstrikeInstalled" -Value "True"
        Set-Asset-Field -Name "CrowdstrikeInstalled" -Value "True"
        Set-Asset-Field -Name "CrowdstrikeInstalled" -Value "True"
        Set-Asset-Field -Name "CrowdstrikeInstalled" -Value "True"
        Set-Asset-Field -Name "CrowdstrikeInstalled" -Value "True"
        Set-Asset-Field -Name "CrowdstrikeInstalled" -Value "True"
        Set-Asset-Field -Name "CrowdstrikeInstalled" -Value "True"
        $i++
    }until($i -eq 40)
}catch{
    write-host "Uh oh, a bad thing happened."
    exit 1
}

if ($FailThisScript.toLower() -eq "true")
{
Write-Host "This is how you exit a script with a error code"
Exit 1
}
else
{
Write-Host "This is how you exit a script with a success code"
Exit 0
}

This script is specifically designed to cause 429 errors due to API rate limits. This script shows a “Successful” status in the console. I can’t actually see the full output for some reason. It just cuts off randomly. I would think that either A: the first catch block should have been triggered by the 429 errors or B: the $FailThisScript if statement should cause the script to exit with an error status.

I apologize if I’m coming across poorly. I really like Syncro as a platform. I’m just really struggling with how to best handle some of these scripting issues.

EDIT: Here’s a cut-down copy of the test script’s log from the Syncro console:

The date and time is: 
Call-SyncroApi: success
[I removed a bunch of successes for brevity]
Call-SyncroApi: success
error> System.Net.WebException: The remote server returned an error: (429) Too Many Requests.
error> HTTP Status: 429 '429'
error> Content-Type: ''
error> Stack:
error>   at Invoke-WebRequest20, C:\ProgramData\Syncro\bin\module.psm1: line 341
error>   at Call-Api, C:\ProgramData\Syncro\bin\module.psm1: line 268
error>   at Call-SyncroApi, C:\ProgramData\Syncro\bin\module.psm1: line 249
error>   at Set-Asset-Field, C:\ProgramData\Syncro\bin\module.psm1: line 150
error>   at <ScriptBlock>, C:\ProgramData\Syncro\bin\e1b69b3a-71a7-404a-a1c5-8ba7403d1a67.ps1: line 16
error>   at <ScriptBlock>, <No file>: line 1
error>   at <ScriptBlock>, <No file>: line 1
error> Call-SyncroApi: failure
[I removed a few of these error blocks for brevity]
error> System.Net.WebException: The remote server returned an error: (429) Too Many Requests.
error> HTTP Status: 429 '429'
error> Content-Type: ''
error> Stack:
error>   at Invoke-WebRequest20, C:\ProgramData\Syncro\bin\module.psm1: line 341
error>   at Call-Api, C:\ProgramData\Syncro\bin\module.psm1: line 268
error>   at Call-SyncroApi, C:\ProgramData\Syncro\bin\module.psm1: line 249
error>   at Set-Asset-Field, C:\ProgramData\Syncro\bin\module.psm1: line 150
error>   at <ScriptBlock>, C:\ProgramData\Syncro\bin\e1b69b3a-71a7-404a-a1c5

It just cuts off there in the middle of that last line. I tested a couple different ways and consistently the script log cuts off after 10,240 characters.

Also, it’s just occurred to me that most of this conversation is moot because it all depends on scripts running. I got my support threads confused and have just remembered that I created this thread because scripts don’t run at all in some cases, but are still listed as a success. If the script fails due to execution policy, nothing I do in the script to catch error matters. Can Syncro not be made to show an error if execution policy causes a script to fail?

Syncro is not parsing the script to look for content. If the script runs, it’s successful, if the script fails to run, it’s a fail. A failure inside of a script that fully runs is not a failed run. By using Exit 1 for failure, you are forcing the script to say I failed, so then Syncro knows it failed. It’s a PowerShell thing. In regards to execution policy, that can be changed and then changed back in the script.

Ok now we’re talking about a lot of different things at once, from Syncro API calls to execution errors to scripts not running. Basically, if you want a script to fail you’ll need to exit the script with a failure code, period.

There should never be a time the script didn’t run and it was recorded as a success, meaning Syncro didn’t trigger the script run.

As Jimmie mentioned above, there are ways in scripting to deal with the execution policy bit.

Ok now we’re talking about a lot of different things at once,

Yes, sorry. I’ve had several issues and they’re getting muddled. To go back to the original topic of this thread - If a script fails to run entirely because of PowerShell execution policy, it’s being reported as a Success. As for what @Jimmie said, how can I change the execution policy using a script if the script never runs?

I’ll create a separate thread on the FailThisScript thing, because that’s still not working for me.

If the script doesn’t run, it won’t even show up in the list that it was attempted. This plagues my weekly reboot script right now. Nothing you can do about that until it gets fixed.

Set-ExecutionPolicy -ExecutionPolicy Unrestricted
Set-ExecutionPolicy -ExecutionPolicy Restricted

Those are the two commands, you could add an if statement to check the execution policy and if doesn’t match unrestricted, exit 1. Scripting requires a lot of logic if you want it to cover every possible scenario. Get-ExecutionPolicy will return the results.

But the IF statement won’t run if the script won’t run.

This topic has definitely been discussed before. I remember chiming in on it in another thread.

As Andy mentioned, if it gets through the script, it considers it successful. Ideally, I think Syncro should adopt what some other RMMs do.

If the script completes, the other RMMs will say Complete, but then they also grab the output streams, StdOut and StdErr, and display those. This at least let’s you easily see if there is any StdErr showing, you know something in the script itself actually failed. Module missing, command incorrect, whatever. In this case, it would display an StdErr for you since the script failed to properly run.

Ah ok, got it. I haven’t ran into that but I understand what you’re saying. I know you can run pwsh.exe from CMD to alter it, but can’t combine CMD and PS at the same time. This is mostly MS fault for how they handle errors. I errored but we processed stuff, so it was a success! Maybe read the contents and trigger based on word matches?

Back with CWA, each line had a box to continue on failure or fail the script. Not PS based and a completely different animal, so can’t adopt that. I wonder if Syncro could alter the execution policy outside of scripts, maybe have an option inside the script window and it runs a separate task before and after? I tested from CMD “powershell.exe set-executionpolicy unrestricted” and it was able to change it.

Also, thinking more about this, is this being ran under user context? I didn’t think executionpolicy applied to the machines system account. I’ve never run into this issue when running scripts through an RMM as system.