Script ending with exit code 1

Action in my task always ends with exit code 1. When I run the failed command locally on the server from a command line, it succeeds without problems. This is the output of the action:

Completed if {architecture of operating system contains “64”}
Completed prefetch ******1 sha1:******1 size:21270 http://besroot.suffix.com:52311/Uploads/******1/MOMCertImport.exe.tmp sha256:******256
Completed extract ******1
Completed endif
Completed if {architecture of operating system does not contain “64”}
Completed prefetch ******1 sha1:******1 size:43974 http://besroot.suffix.com:52311/Uploads/******1/MOMCertImport.exe.tmp sha256:******256
Completed extract ******1
Completed endif
Completed //certificate file
Completed if {not exists folder “C:\SCOM”}
Completed folder create "C:\SCOM"
Completed endif
Completed if {not exists folder “C:\SCOM\cert”}
Completed folder create "C:\SCOM\cert"
Completed endif
Completed //delete "c:\SCOM\cert\cert_{dns name}.crt"
Completed //move "Download\cert{dns name}.crt" "c:\SCOM\cert\cert{dns name}.crt"
Completed delete “C:\SCOM\cert\import-result.txt"
Completed delete C:\SCOM\cert\CertImport.ps1
Completed delete _createfile
Completed createfile until eof
Completed $ToRemove = Get-ChildItem -Path Cert:\LocalMachine\My | ? {{($.Subject -match [System.Net.Dns]::GetHostByName($env:COMPUTERNAME).HostName) -and ($.EnhancedKeyUsageList -match “Client Authentication”) -and ($.EnhancedKeyUsageList -match “Server Authentication”)}
Completed $CertsInStore = Get-ChildItem -Path Cert:\LocalMachine\My
Completed $Store = Get-Item -Path Cert:\LocalMachine\My
Completed foreach ($ToRemove in $CertsInStore)
Completed {
Completed $Store.Open(“ReadWrite”)
Completed $Store.Remove($ToRemove)
Completed $Store.Close()
Completed }
Completed $Certificates = Get-ChildItem -Path Cert:\LocalMachine\My | ? {{$
.Subject -match [System.Net.Dns]::GetHostByName($env:COMPUTERNAME).HostName}
Completed if (-not [string]::IsNullOrEmpty($Certificates))
Completed {
Completed $Result = “Multiple certificates present” | Out-File "C:\SCOM\cert\import-result.txt"
Completed exit
Completed } else {{
Completed Import-Certificate -FilePath "C:\SCOM\cert\cert
{dns name}.crt” -CertStoreLocation Cert:\LocalMachine\My
Completed }
Completed $Certificates = Get-ChildItem -Path Cert:\LocalMachine\My | ? {{$_.Subject -match [System.Net.Dns]::GetHostByName($env:COMPUTERNAME).HostName}
Completed if ([string]::IsNullOrEmpty($Certificates))
Completed {
Completed $Result = “New certificate failed to import.” | Out-File "C:\SCOM\cert\import-result.txt"
Completed } else {{
Completed $Result = “New certificate imported in system store.” | Out-File "C:\SCOM\cert\import-result.txt"
Completed }
Completed eof
Completed move __createfile C:\SCOM\cert\CertImport.ps1
Completed waithidden powershell.exe -ExecutionPolicy Bypass -NonInteractive -Command "& "“C:\SCOM\cert\CertImport.ps1"”"
Completed waithidden cmd /c c:\SCOM\cert\MOMcertipmort.exe /SubjectName {dns name}

Nevertheless, the last row is returned with exit code 1 and the command is not performed.

A little background: I need to replace a certificate for SCOM monitoring agent, that is about to expire, with a new one. For this testing run I first uploaded the certificate file (cert_{dns name}.crt) to a server with a software deployment task and then I ran this task separately. That’s why two of the rows are put to comment. The last command shall “pair” the new certificate with SCOM. unfortunately the tool can’t identify the certificate by anything else than the subject name, that’s why the rather complicated structure of the powershell script, testing if there are multiple certificates with the same name. If there are, the task can’t be done remotely and has to be done manually, because you need the gui to select, which certificate to pair with SCOM. Everything works fine, except the last command does not work and returns exit code 1. What does that mean? This is the relevant part from BF log:

Command started - waithidden cmd /c c:\SCOM\cert\MOMcertipmort.exe /SubjectName server.suffix.com (action:231088)
Command succeeded (Exit Code=1) waithidden cmd /c c:\SCOM\cert\MOMcertipmort.exe /SubjectName server.suffix.com (action:231088)

Could be because the powershell script didn’t work right - something doesn’t look right about the quotations in this line:

Completed waithidden powershell.exe -ExecutionPolicy Bypass -NonInteractive -Command “& ““C:\SCOM\cert\CertImport.ps1”””

I’m pretty sure this is correct. The powershell script was executed normally and all actions to be performed by it were executed properly. And yes, the quotation really looks strange, but it’s the correct way to run a powershell command from a command line.

Did you disable 32-bit operation? Try adding

action uses wow64 redirection false

Before creating any processes with ‘wait’ or ‘run’.

Also go ahead and post the source actionscript, it’s easier to read than the log output.

Here is the original script code:

if {architecture of operating system contains “64”}
prefetch ******1 sha1:******1 size:21270 http://besroot.abb.com:52311/Uploads/******1/MOMCertImport.exe.tmp sha256:******256
extract ******1
endif
if {architecture of operating system does not contain “64”}
prefetch ******1 sha1:******1 size:43974 http://besroot.abb.com:52311/Uploads/******1/MOMCertImport.exe.tmp sha256:******256
extract ******1
endif

//certificate file

if {not exists folder “C:\SCOM”}
folder create "C:\SCOM"
endif
if {not exists folder “C:\SCOM\cert”}
folder create "C:\SCOM\cert"
endif

delete "c:\SCOM\cert\cert_{dns name}.crt"
move “Download\cert{dns name}.crt" "c:\SCOM\cert\cert{dns name}.crt”

delete “C:\SCOM\cert\import-result.txt"
delete C:\SCOM\cert\CertImport.ps1
delete _createfile
createfile until eof
$ToRemove = Get-ChildItem -Path Cert:\LocalMachine\My | ? {{($.Subject -match [System.Net.Dns]::GetHostByName($env:COMPUTERNAME).HostName) -and ($.EnhancedKeyUsageList -match “Client Authentication”) -and ($.EnhancedKeyUsageList -match “Server Authentication”)}
$CertsInStore = Get-ChildItem -Path Cert:\LocalMachine\My
$Store = Get-Item -Path Cert:\LocalMachine\My
foreach ($ToRemove in $CertsInStore)
{
$Store.Open(“ReadWrite”)
$Store.Remove($ToRemove)
$Store.Close()
}
$Certificates = Get-ChildItem -Path Cert:\LocalMachine\My | ? {{$
.Subject -match [System.Net.Dns]::GetHostByName($env:COMPUTERNAME).HostName}
if (-not [string]::IsNullOrEmpty($Certificates))
{
$Result = “Multiple certificates present” | Out-File "C:\SCOM\cert\import-result.txt"
exit
} else {{
Import-Certificate -FilePath "C:\SCOM\cert\cert
{dns name}.crt” -CertStoreLocation Cert:\LocalMachine\My
}
$Certificates = Get-ChildItem -Path Cert:\LocalMachine\My | ? {{$_.Subject -match [System.Net.Dns]::GetHostByName($env:COMPUTERNAME).HostName}
if ([string]::IsNullOrEmpty($Certificates))
{
$Result = “New certificate failed to import.” | Out-File “C:\SCOM\cert\import-result.txt”
} else {{
$Result = “New certificate imported in system store.” | Out-File “C:\SCOM\cert\import-result.txt”
}
eof

move __createfile C:\SCOM\cert\CertImport.ps1

waithidden powershell.exe -ExecutionPolicy Bypass -NonInteractive -Command "& "“C:\SCOM\cert\CertImport.ps1"”"
waithidden cmd /c c:\SCOM\cert\MOMcertipmort.exe /SubjectName {dns name}

I later tried to put the last row into an “if” condition like: if {line 1 of file “import-result.txt” of folder “C:\SCOM\cert” contains “imported”} (I didn’t forget the endif command of course), but the script is not even executed and reports a syntax error after the action is evaluated. I checked for any typos very carefully, but can’t find one. Is it possible, that the script is searching for the file “import-result.txt” before the script starts? In that case, how can I limit the execution of the last command only to the case the file contains the required information?
I’ll try the redirection, but it will take some time before I have an opportunity to test it, I can’t do it without a real business justification.

Furthermore, what confuses me even more, the same fixlet contains another action, which ends with exactly the same command as this one (it is meant to import a certificate to newly activated server, which doesn’t have any old certificates present). In this second action, the command works fine.

Is it possible, that the script is searching for the file “import-result.txt” before the script starts?

Yes, that’s exactly what’s happening in fact. When an actionscript contains a ‘prefetch’ statement, the action is evaluated before it runs, and every relevance substitution is checked. If the evaluation results in an <error> result (such as the file not existing yet) then you’ll encountet this problem.

There are a couple of ways around the error. The easiest in this case is probably to change the downloads to use a prefetch block; in that case only the portion of the script inside the prefetch block is evaluated before the script executes. Other workarounds include things like changing the statement to handle error checking. Some examples might be
if {if (active of action) then ([evaluate the file]) else false}
Or
if {if exists true whose ([evaluate the file]) else false}
Or catch errors with the pipe ‘|’ operator
if {[evaluate the file] | false}

So, in short, try to fix the execution by using action uses wow64 redirection false, and try to fix the pre-action evaluation using a prefetch block or changing your relevance statements to catch errors before the output file is generated.

So you think like this it should work?

begin prefetch block
if {x64 of operating system}
add prefetch item ******1 sha1:******1 size:21270 http://besroot.suffix.com:52311/Uploads/******1/MOMCertImport.exe.tmp sha256:******256
extract ******1
else
add prefetch item ******1sha1:******1 size:43974 http://besroot.suffix.com:52311/Uploads/******1/MOMCertImport.exe.tmp sha256:******256
extract ******1
endif

//certificate file

end prefetch block

action uses wow64 redirection false
if {not exists folder “C:\SCOM\cert”}
folder create "C:\SCOM\cert"
endif

delete "c:\SCOM\cert\cert_{dns name}.crt"
move “Download\cert{dns name}.crt" "c:\SCOM\cert\cert{dns name}.crt”

delete “C:\SCOM\cert\import-result.txt"
delete C:\SCOM\cert\CertImport.ps1
delete _createfile
createfile until eof
$ToRemove = Get-ChildItem -Path Cert:\LocalMachine\My | ? {{($.Subject -match [System.Net.Dns]::GetHostByName($env:COMPUTERNAME).HostName) -and ($.EnhancedKeyUsageList -match “Client Authentication”) -and ($.EnhancedKeyUsageList -match “Server Authentication”)}
$CertsInStore = Get-ChildItem -Path Cert:\LocalMachine\My
$Store = Get-Item -Path Cert:\LocalMachine\My
foreach ($ToRemove in $CertsInStore)
{
$Store.Open(“ReadWrite”)
$Store.Remove($ToRemove)
$Store.Close()
}
$Certificates = Get-ChildItem -Path Cert:\LocalMachine\My | ? {{$
.Subject -match [System.Net.Dns]::GetHostByName($env:COMPUTERNAME).HostName}
if (-not [string]::IsNullOrEmpty($Certificates))
{
$Result = “Multiple certificates present” | Out-File "C:\SCOM\cert\import-result.txt"
exit
} else {{
Import-Certificate -FilePath "C:\SCOM\cert\cert
{dns name}.crt” -CertStoreLocation Cert:\LocalMachine\My
}
$Certificates = Get-ChildItem -Path Cert:\LocalMachine\My | ? {{$_.Subject -match [System.Net.Dns]::GetHostByName($env:COMPUTERNAME).HostName}
if ([string]::IsNullOrEmpty($Certificates))
{
$Result = “New certificate failed to import.” | Out-File “C:\SCOM\cert\import-result.txt”
} else {{
$Result = “New certificate imported in system store.” | Out-File “C:\SCOM\cert\import-result.txt”
}
eof

move __createfile C:\SCOM\cert\CertImport.ps1

waithidden powershell.exe -ExecutionPolicy Bypass -NonInteractive -Command "& "“C:\SCOM\cert\CertImport.ps1"”"
continue if {line 1 of file “import-result.txt” of folder “C:\SCOM\cert” contains “imported”}
waithidden cmd /c c:\SCOM\cert\MOMcertipmort.exe /SubjectName {dns name}

After the //certificate file line would always come the lines generated by the windows software distribution wizard, which I always use to create the prefetch block. In this case I will have to replace “prefetch” with “add prefetch item” then.

Hm, after clicking on OK, the fixlet reports “argument not recognised” on line 3 (add prefetch item). I never used prefetch block so far, but as I checked the syntax on the web it seems correct. So where’s the problem?

https://developer.bigfix.com/action-script/reference/download/add-prefetch-item.html

“Add prefetch item” uses equal signs rather than colons - so " = " not " : ", as in size=12345

Hm, that constitutes a huge problem, because the fixlet shall be used by not only me, but also other colleagues, who are not that proficient with BigFix, and modifying the command could be quite difficult for them to do it correctly. And as I said, it’s necessary to first generate the prefetch command together with extract by using the wizard and then add them to the action script before running the action. If it was possible by using standard prefetch, then it’s quite simple - copy/paste the first two lines from the wizard as-is to the action script. Furthermore, I just found out, that the extract command must be placed outside the prefetch block. So it will not only be necessary to rewrite the prefetch command (add the name and url arguments, change colons for equals, change “prefetch” to “add prefetch item”) but also copy/paste prefetch and extract lines separately. All that leaves huge space for possible typo or other type of error. I guess I’ll have to go with another option than prefetch block. I’ll let you know how does it all work, as soon as I have some business justification to use the fixlet.

I see, in that case you may need to use one of the other workarounds to guard your relevance when you try to read results out of the logfile instead.

disabling 32-bit operation didn’t help. I still get exit code 1 and the command is not executed.

It doesn’t look like you’ve escaped all of the opening curly brackets in the powershell script. every opening curly bracket “{” should be doubled up “{{” otherwise bigfix will think you are trying to do some relevance substitution.

As I said, the powershell script is fine. I discovered, that when there is nothing else than { in the row, doubling is not necessary. So I didn’t do that - anything not necessary is harmful, basically.
I fear it’s some error in the file that is executed on the last row, which then wouldn’t have any solution except for Microsoft to fix it.

So in the end I used completely different approach. I searched for what exactly does the momcertimport file do and discovered, that it just writes the thumbprint of the certificate to registry value. So instead of running the file, which always failed, I changed the registry value to the thumbprint of the certificate I just imported. So problem was basically solved, even though in a different way.