Looping over ansible_hosts from inventory - ansible

I have a bunch of root servers with different IP addresses. I'm trying to configure the ufw firewall on the server with MySQL server to only allow access from my servers (might change to webservers later) with Ansible. Initially, I only had the FQDNs in the inventory but added the ansible_host IPs because the firewall is not going to resolve the host names (makes sense).
Unfortunately I do not know how to access the ansible_host in the loop query("inventory_hostnames", "all")
My inventory:
all:
vars:
ansible_ssh_user: me
children:
sqlserver:
hosts:
sqlserver.my-domain.de:
ansible_ssh_user: mysql_me_user
ansible_host: 1.1.1.1
webserver:
hosts:
webserver1.my-domain.de:
ansible_host: 2.2.2.2
webserver2.my-domain.de:
ansible_host: 3.3.3.3
now I am trying to loop in the playbook:
- hosts: '{{target|default("sqlserver")}}'
roles:
- { name: oefenweb.ufw, become: yes } # needs root but does not define become by itself...
vars:
ufw_logging: true
ufw_rules:
- rule: allow
to_port: 22
protocol: tcp
tasks:
- name: open MySQL for servers in my
ufw:
rule: allow
to_port: 3306
protocol: tcp
from_ip: '{{ item }}'
loop: '{{ hosts|default(query("inventory_hostnames", "all")) }}'
tags: test
become: true

There are many options. Make your choice depending on the use case.
For example, the play below
shell> cat pb.yml
- hosts: '{{ target|default("sqlserver") }}'
tasks:
- debug:
msg: "{{ item }}: {{ ansible_host }}"
loop: "{{ clients|default(groups.webserver) }}"
vars:
ansible_host: "{{ hostvars[item].ansible_host }}"
gives
shell> ansible-playbook pb.yml
PLAY [sqlserver] *****************************************************************************
TASK [debug] *********************************************************************************
ok: [sqlserver.my-domain.de] => (item=webserver1.my-domain.de) =>
msg: 'webserver1.my-domain.de: 2.2.2.2'
ok: [sqlserver.my-domain.de] => (item=webserver2.my-domain.de) =>
msg: 'webserver2.my-domain.de: 3.3.3.3'
PLAY RECAP ***********************************************************************************
sqlserver.my-domain.de: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Example of the project for testing
shell> tree .
.
├── ansible.cfg
├── hosts
└── pb.yml
0 directories, 3 files
shell> cat ansible.cfg
[defaults]
gathering = explicit
inventory = $PWD/hosts
stdout_callback = yaml
shell> cat hosts
all:
vars:
ansible_ssh_user: me
children:
sqlserver:
hosts:
sqlserver.my-domain.de:
ansible_ssh_user: mysql_me_user
ansible_host: 1.1.1.1
webserver:
hosts:
webserver1.my-domain.de:
ansible_host: 2.2.2.2
webserver2.my-domain.de:
ansible_host: 3.3.3.3
shell> cat pb.yml
- hosts: '{{ target|default("sqlserver") }}'
tasks:
- debug:
msg: "{{ item }}: {{ ansible_host }}"
loop: "{{ clients|default(groups.webserver) }}"
vars:
ansible_host: "{{ hostvars[item].ansible_host }}"
Create the list and the dictionary below
clients: "{{ groups.webserver }}"
ah_list: "{{ clients|map('extract', hostvars, 'ansible_host')|list }}"
ah_dict: "{{ dict(clients|zip(ah_list)) }}"
give
ah_list:
- 2.2.2.2
- 3.3.3.3
ah_dict:
webserver1.my-domain.de: 2.2.2.2
webserver2.my-domain.de: 3.3.3.3
Then, all tasks below give the same results
2a)
- debug:
msg: "{{ item.key }}: {{ item.value }}"
with_dict: "{{ ah_dict }}"
2b)
- debug:
msg: "{{ item.0 }}: {{ item.1 }}"
with_together:
- "{{ clients }}"
- "{{ ah_list }}"
2c)
- debug:
msg: "{{ item }}: {{ ah_dict[item] }}"
loop: "{{ clients }}"
Example of a complete playbook for testing
- hosts: '{{ target|default("sqlserver") }}'
vars:
clients: "{{ groups.webserver }}"
ah_list: "{{ clients|map('extract', hostvars, 'ansible_host')|list }}"
ah_dict: "{{ dict(clients|zip(ah_list)) }}"
tasks:
- debug:
var: ah_list
- debug:
var: ah_dict
- debug:
msg: "{{ item.key }}: {{ item.value }}"
with_dict: "{{ ah_dict }}"
- debug:
msg: "{{ item.0 }}: {{ item.1 }}"
with_together:
- "{{ clients }}"
- "{{ ah_list }}"
- debug:
msg: "{{ item }}: {{ ah_dict[item] }}"
loop: "{{ clients }}"

