Ansible playbook vars not working in templates - ansible

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 }}

Related

Use Jinja template for »value«

Within a playbook one can include Jinja Templated like so:
tasks:
name: 'Foo Bar'
template:
src: '~/tmpl/baz.j2'
dest: '/dev/null'
That renders the template and copies it over to the remote host. But is it possible to use a jinja template to generate a value for a module argument?
What I would like to do is:
tasks:
name: 'create server'
a_cloude_server_create:
…
user_data: "{{ render(./tmpl/cloud-init.yml.j2) }}"
…
Can that be done?
Use template lookup, as Ansible documentation is bad recently, check local docs for lookup plugin via ansible-doc -t lookup template.

How to write a variable value in a file using ansible.builtin.template module?

I wanted to log content of a variable in a file. I did it like this,
- name: write infraID to somefile.log
copy:
content: "{{ infraID.stdout }}"
dest: somefile.log
And it worked. But in the documentation for copy here, I found that they have recommended to use template instead of copy in such cases.
For advanced formatting or if content contains a variable, use the
ansible.builtin.template module.
I went through the examples given at the documentation for template module here. But I was unable to figure out something that works in my scenario. Could you please show me how to do this properly in recommended way ?
Thanks in advance !! Cheers!
The template module does not have a content property.
You have to create a file that contains your template, for example:
templates/infra-id-template
{{ infraID.stdoud }}
playbook
---
- hosts: localhost
tasks:
- name: Get infra ID
shell: echo "my-infra-id"
register: infraID
- name: Template the file
template:
src: infra-id-template
dest: infra-id-file

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

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.

How to reference variable in files folder under roles

In one of my ansible roles, I have created a variable via the "defaults/main.yml" file. I am able to reference this variable just fine in "tasks/main.yml" file. However, the variable does not appear to work in "files/some_file.txt"
Is this expected ?
Yes that's expected. tasks/main.yml is being parsed by ansible and will replace variables as you've seen.
Generally files/some_file.txt should contain static files or scripts that should be used with the copy module. As you've discovered it will not be parsed beyond that.
If you want to use variables in a file you should use the template module. Create a templates directory and copy your files there e.g template/some_file.txt. Note that it's common to rename the file with a .j2 extension to indicate that it is a jinja template e.g some_file.j2 but this is not required.
- name: Create fact
set_fact:
my_variable: 123456
- name: Create file from template
template:
src: some_file.j2
dest: "/tmp/some_file.txt"
mode: 0755
some_file.j2 might contain:
This file contains this sentence and the number {{ my_variable }}
After the template task run /tmp/some_file.txt will looks like:
This file contains this sentence and the number 123456
Use template. For example, the role
shell> cat roles/role-17/defaults/main.yml
var1: value1
shell> cat roles/role-17/files/some_file.txt
[{{ var1 }}]
shell> cat roles/role-17/tasks/main.yml
- debug:
msg: task/main.yml [{{ var1 }}]
- debug:
msg: files/some_file.txt {{ lookup('template', 'files/some_file.txt') }}
and the playbook
shell> cat test-17.yml
- hosts: localhost
roles:
- role-17
give
"msg": "task/main.yml [value1]"
"msg": "files/some_file.txt [value1]\n"

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 }}.

Resources