Replace host variables in Jijnja template file in ansible - ansible

I need to replace a file with the variables defined in the host file.
Following is the host variables defined in "host_vars/abc.1234.com"
env: acc
abcserverName:
- name: abc1
- name: abc2
The playbook file has the following contents
- hosts: "abc.1234.com"
become: yes
tasks:
- name: deploy abc control file
template:
src: abc-control.j2
dest: /etc/init.d/{{ env }}-{{ item.name }}
with_items:
- "{{ abcservername }}"
- name: start abcserver
command: /etc/init.d/control-{{ env }}-{{ item.name }} start
with_items:
- "{{ abcserverName }}"
This will copy 2 files in init.d, which is the following:
/etc/init.d/control-acc-abc1
/etc/init.d/control-acc-abc2
Requirement:
Inside each of the above control files, I also need to get the correct "abcserverName"
For example in "/etc/init.d/control-acc-abc1", I want
SERVER_NAME=abc1
and in For example in "/etc/init.d/control-acc-abc2"
SERVER_NAME=abc2
I don't have much knowledge about jinja templates and google shows me complex examples. Any help to achieve this is appreciated.

Take this jinja2 code for example:
SERVER_NAME={{ server_name }}
server_name is not actually defined, and to use it you will need to pass it as a variable to your template.
Using your task:
- name: deploy abc control file
template:
src: abc-control.j2
dest: /etc/init.d/{{ env }}-{{ item.name }}
with_items:
- "{{ abcservername }}"
vars:
server_name: {{ item.name }}
Now "control-acc-abc1" will have server_name set to abc1 and "control-acc-abc2" will have server_name set to abc2

Related

Data from vault with with_items groups

I have a task that generates my configuration from jinja2 to conf.
- name: check password
set_fact:
my_secrets: "{{ lookup('hashi_vault', 'secret=kv/{{ stage }}.d/{{ app }}/{{ item }}/secrets token={{ token }} url={{ url }} validate_certs={{ validate_certs }}')}}"
with_items: "{{ groups['ns'] }}"
- name: copy config powerdns_auth pdns.local.gmysql.conf
template:
src: ../../../update/ns/templates/etc/powerdns/pdns.d/pdns.local.gmysql.conf.j2
dest: ../../../config/{{ stage }}/{{ item }}/etc/powerdns/pdns.d/pdns.local.gmysql.conf
mode: '0644'
with_items: "{{ groups['ns'] }}"
in pdns.local.gmysql.conf.j2
gmysql-password={{ my_secrets.user_password_mysql }}
I have a problem because it saves me the from vault password from the last host to a file.
Is it possible to set the fact depending on the host?
Don't loop over groups, use the "natural" play loop on hosts and delegate the needed tasks to localhost.
Note: I kept your relative paths in the template tasks but it looks ugly and will break one day or an other.
Note2: "moustaches don't stack" => I fixed your code (there are other ways to fix it...) where it was incorrect when fetching from hashicorp vault.
- name: Create config files per hosts
hosts: ns
gather_facts: false
vars:
# All your needed vars that I will not define here for this example
tasks:
- name: check password
vars:
secret: "kv/{{ stage }}.d/{{ app }}/{{ inventory_hostname }}/secrets"
hashi_string: "secret={{ secret }} token={{ token }} url={{ url }} validate_certs={{ validate_certs }}"
set_fact:
my_secrets: "{{ lookup('hashi_vault', hashi_string) }}"
- name: copy config powerdns_auth pdns.local.gmysql.conf
template:
src: ../../../update/ns/templates/etc/powerdns/pdns.d/pdns.local.gmysql.conf.j2
dest: ../../../config/{{ stage }}/{{ inventory_hostname }}/etc/powerdns/pdns.d/pdns.local.gmysql.conf
mode: '0644'
delegate_to: localhost

printing the host name from group in inventory