As I was writting the question I understood that should google for accessing inventory property and found a solution in Accessing inventory host variable in Ansible playbook - use
from_ip: '{{ hostvars[item].ansible_host }}'
for my task.
I hope that this the way to go.

Related

how to make a list from ansible_facts with multiple hosts

I'm trying to make a list with IP addresses of various hosts and then use this list in another task. My question is, how can I pick an IP (I need the public IP) from the output of each host and add it to a list? I need the IPs that do not start with 10..
Later, I need to use this list in another task.
I extract this information by running this playbook:
- hosts: facts
become: true
gather_facts: True
tasks:
- debug:
msg: "The ip: {{ item }}"
with_items: "{{ ansible_all_ipv4_addresses }}"
Later, I need to use this list in another task:
- wait_for:
host: "{{ item[0] }}"
port: "{{ item[1] }}"
state: started
delay: 0
timeout: 2
delegate_to: localhost
become: false
ignore_errors: no
ignore_unreachable: yes
register: result
failed_when: not result.failed
with_nested:
- [ IP LIST HERE]
- [443,80,9200,9300,22,5432,6432]
You can access those values from the hostvars right away, then use a reject filter with a match test in order to reject what you don't want to test for.
Which, in a debug task would gives:
# note: ports list reduced for brevity
- debug:
msg: "I should wait for interface {{ item.0 }}:{{ item.1 }}"
loop: >-
{{
hostvars
| dict2items
| selectattr('key', 'in', ansible_play_hosts)
| map(attribute='value.ansible_all_ipv4_addresses', default=[])
| flatten
| reject('match', '10\..*')
| product(_ports)
}}
loop_control:
label: "{{ item.0 }}"
run_once: true
delegate_to: localhost
vars:
_ports:
- 22
- 80
In my lab, this give:
ok: [ansible-node-1 -> localhost] => (item=172.18.0.3) =>
msg: I should wait for interface 172.18.0.3:22
ok: [ansible-node-1 -> localhost] => (item=172.18.0.3) =>
msg: I should wait for interface 172.18.0.3:80
ok: [ansible-node-1 -> localhost] => (item=172.18.0.4) =>
msg: I should wait for interface 172.18.0.4:22
ok: [ansible-node-1 -> localhost] => (item=172.18.0.4) =>
msg: I should wait for interface 172.18.0.4:80
Try the example below
shell> cat pb.yml
- hosts: all
vars:
ip_list: "{{ ansible_play_hosts|
map('extract', hostvars, 'ansible_all_ipv4_addresses')|
map('first')|list }}"
ip_list_reject: "{{ ip_list|reject('match', '10\\.')|list }}"
tasks:
- setup:
gather_subset: network
- block:
- debug:
var: ip_list
- debug:
var: ip_list_reject
- wait_for:
host: "{{ item.0 }}"
port: "{{ item.1 }}"
state: started
delay: 0
timeout: 2
delegate_to: localhost
register: result
with_nested:
- "{{ ip_list_reject }}"
- [443, 80, 9200, 9300, 22, 5432, 6432]
run_once: true

Ansible variables question from output and filtered with json_query

