Ansible: set_fact variables across hosts blocks [duplicate] - ansible

In Ansible 2.1, I have a role being called by a playbook that needs access to a host file variable. Any thoughts on how to access it?
I am trying to access the ansible_ssh_host in the test1 section of the following inventory host file:
[test1]
test-1 ansible_ssh_host=abc.def.ghi.jkl ansible_ssh_port=1212
[test2]
test2-1 ansible_ssh_host=abc.def.ghi.mno ansible_ssh_port=1212
[test3]
test3-1 ansible_ssh_host=abc.def.ghi.pqr ansible_ssh_port=1212
test3-2 ansible_ssh_host=abc.def.ghi.stu ansible_ssh_port=1212
[all:children]
test1
test2
test3
I have tried accessing the role in the following fashions:
{{ hostvars.ansible_ssh_host }}
and
{{ hostvars.test1.ansible_ssh_host }}
I get this error:
fatal: [localhost]: FAILED! => {"failed": true, "msg": "'ansible.vars.hostvars.HostVars object' has no attribute 'ansible'"}

You are on the right track about hostvars.
This magic variable is used to access information about other hosts.
hostvars is a hash with inventory hostnames as keys.
To access fields of each host, use hostvars['test-1'], hostvars['test2-1'], etc.
ansible_ssh_host is deprecated in favor of ansible_host since 2.0.
So you should first remove "_ssh" from inventory hosts arguments (i.e. to become "ansible_user", "ansible_host", and "ansible_port"), then in your role call it with:
{{ hostvars['your_host_group'].ansible_host }}

[host_group]
host-1 ansible_ssh_host=192.168.0.21 node_name=foo
host-2 ansible_ssh_host=192.168.0.22 node_name=bar
[host_group:vars]
custom_var=asdasdasd
You can access host group vars using:
{{ hostvars['host_group'].custom_var }}
If you need a specific value from specific host, you can use:
{{ hostvars[groups['host_group'][0]].node_name }}

You should be able to use the variable name directly
ansible_ssh_host
Or you can go through hostvars without having to specify the host literally
by using the magic variable inventory_hostname
hostvars[inventory_hostname].ansible_ssh_host

I struggled with this, too. My specific setup is: Your host.ini (with the modern names):
[test3]
test3-1 ansible_host=abc.def.ghi.pqr ansible_port=1212
test3-2 ansible_host=abc.def.ghi.stu ansible_port=1212
plus a play fill_file.yml
---
- remote_user: ec2-user
hosts: test3
tasks:
- name: fill file
template:
src: file.j2
dest: filled_file.txt
plus a template file.j2 , like
{% for host in groups['test3'] %}
{{ hostvars[host].ansible_host }}
{% endfor %}
This worked for me, the result is
abc.def.ghi.pqr
abc.def.ghi.stu
I have to admit it's ansible 2.7, not 2.1. The template is a variation of an example in https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html.
The accepted answer didn't work in my setup. With a template
{{ hostvars['test3'].ansible_host }}
my play fails with "AnsibleUndefinedVariable: \"hostvars['test3']\" is undefined" .
Remark: I tried some variations, but failed, occasionally with "ansible.vars.hostvars.HostVars object has no element "; Some of this might be explained by what they say. in https://github.com/ansible/ansible/issues/13343#issuecomment-160992631
hostvars emulates a dictionary [...]. hostvars is also lazily loaded

I've found also a nice and simple way to address hostsvars right on one of Ansible's Github issues
Looks like you can do this as well:
- debug:
msg: "{{ ansible_ssh_host }}"

Thanks a lot this note was very useful for me!
Was able to send the variable defined under /group_var/vars in the ansible playbook
as indicated below.
tasks:
- name: check service account password expiry
- command:
sh /home/monit/get_ldap_attr.sh {{ item }} {{ LDAP_AUTH_USR }}

Related

Unable to access hostvars when the inventory_host is inserted as a variable

