I need to to build json files using ansible template module where there is a specific key that has a value which is a typescript.
`- myRole
+- files
| `- hello-world.ts
+- templates
| +- fragments
| | `- hw-tag.fragment
| `- payload.json.j2
+- tasks
| `- main.yml
`- vars
`- main.yml
hello-world.ts is for this toy example:
let message: string = "Hello World";
console.log(message);
payload.json.j2:
{
"id": "{{ id }}"
"script": {
{% with script=script %}
{% include "fragments/hw-tag.fragment" %}
{% endwith %}
}
}
hw-tag.fragment:
"name": "{{ script.name }}"
"script": "{{ script.content }}"
Now, obviously my naive approach was to read the contents of the file using lookup('file', ...) store the result in a var using set_fact, and pass that variable to template:
- set_fact:
script: "{{ script|default({}) | combine({name: lookup('file', ...) | replace('\n', '\\n')}) }}"
- debug:
msg: "{{ script }}"
- template:
src: templates/payload.json.j2
dest: "{{ workdir }}/payload.json"
vars:
script: "{{ script }}"
In doing so, ansible generates payload.json, however the formatting is broken, quotes are not escaped, etc. Whereas I would have expected:
{
" id": "1",
"script": {
"name": "Hello World",
"script": "let message: string = \"Hello World\";\\nconsole.log(message);"
}
}
Instead however, I got this:
{
"id": "1",
"script": {
"name": "Hello World",
"script": "let message: string = "Hello World";\nconsole.log(message);"
}
}
Is it possible to achieve what I'm after? This was a really simple script, things are a tad more hectic with more complex examples.
Ansible doesn't know that if you're placing text into a JSON file, an XML file, a simple text file, or something else. If you want escaping, you need to be explicit about that. For example, we can modify your hw-tag.fragment
template to use the to_json
filter:
"name": "{{ script.name }}",
"script": {{ script.content|to_json }}
With this change, the output from payload.json.j2
looks like:
{
"script": {
"name": "hello-world.ts",
"script": "let message: string = \"Hello World\";\nconsole.log(message);" }
}
That's valid JSON, with the script content properly escaped.
For what you've shown in your question, I'm not sure that templating is really the right way to go. This all might be simpler if you were to work primarily with data structures in Ansible. For example, here we create a payload
variable with the desired values, and then simply pass the variable through the to_nice_json
filter when writing it out:
- hosts: localhost
gather_facts: false
vars:
script_name: hello-world.ts
id: some_id
tasks:
- set_fact:
payload:
id: "{{ id }}"
script:
name: "{{ script_name }}"
content: "{{ lookup('file', script_name) }}"
- copy:
content: "{{ payload|to_nice_json }}"
dest: payload.json