I am trying to get the pertinent fields from a vmware module output, I am filtering with json, but the says my variable target_vm_name is undefined. how do I get a variable with only the matching criteria which is filtered by the ip address of the list of hosts I want to iterate through?
code is below:
- name: Loop thru hosts and get IPs
hosts: rpa_test
vars:
ip_addr:
"{{ lookup('dig', ansible_host) }}"
tasks:
- debug:
msg: "System {{ inventory_hostname }} has ip address of {{ ip_addr }}"
- name: get vm info based on ip
community.vmware.vmware_vm_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vault_vcenter_admin_name }}"
password: "{{ vault_vcenter_admin_password }}"
validate_certs: False
delegate_to: localhost
register: vm_info
vars:
target_vm_name: "{{ vm_info.virtual_machines | json_query(query) }}"
query: "[?ip_address=={{ ip_addr }}]"
- debug:
msg: "{{ target_vm_name.virtual_machines.guest_name }}"
vars is not at the right place inside the community.vmware.vmware_vm_info task ,
you should put it in the debug task:
tasks:
- debug:
msg: "System {{ inventory_hostname }} has ip address of {{ ip_addr }}"
- name: get vm info based on ip
community.vmware.vmware_vm_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vault_vcenter_admin_name }}"
password: "{{ vault_vcenter_admin_password }}"
validate_certs: False
delegate_to: localhost
register: vm_info
- debug:
msg: "{{ target_vm_name.virtual_machines.guest_name }}"
vars:
target_vm_name: "{{ vm_info.virtual_machines | json_query(query) }}"
query: "[?ip_address=={{ ip_addr }}]"

Get facts when var defined in hostvars

ansible 2.9.1
I have inventory:
[group1]
server1 master=yes
server2 master=no
server3 master=no
How get ansible_fqdn with master=yes from server2, server3?
Example:
server2 or server3 facts:
...
master_server: server1
...
I think so, but it did not work:
- name: set fact
set_fact:
master_server: {{ ansible_fqdn }}
when: master == 'yes'
delegate_to: "{{ item }}"
loop: "{{ ansible_play_hosts }}"
UPDATE. RESOLVE
add:
run_once: True
Q: "How to get ansible_fqdn with master=yes from server2, server3?"
A: There are more options. For example, either use selectattr
- hosts: group1
tasks:
- set_fact:
master_server: "{{ (groups.group1|
map('extract', hostvars)|
selectattr('master', 'eq', True)|
list|
first).ansible_fqdn }}"
run_once: true
- debug:
var: master_server
, or use json_query
- hosts: group1
tasks:
- set_fact:
master_server: "{{ groups.group1|
map('extract', hostvars)|
list|
json_query('[?master].ansible_fqdn')|
first }}"
run_once: true
- debug:
var: master_server
Both options give
ok: [test_02] => {
"master_server": "test_01.example.org"
}
ok: [test_01] => {
"master_server": "test_01.example.org"
}
ok: [test_03] => {
"master_server": "test_01.example.org"
}
Inventory used in the examples
$ cat hosts
group1:
hosts:
test_01:
master: yes
ansible_fqdn: test_01.example.org
test_02:
master: no
ansible_fqdn: test_02.example.org
test_03:
master: no
ansible_fqdn: test_03.example.org

ansible sharing parameters between tasks

