jinja2 - AnsibleUndefinedVariable: 'dict object' has no attribute '"{{ target_hosts }}"'" - ansible

This is my Ansible playbook to update /etc/hosts file:
- name: Update /etc/hosts file
hosts: "{{ target_hosts }}"
remote_user: awx
become: yes
become_method: sudo
tasks:
- debug:
msg: 'show me the variable: {{ target_hosts }}'
- name: Update /etc/hosts file
template: src=../../templates/hosts.j2 dest=/etc/hosts
And this is the jinja template:
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
{% for item in groups['"{{ target_hosts }}"'] %}
{{ hostvars[item]['ansible_ssh_host'] }} {{ hostvars[item]['openstack']['name'] }}
{% endfor %}
Everything works fine if I put a static value in the template (for example for item in groups['my-server-group']), but I would like to use a variable, passed dinamically to the playbook.
The error I get is:
AnsibleUndefinedVariable: 'dict object' has no attribute '\"{{ target_hosts }}\"'"
With the debug msg I'm sure that the playbook gets the parameter:
> "msg": "show me the variable: my-server-group".
Maybe the j2 template doesn't?
Is the syntax wrong? I tried both with quotes, double quotes and combination of two.

Is the syntax wrong?
Yes. Don't nest Jinja2 expressions. In your case {{ inside of {%.
Correct syntax:
{% for item in groups[target_hosts] %}

name: Update /etc/hosts file
hosts: "{{ target_hosts }}"
remote_user: awx
become: yes
become_method: sudo
tasks:

Related

Ansible Looping over dynamic inventory using jinja template

Here is my Ansible playbook. It reads /etc/waagent.conf file and checks if variable AutoUpdate.Enabled=y or not.And it uses Jinja template to generate output.csv file.
ansibleuser#server:~/plays$ cat report_waagent_local.yaml
---
- name: waagent auto update report
hosts: localhost
connection: ssh
remote_user: ewxxxx
become: true
become_user: root
gather_facts: true
tasks:
- name: "Ensure status of AutoUpdate.Enabled in /etc/waagent.conf"
lineinfile:
name: /etc/waagent.conf
line: AutoUpdate.Enabled=y
state: present
check_mode: yes #means make no change , just check
register: conf
failed_when: (conf is changed) or (conf is failed)
ignore_errors: yes
# logic
# if "conf.changed": false --> that mean AutoUpdate.Enabled=y
# if "conf.changed": true --> that means value is not set in file.
- name: generate report
template:
src: report_waagent_local.j2
dest: ./output.csv
ansibleuser#server:~/plays$
And here is Jinja Template.
ansibleuser#server:~/plays$ cat templates/report_waagent_local.j2
{% if conf.changed == false %}
{{ ansible_host }} , AutoUpdate.Enabled=y
{% else %}
{{ ansible_host }} , AutoUpdate.Enabled=n
{% endif %}
ansibleuser#server:~/plays$
It produces output.csv as expected.
127.0.0.1, AutoUpdate.Enabled=y
Now, I need to fetch similar reports for all the servers present in the Azure subscription.
I modified my playbook. Note: I am using dynamic inventory in azure, I have a group named "all_pls" on which I need to run a playbook.
ansibleuser#server:~/plays$ cat report_waagent.yaml
---
- name: "generate waagent report"
hosts: all
connection: ssh
remote_user : ewxxxxx
become: True
become_user: root
gather_facts: True
tasks:
- name: "Ensure status of AutoUpdate.Enabled in /etc/waagent.conf"
lineinfile:
name: /etc/waagent.conf
line: AutoUpdate.Enabled=y
state: present
check_mode: yes #means make no change , just check
register: conf
failed_when: (conf is changed) or (conf is failed)
ignore_errors: yes
# if "conf.changed": false --> that mean AutoUpdate.Enabled=y
# if "conf.changed": true --> that means the value is not set in the file.
- name: generate report
template:
src: report_waagent_local.j2
dest: ./output.csv
ansibleuser#server:~/plays$
I am running my playbook and getting no issues.
But I am getting no output in output.csv.
ansible-playbook --limit all_pls report_waagent.yaml
I guess I need to loop over hosts in a group name and also check conf.changed in the Jinja template. Can someone help please?
I fixed the issue.
- name: log conf.changed in output.csv using a template
lineinfile:
line: "{{ lookup('template', 'report_waagent_local.j2') }}"
insertafter: EOF
dest: output.csv
delegate_to: localhost

populate yaml variable from jinja variable

I want to populate the nifi.web.https.host below after I run the below playbook. I am new to ansible jinja/yaml so I am not sure why is not working.
{% set external_ip = 'curl 169.254.169.254/2009-04-04/meta-data/public-ipv4' %}
- hosts: localhost
become: yes
roles:
- my.nifi
vars:
nifi_properties:
# HTTPS properties
nifi.web.https.host: 'external_ip'
nifi.web.https.port: 8443
nifi.web.https.network.interface.default: eth0
After I run the playbook I get this error:
{% set external_ip = 'curl 169.254.169.254/2009-04-04/meta-data/public-ipv4' %}
^ here
- hosts: localhost
become: yes
roles:
- my.nifi
pre_tasks:
- name: Get EC2 public IP
raw: curl http://169.254.169.254/2009-04-04/meta-data/public-ipv4
register: ec2_public_ip
vars:
nifi_properties:
# HTTPS properties
nifi.web.https.host: "{{ ec2_public_ip.stdout }}"
nifi.web.https.port: 8443
nifi.web.https.network.interface.default: eth0

