Ansible hostvars variable undefined when debugging/templating etc - ansible

I'm trying to dynamically generate part of an /etc/hosts file with ansible, by gathering facts from all the hosts and looping through the resulting hostvars to grab the IP of the second interface (I need a private IP on this interface, rather than the public IP of the other interface)
I can grab this information from a single host using the following plays:
- name: play 1
hosts: all
- name: play 2
hosts: localhost
connection: local
become: no
tasks:
- debug:
var: hostvars['mysinglehost'].ansible_all_ipv4_addresses[1]
...but what I'd like to do is loop through all the hosts and get this value from every host, eventually writing this information to my hosts file, but I'd settle for just getting some debug information :)
I tried
...
- debug:
var: hostvars[item].ansible_all_ipv4_addresses[1]
with_inventory_hostnames:
- all
...
...which gives the output I expect, yet when I try to output a msg with this debug task:
...
- debug:
msg: "{{ hostvars[item].ansible_all_ipv4_addresses[1] }}"
with_inventory_hostnames:
- all
...
I get the following error (as I do when I attempt to e.g. write to a file using lineinfile):
fatal: [localhost]: FAILED! =>
msg: |-
The task includes an option with an undefined variable. The error was: 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'ansible_all_ipv4_addresses'
I'm not sure why this is happening, as it's trying to reference the exact same variable. Is there a way to do this?

I've not tested this, but in principle, perhaps this would work?
- hosts: all
- hosts: localhost
gather_facts: false
tasks:
- set_fact:
my_hosts: |
{
{% for a_host in hostvars | dict2items %}
"{{ a_host.key }}": "{{ a_host.value.ansible_all_ipv4_addresses[1] }}",
{% endfor %}
}
Then, instead of looping over hostvars, you just loop over my_hosts?

Related

How do I access values of Ansible Facts when using a loop?