I have below inventory file:
[web]
10.0.1.0
[db]
10.0.3.0
[frontend]
10.0.5.0
[X_all:children]
web
db
frontend
Now in my playbook: I'm trying to print the hostname under X_all group in the name line.
- name: "Copying the output of Registry run to local machine from {{ hostvars[item]['inventory_hostname'] }}"
become: true
become_user: "{{ login }}"
fetch:
src: /tmp/DeploymentRegistry.txt
dest: /tmp/DeploymentRegistry-{{ inventory_hostname }}.txt
flat: yes
with_items:
- "{{ groups['X_all'] }}"
Is it even possible to add the hostname in the name line?
I tried replacing {{ hostvars[item]['inventory_hostname'] }} with inventory_hostname but it's also not working.
Regarding your requirement
I'm trying to print the hostname under test group in the name line.
take note that the group has more than one member and therefore more than one hostname, but a list of hostnames.
- name: Show hosts {{ groups['test'] }}
delegate_to: localhost
debug:
msg: "{{ item }}" # will show the group member hostname(s)
with_items:
- "{{ groups['test'] }}"
Is it even possible to add the hostname in the name line? I tried ... with inventory_hostname but it's also not working.
Yes, it is.
- name: Show host {{ inventory_hostname }}
debug:
msg: "{{ item }}"
with_items:
- "{{ groups['test'] }}"
But it will print the first hostname from list only.

Use Dict in Vars with Templates in Ansible

