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

Can't use string containing \" in multi-line win_shell

Open davetapley opened this issue 3 years ago • 5 comments

SUMMARY

When using a multi-line win_shell command, such as:

 - name: Enable basic auth
      ansible.windows.win_shell: |
        Import-Module WebAdministration
        $IISPath = "IIS:\"
...

ansible-playbook fails with:

ERROR! failed at splitting arguments, either an unbalanced jinja2 block or quotes:
ISSUE TYPE
  • Bug Report
COMPONENT NAME

Module ansible.windows.win_shell.

ANSIBLE VERSION
ansible 2.10.1
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/dave/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/dave/.local/lib/python3.6/site-packages/ansible
  executable location = /home/dave/.local/bin/ansible
  python version = 3.6.9 (default, Oct  8 2020, 12:12:24) [GCC 8.4.0]
CONFIGURATION
DEFAULT_VAULT_PASSWORD_FILE(env: ANSIBLE_VAULT_PASSWORD_FILE) = /home/dave/code/ops/.vault_pass.txt
OS / ENVIRONMENT

Target is Windows Server 2019 Standard (1809). Connecting with WinRM.

 $PSVersionTable.PSVersion

Major  Minor  Build  Revision
-----  -----  -----  --------
5      1      17763  1432
STEPS TO REPRODUCE
 - name: Enable basic auth
      ansible.windows.win_shell: |
        $ErrorActionPreference = "Stop"
        Import-Module WebAdministration
        $IISPath = "IIS:\"
        $FTPUserGroupName = "FTP Users"
        $FTPSiteName = 'FTP Site'
        $FTPSitePath = "IIS:\Sites\$FTPSiteName"
        $BasicAuth = 'ftpServer.security.authentication.basicAuthentication.enabled'
        Set-ItemProperty -Path $FTPSitePath -Name $BasicAuth -Value $True
        $Param = @{
            Filter   = "/system.ftpServer/security/authorization"
            Value    = @{
                accessType  = "Allow"
                roles       = "$FTPUserGroupName"
                permissions = 1
            }
            Location = $FTPSiteName
            PSPath = $IISPath
        }
        Add-WebConfiguration @param
        echo $null >> C:\ftp\.ansible_state\basic_auth
      args:
        creates: C:\ftp\.ansible_state\basic_auth
EXPECTED RESULTS

Task completes.

ACTUAL RESULTS
$ ansible-playbook -i hosts.yml -l ftp_servers deploy.yml 
ERROR! failed at splitting arguments, either an unbalanced jinja2 block or quotes: $ErrorActionPreference = "Stop"
Import-Module WebAdministration
$IISPath = "IIS:\"
$FTPUserGroupName = "FTP Users"
$FTPSiteName = 'FTP Site'
$FTPSitePath = "IIS:\Sites\$FTPSiteName"
$BasicAuth = 'ftpServer.security.authentication.basicAuthentication.enabled'
Set-ItemProperty -Path $FTPSitePath -Name $BasicAuth -Value $True
$Param = @{
    Filter   = "/system.ftpServer/security/authorization"
    Value    = @{
        accessType  = "Allow"
        roles       = "$FTPUserGroupName"
        permissions = 1
    }
    Location = $FTPSiteName
    PSPath = $IISPath
}
Add-WebConfiguration @param
echo $null >> C:\ftp\.ansible_state\basic_auth


The error appears to be in '/home/dave/code/ops/deploy.yml': line 116, column 7, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:


    - name: Enable basic auth
      ^ here

davetapley avatar Nov 05 '20 22:11 davetapley

I tried to workaround with various combinations of escaping, but always got the same error if there was a \ followed by " anywhere. Changing " to ' did not help.

As a workaround I did this:

        $IISPath = "IIS:\ Padding for Jinja2/Ansible bug".substring(0, 5)

🤢

davetapley avatar Nov 05 '20 22:11 davetapley

There's definitely some weirdness in using multi-line strings in the parser (I don't think this is related to win_shell, it would happen with anything, even just a var definition). For example, you can't have comments (starting with #) in the multi-line string either. I wasted an hour on that the other day.

If you're not doing any jinja templating in the string (using {{ }}) (or actually, maybe even if you are it doesn't matter because https://github.com/ansible/ansible/issues/4638) you can mark it as raw for jinja purposes, which is how I worked around the weirdness.

 - name: Enable basic auth
      ansible.windows.win_shell: |
        {% raw %}
        $ErrorActionPreference = "Stop"
        Import-Module WebAdministration
        $IISPath = "IIS:\"
        $FTPUserGroupName = "FTP Users"
        $FTPSiteName = 'FTP Site'
        $FTPSitePath = "IIS:\Sites\$FTPSiteName"
        $BasicAuth = 'ftpServer.security.authentication.basicAuthentication.enabled'
        Set-ItemProperty -Path $FTPSitePath -Name $BasicAuth -Value $True
        $Param = @{
            Filter   = "/system.ftpServer/security/authorization"
            Value    = @{
                accessType  = "Allow"
                roles       = "$FTPUserGroupName"
                permissions = 1
            }
            Location = $FTPSiteName
            PSPath = $IISPath
        }
        Add-WebConfiguration @param
        echo $null >> C:\ftp\.ansible_state\basic_auth
        {% endraw %}
      args:
        creates: C:\ftp\.ansible_state\basic_auth

That may be a better workaround for your use case; I'm certainly going to keep it in mind whenever I'm pasting existing scripts into win_shell. Give it a try!

briantist avatar Nov 13 '20 15:11 briantist

Yea this is definitely not an issue with win_shell but rather how Ansible parses a raw string in a module. It is trying to handle the older style of module args like - win_shell: Test-Function chdir=C:\Windows. Unfortunately there are some false positives when it comes to interpreting quotes, especially ones after \. There's probably some more things we can do to make it better but this should be reported in ansible/ansible as that is where the parsing is happening.

I thought this was already a reported issue but I can't seem to find it right now so will keep this open until I do or have opened a new issue for it.

jborean93 avatar Nov 24 '20 03:11 jborean93

Just a heads up: Using {% raw %} / {% endraw %} as given above doesn't resolve the issue for me 😞

davetapley avatar Nov 25 '20 00:11 davetapley

The only workaround I know when you have a string that contains \" is to template the backslash like so

- set_fact:
    backslash: \

- win_shell: |
    Get-Item -Path "C:\Windows{{ backslash }}"

Not ideal but it works because Ansible parses the value first to scan for module args before templating the values.

jborean93 avatar Nov 25 '20 00:11 jborean93