diff --git a/changelogs/fragments/psrp-output.yaml b/changelogs/fragments/psrp-output.yaml new file mode 100644 index 00000000000..ab662e4c007 --- /dev/null +++ b/changelogs/fragments/psrp-output.yaml @@ -0,0 +1,3 @@ +bugfixes: +- psrp - Fix blank newlines appearing before ``stdout`` when using ``script`` or ``raw`` with the ``psrp`` connection plugin +- psrp - Fix issues with propagating errors back to Ansible with ``raw`` tasks diff --git a/lib/ansible/executor/powershell/exec_wrapper.ps1 b/lib/ansible/executor/powershell/exec_wrapper.ps1 index c473c79ff45..59ce2e88fc3 100644 --- a/lib/ansible/executor/powershell/exec_wrapper.ps1 +++ b/lib/ansible/executor/powershell/exec_wrapper.ps1 @@ -219,7 +219,7 @@ $($ErrorRecord.InvocationInfo.PositionMessage) $b64_output = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($output)) Write-Output -InputObject $b64_output } else { - Write-Output -InputObject $output + $output } } catch { Write-AnsibleError -Message "internal error: failed to run exec_wrapper action $action" -ErrorRecord $_ diff --git a/lib/ansible/plugins/connection/psrp.py b/lib/ansible/plugins/connection/psrp.py index ae3185f9e4c..5704153a917 100644 --- a/lib/ansible/plugins/connection/psrp.py +++ b/lib/ansible/plugins/connection/psrp.py @@ -402,9 +402,10 @@ class Connection(ConnectionBase): else: display.vvv("PSRP: EXEC %s" % script, host=self._psrp_host) else: - # in other cases we want to execute the cmd as the script - script = cmd - display.vvv("PSRP: EXEC %s" % script, host=self._psrp_host) + # In other cases we want to execute the cmd as the script. We add on the 'exit $LASTEXITCODE' to ensure the + # rc is propagated back to the connection plugin. + script = to_text(u"%s\nexit $LASTEXITCODE" % cmd) + display.vvv(u"PSRP: EXEC %s" % script, host=self._psrp_host) rc, stdout, stderr = self._exec_psrp_script(script, in_data) return rc, stdout, stderr @@ -734,29 +735,29 @@ if ($bytes_read -gt 0) { stdout_list.append(output_msg) - stdout = u"\r\n".join(stdout_list) if len(self.host.ui.stdout) > 0: - stdout += u"\r\n" + u"".join(self.host.ui.stdout) + stdout_list += self.host.ui.stdout + stdout = u"\r\n".join(stdout_list) stderr_list = [] for error in pipeline.streams.error: # the error record is not as fully fleshed out like we usually get # in PS, we will manually create it here - error_msg = "%s : %s\r\n" \ - "%s\r\n" \ + command_name = "%s : " % error.command_name if error.command_name else '' + position = "%s\r\n" % error.invocation_position_message if error.invocation_position_message else '' + error_msg = "%s%s\r\n%s" \ " + CategoryInfo : %s\r\n" \ " + FullyQualifiedErrorId : %s" \ - % (error.command_name, str(error), - error.invocation_position_message, error.message, - error.fq_error) + % (command_name, str(error), position, + error.message, error.fq_error) stacktrace = error.script_stacktrace if self._play_context.verbosity >= 3 and stacktrace is not None: error_msg += "\r\nStackTrace:\r\n%s" % stacktrace stderr_list.append(error_msg) - stderr = "\r\n".join(stderr_list) if len(self.host.ui.stderr) > 0: - stderr += "\r\n" + "".join(self.host.ui.stderr) + stderr_list += self.host.ui.stderr + stderr = u"\r\n".join([to_text(o) for o in stderr_list]) display.vvvvv("PSRP RC: %d" % rc, host=self._psrp_host) display.vvvvv("PSRP STDOUT: %s" % stdout, host=self._psrp_host) diff --git a/test/integration/targets/win_raw/tasks/main.yml b/test/integration/targets/win_raw/tasks/main.yml index 34d04c54963..31f90b85a6c 100644 --- a/test/integration/targets/win_raw/tasks/main.yml +++ b/test/integration/targets/win_raw/tasks/main.yml @@ -93,15 +93,30 @@ that: - "raw_result.stdout_lines[0] == 'wwe=raw'" -# TODO: this test doesn't work anymore since we had to internally map Write-Host to Write-Output -- name: run a raw command with unicode chars and quoted args (from https://github.com/ansible/ansible-modules-core/issues/1929) - raw: Write-Host --% icacls D:\somedir\ /grant "! ЗАО. Руководство":F - register: raw_result2 - -- name: make sure raw passes command as-is and doesn't split/rejoin args - assert: - that: - - "raw_result2.stdout_lines[0] == '--% icacls D:\\\\somedir\\\\ /grant \"! ЗАО. Руководство\":F'" +- name: unicode tests for winrm + when: ansible_connection != 'psrp' # Write-Host does not work over PSRP + block: + - name: run a raw command with unicode chars and quoted args (from https://github.com/ansible/ansible-modules-core/issues/1929) + raw: Write-Host --% icacls D:\somedir\ /grant "! ЗАО. Руководство":F + register: raw_result2 + + - name: make sure raw passes command as-is and doesn't split/rejoin args + assert: + that: + - "raw_result2.stdout_lines[0] == '--% icacls D:\\\\somedir\\\\ /grant \"! ЗАО. Руководство\":F'" + +- name: unicode tests for psrp + when: ansible_connection == 'psrp' + block: + # Cannot test unicode passed into separate exec as PSRP doesn't run with a preset CP of 65001 which reuslts in ? for unicode chars + - name: run a raw command with unicode chars + raw: Write-Output "! ЗАО. Руководство" + register: raw_result2 + + - name: make sure raw passes command as-is and doesn't split/rejoin args + assert: + that: + - "raw_result2.stdout_lines[0] == '! ЗАО. Руководство'" # Assumes MaxShellsPerUser == 30 (the default) diff --git a/test/integration/targets/win_script/tasks/main.yml b/test/integration/targets/win_script/tasks/main.yml index 0134f12d508..4d57eda2ba3 100644 --- a/test/integration/targets/win_script/tasks/main.yml +++ b/test/integration/targets/win_script/tasks/main.yml @@ -71,11 +71,22 @@ assert: that: - test_script_with_large_args_result.rc == 0 - - test_script_with_large_args_result.stdout == long_string + "\r\n" - not test_script_with_large_args_result.stderr - test_script_with_large_args_result is not failed - test_script_with_large_args_result is changed +- name: check that script ran and received arguments correctly with winrm output + assert: + that: + - test_script_with_large_args_result.stdout == long_string + "\r\n" + when: ansible_connection != 'psrp' + +- name: check that script ran and received arguments correctly with psrp output + assert: + that: + - test_script_with_large_args_result.stdout == long_string + when: ansible_connection == 'psrp' + - name: run test script that takes parameters passed via splatting script: test_script_with_splatting.ps1 @{ This = 'this'; That = '{{ test_win_script_value }}'; Other = 'other'} register: test_script_with_splatting_result