ansible.windows icon indicating copy to clipboard operation
ansible.windows copied to clipboard

win_shell: captured stdout is incomplete when multi-line string is used in conjonction with cmd executable

Open Wenzel opened this issue 1 year ago • 3 comments

SUMMARY

When using win_shell with the cmd executable and a multi-line string, the captured stdout is limited to the first command executed.

ISSUE TYPE
  • Bug Report
COMPONENT NAME

win_shell

ANSIBLE VERSION
ansible [core 2.15.0]
  config file = /home/mtarral/kafl/kafl/examples/templates/windows/ansible.cfg
  configured module search path = ['/home/mtarral/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/mtarral/kafl/kafl/examples/venv/lib/python3.10/site-packages/ansible
  ansible collection location = /home/mtarral/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/mtarral/kafl/kafl/examples/venv/bin/ansible
  python version = 3.10.6 (main, May 29 2023, 11:10:38) [GCC 11.3.0] (/home/mtarral/kafl/kafl/examples/venv/bin/python3)
  jinja version = 3.1.2
  libyaml = True
COLLECTION VERSION

ansible-galaxy collection list ansible.windows

# /home/mtarral/kafl/kafl/examples/venv/lib/python3.10/site-packages/ansible_collections
Collection      Version
--------------- -------
ansible.windows 1.14.0
CONFIGURATION
CONFIG_FILE() = /home/mtarral/kafl/kafl/examples/templates/windows/ansible.cfg
DEFAULT_STDOUT_CALLBACK(/home/mtarral/kafl/kafl/examples/templates/windows/ansible.cfg) = yaml
EDITOR(env: EDITOR) = vim
OS / ENVIRONMENT
  • Ubuntu 22.04.1
STEPS TO REPRODUCE
- name: Deploy
  hosts: default
  tasks:
    - name: execute win_shell free form
      win_shell: |
        echo 'First line'
        echo 'Second line'
        echo 'Third line'
      args:
        executable: cmd
      register: output

    - name:
      debug:
        var: output.stdout_lines
EXPECTED RESULTS

The stdout_lines should have contained the 3 strings that were echoed by cmd free-form script

ACTUAL RESULTS
    qemu.windows: TASK [Gathering Facts] *********************************************************
    qemu.windows: ok: [default]
    qemu.windows:
    qemu.windows: TASK [execute win_shell free form] *********************************************
    qemu.windows: changed: [default] => changed=true
    qemu.windows:   cmd: |-
    qemu.windows:     echo 'First line'
    qemu.windows:     echo 'Second line'
    qemu.windows:     echo 'Third line'
    qemu.windows:   delta: '0:00:00.140708'
    qemu.windows:   end: '2023-06-13 23:58:45.769936'
    qemu.windows:   rc: 0
    qemu.windows:   start: '2023-06-13 23:58:45.629227'
    qemu.windows:   stderr: ''
    qemu.windows:   stderr_lines: <omitted>
    qemu.windows:   stdout: |-
    qemu.windows:     'First line'
    qemu.windows:   stdout_lines: <omitted>
    qemu.windows:
    qemu.windows: TASK [debug] *******************************************************************
    qemu.windows: ok: [default] =>
    qemu.windows:   output.stdout_lines:
    qemu.windows:   - '''First line'''
    qemu.windows:
    qemu.windows: PLAY RECAP *********************************************************************
    qemu.windows: default                    : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
git clone https://github.com/Wenzel/kafl.targets -b bug_win_shell_stdout
cd templates/windows
python3 -m venv venv
source venv/bin/activate
pip install ansible==8.0.0 pywinrm
packer build -var-file win10.pkrvars.hcl -on-error=ask windows.pkr.hcl

Wenzel avatar Jun 13 '23 18:06 Wenzel

I believe this is a limitation of cmd itself, essentially this command is running cmd.exe /c {{ raw_params }} and it is up to cmd to parse this. It seems like it doesn't work with newline literals so you would have to use && or || to chain the commands accordingly.

- win_shell: >-
    echo "one" &&
    echo "two" &&
    echo "three"
  args:
    executable: cmd

jborean93 avatar Jun 13 '23 18:06 jborean93

Thanks, I see.

while the solutoon you proposed works, it breaks a bit the expected usability of the multi-line form. If we take the example from the documentation:

- name: Run multi-lined shell commands
  ansible.windows.win_shell: |
    $value = Test-Path -Path C:\temp
    if ($value) {
        Remove-Item -Path C:\temp -Force
    }
    New-Item -Path C:\temp -ItemType Directory

Here it feels like we are writing a real powershell script, not just a succession of chained commands. It would be nice to keep the same feeling with cmd.

What about dumping the received string inside a temporary bat script that would be executed ?

It seems more robust that way, and it would be more consistent with the user's expectations in my opinion.

Wenzel avatar Jun 14 '23 11:06 Wenzel

Here it feels like we are writing a real powershell script, not just a succession of chained commands. It would be nice to keep the same feeling with cmd.

Sure it's because PowerShell supports such a thing whereas cmd.exe does not seem to.

What about dumping the received string inside a temporary bat script that would be executed ?

When a custom executable is used we run it through the command line {{ executable }} /c {{ raw_params }}. While yes we could have it written to a temporary file we do not and I don't think we should as now the data being provided will have touched the disk. Having a file be created now means the script mentioned can persist even beyond it being deleted by us and if it contains sensitive values that means we've exposed those values. It also means that we would need to prepend things like @echo off to stop it from echoing the commands. It's not a dealbreaker but having to modify something that is run never sits well with me.

It's also already possible using the script module which does the work for you to temporarily copy across the file and execute it.

- script:
    cmd: test.bat
    executable: cmd.exe /c

The test.bat was stored on the controller side with

@echo off

echo "one"
echo "two"

The output after running

changed: [SERVER2022] => changed=true
  rc: 0
  stderr: ''
  stderr_lines: <omitted>
  stdout: |-
    "one"
    "two"
  stdout_lines: <omitted>

Script already has the expectation that a file is copied across whereas win_shell is known to run in memory.

jborean93 avatar Jun 14 '23 19:06 jborean93