Ansible lineinfile append

I have a hosts inventory file. That has
url.example.com hostnames='["hostname1.example.com", "hostname2.example.com"]'
Im trying to loop over it to add all the hostnames to /etc/hosts file
I am trying to use lineinfile, but how can I get it to append all of the hostnames to one specific line
line: "{{ ansible_default_ipv4.address }} {{ inventory_hostname }} {{ inventory_hostname_short}}" + append item here
with_items: "{{ hostnames }}"
state: present
How can i append all the items in the end of the line.
The iteration is not needed, I think. Is this what you're looking for?
line: "{{ ansible_default_ipv4.address }}
{{ inventory_hostname }}
{{ inventory_hostname_short}}
{{ hostnames|join(' ') }}"
For example, given the file
shell> cat hosts
10.1.0.27 localhost localhost
The playbook
shell> cat playbook.yml
- hosts: localhost
vars:
hostnames: [hostname1.example.com, hostname2.example.com]
tasks:
- lineinfile:
path: hosts
regex: '^{{ ansible_default_ipv4.address }}\s+(.*)$'
line: "{{ ansible_default_ipv4.address }}
{{ inventory_hostname }}
{{ inventory_hostname_short}}
{{ hostnames|join(' ') }}"
works as expected
shell> ansible-playbook playbook.yml -CD
TASK [lineinfile] ***************************************************************
--- before: hosts (content)
+++ after: hosts (content)
## -1 +1 ##
-10.1.0.27 localhost localhost
+10.1.0.27 localhost localhost hostname1.example.com hostname2.example.com
changed: [localhost]

Ansible search sublists for value

A webhook triggers an AWX job and I want to run the deployment on a certain host depending on the service, since they run on different servers. I need to know which server uses that service to set is as a var so it can be used as a host in the following play.
My variable inside vars.yaml looks like this:
staging_hosts:
server1: ['service1', 'service2', 'service3']
server2: ['service4', 'service5', 'service6']
server3: ['service7', 'service8', 'service9']
Playbook:
- name: write deployment hosts
hosts: localhost
vars:
deployment_hosts: absent
vars_files:
- ./group_vars/vars.yaml
tasks:
- set_fact:
modified_repos: (small regex filter to find modified repository)
- set_fact:
deployment_hosts: "{{ item }}"
when: '{{ modified_repos }} in {{ item }}'
with_list:
- "{{ staging_hosts }}"
- name: connect to Cluster
hosts: "{{ hostvars['localhost']['deployment_hosts'] }}"
What can I do against this warning and error?
[WARNING]: conditional statements should not include jinja2 templating
delimiters such as {{ }} or {% %}. Found: {{ modified_repos }} in {{ item }}
fatal: [localhost]: FAILED! => {"msg": "The conditional check '{{ modified_repos }} in {{ item }}' failed. True {% else %} False {% endif %}): unhashable type: 'list'
Oh I forgot to mention. It is important, that deployment_hosts could also contain two hosts if modified repos include for example service1 and service4.
Q: "deployment_hosts could also contain two hosts if modified repos include for example service1 and service4."
A: Use intersect filter. For example, the playbook
- hosts: localhost
vars:
staging_hosts:
server1: ['service1', 'service2', 'service3']
server2: ['service4', 'service5', 'service6']
server3: ['service7', 'service8', 'service9']
modified_repos: ['service1', 'service4']
tasks:
- set_fact:
deployment_hosts: "{{ deployment_hosts|default([]) + [item.key] }}"
loop: "{{ staging_hosts|dict2items }}"
when: modified_repos|intersect(item.value)|length > 0
- debug:
var: deployment_hosts
gives
deployment_hosts:
- server1
- server2

How to reuse an Ansible variable inside jinja2 template?

I've got the following:
Ansible playbook:
-
hosts: all
gather_facts: true
#remote_user: ansible
tasks:
-
name: "check dir size"
shell: "du -sh /tmp/service/log"
register: size
-
debug:
msg: "{{ size.stdout }}"
-
template:
src: template2.j2
dest: ./file.out
delegate_to: localhost
run_once: true
template2.j2
{% for i in groups['all'] %}
{{ loop.index0 }} {{ hostvars[i].inventory_hostname }} {{ size.stdout }}
{% endfor %}
Result:
$ cat file.out
0 host00 1.7G /tmp/service/log
1 host11 1.7G /tmp/service/log
2 host12 1.7G /tmp/service/log
In the output file the directory size remains constant.
How can I check the size of a directory for each host in the jinja2 loop?
template2.j2 is being templated at localhost (because of delegate_to: localhost), so size is for localhost.
You might want to use hostvars[i].size.stdout, as you use for inventory_hostname.

Resources