This works when I don't use a loop:
---
- hosts: localhost
tasks:
- name: Get a cert from gRPC port
get_certificate:
host: "labrouter.abc.com"
port: 50051
delegate_to: localhost
run_once: true
ignore_errors: yes
register: cert
- name: Get Cert Data
debug:
msg: "Not_After: {{ cert.not_after }}"
Output:
TASK [Get Cert Data] ****************************************
ok: [localhost] => {
"msg": "Not After: 20221210143235Z"
}
However, when I try to loop through my hosts like this:
---
- hosts: localhost
tasks:
- name: Get a cert from gRPC port
get_certificate:
host: "{{ item }}"
port: 50051
delegate_to: localhost
run_once: true
ignore_errors: yes
register: cert
loop: "{{ groups['lab'] }}"
- name: Get Cert Data
debug:
msg: "Not After: {{ cert.not_after }}"
I get this error:
The error was: 'dict object' has no attribute 'not_after'
My inventory file contains a single group called "lab" with one device "labrouter.abc.com" associated to it.
I want to be able to add more devices to the "lab" group and run this playbook so I can get the "not_after" value for each device. I can't figure out how to access the "not_after" value for each item in the loop.
Quoting the documentation:
When you use register with a loop, the data structure placed in the variable will contain a results attribute that is a list of all responses from the module. This differs from the data structure returned when using register without a loop
The easiest way to fix your actual problem is to loop over the results:
- name: Get Cert Data
debug:
msg: "Not After: {{ item.not_after }}"
loop: "{{ cert.results }}"
Although this is a direct answer to your question, if the final goal is to take actions on each target in the lab group, see #Jack's answer and drop your loop.
Stop looping, and have Ansible do its implicit looping over the hosts:
---
- hosts: lab
tasks:
- name: Get a cert from gRPC port
get_certificate:
host: "{{ ansible_host }}"
port: 50051
delegate_to: localhost
ignore_errors: yes
register: cert
- name: Get Cert Data
debug:
msg: "Not After: {{ cert.not_after }}"
(As a general "best practices" thing, if you are looping over hosts, there's probably a better way to do it by setting the hosts value for the play to those hosts, and letting Ansible do its implicit looping.)

access a variable in a variable

I am 90% sure this doesn't work because i'm doing it the wrong way, but i can't figure out what is the "right way",I hope you can get my point :
I am trying to access an ipv4 of a certain interface,I have in my hosts file interface_lan = enp4s0 because i need it in a role,so i thought i might just use it to have the IP address of that interface :
"{{hostvars[inventory_hostname]['ansible_{{interface_lan}']['ipv4.address']}}"
with that command,he is looking for "ansible_{{interface_lan}}" but i want him to look for
"ansible_"{{interface_lan}}"" and to consider "{{interface_lan}}" as a variable,not as a string.
I tried my best to explain,sorry if you did not understand you are free to enjoy the rest of your day without helping me, i have been ignoring this line for a few days now.
thank you !
Concatenate the name of the attribute an use it in the index. For example
- hosts: localhost
gather_facts: false
vars:
interface_lan: enp4s0
ansible_enp4s0:
ipv4:
address: 10.1.0.10
tasks:
- set_fact:
ansible_enp4s0: "{{ ansible_enp4s0 }}"
- debug:
msg: "{{ hostvars[inventory_hostname][my_ifc]['ipv4']['address'] }}"
vars:
my_ifc: "{{ 'ansible_' ~ interface_lan }}"
gives (abridged)
msg: 10.1.0.10
Note: set_fact is needed in the example to put the dictionary ansible_enp4s0 into the hostvars.
The indirect addressing of variables without hostvars is possible with the lookup plugin vars. For example
- hosts: localhost
vars:
test_eth0: 10.1.0.10
test_eth1: 10.1.0.11
tasks:
- debug:
msg: "{{ item }}: {{ lookup('vars', 'test_' ~ item ) }}"
loop:
- eth0
- eth1
gives (abridged)
msg: 'eth0: 10.1.0.10'
msg: 'eth1: 10.1.0.11'

Filter hostvar by special propery

I have host.yml like this
---
all:
hosts:
server-a:
server_dc: "Hetzner"
ansible_host: 192.168.1.1
server-b:
server_dc: "OVH"
ansible_host: 192.168.1.2
And play book debug is:
- name: sample
debug:
var: hostvars
And all hostvars debug success.
How to get same hostvars variable but filtered. Any of that server_dc is equal OVH
I dont want to iterate for template, i just one new filtered variable that contain all other properies.
I need another variable that i debug see this output:
['server-b']
This I believe meets your requirement (removing 'no_log: true', will result in the complete dictionary being printed in your playbook output):
- set_fact:
filtered_hosts: "{{ filtered_hosts | default({}) | combine({item.key: item.value}) }}"
when: "item.value.server_dc == 'OVH'"
with_dict: "{{ hostvars }}"
no_log: true
- debug:
var: filtered_hosts

Return Variable from Included Ansible Playbook

I have seen how to register variables within tasks in an ansible playbook and then use those variables elsewhere in the same playbook, but can you register a variable in an included playbook and then access those variables back in the original playbook?
Here is what I am trying to accomplish:
This is my main playbook:
- include: sub-playbook.yml job_url="http://some-jenkins-job"
- hosts: localhost
roles:
- some_role
sub-playbook.yml:
---
- hosts: localhost
tasks:
- name: Collect info from Jenkins Job
script: whatever.py --url "{{ job_url }}"
register: jenkins_artifacts
I'd like to be able to access the jenkins_artifacts results back in main_playbook if possible. I know you can access it from other hosts in the same playbook like this: "{{ hostvars['localhost']['jenkins_artifacts'].stdout_lines }}"
Is it the same idea for sharing across playbooks?
I'm confused what this question is about. Just use the variable name jenkins_artifacts:
- include: sub-playbook.yml job_url="http://some-jenkins-job"
- hosts: localhost
debug:
var: jenkins_artifacts
This might seem complicated but I love doing this in my Playbooks:
rc defines the name of the variable which contains the return value
ar gives the arguments to the include tasks
master.yml:
- name: verify_os
include_tasks: "verify_os/main.yml"
vars:
verify_os:
rc: "isos_present"
ar:
image: "{{ os.ar.to_os }}"
verify_os/main.yml:
---
- name: check image on device
ios_command:
commands:
- "sh bootflash: | inc {{ verify_os.ar.image }}"
register: image_check
- name: check if available
shell: "printf '{{ image_check.stdout_lines[0][0] }}\n' | grep {{ verify_os.ar.image }} | wc -l"
register: image_available
delegate_to: localhost
- set_fact: { "{{ verify_os.rc }}": "{{ true if image_available.stdout == '1' else false }}" }
...
I can now use the isos_present variable anywhere in the master.yml to access the returned value.

Ansible: How can I access a variable of other host?

How can I access a variable of other host? I'd like to access the slack_token varaiable of my localhost on the working_host.
- hosts: localhost
vars:
slack_token: 123123123
tasks:
- block:
- name: test
debug: msg="{{ slack_token }}"
- hosts: "{{ working_host }}"
vars:
slack_token: "{{ hostvars['localhost']['slack_token'] }}"
tasks:
- block:
- name: test2
debug: msg={{ slack_token }}
The error message:
fatal: [localhost]: FAILED! => {"failed": true, "msg": "the field
'args' has an invalid value, which appears to include a variable that
is undefined. The error was: {{ hostvars['localhost']['slack_token']
}}: 'dict object' has no attribute 'slack_token'
Any idea?
Just answered a somewhat same question in my previous post.
Here's what I used:
set_fact:
myVar: "{{ hostvars[groups['all'][0]]['slack_token'] | default(False) }}"
But you're using two plays in a playbook.
You can also try to copy a file to a machine stating the fact.
To access slack_token from everywhere, either:
pass it as extra variable with -e slack_token=zzzz
define it in your inventory under all group

Resources