Reputation: 1783
Iam trying to return a negative return code from a PowerShell script (.ps1) to Ansible. However the -ve codes that the script returns get converted into a large +ve integer for some reason. The script works fine if the return codes are +ve integers.
BTW The large +ve integer can be calculated like so:
4294967197 = 4294967296-99 (where -99 is the return code from PowerShell and 4294967296 = 4 * 1024**3 )
Appreciate any help. Thanks!
Details:
Also if i try this from the Windows Command Prompt it works !:
C:\> powershell -NoProfile %USERPROFILE%\Documents\ps_file_create.ps1
C:\> echo %errorlevel%
-99
This is what I have tried .. everything commented in the ansible/ps scripts below is what i have tried( I have also tried win_shell and get same results )
PowerShell Script (ps_file_create.ps1 ):
$return_code = -99 ## createfile $FileName
#return $return_code
#exit $return_code
$host.SetShouldExit($return_code)
exit
Ansible code:
- name: Execute the PowerShell Script that returns a -ve return code
#win_command: powershell -NonInteractive -NoProfile Invoke-Expression -Command ".\ps_file_create.ps1"
#win_command: powershell -NonInteractive -NoProfile .\ps_file_create.ps1
#win_command: powershell -NonInteractive -NoProfile -file ".\ps_file_create.ps1"
#win_command: powershell -NonInteractive -NoProfile -Command ".\ps_file_create.ps1"
win_command: powershell .\ps_file_create.ps1
args:
chdir: '%USERPROFILE%\Documents'
register: win_ps_out
ignore_errors: true
- name: Print Results of tasks
debug:
msg:
- "Powershell Out put: {{ win_ps_out | to_nice_json}}"
Output:
TASK [Print Results of tasks]*****************************************************************
ok: [myserver.xyz.abc.com] =>
msg:
- |-
Powershell Out put: {
"changed": true,
"cmd": "powershell .\\ps_file_create.ps1",
"delta": "0:00:00.532040",
"end": "2020-04-20 01:32:30.515580",
"failed": true,
"msg": "non-zero return code",
"rc": 4294967197, ## Expected -99 ##
"start": "2020-04-20 01:32:29.983539",
"stderr": "",
"stderr_lines": [],
"stdout": "",
"stdout_lines": []
}
Upvotes: 3
Views: 3028
Reputation: 13480
Long story short, here is a workaround. You can define conditional tasks or a conditional output like this:
---
- hosts: windows
tasks:
- name: Execute the PowerShell Script that returns a -ve return code
win_command: powershell .\ps_file_create.ps1
args:
chdir: '%USERPROFILE%\Documents'
register: win_ps_out
ignore_errors: true
- name: Conditional task for positive rc
debug:
msg: "PowerShell rc: {{ win_ps_out.rc }}"
when: win_ps_out.rc <= 2147483647
- name: Conditional task for negative rc
debug:
msg: "PowerShell rc: {{ win_ps_out.rc - 4294967296 }}"
when: win_ps_out.rc > 2147483647
- name: Conditional output
debug:
msg: "PowerShell rc: {% if win_ps_out.rc > 2147483647 %}{{ win_ps_out.rc - 4294967296 }}{% else %}{{ win_ps_out.rc }}{% endif %}"
Output for a return code of 42:
TASK [Conditional task for positive rc] ****************************************
ok: [192.168.0.200] => {
"msg": "PowerShell rc: 42"
}
TASK [Conditional task for negative rc] ****************************************
skipping: [192.168.0.200]
TASK [Conditional output] ******************************************************
ok: [192.168.0.200] => {
"msg": "PowerShell rc: 42"
}
And for a return code of -99:
TASK [Conditional task for positive rc] ****************************************
skipping: [192.168.0.200]
TASK [Conditional task for negative rc] ****************************************
ok: [192.168.0.200] => {
"msg": "PowerShell rc: -99"
}
TASK [Conditional output] ******************************************************
ok: [192.168.0.200] => {
"msg": "PowerShell rc: -99"
}
Background
The return code shown in the question is "correct". The binary representation of a negative integer value is commonly calculated by the two's complement method. E. g., the binary representation of -99 (using 32 bits) is:
1111 1111 1111 1111 1111 1111 1001 1101
But from reading these bits, you don't know if they represent a negative number in two's complement form or just a large positive number. You can interpret these bits either as the small negative number -99 or as the large positive number 4.294.967.197, which is what you are seeing in your output.
PowerShell returns exactly those 4 bytes shown above. You can confirm that PowerShell uses 32-bit exit codes even on a 64-bit operating system:
PS C:\> [Environment]::Is64BitProcess -and [Environment]::Is64BitOperatingSystem
True
PS C:\> $process = Start-Process powershell -ArgumentList "exit 0xffffffff" -Wait -PassThru
PS C:\> $process.ExitCode
-1
PS C:\> $process = Start-Process powershell -ArgumentList "exit 0x1ffffffff" -Wait -PassThru
PS C:\> $process.ExitCode
0
PS C:\> $process.ExitCode.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType
You can see that the exit code is of type Int32
and also that PowerShell has an exit code of 0
if the "real" exit code exceeds the range of Int32
.
So PowerShell does nothing wrong here. It is up to the user who evaluates those 4 bytes to interpret them as a signed or an unsigned integer. So it is up to ansible.
As far as I know, you don't have many possibilities to influence ansible on how to capture results from a task. You can only use filters and algorithms to process the results.
There is no such thing as type casting as far as I know ({{ win_ps_out.rc | int }}
is actually a jinja2 filter, not a cast). And ansible does not seem to care about variable sizes. E. g., ansible is not aware of int32
or int64
. Just int
, and that can be very large numbers, even larger than int64
could hold. So what ansible does, is taking the 4 bytes from PowerShell and storing them as an integer with additional - I don't know how many - leading zeros. This would be an example for 64 bits:
0000 0000 0000 0000 0000 0000 0000 0000 1111 1111 1111 1111 1111 1111 1001 1101
Now, this number cannot be interpreted as a negative number in two's complement form, because the most significant bit is set to 0
. It can only be interpreted as 4.294.967.197, what happens in your case.
It is now up to you to handle this value. On the top of my answer I present two workarounds to process the returned value and treat it as a signed integer with a size of 32 bits.
Upvotes: 1
Reputation: 72640
What happened if in your PowerShell script you just try :
exit -99
in spite of using $host.SetShouldExit($return_code)
When using cmd.exe
, I try it the result seems to be Ok :
c:\windows\system32\WindowsPowerShell\v1.0\powershell.exe -command "exit -99"
echo %errorlevel%
-99
In fact your code gives the same result :
c:\windows\system32\WindowsPowerShell\v1.0\powershell.exe -command "$host.SetShouldExit(-99)"
echo %errorlevel%
-99
You should have a look on Ansible side where it seems to be interpreted as a 64 bits, on PowerShell side it'a a 32 bits.
Upvotes: 1