I'm trying to use templates with different sets of variables for each itteration of a determined set of tasks. For example, in one of the tasks I'd like to set specific values for postgres:
- name: Define values for postgres-ds
template:
src: postgres-ds.xml.j2
dest: /opt/ear_{{ instance_control.value }}/postgres-ds.xml
vars: "{{ postgres_desenv }}"
notify: Restart Service
In role/vars/main.yaml, I defined:
postgres_desenv:
var1: somevalue
var2: someothervalue
...
Still, I get the following error:
fatal: [rmt]: FAILED! => {
"failed": true,
"reason": "Vars in a Task must be specified as a dictionary, or a list of dictionaries
...
When I try to use the same variable in another context, it works fine:
- debug:
msg: "{{ item.key }} - {{ item.value }}"
with_dict: "{{ postgres_desenv }}"
I tried following the answers to this question but I'm still stuck.
My next step is to use a variable to call the variable inside vars, something like:
- name: Define values for postgres-ds
template:
src: postgres-ds.xml.j2
dest: /opt/ear_{{ instance_control.value }}/postgres-ds.xml
vars: postgres_{{ another_var }}
notify: Restart Service
You can do something like this:
- name: Define values for postgres-ds
template:
src: postgres-ds.xml.j2
dest: /opt/ear_{{ instance_control.value }}/postgres-ds.xml
vars:
settings: "{{ postgres_desenv }}"
notify: Restart Service
Then within the template you could refer to, e.g.,
{{ settings.var1 }}
In my case, following the answer above, all i had to do is using {{ item.value.(mydictkey) }} and that's it
In my case i defined a global variable like so:
vars:
vhosts:
web1
port: 8080
dir: /mywebsite
web2:
...
Then in the task I used:
- name: Render template
template:
src: "../templates/httpd.vhost.conf.j2" # Local template
dest: "/etc/httpd/conf.d/{{ item.key }}.conf" # Remote destination
owner: root
group: root
mode: 644
with_dict: "{{ vhosts }}"
In the template I used:
<VirtualHost *:{{ item.value.port }}>
DocumentRoot /var/www/{{ item.value.dir }}
</VirtualHost>
If postgres_desenv is defined in vars/main.yml that will be loaded automatically and be available to the role and rest of the playbook. Why do you have to specify that again using "vars" option in the template module task?

Ansible recursive checks in playbooks

We need to go through this structure
Zone spec
https://gist.github.com/git001/9230f041aaa34d22ec82eb17d444550c
I was able to run the following snipplet but now I'm stucked at the error checking.
playbook
--
- hosts: all
gather_facts: no
vars_files:
- "../doc/application-zone-spec.yml"
roles:
- { role: ingress_add, customers: "{{ application_zone_spec }}" }
role
- name: check if router exists
shell: "oc get dc -n default {{ customers.zone_name }}-{{ item.type }}"
with_items: "{{ customers.ingress }}"
ignore_errors: True
register: check_router
- name: Print ingress hostnames
debug: var=check_router
- name: create new router
shell: "echo 'I will create a router'"
with_items: "{{ customers.ingress }}"
when: check_router.rc == 1
Output of a ansible run
https://gist.github.com/git001/dab97d7d12a53edfcf2a69647ad543b7
The problem is that I need to go through the ingress items and I need to map the error of the differnt types from the "check_router" register.
It would be nice to make something like.
Pseudo code.
Iterate through the "customers.ingress"
check in "check_router" if the rc is ! 0
execute command.
We use.
ansible-playbook --version
ansible-playbook 2.1.0.0
config file = /etc/ansible/ansible.cfg
configured module search path = Default w/o overrides
You can replace the second loop with:
- name: create new router
shell: "echo 'I will create a router with type {{ item.item }}'"
with_items: "{{ check_router.results }}"
when: item.rc == 1
This will iterate over every step of check_route loop and you can access original items via item.item.

How to use template module with different set of variables?

My use case is the following :
I have a template file, and I would like to create 2 different files from that template, with the variables being filled by a different set of variables for each file.
For example, lets say I want to template the file containing the line:
mkdir -p {{myTemplateVariable}}
I would like to find a proper way to get this variable filled by "File1" and "File2". Something like :
- name: template test 1
template:
src=myTemplateFile
dest=result1
- name: template test 2
template:
src=myTemplateFile
dest=result2
where I could specify for the first templating that the variable to use is a = "File1" and for the second, b = "File2".
With Ansible 2.x you can use vars: with tasks.
Template test.j2:
mkdir -p {{myTemplateVariable}}
Playbook:
- template: src=test.j2 dest=/tmp/File1
vars:
myTemplateVariable: myDirName
- template: src=test.j2 dest=/tmp/File2
vars:
myTemplateVariable: myOtherDir
This will pass different myTemplateVariable values into test.j2.
For Ansible 2.x:
- name: template test
template:
src: myTemplateFile
dest: result1
vars:
myTemplateVariable: File1
- name: template test
template:
src: myTemplateFile
dest: result2
vars:
myTemplateVariable: File2
For Ansible 1.x:
Unfortunately the template module does not support passing variables to it, which can be used inside the template. There was a feature request but it was rejected.
I can think of two workarounds:
1. Include
The include statement supports passing variables. So you could have your template task inside an extra file and include it twice with appropriate parameters:
my_include.yml:
- name: template test
template:
src=myTemplateFile
dest=destination
main.yml:
- include: my_include.yml destination=result1 myTemplateVariable=File1
- include: my_include.yml destination=result2 myTemplateVariable=File2
2. Re-define myTemplateVariable
Another way would be to simply re-define myTemplateVariable right before every template task.
- set_fact:
myTemplateVariable: File1
- name: template test 1
template:
src=myTemplateFile
dest=result1
- set_fact:
myTemplateVariable: File2
- name: template test 2
template:
src=myTemplateFile
dest=result2
You can do this very easy, look my Supervisor recipe:
- name: Setup Supervisor jobs files
template:
src: job.conf.j2
dest: "/etc/supervisor/conf.d/{{ item.job }}.conf"
owner: root
group: root
force: yes
mode: 0644
with_items:
- { job: bender, arguments: "-m 64", instances: 3 }
- { job: mailer, arguments: "-m 1024", instances: 2 }
notify: Ensure Supervisor is restarted
job.conf.j2:
[program:{{ item.job }}]
user=vagrant
command=/usr/share/nginx/vhosts/parclick.com/app/console rabbitmq:consumer {{ item.arguments }} {{ item.job }} -e prod
process_name=%(program_name)s_%(process_num)02d
numprocs={{ item.instances }}
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/{{ item.job }}.stderr.log
stdout_logfile=/var/log/supervisor/{{ item.job }}.stdout.log
Output:
TASK [Supervisor : Setup Supervisor jobs files] ********************************
changed: [loc.parclick.com] => (item={u'instances': 3, u'job': u'bender', u'arguments': u'-m 64'})
changed: [loc.parclick.com] => (item={u'instances': 2, u'job': u'mailer', u'arguments': u'-m 1024'})
Enjoy!
This is a solution/hack I'm using:
tasks/main.yml:
- name: parametrized template - a
template:
src: test.j2
dest: /tmp/templateA
with_items: var_a
- name: parametrized template - b
template:
src: test.j2
dest: /tmp/templateB
with_items: var_b
vars/main.yml
var_a:
- 'this is var_a'
var_b:
- 'this is var_b'
templates/test.j2:
{{ item }}
After running this, you get this is var_a in /tmp/templateA and this is var_b in /tmp/templateB.
Basically you abuse with_items to render the template with each item in the one-item list. This works because you can control what the list is when using with_items.
The downside of this is that you have to use item as the variable name in you template.
If you want to pass more than one variable this way, you can dicts as your list items like this:
var_a:
-
var_1: 'this is var_a1'
var_2: 'this is var_a2'
var_b:
-
var_1: 'this is var_b1'
var_2: 'this is var_b2'
and then refer to them in your template like this:
{{ item.var_1 }}
{{ item.var_2 }}
I did it in this way.
In tasks/main.yml
- name: template test
template:
src=myTemplateFile.j2
dest={{item}}
with_dict: some_dict
and in vars/main.yml
some_dict:
/path/to/dest1:
var1: 1
var2: 2
/path/to/dest2:
var1: 3
var2: 4
and in templates/myTemplateFile.j2
some_var = {{ item.value.var1 }}
some_other_var = {{ item.value.var2 }}
Hope this solves your problem.
I had a similar problem to solve, here is a simple solution of how to pass variables to template files, the trick is to write the template file taking advantage of the variable. You need to create a dictionary (list is also possible), which holds the set of variables corresponding to each of the file. Then within the template file access them.
see below:
the template file: test_file.j2
# {{ ansible_managed }} created by xbalaji#gmail.com
{% set dkey = (item | splitext)[0] %}
{% set fname = test_vars[dkey].name %}
{% set fip = test_vars[dkey].ip %}
{% set fport = test_vars[dkey].port %}
filename: {{ fname }}
ip address: {{ fip }}
port: {{ fport }}
the playbook
---
#
# file: template_test.yml
# author: xbalaji#gmail.com
#
# description: playbook to demonstrate passing variables to template files
#
# this playbook will create 3 files from a single template, with different
# variables passed for each of the invocation
#
# usage:
# ansible-playbook -i "localhost," template_test.yml
- name: template variables testing
hosts: all
gather_facts: false
vars:
ansible_connection: local
dest_dir: "/tmp/ansible_template_test/"
test_files:
- file_01.txt
- file_02.txt
- file_03.txt
test_vars:
file_01:
name: file_01.txt
ip: 10.0.0.1
port: 8001
file_02:
name: file_02.txt
ip: 10.0.0.2
port: 8002
file_03:
name: file_03.txt
ip: 10.0.0.3
port: 8003
tasks:
- name: copy the files
template:
src: test_file.j2
dest: "{{ dest_dir }}/{{ item }}"
with_items:
- "{{ test_files }}"
- name: copy vhosts
template: src=site-vhost.conf dest=/etc/apache2/sites-enabled/{{ item }}.conf
with_items:
- somehost.local
- otherhost.local
notify: restart apache
IMPORTANT: Note that an item does not have to be just a string, it can be an object with as many properties as you like, so that way you can pass any number of variables.
In the template I have:
<VirtualHost *:80>
ServerAdmin me#example.org
ServerName {{ item }}
DocumentRoot /vagrant/public
ErrorLog ${APACHE_LOG_DIR}/error-{{ item }}.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
Another real world example using a list
an extract for a template for php.ini
{% if 'cli/php.ini' in item.d %}
max_execution_time = 0
memory_limit = 1024M
{% else %}
max_execution_time = 300
memory_limit = 512M
{% endif %}
This is the var
php_templates:
- { s: 'php.ini.j2', d: "/etc/php/{{php_version}}/apache2/php.ini" }
- { s: 'php.ini.j2', d: "/etc/php/{{php_version}}/cli/php.ini" }
Then i deploy with this
- name: push templated files
template:
src: "{{item.s}}"
dest: "{{item.d}}"
mode: "{{item.m | default(0644) }}"
owner: "{{item.o | default('root') }}"
group: "{{item.g | default('root') }}"
backup: yes
with_items: "{{php_templates}}"

Resources