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

@mgiordano RE: context - I run basically every script under system context. I do have one script I run under user context and it fails if no one is logged in, but I’m fine with it. Its only purpose is to display something to logged-in users, so if no one is logged in I don’t care. Also, execution policy can apply to the system account. There are separate policies for User and Machine. For example:

get-executionpolicy -list

        Scope ExecutionPolicy
        ----- ---------------
MachinePolicy       Undefined
      UserPolicy       Undefined
          Process       Undefined
    CurrentUser       Undefined
     LocalMachine    Unrestricted

I think I’m seeing the issue now that I’m looking at how Syncro runs the scripts.

  1. Syncro downloads the script to the machine in a file that looks like this: 97206d82-9b3e-4fee-a0e2-1ff08239a8e2.ps1
  2. Syncro runs the script with a command something like this: C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe" -Sta -ExecutionPolicy Unrestricted -Command "& {C:\ProgramData\Syncro\bin\a575560c-0b80-42cb-b0bd-0349d86e110e.ps1; exit $LASTEXITCODE}
  3. Syncro uses the $LASTEXITCODE variable passed on by the script to presumably pass to the Sycnro console, where it displays Success or Failure.

I guess the problem here is that - when execution policy prevents the script from loading in the first place - there is no exit code to pass on because the script didn’t technically exit, it never ran in the first place. I see that when a script runs without explicitly being told that it should pass an exit code, $lastexitcode is empty. So you can’t assume that empty means an error, because it could just mean the script didn’t pass an exit code.

@Andy I may be way out of my lane here, but how difficult would it be to modify the way Syncro runs these script files to add a simple statement at the end? I see you’re already using “exit $LASTEXITCODE” to presumably pass on the exit code to the console. There’s also the $? variable that could be used, which shows if the last command to run was successful or not. It should remain True unless either the command to run the script fails (like if execution policy doesn’t allow it, I tested), or the script itself runs into an unhandled terminating error. What if you added something to the script run statement so it looked like this?

C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe" -Sta -ExecutionPolicy Unrestricted -Command "& {C:\ProgramData\Syncro\bin\a575560c-0b80-42cb-b0bd-0349d86e110e.ps1; if(($? -eq $False) and !($LASTEXITCODE)){$LASTEXITCODE = 1}; exit $LASTEXITCODE}

This would read the system $? variable and $LASTEXITCODE. If $? is False, the last command to run failed. That could mean the command to run the script failed, or that the script ran into an unhandled terminating error. You wouldn’t necessarily want to override the last exit code, so it also checks if that variable has a value. If there’s no value for $LASTEXITCODE and $? is False, it should be safe to assume that something went wrong in an unexpected way, and so it should be safe to set $LASTEXITCODE to 1 and then exit and pass that on to the Syncro console.

I could be completely misunderstanding how Syncro is set up in this regard, but from what I understand it seems like adding this little bit to the statement Syncro uses to run scripts might be an easy way to get the console to correctly display certain failures. Just trying to be helpful if I can.

I know there are different scopes that it can be applied to, such as the ones when you do -list, but I’ve just never come across an issue running a script as system through an RMM before, like, ever lol. And I can’t really find anything concrete stating that the system account is effected by the executionpolicy scopes. Anyway, that’s not really the root of your issue anyway, my brain just started going off down a path because I’ve never seen this happen as I mentioned in this particular scenario.

:man_shrugging:

Most all of the machines we’re managing are fine. I discovered this issue on a few at different clients because I ran a search for assets that should have our AV installed that didn’t have the custom field set. Most were because they’d just failed to run the installer correctly, there was some sort of conflict, or whatever, but I discovered a few that had errors on every single script due to execution policy. It wasn’t even consistent across orgs, oddly.

I resolved issues with one client by setting a GPO for execution policy to RemoteSigned, which should allow local scripts unsigned, and any scripts downloaded from the internet if they’re signed. The Syncro scripts aren’t signed, but PowerShell doesn’t seem to think the scripts were downloaded from the internet so it works.

That runtime error reporting is entirely on us is a large frustration that almost kept us off the platform. The other is the non-standard script parameter handling (via ad-hoc code injection that actually breaks standard PS params).

But we loved other stuff so much that we ended up writing a Syncro-specific script pre-processor to workaround it. Unfortunately, it adds ~9K overhead to all our standard RMM scripts, but makes them Syncro-effective without forcing our source code to be all proprietary.