This may be an elementary question. I am using the following Ansible modules and was wanting to streamline my playbooks by reducing duplicate lines/variables.
https://github.com/HewlettPackard/hpe3par_ansible_module
I see that each task references a module that is using the same connection parameters. The variables are already defined in a parameters file but is there a way to move parameters to be more global so it doesn't have to be repeated in each task.
My playbook:
---
- name: Create 3PAR host and volume
hosts: localhost
tasks:
- name: Load Storage System Vars
include_vars: 'properties/storage_system_properties.yml'
- name: Load Host Vars
include_vars: 'properties/host_properties.yml'
- name: Create Host "{{ host_name }}"
hpe3par_host:
storage_system_ip="{{ storage_system_ip }}"
storage_system_username="{{ storage_system_username }}"
storage_system_password="{{ storage_system_password }}"
state=present
host_name="{{ host_name }}"
host_persona="{{ host_persona }}"
host_domain="{{ host_domain }}"
host_iscsi_names="{{ host_iscsi_names }}"
- name: Create Volume "{{ volume_name }}"
hpe3par_volume:
storage_system_ip="{{ storage_system_ip }}"
storage_system_username="{{ storage_system_username }}"
storage_system_password="{{ storage_system_password }}"
state=present
volume_name="{{ volume_name }}"
cpg="{{ cpg }}"
size="{{ size }}"
- name: Create VLUN
hpe3par_vlun:
storage_system_ip="{{ storage_system_ip }}"
storage_system_username="{{ storage_system_username }}"
storage_system_password="{{ storage_system_password }}"
state=export_volume_to_host
volume_name="{{ volume_name }}"
host_name="{{ host_name }}"
Desired playbook.
---
- name: Create 3PAR host and volume
hosts: localhost
vars_file:
- properties/storage_system_properties.yml
tasks:
- name: Load Host Vars
include_vars: 'properties/host_properties.yml'
- name: Create Host "{{ host_name }}"
hpe3par_host:
state=present
host_name="{{ host_name }}"
host_persona="{{ host_persona }}"
host_domain="{{ host_domain }}"
host_iscsi_names="{{ host_iscsi_names }}"
- name: Create Volume "{{ volume_name }}"
hpe3par_volume:
state=present
volume_name="{{ volume_name }}"
cpg="{{ cpg }}"
size="{{ size }}"
- name: Create VLUN
hpe3par_vlun:
state=export_volume_to_host
volume_name="{{ volume_name }}"
host_name="{{ host_name }}"
properties/storage_system_properties.yml
storage_system_ip: "192.168.1.10"
storage_system_username: "3paruser"
storage_system_password: "3parpass"
An option would be to use include_tasks. See example below.
> cat tasks-001.yml
- debug: msg="{{ var_001 }}-{{ var_002 }}-{{ var_003 }}"
> cat test-19.yml
- hosts: localhost
gather_facts: no
vars:
var_001: "001"
var_002: "002"
var_003: "003"
tasks:
- include_tasks: tasks-001.yml
- include_tasks: tasks-001.yml
vars:
var_003: "444"
- include_tasks: tasks-001.yml
vars:
var_003: "555"
- include_tasks: tasks-001.yml
vars:
var_001: "111"
var_002: "222"
- include_tasks: tasks-001.yml
vars:
var_001: "111"
var_002: "222"
var_003: "333"
- include_tasks: tasks-001.yml
> ansible-playbook test-19.yml | grep msg
"msg": "001-002-003"
"msg": "001-002-444"
"msg": "001-002-555"
"msg": "111-222-003"
"msg": "111-222-333"
"msg": "001-002-003"

Building vars dynamically

I'm trying to leverage ovirt-ansible (https://github.com/oVirt/ovirt-ansible) but it's vars are a bit verbose and I'd like to generate them off some simpler ones to reduce the chance of human error.
For example, something like this which obviously doesn't work:
vars:
cluster_name: test
domain_suffix: blah
subnet_mgmt: 1.2.3
myhosts:
- id: 001
ip_suffix: 101
hosts:
{% for host in myhosts %}
- name: "{{ cluster_name }}-{{ host[id] }}.{{ domain_suffix }}"
address: "{{ subnet_mgmt }}.{{ host[ip_suffix] }}"
{% endfor %}
Can anyone advise best way to go about doing this, without simply forking their repo and rewriting the playbook to read the variables in my own format? I'm hoping to avoid having to maintain a fork going forwards.
Is this the code that you're looking for?
> cat dynamic_var.yml
- hosts: localhost
vars:
cluster_name: test
domain_suffix: blah
subnet_mgmt: 1.2.3
myhosts:
- { id: "001", ip_suffix: "101" }
tasks:
- set_fact:
hosts:
- name: "{{ cluster_name }}-{{ item.id }}.{{ domain_suffix }}"
address: "{{ subnet_mgmt }}.{{ item.ip_suffix }}"
loop: "{{ myhosts }}"
- debug:
msg: "{{ hosts }}"
> ansible-playbook dynamic_var.ym
(abridged)
TASK [debug]
"address": "1.2.3.101",
"name": "test-001.blah"

Resources