Ansible - Using Jinja 2 for Dynamic Roles - ansible

I'm trying to make an Ansible playbook that that that calls rolls dynamically. Now, Ansible doesn't support that natively, so I thought I could circumvent this with some Jinja2 templates... but I'm not even sure how to actually do that. So let's say I have a list of roles in a variable
role_vars:
- role1
- role2
- role3
My template:
- hosts: localhost
roles: >
{%- for name in role_vars %}
{{- '- ' + name + '\n ' -}}
{%- endfor %}
I'd assume it should yield something like this:
- hosts: localhost
roles:
- role1
- role2
- role3
but I can't get it to work. So how do I? Is it even possible to?

You've placed too much trimming modifiers. Try this:
Template:
- hosts: localhost
roles:
{% for name in role_vars -%}
- {{ name }}
{% endfor %}

---
- hosts: localhost
gather_facts: no
tasks:
- name: test
set_fact: var=[{% for i in groups['server'] %}{% if hostvars[i].clusterName is defined %}"{{ hostvars[i].clusterName }}"{% if not loop.last %},{% endif %}{% endif %}{% endfor %}]
- name: debug
debug: msg={{ var }},{{ var | length }}

Related

Is there any way to loop through Ansible list of dictionary registered variable in combination with Jinja2?

In my inventory I have 3 servers in a group. I want to be able to increase that size in the future so I can add more nodes into the network and generate the template with Jinja2.
- name: Gathering API results
shell:
cmd: "curl {{ groups['nodes'][node_index] }}/whatever/api/result "
loop: "{{ groups['nodes'] }}"
loop_control:
index_var: node_index
register: api_value
If I run some debug tasks hardcoding which list I want to use everyhing works fine
- debug: "msg={{ api_value.results.0.stdout }}"
- debug: "msg={{ api_value.results.1.stdout }}"
- debug: "msg={{ api_value.results.2.stdout }}"
output:
ok: [server-1] => {
"msg": "random-value-a"
ok: [server-2] => {
"msg": "random-value-b"
ok: [server-3] => {
"msg": "random-value-c"
The problem is when I try to increase the list number in Jinja template. I tried several for loops combination, nested for loops and many other things but nothing seems to be working.
For example I want my Jinja template look similar like this:
{% for vm in groups['nodes'] %}
NODE_{{ loop.index }}={{ api_value.results.{loop.index}.stdout }}
{% endfor %}
This way I want to achieve this output:
NODE_0=random-value-a
NODE_1=random-value-b
NODE_2=random-value-c
Is there any other way to workaround this? Or maybe is something I could do better in the "Gathering API results" task?
Given the inventory
shell> cat hosts
[nodes]
server-1
server-2
server-3
Either run it in the loop at a single host, e.g.
- hosts: localhost
gather_facts: false
vars:
whatever_api_result:
server-1: random-value-a
server-2: random-value-b
server-3: random-value-c
tasks:
- command: "echo {{ whatever_api_result[item] }}"
register: api_value
loop: "{{ groups.nodes }}"
- debug:
msg: "{{ api_value.results|json_query('[].[item, stdout]') }}"
gives
msg:
- - server-1
- random-value-a
- - server-2
- random-value-b
- - server-3
- random-value-c
Then, in the Jinja template, fix the index variable
- debug:
msg: |-
{% for vm in groups.nodes %}
NODE_{{ loop.index0 }}={{ api_value.results[loop.index0].stdout }}
{% endfor %}
gives what you want
msg: |-
NODE_0=random-value-a
NODE_1=random-value-b
NODE_2=random-value-c
Optionally, iterate api_value.results. This gives the same result
- debug:
msg: |-
{% for v in api_value.results %}
NODE_{{ loop.index0 }}={{ v.stdout }}
{% endfor %}
Or run it in the group, e.g.
- hosts: nodes
gather_facts: false
vars:
whatever_api_result:
server-1: random-value-a
server-2: random-value-b
server-3: random-value-c
tasks:
- command: "echo {{ whatever_api_result[inventory_hostname] }}"
register: api_value
delegate_to: localhost
- debug:
msg: "{{ api_value.stdout }}"
(delegate to localhost for testing)
gives
ok: [server-1] =>
msg: random-value-a
ok: [server-2] =>
msg: random-value-b
ok: [server-3] =>
msg: random-value-c
Then, in the Jinja template, use hostvars
- debug:
msg: |-
{% for vm in groups.nodes %}
NODE_{{ loop.index0 }}={{ hostvars[vm].api_value.stdout }}
{% endfor %}
run_once: true
gives also what you want
msg: |-
NODE_0=random-value-a
NODE_1=random-value-b
NODE_2=random-value-c
Optionally, iterate hostvars. This gives the same result
- debug:
msg: |-
{% for k,v in hostvars.items() %}
NODE_{{ loop.index0 }}={{ v.api_value.stdout }}
{% endfor %}
run_once: true

Not able to use for loop in Ansible

- name: checking whether symbolic link created or not
{% for item in ['/opt/lsf/bin/bsh','/opt/praveen'] %}
{{ item }}:
debug:
msg: "{{ item }}"
{% endfor %}
Please let me know if anything is wrong in this
Resolved Using Ansible:
You need to create a Playbook.yaml :
---
- hosts: all
become: yes # ask ansible to be a super user (working like sudo )
roles:
- template-module
also Create a role for templates:
In you main.yaml put this code:
- name: checking whether symbolic link created or not
template:
src: test.j2
dest: /tmp/test.yaml
and in you jinaj2 "test.j2" template put this ;
{% for item in ['/opt/lsf/bin/bsh','/opt/praveen'] %}
{{ item }}:
debug:
msg: "{{ item }}"
{% endfor %}
This can hel you to resolve your issue.

Ansible concat vars to string

I've spent most of the day trying to solve this problem and have thus far failed. I am building some playbooks to automate functions in Splunk, and am attempting to convert a list of hosts from an inventory group E.G.
[search_head]
1.2.3.4
5.6.7.8
My expected (desired) result from the debug output of the play should be:
https://1.2.3.4:8089, https://5.6.7.8:8089
I am attempting to complete this by running the following playbook against a running host:
---
- name: Build search head list to initialize the captain
hosts: search_head
remote_user: ansible
vars:
inventory_file: ./inventory-ec2-single-site
search_head_uri: "{{ lookup('template', './bootstrap-sh-deployer.j2') }}"
pre_tasks:
- include_vars:
dir: 'group_vars'
extensions:
- yml
- yaml
tasks:
- name: dump array
debug:
msg: "{{ search_head_uri }}"`
With the template bootstrap-sh-deployer.j2:
{%- set search_head_uri = [] %}
{% for host in groups['search_head'] %}
{%- if search_head_uri.append("https://{{ host }}:8089") %}
{%- endif %}
{%- if not loop.last %}, {% endif -%}
{%- endfor %}
However, the current play returns search_head_uri: ", " which tells me that the loop is running, but {{ host }} is not resolving.
Once you open a Jinja2 expression or a statement you should use Jinja2 syntax. You cannot nest them (i.e. you can't use {{ }} inside {% %}).
{%- if search_head_uri.append("https://" + host + ":8089") %}
This worked - Combination of the answer above to fix jinja formatting and using hostvars to get to the ansible_nodename.
{%- set search_head_uri = [] %}
{% for host in groups['search_head'] %}
{{ "https://" + hostvars[host]['ansible_nodename'] + ":8089" }}
{%- if not loop.last %}, {% endif -%}
{%- endfor %}

Merge 2 groups in Ansible

I have 2 host groups in my inventory in ansible as follows:
[loadbalancer-add]
172.23.130.97
172.23.130.98
[loadbalancer-remove]
172.23.130.99
172.23.130.100
I would like to merge these groups to pass them to a loadbalancer API so I can add a server, then remove a server. So I need to merge the groups to create a group as follows:
[loadbalancer]
172.23.130.97
172.23.130.99
172.23.130.98
172.23.130.100
I have the following task but it is not producing the correct output
- name: Merge Dictionaries
gather_facts: false
hosts: localhost
become: true
no_log: false
tasks:
- add_host:
name: "{{ item }}"
ansible_ssh_port: 2020
action: remove
group: loadbalancer
with_items:
- "{{ groups['loadbalancer-remove'] }}"
- "{{ groups['loadbalancer-add'] }}"
delegate_to: localhost
This produces
[loadbalancer]
172.23.130.99
172.23.130.100
172.23.130.97
172.23.130.98
Is it possible to get the output that I require?
Thanks
May be this is what you need:
[loadbalancer-add]
172.23.130.97
172.23.130.98
[loadbalancer-remove]
172.23.130.99
172.23.130.100
[loadbalancer:children]
loadbalancer-remove
loadbalancer-add
So now you could reference as one group
groups['loadbalancer']*
.
I did manage to find a way of doing this. It may not be the best way but here is my solution anyway:
- name: Merge Dictionaries
gather_facts: false
hosts: localhost
no_log: false
vars:
merged_lb_hosts: |
{% if (groups['loadbalancer-add'] | length) >= (groups['loadbalancer-remove'] | length) %}
{% for i in range(0, groups['loadbalancer-add'] | length) -%}
{{ groups['loadbalancer-add'][i] | default('') }}:add|{{ groups['loadbalancer-remove'][i] | default('') }}:remove|
{%- endfor %}
{% else %}
{% for i in range(0, groups['loadbalancer-remove'] | length) -%}
{{ groups['loadbalancer-add'][i] | default('') }}:add|{{ groups['loadbalancer-remove'][i] | default('') }}:remove|
{%- endfor %}
{% endif %}
tasks:
- debug: msg="{{ merged_lb_hosts }}"
- add_host:
name: "{{ item.split(':')[0] }}"
action: "{{ item.split(':')[1] }}"
ansible_ssh_port: 2020
group: loadbalancer
with_items:
- "{{ (merged_lb_hosts | trim()).split('|') }}"
when: "{{item.split(':')[0] != ''}}"
This will take the add and remove groups and merge them into a var called merged_lb_hosts
The add_host task will split this var in it with_items section to create an iterable list.
It feels very hacky but it does the job that I was after

Vague deprecation error when running ansible playbook

My playbook contains vars that are passed to a role. When I run it, I get [DEPRECATION WARNING]: Skipping task due to undefined Error, in the future this will be a fatal error..
Here's what I have:
---
- hosts: hadoopL0X
become: yes
become_method: sudo
vars:
logrotate_scripts:
- name: "{{ item }}"
with_items:
- zookeeper
- sa
path: "/var/log{{ item }}/{{ item }}.log "
options:
- daily
- rotate 3
- missingok
- compress
- notifempty
roles:
- log-rotation
...
The role is as such:
log-rotation/tasks/main.yml
---
- name: Setup logrotate.d scripts
template:
src: logrotate.d.j2
dest: "{{ logrotate_conf_dir }}{{ item }}"
with_items: "{{ logrotate_scripts }}"
...
log-rotation/defaults/main.yml
---
logrotate_conf_dir: "/etc/logrotate.d/"
logrotate_scripts: []
...
log-rotation/templates/logrotate.d.j2
# {{ ansible_managed }}
"{{ item.path }}" {
{% if item.options is defined -%}
{% for option in item.options -%}
{{ option }}
{% endfor -%}
{% endif %}
{%- if item.scripts is defined -%}
{%- for name, script in item.scripts.iteritems() -%}
{{ name }}
{{ script }}
endscript
{% endfor -%}
{% endif -%}
}
Any help would be much appreciated!
with_items can only be used with tasks, it cannot be used when defining variables, and because of that item isn't defined. It also looks like that the service variable isn't defined as well.

Resources