Is there any way to prevent ansible changing my JSON stored in a variable?
Edit: As suggested by Vladimir Botka here is a minimal example.
I have this template file json.j2
{"foo":"{{ var }}"}
which I would like to render into a variable and insert that variable into another template
- name render it
ansible.builtin.template:
src: another.j2
[...]
vars:
json_from_template: "{{ lookup('template', 'json.j2') }}"
As it seems ansible interprets the json from json.j2 and changes it to
{ 'foo': 'var_value' }
But "because of reasons" I need the json unchanged, no spaces, no single quotes, as specified in the json.j2.
Can I tell ansible to keep the result of json.j2 as a raw string?
I already tried !unsafe
, but this
vars:
json_from_template: !unsafe "{{ lookup('template', 'json.j2') }}"
only renders the raw lookup command without any templating at all, and this
vars:
json_from_template: "{{ !unsafe lookup('template', 'json.j2') }}"
does not work at all.
Solution 1:
Note: In the snippets below, the callback yaml is used to display the output to the screen.
shell> ansible-config dump | grep DEFAULT_STDOUT_CALLBACK
DEFAULT_STDOUT_CALLBACK(/home/admin/.ansible.cfg) = yaml
Ansible, when templating Jinja2, tries and converts strings that look like python data structures to python data structures. For example
shell> cat json.j2
{"foo":"{{ var }}"}
- set_fact:
json_from_template: "{{ lookup('template', 'json.j2') }}"
vars:
var: var_value
- debug:
var: json_from_template
- debug:
var: json_from_template|type_debug
gives
json_from_template:
foo: var_value
json_from_template|type_debug: dict
You can see the type of the variable json_from_template is the dictionary. What you need is a string. You have to explicitly declare it
- set_fact:
json_from_template: "{{ lookup('template', 'json.j2')|string }}"
vars:
var: var_value
- debug:
var: json_from_template|string
- debug:
var: json_from_template|type_debug
gives
json_from_template|string: |-
{"foo":"var_value"}
json_from_template|type_debug: str
It seems that this "magic" conversion works for valid JSON only. See the variable test_2 below. Other variables remain strings despite the fact that they are valid YAML dictionaries
vars:
val1: foo
test1: |-
{"key1": "foo"}
test2: |-
{"key1": "{{ val1 }}"}
test3: |-
{"key1": {{ val1 }}}
test4: |-
{key1: {{ val1 }}}
test5: |-
key1: {{ val1 }}
tasks:
- debug:
msg: |
test1: {{ test1|type_debug }}
test2: {{ test2|type_debug }}
test3: {{ test3|type_debug }}
test4: {{ test4|type_debug }}
test5: {{ test5|type_debug }}
- debug:
msg: |
test1|from_yaml: {{ test1|from_yaml|type_debug }}
test3|from_yaml: {{ test3|from_yaml|type_debug }}
test4|from_yaml: {{ test4|from_yaml|type_debug }}
test5|from_yaml: {{ test5|from_yaml|type_debug }}
gives
msg: |-
test1: AnsibleUnicode
test2: dict
test3: str
test4: str
test5: str
msg: |-
test1|from_yaml: dict
test3|from_yaml: dict
test4|from_yaml: dict
test5|from_yaml: dict
The "magic" conversion applies to Jinja2 templates in Ansible expressions only. For example
- debug:
msg: "{{ json_from_template|string }}"
- debug:
msg: "{{ json_from_template }}"
gives
msg: |-
{"foo":"var_value"}
msg:
foo: var_value
Jinja2 templates inside the template module evaluate always to strings. For example
shell> cat another.j2
{{ json_from_template|string }}
{{ json_from_template }}
- template:
src: another.j2
dest: output.txt
gives
shell> cat output.txt
{"foo":"var_value"}
{"foo":"var_value"}