I have a playbook that contains two plays.
I understand (ish) that the facts created are associated with the hosts of the individual plays and, hostvars could normally be used for this purpose. The trouble i'm having, in my first play, the hosts are defined as a variable that gets passed in as an extra var when calling the playbook. I don't know how to use hostvars in this scenario.
For reproduction of the issue inventory is as follows
[p1]
mgmt_server_01.us.acme.com
[p1:vars]
password = "something"
[p2]
mgmt_server_02.apac.acme.com
[p2:vars]
password = "something_else"
[p3]
mgmt_server_03.emea.acme.com
[p3:vars]
password = "yet_another_something"
[mgmt_servers:children]
p1
p2
p3
[mgmt_servers:vars]
username = "admin"
The "dynamic inventory" is sample.json
{
"servers": [
"server01.us.acme.com",
"server03.us.acme.com",
"server04.us.acme.com",
"server02.us.acme.com"
]
}
The playbook is dynamic_inv.yml
---
- hosts: "{{ upgrade }}"
name: Read sample of servers from json and create dynamic inventory. Tasks happen for ALL inventory Servers
gather_facts: false
connection: local
vars:
json_var: "{{ lookup('file', './sample.json') | from_json }}"
tasks:
- name: set json_var to fact
set_fact:
my_fact: "{{json_var}}"
- name: Read json to get sample servers
debug:
msg: "Server names are {{item}}"
with_items: "{{json_var.servers}}"
- name: Create dynamic inventory from json_vars
add_host:
name: "{{ item }}"
groups: sample_group
with_items: "{{ json_var.servers }}"
no_log: false
- name: Print the dynamic inventory
debug:
var: groups.sample_group
- hosts: sample_group
name: tasks iterate sequentially, one server at a time
gather_facts: false
connection: local
order: sorted
serial: 1
tasks:
- name: Print something to show the flow of the playbook
debug: msg="This line is printed in the beginning for activity on each sample server in the inventory"
- name: Print the Server name
debug:
msg: "The Server name is {{inventory_hostname}}"
- name: Print something from my_fact
debug:
msg: "something from my_fact is {{item}}"
with_items: "{{my_fact}}"
To run the playbook ansible-playbook testing/dynamic_inv.yml -i testing/inventory -e upgrade=mgmt_server_02.apac.acme.com
The error returned is
TASK [Print the dynamic inventory] ********************************************************************************************************************************************************************************
ok: [mgmt_server_02.apac.acme.com] => {
"groups.sample_group": [
"server01.us.acme.com",
"server03.us.acme.com",
"server04.us.acme.com",
"server02.us.acme.com"
]
}
PLAY [tasks iterate sequentially, one server at a time] ***********************************************************************************************************************************************************
TASK [Print something to show the flow of the playbook] ***********************************************************************************************************************************************************
ok: [server01.us.acme.com] => {
"msg": "This line is printed in the beginning for activity on each sample server in the inventory"
}
TASK [Print the Server name] **************************************************************************************************************************************************************************************
ok: [server01.us.acme.com] => {
"msg": "The Server name is server01.us.acme.com"
}
TASK [Print something from my_fact] *******************************************************************************************************************************************************************************
fatal: [server01.us.acme.com]: FAILED! => {"msg": "'my_fact' is undefined"}
PLAY RECAP ********************************************************************************************************************************************************************************************************
mgmt_server_02.apac.acme.com : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
server01.us.acme.com : ok=2 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
How can i modify the following to use hostvars when the first host is passed as an extra variable ?
- name: Print something from my_fact
debug:
msg: "something from my_fact is {{item}}"
with_items: "{{my_fact}}"
Are hostvars the best option or, is there a better solution ?
Instead of a separate set_fact
task, set the variable directly on the newly created hosts:
- hosts: "{{ upgrade }}"
name: Read sample of servers from json and create dynamic inventory. Tasks happen for ALL inventory Servers
gather_facts: false
vars:
json_var: "{{ lookup('file', 'sample.json') | from_json }}"
tasks:
- name: Create dynamic inventory from json_var
add_host:
name: "{{ item }}"
groups: sample_group
my_fact: "{{ json_var }}"
loop: "{{ json_var.servers }}"
- hosts: sample_group
name: tasks iterate sequentially, one server at a time
gather_facts: false
order: sorted
serial: 1
tasks:
- name: Print something from my_fact
debug:
msg: "something from my_fact is {{ item }}"
loop: "{{ my_fact | list }}"
This results in:
PLAY [Read sample of servers from json and create dynamic inventory. Tasks happen for ALL inventory Servers] ***
TASK [Create dynamic inventory from json_var] **********************************
changed: [mgmt_server_02.apac.acme.com] => (item=server01.us.acme.com)
changed: [mgmt_server_02.apac.acme.com] => (item=server03.us.acme.com)
changed: [mgmt_server_02.apac.acme.com] => (item=server04.us.acme.com)
changed: [mgmt_server_02.apac.acme.com] => (item=server02.us.acme.com)
PLAY [tasks iterate sequentially, one server at a time] ************************
TASK [Print something from my_fact] ********************************************
ok: [server01.us.acme.com] => (item=servers) =>
msg: something from my_fact is servers
PLAY [tasks iterate sequentially, one server at a time] ************************
TASK [Print something from my_fact] ********************************************
ok: [server02.us.acme.com] => (item=servers) =>
msg: something from my_fact is servers
PLAY [tasks iterate sequentially, one server at a time] ************************
TASK [Print something from my_fact] ********************************************
ok: [server03.us.acme.com] => (item=servers) =>
msg: something from my_fact is servers
PLAY [tasks iterate sequentially, one server at a time] ************************
TASK [Print something from my_fact] ********************************************
ok: [server04.us.acme.com] => (item=servers) =>
msg: something from my_fact is servers
PLAY RECAP *********************************************************************
mgmt_server_02.apac.acme.com : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
server01.us.acme.com : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
server02.us.acme.com : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
server03.us.acme.com : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
server04.us.acme.com : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
I also did some other cleanup. For example, connection: local
does nothing when everything you're running is controller-side tasks like debug
and add_host
. If your full playbook also runs some normal modules that you want to execute on the controller you should use delegate_to: localhost
instead of messing with the connection settings, because that has some weird, unintuitive side effects.