ansible-collections/ansible.windows

win_shell: command line execution is getting confused by ansible.windows.quote when used twice or more

Wenzel opened this issue · 3 comments

Wenzel commented
SUMMARY

win_shell module is confused when the ansible.windows.quote function is used twice in the command line and fails to execute the given command.

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
  vars:
    iexplore_path: "{{ ansible_env['ProgramFiles(x86)'] }}\\Internet Explorer\\iexplore.exe"
  tasks:
    - name: test one ansible.windows.quote
      win_shell: "{{ iexplore_path | ansible.windows.quote }} /?"
      args:
        executable: cmd
      register: result
      failed_when:
        - result.rc != 0
        - result.rc != 1
      
    - name: test two ansible.windows.quote
      win_shell: "{{ iexplore_path | ansible.windows.quote }} {{ iexplore_path | ansible.windows.quote }}"
      args:
        executable: cmd
EXPECTED RESULTS

On the second task, iexplore.exe executable should have been called.

ACTUAL RESULTS
    qemu.windows: TASK [Gathering Facts] *********************************************************
    qemu.windows: ok: [default]
    qemu.windows:
    qemu.windows: TASK [test one ansible.windows.quote] ******************************************
    qemu.windows: changed: [default] => changed=true
    qemu.windows:   cmd: '"C:\Program Files (x86)\Internet Explorer\iexplore.exe" /?'
    qemu.windows:   delta: '0:00:00.187520'
    qemu.windows:   end: '2023-06-13 23:00:26.996222'
    qemu.windows:   failed_when_result: false
    qemu.windows:   msg: non-zero return code
    qemu.windows:   rc: 1
    qemu.windows:   start: '2023-06-13 23:00:26.808702'
    qemu.windows:   stderr: ''
    qemu.windows:   stderr_lines: <omitted>
    qemu.windows:   stdout: ''
    qemu.windows:   stdout_lines: <omitted>
    qemu.windows:
    qemu.windows: TASK [test two ansible.windows.quote] ******************************************
    qemu.windows: fatal: [default]: FAILED! => changed=true
    qemu.windows:   cmd: '"C:\Program Files (x86)\Internet Explorer\iexplore.exe" "C:\Program Files (x86)\Internet Explorer\iexplore.exe"'
    qemu.windows:   delta: '0:00:00.093752'
    qemu.windows:   end: '2023-06-13 23:00:29.996204'
    qemu.windows:   msg: non-zero return code
    qemu.windows:   rc: 1
    qemu.windows:   start: '2023-06-13 23:00:29.902452'
    qemu.windows:   stderr: |-
    qemu.windows:     'C:\Program' is not recognized as an internal or external command,
    qemu.windows:     operable program or batch file.
    qemu.windows:   stderr_lines: <omitted>
    qemu.windows:   stdout: ''
    qemu.windows:   stdout_lines: <omitted>
    qemu.windows:
    qemu.windows: PLAY RECAP *********************************************************************
    qemu.windows: default                    : ok=2    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

Reproduce Bug

You can find the following repo to repro the bug:

git clone https://github.com/Wenzel/kafl.targets -b bug_ansible_quote
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

Why do you expect this to work? It's invalid to have 2 quoted objects in an expression without anything else in pwsh

image

I also cannot replicate your error message, I get the one shown in PowerShell which is expected

image

Wenzel commented

Hi @jborean93 and thanks for your reply !

It's invalid to have 2 quoted objects in an expression without anything else in pwsh

So remember that i'm using executable: cmd here, so it will not be interpreted as Powershell syntax.

my use case was the following.
I wanted to run the vcvars64.bat Visual Studio script, compile a driver, and link it with ntoskrnl.lib from the WDK.

Both the vcvars64and ntoskrnl.libare located under Program Files (x86), so they require quoting since the path contains spaces.

This command line executes well in my cmd:

image

But when I try to replicate the same within Ansible:

    vcvars_path: "{{ ansible_env['ProgramFiles(x86)'] }}\\Microsoft Visual Studio\\2017\\BuildTools\\VC\\Auxiliary\\Build\\vcvars64.bat"
    ntoskrnllib_path: "C:\\Program Files (x86)\\Windows Kits\\10\\Lib\\10.0.17763.0\\km\\x64\\ntoskrnl.lib"
  tasks:
    - name: set content
      win_shell: echo "int main() {}" > main.c 
      args:
        executable: cmd

    - name: test
      win_shell: "{{ vcvars_path | ansible.windows.quote }} && cl main.c && link main.obj {{ ntoskrnllib_path | ansible.windows.quote }}"
      args:
        executable: cmd

The command fails to vcvars64.batbecause of a quoting issue.

TASK [set content] *************************************************************

[stdout] changed: [192.168.122.55]

TASK [test] ********************************************************************

[stdout] fatal: [192.168.122.55]: FAILED! => changed=true 
  cmd: '"C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat" && cl main.c && link main.obj "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.17763.0\km\x64\ntoskrnl.lib"'
  delta: '0:00:00.078106'
  end: '2023-06-12 19:44:32.000440'
  msg: non-zero return code
  rc: 1
  start: '2023-06-12 19:44:31.922334'
  stderr: |-
    'C:\Program' is not recognized as an internal or external command,
    operable program or batch file.
  stderr_lines: <omitted>
  stdout: ''
  stdout_lines: <omitted>

I also cannot replicate your error message, I get the one shown in PowerShell which is expected

I provided a repository to reproduce the error.
Did you try to build the VM and execute the playbook ?

Thanks !

So remember that i'm using executable: cmd here, so it will not be interpreted as Powershell syntax.

My apologies I must have missed that when looking at your example

There are some really complex quotation rules for cmd listed under https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/cmd#remarks

I can't claim to understand the escaping rules here but to get this working you need to enclose your entire command with quotes like so:

- win_shell: "\"{{ my_path | ansible.windows.quote }} {{ my_path | ansible.windows.quote }}\""
  args:
    executable: cmd
  vars:
    my_path: C:\folder with space\print_argv.exe

Some alternatives are that lead to the same raw value are:

- win_shell: '"{{ my_path | ansible.windows.quote }} {{ my_path | ansible.windows.quote }}"'
  args:
    executable: cmd
  vars:
    my_path: C:\folder with space\print_argv.exe

- win_shell: >-
    "{{ my_path | ansible.windows.quote }} {{ my_path | ansible.windows.quote }}"
  args:
    executable: cmd
  vars:
    my_path: C:\folder with space\print_argv.exe

I believe you can also enclose it within () like so

- win_shell: ({{ my_path | ansible.windows.quote }} {{ my_path | ansible.windows.quote }})
  args:
    executable: cmd
  vars:
    my_path: C:\folder with space\print_argv.exe

Unfortunately this is the same problem as #514 where we are at the mercy of the parsing rules of cmd.exe /c {{ raw_params }}. We can't really change it as people have been relying on the current behaviour (whether implicitly or explicitly). If you have any other suggestions that will preserve backwards compatibility then I'm happy to hear it but unfortunately I don't think there are going to be any.