Can I render ansible templates to a variable vs. to disk? - ansible

Is writing a file the only way to render an ansible template? In the past, with python and jinja2, I've rendered jinja templates to python vars directly and was hoping I can do the same with ansible.
What I'm trying to do is take the content of my template and pipe it to another command without writing the template to a file on disk and cat'ing the file. Doable?

You have several options for creating a variable from a template. For small templates, you can just use an inline template with set_fact:
- name: render a template to a variable
set_fact:
myvar: |-
This is a template.
This host is {{ inventory_hostname }}.
For longer templates, you can use the template lookup:
- name: render a template to a variable
set_fact:
myvar: "{{ lookup('template', 'template_name.j2') }}"
And of course this isn't limited to the set_fact module. You can do this anywhere you can put a string value in Ansible.

Related

How to reference value stored in "register: " inside my jinja template? { the output in register are show command outputs }

I am trying to write the output of a series of show commands to a text file on Ansible. My approach is to reference the registers on my jinja2 template. I am unclear on how to reference these register values if the return type is not a dictionary/list. How do I reference these variables in my template ?
I have tried calling them as msg."register name". This writes to the file, but is haphazard with barely any formatting structure.
Part of my Playbook
register: int_status
- set_fact:
int_status={{ int_status.stdout }}
- name: Print the interface status
debug:
msg: "{{ int_status }}"
My jinja2 file:
Host information:
------------------------------
ip address: {{device_info.ansible_facts['ansible_net_all_ipv4_addresses']}}
Interfaces: {{msg}}

Escape or differentiate between jinja template variables in Ansible and <service>.

History:
We've been using Ansible to deploy our services and config files over the last few months and we've been making use of the Ansible variables.
The variables get placed in our (config_name).yml.j2 files which allows us to easily make changes without having to hard code all of the config.
For example in Ansibles group_vars we may have:
metric_port_var: "9100"
And the (config_name).yml.j2 will contain a line somewhat:
EXPOSE_METRIC_PORT={{ metric_port_var}}
When the config is deployed the config on the box is now:
EXPOSE_METRIC_PORT=9100
Problem:
Now we're deploying configuration for AlertManager/Prometheus.
The issue that has arisen is that AlertManager is also using jinja template variables in it's own configuration file that we are trying to deploy. These other jinja templates will come from other config files on the box.
This means our (config_name).yml.j2 will in theory contain a mix of curly brace variables where some might belong to Ansible and others will belong to this other file.
We can no longer use Ansible's "Template" module to deploy our config due to an error being thrown when a variable isn't found in group_vars, as instead the particular variable should come from AlertManager.
We need a way of either mixing the jinja templates or escaping some curly braces but not others. For now we're back to hard coding our config and letting AlertManager use all the variables.
This works fine for me:
template.j2:
foo {{ ansible_var }}
bar {{ '{{' }} other_var {{ '}}' }}
zzz {%raw%}{{ another_var }}{%endraw%}
output:
foo val
bar {{ other_var }}
zzz {{ another_var }}
AlertManager is also using jinja template variables in it's own configuration file
The alertmanager uses Go templates.
See the escaping section of the Jinja docs:
key={{ '{{' }}the_var{{ '}}' }}
Will be rendered as:
key={{the_var}}
Brian-Brazil is correct. (As to be expected, google his name)
We use this technique a lot here and have .j2.j2 files in our Ansible templates that translate to .j2 files in dockers who are parsed on container start.
Here is an example more specific to your use-case.
The alertmanager indeed uses Go templates but this might look confusing on first sight when mixed in a jinja template, I agree.
Say you have a file called alertmanager.yml.j2 of which the following lines are an extract.
receivers:
- name: '{{ name_of_receiver_group }}'
opsgenie_configs:
- api_key: 123-123-123-123-123
send_resolved: false
{% raw %}
# protecting the go templates inside the raw section.
details: { details: "{{ .CommonAnnotations.SortedPairs.Values | join \" \" }}" }
{% endraw %}
You'll have an Ansible task that looks similar like this.
- name: copy helper scripts
template: src={{ item }}.j2 dest={{ container_dir }}/{{ item }} mode=0755
with_items:
- alertmanager.yml

