Ansible concat vars to string - ansible

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

Related

How to exclude an host in Ansible Jinja2 template for-loop?

I have a template named foo.yml.j2 used in an Ansible task to generate a foo.yml file:
{% for host in ansible_play_hosts_all %}
{{ host }},
{% endfor %}
Everything works fine except I need something like the following statement: For every host in ansible_play_hosts_all except for host==bar do this or that.
Is this achievable or the only way to do this is to categorize my hosts in different groups and use ansible_play_hosts_group?
You can use the reject filter to take your host out of the list so that it is not part of your loop:
{% for host in ansible_play_hosts_all | reject('==', 'bar') %}
{{ host }},
{% endfor %}
There are more options. The trivial one is using the condition in Jinja
{% for host in ansible_play_hosts_all %}
{% if host != 'bar' %}
{{ host }},
{% endif %}
{% endfor %}
Fit the format to your needs.
Example of a complete playbook for testing
- hosts: host_1,host_2,host_3
gather_facts: false
tasks:
- debug:
msg: |
{% for host in ansible_play_hosts_all %}
{% if host != 'host_2' %}
{{ host }},
{% endif %}
{% endfor %}
run_once: true
gives (abridged)
msg: |-
host_1,
host_3,
The next option is to remove blacklisted hosts from the loop, e.g.
blacklist: [host_2]
The template below gives the same result
{% for host in ansible_play_hosts_all|difference(blacklist) %}
{{ host }},
{% endfor %}

ansible match exact string from register result

I have below ansible tasks which compares list of packages against register output obtained from yum module.
- hosts: localhost
vars:
pkgs: [ 'git', 'rsync', 'python36' ]
tasks:
- name: stats for installed pkgs
yum:
state: installed
register: installed
- name: Compare against register
set_fact:
unwanted_pkgs: >-
[
{% for value in installed.results %}
{% for pkg in pkgs %}
{% if value.name | regex_search(pkg) %}
" {{ value.name }}",
{% endif %}
{% endfor %}
{% endfor %}
]
- name: print
debug:
msg: "{{ unwanted_pkgs }}"
Upon executing above playbook, debug task print output as :
git
git-core
git-core-doc
python36
python36-devel
While executing the task it prints additional packages (eg: git-core, git-core-doc and python36-devel which I have not defined in pkgs variable). How this can be avoided ?
Is this what you want?
- debug:
msg: |-
{% for value in installed.results %}
{% for pkg in pkgs %}
{% if value.name == pkg %}
{{ value.name }}
{% endif %}
{% endfor %}
{% endfor %}
gives
msg: |-
git
python36
The same can be achieved by intersecting the lists
unwanted_pkgs: "{{ installed.results|map(attribute='name')|intersect(pkgs) }}"
gives
unwanted_pkgs:
- git
- python36

"AnsibleError: template error while templating string: unexpected 'end of statement block'

I have a jinja2 template that looks like this:
token: {{ RANCHER_SERVER_TOKEN}}
server: https://{{ RANCHER_RKE2_SERVER }}:9345
{% endif %}{% if RANCHER_TLS_SAN is defined %}
tls-san:{% for item in RANCHER_TLS_SAN +%}
- {{ item }}{% endfor +%}
{% endif %}
{% if RANCHER_NODE_LABELS is defined %}
node-label:{% for item in RANCHER_NODE_LABELS +%}
- {{ item }}{% endfor +%}
{% endif %}
{% if RANCHER_NODE_TAINTS is defined %}
node-taint:{% for item in RANCHER_NODE_TAINTS +%}
- {{ item }}{% endfor +%}
{% endif %}
in my inventory group in group_vars I have this data structure
---
RANCHER_TLS_SAN:
- k3-crontroller-1.prometheus.internal
RANCHER_NODE_TAINTS:
- "CriticalAddonsOnly=true:NoExecute"
I test it through this link: https://ansible.sivel.net/test/
and it renders out great.
However I get this issue when running the playbook:
fatal: [k3-rancher-controller-1]: FAILED! => {"changed": false, "msg": "AnsibleError: template error while templating string: unexpected 'end of statement block'. String: {%if RANCHER_DOWNSTREAM_SERVER_AGENT_NODE is defined %}\ntoken: {{ RANCHER_SERVER_TOKEN }}\nserver: https://{{ RANCHER_RKE2_SERVER }}:9345\n{% endif %}{% if RANCHER_TLS_SAN is defined %}\ntls-san:{% for item in RANCHER_TLS_SAN +%}\n - {{ item }}{% endfor %}\n{% endif %}\n{% if RANCHER_NODE_LABELS is defined %}\nnode-label:{% for item in RANCHER_NODE_LABELS +%}\n - {{ item }}{% endfor %}\n{% endif %}\n{% if RANCHER_NODE_TAINTS is defined +%}\nnode-taint:{% for item in RANCHER_NODE_TAINTS +%}\n - {{ item }}{% endfor %}\n{% endif %}"}
My playbook looks like this:
---
- name: Create VM for Rancher Kubernetes Control Plane Nodes
hosts: master
gather_facts: no
become: yes
become_user: root
tasks:
- name: create directory /etc/rancher/rke2
file:
path: /etc/rancher/rke2
state: directory
- name: Create template and copy to Agent nodes
template:
src: playbooks/templates/config.j2
dest: /etc/rancher/rke2/config.yaml
I am using ansible 2.9.27.
Does anyone know what is going on?
Any help is much appreciated. Many thanks

How to set a value in defaults/main.yml dynamically from inventory

I have a dynamic inventory for my project and on deployment the host_groups get loaded with inventory list. Meantime, I want to set a variable named master_ip with value from host_vars [master] in a role/defaults/main.yml.
My hosts are grouped as below:
[master]
xx.eco.project.com
[slave]
yy.eco.project.com
zz.eco.project.com
As i cannot use if/else in YAML. Like in templates i use,
{% if inventory_hostname in groups['master'] %}
master_ip: {{ lookup('dig', inventory_hostname) }}
as master_ip: 10.0.1.1. How is it possible?
I have solved this issue by defining a macro function in templates to determine the master and return the value to the template variables for master_ip. I find this more meaningful than the initial idea of getting the value in defaults/main.yml .
{% macro master_ip() -%}
{% for inventory_hostname in groups['master'] -%}
{{ lookup('dig', inventory_hostname) }}
{%- endfor -%}
{% endmacro -%}
Use in template
bind {{ master_ip() }}

How to set concatenated string variable in Ansible template based on a condition

I need to create a string in a template that can change between hosts, and it needs to be in the form of:
"cores": "0,1,2,3"
And the reason the string is "0,1,2,3" in this example is because the host has 4 processor cores.
So I got stuck with something which seems too convoluted to me and I'm not even sure how to use this core_count variable in my template file.
{% set core_count = '' %}
{% for i in range(ansible_processor_cores) %}
{% set core_count = core_count ~ i %}
{% if not loop.last %}
{% set core_count = core_count ~ ',' %}
{% endif %}
{% endfor %}
There are many handy lookup plugins in Ansible. Take sequence:
- hosts: localhost
gather_facts: yes
tasks:
- debug:
msg: '"cores": "{{ lookup("sequence","start=0 count="+(ansible_processor_cores|string)) }}"'

Resources