serious issues with autoescaping quotes in variables with output

Simple problem, apparently not a simple solution.

tasks:

  • name: packages
    shell: dpkg -l | awk -F ’ ’ ‘{print “{"package":"”$1"", “"version":"”$2""}"}’
    register: bleh
    ignore_errors: yes

  • set_fact:
    packages: “{{ bleh.stdout_lines }}”

-name: always fails here no matter what
local_action: copy content=“{{ packages | to_json }}” dest=“myfile” ####<— this always prints garbage

Using content with copy should be used minimally. Instead you should use a real template, and the template module.

Typically speaking, I recommend only using content with static simple strings. If you are using templating in content you should probably use the template module.

Ok, so I have tried this.

local_action: template src=“{{ myvarwithrawquotes }}” dest=“myfile”

and it blows up spectacularly.

I do not see how I can use template with a variable full of raw json to print that variable (a list) to the local filesystem without trashing the variable.

You need an actual template file. Not supplying a template string to src.

Such a file (packages.json.j2) would have content that looks like:

{{ packages | to_json }}

Then you would have a template task like:

  • template:
    src: packages.json.j2
    dest: myfile.json
    delegate_to: localhost

So I have tried this (templating). If I use packages.stdout, no matter what I put in the template, I still get garbage:
“{
{"a":"b"}\n
{"c":"d"}\n
.
.
}”

If I do this with stdout_lines, I get:
["

{"a":"b"},{"c":"d"},{"e":"f"}
.
.

"]

The source that went into the variable was:
{“a”:“b”}
{“c”:“d”}

This is the root of my problem. The json is not a native json from something like .results. It is json i wrote, and shoved into a variable, to be as printed plain text to a file. I don’t understand why the absolute only way this works is for me to local_action: shell echo {{var}} >> file, using with_items. Every thing I have tried prints this with my quotes in my variable as escaped, no matter if I use template or copy.

What am I doing wrong with my variable? Why does everything I try to do force escapes into the content? I tried {% var %} but that doesn’t seem to work, and I can’t see any way to output raw, untouched, unmangled variables.

So, after about ~8 hours on this simple task, it looks like there is absolutely no way to output a variable populated with strings to a file without ansible escaping the quotes in it, unless you loop with_items and shell echo it out.

This is absurd. Frankly, I don’t understand how noone else seems to have run into this horribly simple problem.

Because we don't pipe through a filter when we don't need to

local_action: copy content="{{ bleh.stdout }}" dest="myfile"

A few things I’ll mention. Your shell command doesn’t work for me. Even after fixing the quoting, it includes lines that are not packages.

You would likely be better off using the package_facts module, and then writing out a template using that registered data.

Package_facts doesn’t work all the time. See:

https://github.com/ansible/ansible/issues/55850

I am loathe to use yum list installed, and have to work on dealing with rpm -qa in the future, but for now yum deals with it.

Playbook below (again, no matter what I do the variables get printed to local disk with escapes)

Anyone have any idea why you just can’t write out the packages variable without all the escaping, as populated in the playbook below? Neither copy content nor template work, forcing the use of shell which consumes the escaped chars to “fix” ansibles output. Hundreds/thousands of lines as a line-by-line echo into a file are unacceptable. Writing to remote host and pulling is also a nonstarter, as our requirements are to do as little destructive (disk writes) processes as possible.

Hopefully someone understands what is going on here. :slight_smile:

Have you read my post yesterday? I believe the answer is in there.

Unfortunately the answer is not in your post. I can dump it raw {{ packages }} or {{ dpkg_out.stdout_lines }}, with no filter, and it STILL pumps out escaped quotes, whether I use template, copy, lineinfile, blockinfile, or any other ansible output method that isn’t shell echo.

Is it actually pumping out the escaped quotes or is it just the callback plugin escaping the json to fit in a json string. The default callback plugin in Ansible uses json to display the status so if the stdout return value contains json it will be escaped, i.e. “ will become \” and so on.

Try creating a file with the stdout result of your task and look at the file directly.

It is actually pumping out escaped quotes... not just escaped quotes
in text you would expect from debug or what have you. I stopped using
set_fact entirely and just called the parent variable.stdout and it
worked. Im curious to know if this behavior is caused by something
getting mangled by pushing the var into a fact, but to be honest I am
just happy to have this work now.

It is actually pumping out escaped quotes... not just escaped quotes
in text you would expect from debug or what have you. I stopped using
set_fact entirely and just called the parent variable.stdout and it
worked. Im curious to know if this behavior is caused by something
getting mangled by pushing the var into a fact, but to be honest I am
just happy to have this work now.

Nothing is mangled, it's because of using the wrong variable(see the excerpt from the documentation bellow.)
In all the set_fact you have posted in this thread you have used stdout_lines and not stdout as you can see from my copy-paste.

- set_fact:
    packages: "{{ bleh.stdout_lines }}"

- set_fact:
    packages: "{{ dpkg_out.stdout_lines }}"
  when:
    - dpkg_rc.rc == 0
    - ansible_facts.packages is not defined

- set_fact:
    packages: '{{ rpm_out.stdout_lines }}'
  when:
    - dpkg_rc.rc != 0
    - ansible_facts.packages is not defined

From the documentation:

stdout [1]
Some modules execute command line utilities or are geared for executing commands directly (raw, shell, command, etc). This field contains the normal output of these utilities.

stdout_lines [2]
When stdout is returned, Ansible always provides a list of strings, each containing one item per line from the original output.

So output.stdout_lines[0] is the fist line, output.stdout_lines[1] second line and so on.
stdout_lines is usually used when needing to loop over the result line by line.

[1] https://docs.ansible.com/ansible/2.8/reference_appendices/common_return_values.html#stdout
[2] https://docs.ansible.com/ansible/2.8/reference_appendices/common_return_values.html#stdout-lines