How are Ansible group variable files parsed?

In particular, I have a group_vars/all.yml file, with essentially the following contents:
my_foo: asdf
my_bar: '{{ my_foo }}'
If I later, while templating a file in a playbook, such as:
- name: template a file
template:
src: something.j2
dest: '....'
And in that something.j2 file, I use {{ my_bar }}, I somehow get asdf. How?
My prior understanding was that files such as group_vars/all.yml where essentially parsed as parse_yaml(parse_and_evaluate_jinja2(contents_of_the_file)). But if this were true, the line my_bar: '{{ my_foo }}' would not correctly evaluate: either we'd get an error, since my_foo is undefined to Jinja, or some default text, like "undefined", or ''. It's like the YAML was being parsed at the same time as the the Jinja2, which seems unlikely. Is that really what Ansible does? Or am I missing something else entirely?
(I couldn't find any exact documentation on how variable files are parsed in Ansibles docs, so if they exist, links are appreciated.)
My prior understanding was that files such as group_vars/all.yml where essentially parsed as parse_yaml(parse_and_evaluate_jinja2(contents_of_the_file))
Wrong. Ansible does all evaluation in runtime, for example:
You tell Ansible:
- debug:
msg: "{{ my_bar }}"
So when Ansible is going to pass msg parameter to debug module, it will do:
What is the parameter?
The parameter is {{ my_bar }}. Oh, it's a template and there's variable my_bar.
What is the my_bar's value?
The value is {{ my_foo }}. Oh, it's a template again, and there's variable my_foo.
What is the my_foo's value?
The value is asdf. Good! Nothing more to template.
So, my_bar's value is now asdf.
And parameter value is not asdf.
Let's print asdf.
By writing:
my_var: "{{ my_other_var }}"
you don't assign variables as in general programming, but write a simple template to calculate my_var's value in any given time (runtime), and not in time of parsing.

Ansible items in separate files

Is it possible to have few .yml files and then read them as separate items for task?
Example:
- name: write templates
template: src=template.j2 dest=/some/path
with_items: ./configs/*.yml
I have found pretty elegant solution:
---
- hosts: localhost
vars:
my_items: "{{ lookup('fileglob', './configs/*.yml', wantlist=True) }}"
tasks:
- name: write templates
template: src=template.j2 dest=/some/path/{{ (item | from_yaml).name }}
with_file: "{{ my_items }}"
And then in template you have to add {% set item = (item | from_yaml) %} at the beginning.
Well, yes and no. You can loop over files and even use their content as variables. But the template module does not take parameters. There is an ugly workaround by using an include statement. Includes do take parameters and if the template task is inside the included file it will have access to them.
Something like this should work:
- include: other_file.yml parameters={{ lookup('file', item) | from_yaml }}
with_fileglob: ./configs/*.yml
And in other_file.yml then the template task:
- name: write template
template: src=template.j2 dest=/some/path
The ugly part here, beside the additional include, is that the include statement only takes parameters in the format of key=value. that's what you see in above task as parameters=.... parameters here has no special meaning, it just is the name of the variable with which the content of the file will be available inside the include.
So if your vars files have a variable foo defined, you would be able to access it in the template as {{ parameters.foo }}.

Ansible playbook vars not working in templates

I have problem getting variables work in my templates. Variables work in my playbook but in templates, they are rendered 'as is' without getting replaced by their values. Here is a simple test-playbook.yml that I am trying.
---
- name: Test playbook vars
hosts: webservers
vars:
hello_var: Hello World
hello_file_path: /tmp/hello_file.txt
tasks:
- name: Copy hello world file
copy: src=templates/hello_world.txt.j2 dest={{ hello_file_path }}
In my templates/hello_world.txt.j2, I have the following contents
hi {{ hello_var }}
After running the playbook, I have on the host at /tmp/hello_world.txt the same content as in my template
hi {{ hello_var }}
The variable hello_file_path used in the playbook works but the variable hello_var used in my template is not working.
Inside the task you using copy module which simply copies the file without any template processing. In order to use template you need to use template module.
- name: Copy hello world file
template: src=templates/hello_world.txt.j2 dest={{ hello_file_path }}

Resources