I have a jinja2 template, and I'm trying to loop through a host group, and insert the ipv4 address of all the hosts in my template. But I'm getting an error when I do it, even though the way I'm doing it is the way every post and article suggest it should be done.
Here's the template that's producing the error:
{% if groups['linux-hosts'] %}
{% for item in groups['linux-hosts'] %}
define host {
use generic-host-normal
host_name {{ item }}
alias {{ item }}
address {{ hostvars[item].ansible_default_ipv4.address }}
}
{% endfor %}
{% endif %}
And the error I'm getting is:
failed: [server] (item=servers.cfg) => {"changed": false, "item": "servers.cfg", "msg": "AnsibleUndefinedVariable: 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'ansible_default_ipv4'"}
If I don't use the variable 'item' in the square brackets, but instead specify a specific host from the inventory, Ansible is able to get the ipv4 address. Example ('server' is the name of a host from my inventory):
{{ hostvars['server'].ansible_default_ipv4.address }}
I came across this problem with a similar use case. My problem turned out to be that I included hosts in a group which were not the target of the play. Therefore gather facts had not run against all the hosts in the group. I fixed this by running setup against all the hosts I needed like this
- name: get cluster facts
hosts: k8s-cluster
tags:
- always
tasks:
- name:
setup:
become: true
- name: deploy HA Proxy
hosts: kube-master
become: yes
roles:
- { role: ansible-role-haproxy }
Note: kube-master is a subset of k8s-cluster
It's because you are missing the gather_facts: yes or an equivalent - setup: task in your playbook; those facts don't magically appear unless requested, which happens by default, but one can certainly switch off through gather_facts: no
A simple test will show what I mean:
- hosts: all
gather_facts: yes
tasks:
- debug: var=ansible_default_ipv4 verbosity=0
and then change gather_facts: no and observe the kaboom
The solution described here worked for me.
Add an empty play at the top of your playbook to gather facts for all hosts.
Ansible hostvars has no attribute 'ansible_nodename'
I had the same issue.
For me that problem has been that I have run my playbook with the --tags flag.
This lead to the effect that Ansible does not gather any facts!

Ansible wait_for to test firewall openings to a host in the inventory with host alias [duplicate]

In Ansible 2.1, I have a role being called by a playbook that needs access to a host file variable. Any thoughts on how to access it?
I am trying to access the ansible_ssh_host in the test1 section of the following inventory host file:
[test1]
test-1 ansible_ssh_host=abc.def.ghi.jkl ansible_ssh_port=1212
[test2]
test2-1 ansible_ssh_host=abc.def.ghi.mno ansible_ssh_port=1212
[test3]
test3-1 ansible_ssh_host=abc.def.ghi.pqr ansible_ssh_port=1212
test3-2 ansible_ssh_host=abc.def.ghi.stu ansible_ssh_port=1212
[all:children]
test1
test2
test3
I have tried accessing the role in the following fashions:
{{ hostvars.ansible_ssh_host }}
and
{{ hostvars.test1.ansible_ssh_host }}
I get this error:
fatal: [localhost]: FAILED! => {"failed": true, "msg": "'ansible.vars.hostvars.HostVars object' has no attribute 'ansible'"}
You are on the right track about hostvars.
This magic variable is used to access information about other hosts.
hostvars is a hash with inventory hostnames as keys.
To access fields of each host, use hostvars['test-1'], hostvars['test2-1'], etc.
ansible_ssh_host is deprecated in favor of ansible_host since 2.0.
So you should first remove "_ssh" from inventory hosts arguments (i.e. to become "ansible_user", "ansible_host", and "ansible_port"), then in your role call it with:
{{ hostvars['your_host_group'].ansible_host }}
[host_group]
host-1 ansible_ssh_host=192.168.0.21 node_name=foo
host-2 ansible_ssh_host=192.168.0.22 node_name=bar
[host_group:vars]
custom_var=asdasdasd
You can access host group vars using:
{{ hostvars['host_group'].custom_var }}
If you need a specific value from specific host, you can use:
{{ hostvars[groups['host_group'][0]].node_name }}
You should be able to use the variable name directly
ansible_ssh_host
Or you can go through hostvars without having to specify the host literally
by using the magic variable inventory_hostname
hostvars[inventory_hostname].ansible_ssh_host
I struggled with this, too. My specific setup is: Your host.ini (with the modern names):
[test3]
test3-1 ansible_host=abc.def.ghi.pqr ansible_port=1212
test3-2 ansible_host=abc.def.ghi.stu ansible_port=1212
plus a play fill_file.yml
---
- remote_user: ec2-user
hosts: test3
tasks:
- name: fill file
template:
src: file.j2
dest: filled_file.txt
plus a template file.j2 , like
{% for host in groups['test3'] %}
{{ hostvars[host].ansible_host }}
{% endfor %}
This worked for me, the result is
abc.def.ghi.pqr
abc.def.ghi.stu
I have to admit it's ansible 2.7, not 2.1. The template is a variation of an example in https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html.
The accepted answer didn't work in my setup. With a template
{{ hostvars['test3'].ansible_host }}
my play fails with "AnsibleUndefinedVariable: \"hostvars['test3']\" is undefined" .
Remark: I tried some variations, but failed, occasionally with "ansible.vars.hostvars.HostVars object has no element "; Some of this might be explained by what they say. in https://github.com/ansible/ansible/issues/13343#issuecomment-160992631
hostvars emulates a dictionary [...]. hostvars is also lazily loaded
I've found also a nice and simple way to address hostsvars right on one of Ansible's Github issues
Looks like you can do this as well:
- debug:
msg: "{{ ansible_ssh_host }}"
Thanks a lot this note was very useful for me!
Was able to send the variable defined under /group_var/vars in the ansible playbook
as indicated below.
tasks:
- name: check service account password expiry
- command:
sh /home/monit/get_ldap_attr.sh {{ item }} {{ LDAP_AUTH_USR }}

