How to end up with a single backslash

I need to select registry keys that match a regex, and then reconstruct the key paths. I’m trying to do:

    - name: Get registry entries
      ansible.windows.win_reg_stat:
        path: 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'
      register: uninstall

    - set_fact:
        keys: |
          {{ ['HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'] | product(uninstall.sub_keys | select('search', '^(nbi|BeyondTrust Remote Support Jump Client)')) | map('join', '\\') }}

This ends up with:

        "keys": [
            "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\\\BeyondTrust Remote Support Jump Client [nwra.beyondtrustcloud.com-62F2CDFD]",
            "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\\\nbi-glassfish-mod-4.1.1.0.1",
            "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\\\nbi-nb-all-21.0.0.240215.0",
            "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\\\nbi-nb-all-22.0.0.240522.0",
            "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\\\nbi-nb-all-23.0.0.240913.0",
            "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\\\nbi-nb-base-8.2.0.0.201609300101"
        ]

If I change the map to '\' I get:

The full traceback is:
Traceback (most recent call last):
  File "/usr/lib/python3.9/site-packages/ansible/executor/task_executor.py", line 505, in _execute
    self._task.post_validate(templar=templar)
  File "/usr/lib/python3.9/site-packages/ansible/playbook/task.py", line 291, in post_validate
    super(Task, self).post_validate(templar)
  File "/usr/lib/python3.9/site-packages/ansible/playbook/base.py", line 541, in post_validate
    value = method(attribute, getattr(self, name), templar)
  File "/usr/lib/python3.9/site-packages/ansible/playbook/task.py", line 299, in _post_validate_args
    args = templar.template(value)
  File "/usr/lib/python3.9/site-packages/ansible/template/__init__.py", line 764, in template
    d[k] = self.template(
  File "/usr/lib/python3.9/site-packages/ansible/template/__init__.py", line 738, in template
    result = self.do_template(
  File "/usr/lib/python3.9/site-packages/ansible/template/__init__.py", line 963, in do_template
    data = _escape_backslashes(data, myenv)
  File "/usr/lib/python3.9/site-packages/ansible/template/__init__.py", line 138, in _escape_backslashes
    for token in jinja_env.lex(d2):
  File "/usr/lib/python3.9/site-packages/ansible/_vendor/jinja2/lexer.py", line 864, in tokeniter
    raise TemplateSyntaxError(
jinja2.exceptions.TemplateSyntaxError: unexpected char "'" at 177
  line 1
fatal: [mark03.ad.nwra.com]: FAILED! => {
    "changed": false
}

Similar if I add a \ to the end of the initial string path.

This is all a bit complicated to explain. But I’ll give it a try:

  1. In the normal callback output, you will not see a single \ represented, because JSON requires that it be escaped, so internally a single \ will always show up in JSON output as \\. So from that perspective you are ok.
  2. When using single quotes in YAML around value, it is treated as the equivalent of a raw string in python, and in YAML it means you don’t have to escape a single \ when writing it. More info at YAML Ain’t Markup Language (YAML™) revision 1.2.2
  3. Folded or block scalars, as well as unquoted lines act like single quoted lines
  4. If using double quotes you would need to type \\ to represent a single \, because it requires escaping

So, with the above in mind, you don’t want to use map('join', '\\') you would only use '\' in your specific example.

    - set_fact:
        keys: |
          {{ ['HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'] | product(uninstall.sub_keys | select('search', '^(nbi|BeyondTrust Remote Support Jump Client)')) | map('join', '\\') }}

This is because block scalars or those specified with | in YAML are treated like the docs call out for single quotes string values. So using \\ in that format, is a literal double slash, and not an escaped slash

If you were to use double quotes, then you would need \\:

    - set_fact:
        keys: "{{ ['HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'] | product(uninstall.sub_keys | select('search', '^(nbi|BeyondTrust Remote Support Jump Client)')) | map('join', '\\') }}"

And as I mention above, seeing \\ in the ansible output, is normal, and actually represents an underlying string with only a single \, due to serialization requirements.

[
    "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\BeyondTrust Remote Support Jump Client [nwra.beyondtrustcloud.com-62F2CDFD]",
    "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\nbi-glassfish-mod-4.1.1.0.1",
    "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\nbi-nb-all-21.0.0.240215.0",
    "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\nbi-nb-all-22.0.0.240522.0",
    "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\nbi-nb-all-23.0.0.240913.0",
    "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\nbi-nb-base-8.2.0.0.201609300101"
]

I should also note, that when building a jinja2 template in a template file itself, and not as part of a YAML value, that everything I talked about from YAML formatting above doesn’t apply. So in a whatever.j2 file, you would need to use '\\' to represent a single escaped \.

So yeah, kinda complicated.

Also just a note, if you did something like:

foo: "\bar"

That would parse, but would not represent a literal \bar, it would instead be treated as a python escape sequence for \b which is hex 0X08 for the BEL ascii character.

2 Likes

Thanks, but I think I understood most of that already, and it doesn’t seem like any of the suggested options work. The first version is the same as my first one and ends up with a double backslash between the base path and the key item: Uninstall\\\\BeyondTrust.

If I try the double-quote version I get:

ERROR! We were unable to read either as JSON nor YAML, these are the errors we got from each:
JSON: Expecting value: line 1 column 1 (char 0)

Syntax Error while loading YAML.
  found unknown escape character

The error appears to be in '/home/orion-admin/ansible-nwra/playbooks-windows/fix-registry-uninstall.yml': line 13, column 26, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

    - set_fact:
        keys: "{{ ['HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'] | product(uninstall.sub_keys | select('search', '^(nbi|BeyondTrust Remote Support Jump Client)')) | map('join', '\\') }}"
                         ^ here

So then I try:

        keys: "{{ ['HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall'] | product(uninstall.sub_keys | select('search', '^(nbi|BeyondTrust Remote Support Jump Client)')) | map('join', '\\') }}"

and get:

jinja2.exceptions.TemplateSyntaxError: unexpected char "'" at 177
  line 1

I ended up going with this:

  vars:
    backslash: \
...
        keys: |
          {{ ['HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'] | product(uninstall.sub_keys | select('search', '^(nbi|BeyondTrust Remote Support Jump Client)')) | map('join', backslash) }}
1 Like