Reputation: 4293
I have a Jinja2 template which I'm running with Ansible.
I would like my template to load another file, as a template (i.e. evaluating {{ var }}
), then I'll filter that, and then paste the result in to the top level template.
I think I'm almost there, I just need to find a Jinja2 filter which takes in a string and parses it as a template.
In this example lets assume the filter I want to apply is just to make the file uppercase. (Obviously this case is so simple I could do it in one template file. But my real use case is more complex.)
Top level template main.yaml.j2
:
---
something:
blah:
x: {{ y }}
{%- set names = [ 'John', 'Amy' ] %}
z: >
{{ lookup('file', './other-file.j2') | upper | indent(4*2) }}
other-file.j2
:
{%- for name in names %}
Hello {{ name }}
{%- endfor %}
Running it with this Ansible playbook:
---
- hosts: localhost
connection: local
tasks:
- name: generate template
template:
src: "main.yaml.j2"
dest: "output.yaml.j2"
trim_blocks: False
register: templating
vars:
y: 5
---
something:
blah:
x: 5
z: >
HELLO JOHN
HELLO AMY
---
something:
blah:
x: 5
z: >
{%- FOR NAME IN NAMES %}
HELLO {{ NAME }}
{%- ENDFOR %}
I think I'm almost there. I just need a filter which applies a Jinja2 template to text.
i.e. something like:
{{ lookup('file', './other-file.j2') | template | upper | indent(4*2) }}
(But template
is not a real filter. Maybe there's another name?)
{{ include './other-file.j2' | upper | indent(4*2) }}
doesn't work.
fatal: [127.0.0.1]: FAILED! => {"changed": false, "msg": "AnsibleError: template error while templating string: expected token 'end of print statement', got 'string'. String:
---\nsomething:\n blah:\n x: {{ y }}\n {%- set names = [ 'John', 'Amy' ] %}\n z: >\n {{ include './other-file.j2' | upper | indent(4*2) }}"}
{% include './other-file.j2' | upper | indent(4*2) %}
"TemplateNotFound: ./OTHER-FILE.J2"
doesn't work.
For context, my use case is that I have a Jinja2 template generating AWS CloudFormation templates. I'm trying to do it all in YAML, not JSON. (Because YAML can have comments, and you don't have to worry about whether the last item in a list has a trailing comma, and it's generally easier to read and write and debug.) Some CloudFormation resources need literal JSON pasted into the YAML file. (e.g. CloudWatch Dashboard bodies). So I want to have another file in YAML, which Jinja2 converts to json, and pastes into my overall YAML template. I want this dashboard to be generated with a for loop, and to pass in variables. I would like to have a separate
Upvotes: 1
Views: 4711
Reputation: 68074
Instead of file
plugin
lookup('file', './other-file.j2')
use template
plugin
lookup('template', './other-file.j2')
Note that the scope of the variable {% set names = ['John', 'Amy'] %}
is the template main.yaml.j2. If this variable is used in the template other-file.j2
the command lookup('template', './other-file.j2')
will crash with the error:
"AnsibleUndefinedVariable: 'names' is undefined"
Solution
Declare the variable in the scope of the playbook. For example
- template:
src: "main.j2"
dest: "output.txt"
vars:
names: ['John', 'Amy']
main.j2
{{ lookup('template', './other-file.j2') }}
other-file.j2
{% for name in names %}
Hello {{ name }}
{% endfor %}
give
shell> cat output.txt
Hello John
Hello Amy
Upvotes: 2