Ansible - use fact from local host in remote host template

I have a playbook that contains roles for localhost and roles for remote hosts.
In one of the localhost roles I set a fact called git_tag.
I want to use this fact in a template for the remote hosts.
I tried:
- name: Read Version
set_fact:
git_tag: "{{ package_json.stdout | from_json | json_query('version')}}"
delegate_to: "test-server"
But when Ansible reaches the role that reads the template that has {{ git_tag }} it says that git_tag is undefined.
I'm sure I'm doing something wrong. How can I do it?
You should use a hostvars magic variable:
{{ hostvars['localhost']['git_tag'] }}
you can use this
{{ hostvars['test-server']['git_tag'] }}

Accessing inventory host variable in Ansible playbook

In Ansible 2.1, I have a role being called by a playbook that needs access to a host file variable. Any thoughts on how to access it?
I am trying to access the ansible_ssh_host in the test1 section of the following inventory host file:
[test1]
test-1 ansible_ssh_host=abc.def.ghi.jkl ansible_ssh_port=1212
[test2]
test2-1 ansible_ssh_host=abc.def.ghi.mno ansible_ssh_port=1212
[test3]
test3-1 ansible_ssh_host=abc.def.ghi.pqr ansible_ssh_port=1212
test3-2 ansible_ssh_host=abc.def.ghi.stu ansible_ssh_port=1212
[all:children]
test1
test2
test3
I have tried accessing the role in the following fashions:
{{ hostvars.ansible_ssh_host }}
and
{{ hostvars.test1.ansible_ssh_host }}
I get this error:
fatal: [localhost]: FAILED! => {"failed": true, "msg": "'ansible.vars.hostvars.HostVars object' has no attribute 'ansible'"}
You are on the right track about hostvars.
This magic variable is used to access information about other hosts.
hostvars is a hash with inventory hostnames as keys.
To access fields of each host, use hostvars['test-1'], hostvars['test2-1'], etc.
ansible_ssh_host is deprecated in favor of ansible_host since 2.0.
So you should first remove "_ssh" from inventory hosts arguments (i.e. to become "ansible_user", "ansible_host", and "ansible_port"), then in your role call it with:
{{ hostvars['your_host_group'].ansible_host }}
[host_group]
host-1 ansible_ssh_host=192.168.0.21 node_name=foo
host-2 ansible_ssh_host=192.168.0.22 node_name=bar
[host_group:vars]
custom_var=asdasdasd
You can access host group vars using:
{{ hostvars['host_group'].custom_var }}
If you need a specific value from specific host, you can use:
{{ hostvars[groups['host_group'][0]].node_name }}
You should be able to use the variable name directly
ansible_ssh_host
Or you can go through hostvars without having to specify the host literally
by using the magic variable inventory_hostname
hostvars[inventory_hostname].ansible_ssh_host
I struggled with this, too. My specific setup is: Your host.ini (with the modern names):
[test3]
test3-1 ansible_host=abc.def.ghi.pqr ansible_port=1212
test3-2 ansible_host=abc.def.ghi.stu ansible_port=1212
plus a play fill_file.yml
---
- remote_user: ec2-user
hosts: test3
tasks:
- name: fill file
template:
src: file.j2
dest: filled_file.txt
plus a template file.j2 , like
{% for host in groups['test3'] %}
{{ hostvars[host].ansible_host }}
{% endfor %}
This worked for me, the result is
abc.def.ghi.pqr
abc.def.ghi.stu
I have to admit it's ansible 2.7, not 2.1. The template is a variation of an example in https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html.
The accepted answer didn't work in my setup. With a template
{{ hostvars['test3'].ansible_host }}
my play fails with "AnsibleUndefinedVariable: \"hostvars['test3']\" is undefined" .
Remark: I tried some variations, but failed, occasionally with "ansible.vars.hostvars.HostVars object has no element "; Some of this might be explained by what they say. in https://github.com/ansible/ansible/issues/13343#issuecomment-160992631
hostvars emulates a dictionary [...]. hostvars is also lazily loaded
I've found also a nice and simple way to address hostsvars right on one of Ansible's Github issues
Looks like you can do this as well:
- debug:
msg: "{{ ansible_ssh_host }}"
Thanks a lot this note was very useful for me!
Was able to send the variable defined under /group_var/vars in the ansible playbook
as indicated below.
tasks:
- name: check service account password expiry
- command:
sh /home/monit/get_ldap_attr.sh {{ item }} {{ LDAP_AUTH_USR }}

Save playbook information to hosts file

I have a playbook that spins up a new droplet on DigitalOcean using the core module built into Ansible:
- name: Provision droplet on DigitalOcean
local_action: digital_ocean
state=present
ssh_key_ids=1234
name=mydroplet
client_id=ABC
api_key=ABC
size_id=1
region_id=2
image_id=3
wait_timeout=500
register: my_droplet
- debug: msg="ID is {{ my_droplet.droplet.id }}"
- debug: msg="Your droplet has the IP address of {{ my_droplet.droplet.ip_address }}"
I run this using (note the local argument):
ansible-playbook playbooks/create_droplet.yml -c local -i playbooks/hosts
My hosts file initially looks like this:
[production]
TBA
[localhost]
localhost
When the above playbook finishes I can see the debug information in STDOUT:
ok: [localhost] => {
"msg": "Your droplet has the IP address of 255.255.255.255"
}
Is there any way for this playbook to retain the my_droplet.ip_address variable and save the TBA in the hosts file instead of having to manually copy-pasta it there? I ask because I want to add this provisioning playbook to a ruby script that subsequently bootstraps the VPS with another playbook.
I am doing the same, having written a play that creates servers from a dict (approximately 53 servers at a go, dynamically creating a full test environment). To use a static hosts file, I add the following:
- name: Create in-memory inventory
add_host:
name: "{{ item.value.ServerName }}"
groups: "{{ item.value.RoleTag }},tag_Environment_{{ env_name }}"
when: item.value.template is defined
with_dict: server_details
- name: create file
become: yes
template:
src: ansible-hosts.j2
dest: "{{ wm_inventory_file }}"
The ansible-hosts.j2 template is simply:
{% for item in groups %}
[{{item}}]
{% for entry in groups[item] %}
{{ entry }}
{% endfor %}
{% endfor %}
I'm launching instances with ec2, and I originally wanted to do the same thing. I found some examples of using lineinfile and massaging it to the do the same thing as so:
- name: Launching new instances for {{ application }}
local_action: ec2 group={{ security_group }} instance_type={{ instance_type}} image={{ image }} wait=true region={{ region }} keypair={{ keypair }} vpc_subnet_id={{ subnet }} count={{ instance_count }}
register: ec2
- name: Add instance to local host group
local_action: lineinfile dest=ansible_hosts regexp="{{ item.public_ip }}" insertafter="\[{{ application }}\]" line="{{ item.public_ip }} ansible_ssh_private_key_file=~/ec2-keys/{{ keypair }}.pem" state=present
with_items: ec2.instances
But, I have to agree that it's generally not something you want to do. I found it to be quite a pain. I've since switched to using add_host and life is much better. BTW, application would be the value I used for the group name...
Is there any way for this playbook to retain the my_droplet.ip_address
variable and save the TBA in the hosts file instead of having to
manually copy-pasta it there?
You can retain the ip address of your new host by using the add_host module which allows you to dynamically change the in-memory inventory during an ansible-playbook run. This is useful for when you want to provision a new host and then configure it in a single playbook.
For example
local_action: >
add_host
hostname={{ my_droplet.droplet.id }}
groupname=launched
And then later in your playbook:
- name: Configure instance(s)
hosts: launched
tasks:
...
the second part of your questions:
... and save the TBA in the hosts file instead of having to
manually copy-pasta it there?
There is no built-in ansible way to write additions to the inventory file that is on disk. This is generally not something you want to do. In this case you would need to add it or use a dynamic inventory script to discover the host for future configuration runs.
You should use a dynamic inventory script for this. Using name to distinguish instances, you can subsequently refer to your droplets.
Check https://github.com/ansible/ansible/blob/devel/contrib/inventory/digital_ocean.py for an example script.

Resources