I am trying to read some custom variables created in Ansible host file, but i am not able to read it somehow and it is throwing exception
Hostfile
[webserver]
xx.xx.45.12 uname=abc123
xx.xx.45.13 uname=pqr456
Playbook yaml
- name: sample playbook
hosts: all
tasks:
- name: sample echo command
shell:
cmd: echo {{hostvars['all'].uname}}
I couldn't find any document which is talking clearly how to read host variable
When i am running above I am getting below error.
fatal: [xx.xx.45.12]: FAILED! => {"msg": "The task includes an option with an undefined variable.
The error was: \"hostvars['webserver']\" is undefined\n\nThe error appears to be in
'/mnt/c/Users/ManishBansal/Documents/work/MSS/scripts/run.yaml': line 6, column 7, but may\nbe elsewhere
in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n
- name: This command will get file list\n ^ here\n"}
Q: "How to read host variable?"
A: Simply reference the variable
- command: echo {{ uname }}
For example the inventory and the playbook below
shell> cat hosts
[webserver]
test_01 uname=abc123
test_02 uname=pqr456
shell> cat playbook.yml
- hosts: all
tasks:
- debug:
var: uname
give (abridged)
shell> ansible-playbook -i hosts playbook.yml
ok: [test_01] =>
uname: abc123
ok: [test_02] =>
uname: pqr456
Use hostvars to reference variables registered with other hosts. For example
shell> cat playbook.yml
- hosts: localhost
tasks:
- debug:
var: hostvars[item].uname
loop: "{{ groups.webserver }}"
gives (abridged)
shell> ansible-playbook -i hosts playbook.yml
ok: [localhost] => (item=test_01) =>
ansible_loop_var: item
hostvars[item].uname: abc123
item: test_01
ok: [localhost] => (item=test_02) =>
ansible_loop_var: item
hostvars[item].uname: pqr456
item: test_02
Notes
See Caching facts
Quoting from module shell notes
"... it may be better to use the command module instead ..."
Related
Ansible store only the first output in a file
Example
I have 3 hosts inside the inventory
My playbook ask for memory info.
with
- name: Check memory
hosts: all
tasks:
- name: Check Memory
shell: free
register: memory_output
- name: save
lineinfile:
path: "mypc/test.log"
line: "--{{ memory_output.stdout }}% "
create: yes
delegate_to: localhost
output write in file sometimes all the hosts memory,sometimes only the first,sometimes only the last
How i append every result from every hosts in one file.
Sometimes it export all the results but not every time
For example, given the inventory
shell> cat hosts
test_11
test_12
test_13
declare the below variable and put it into the vars
vmstat: "{{ out.stdout|community.general.jc('vmstat') }}"
Get the free memory
- command: vmstat
register: out
- set_fact:
free_mem: "{{ vmstat.1.free_mem }}"
- debug:
var: free_mem
gives (abridged)
ok: [test_11] =>
free_mem: '3434124'
ok: [test_12] =>
free_mem: '3496908'
ok: [test_13] =>
free_mem: '3434992'
Q: "How to store multiple 'register' in one file with one playbook."
A: Write it to the log
- lineinfile:
create: true
path: /tmp/test.log
line: >-
{{ '%Y-%m-%d %H:%M:%S'|strftime() }}
{{ item }}
{{ hostvars[item].free_mem }}
loop: "{{ ansible_play_hosts }}"
delegate_to: localhost
run_once: true
gives
shell> cat /tmp/test.log
2022-09-12 13:39:48 test_11 3434124
2022-09-12 13:39:49 test_12 3496908
2022-09-12 13:39:49 test_13 3434992
Example of a complete playbook for testing
- hosts: test_11,test_12,test_13
vars:
vmstat: "{{ out.stdout|community.general.jc('vmstat') }}"
tasks:
- command: vmstat
register: out
- set_fact:
free_mem: "{{ vmstat.1.free_mem }}"
- debug:
var: free_mem
- lineinfile:
create: true
path: /tmp/test.log
line: >-
{{ '%Y-%m-%d %H:%M:%S'|strftime() }}
{{ item }}
{{ hostvars[item].free_mem }}
loop: "{{ ansible_play_hosts }}"
delegate_to: localhost
run_once: true
Here's the simplest example. Assuming that you are running ansible in controller machine and you have to append the output of executing tasks in remote machines. The host list will obviously be different for you and will have all the remote machines.
- hosts: localhost
tasks:
## Playbook to copy the file from controller machine to remote machine
- name: Copy the file from controller machine to remote machine
copy:
src: /tmp/tmpdir/output.txt
dest: /tmp/tmpdir/output.txt
## Playbook to store the shell output to a variable
- name: Store the output of the shell command to a variable
shell: "echo '\nHello World'"
register: output
- name: Print the output of the shell command
debug:
msg: "{{ output.stdout }}"
## Playbook to append output to a file
- name: Append output to a file
lineinfile:
path: /tmp/tmpdir/output.txt
line: "{{ output.stdout }}"
create: yes
## Playbook to copy the file from remote machine to controller machine
- name: Copy the file from remote machine to controller machine
fetch:
src: /tmp/tmpdir/output.txt
dest: /tmp/tmpdir/output.txt
flat: yes
After running it the third time
╰─ ansible-playbook test.yaml
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [localhost] *******************************************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************************************************
ok: [localhost]
TASK [Copy the file from controller machine to remote machine] *********************************************************************************************
ok: [localhost]
TASK [Store the output of the shell command to a variable] *************************************************************************************************
changed: [localhost]
TASK [Print the output of the shell command] ***************************************************************************************************************
ok: [localhost] => {
"msg": "\nHello World"
}
TASK [Append output to a file] *****************************************************************************************************************************
changed: [localhost]
TASK [Copy the file from remote machine to controller machine] *********************************************************************************************
ok: [localhost]
PLAY RECAP *************************************************************************************************************************************************
localhost : ok=6 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
╰─ cat output.txt
Hello World
Hello World
Hello World
So on every machine you run the controller machine will get the latest file output. We will copy the file to remote, add contents to the file and then copy it back to controller. Continue the same until all the hosts have been completed.
If you want to take the result from selective servers then the last task can be replaced by following. Replace the hostnames with required values
## Playbook to copy the file from remote machine to controller machine if the hostname maches localhost1 or localhost2
- name: Copy the file from remote machine to controller machine if the hostname maches localhost1 or localhost2
fetch:
src: /tmp/tmpdir/output.txt
dest: /tmp/tmpdir/output.txt
flat: yes
fail_on_missing: yes
when: inventory_hostname == 'localhost1' or inventory_hostname == 'localhost2'
I have got a simple playbook:
- hosts: all
serial: 1
order: "{{ run_order }}"
gather_facts: false
tasks:
- ping:
register: resultt
My inventory tree:
.
├── group_vars
│ └── g1
│ └── values.yml
├── host_vars
└── inv
inv file:
[g1:children]
m1
m2
[g2:children]
m3
m4
[m1]
192.168.0.60
[m2]
192.168.0.61
[m3]
192.168.0.62
[m4]
192.168.0.63
group_vars/g1/values.yml:
---
run_order: 'reverse_sorted'
When I try to run playbook:
ansible-playbook -i inv-test/inv --limit='g1' my-playbook.yml
I get the error:
ERROR! The field 'order' has an invalid value, which includes an undefined variable. The error was: 'run_order' is undefined
The error appears to be in '/home/mk/throttle': line 1, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- hosts: all
^ here
Although the output from ansible-inventory -i inv --graph --vars is correct
Why ansible doesn't parse vars from inventory groups_vars?
Ansible version 2.10.15
The problem is that you want to use a host variable in the scope of a play. See Scoping variables. It's possible if you create such a host variable first. Then you can use hostvars to reference the host variable. For example, given the simplified inventory
shell> cat hosts
[g1]
host_01
host_02
and the group_vars
shell> cat group_vars/g1.yml
run_order: 'reverse_sorted'
The playbook
shell> cat pb.yml
- hosts: all
gather_facts: false
tasks:
- debug:
var: run_order
gives (abridged)
shell> ansible-playbook -i hosts pb.yml
ok: [host_01] =>
run_order: reverse_sorted
ok: [host_02] =>
run_order: reverse_sorted
But, the playbook below
shell> cat pb.yml
- hosts: all
gather_facts: false
order: "{{ run_order }}"
tasks:
- debug:
var: run_order
fails because the variable run_order is not defined in the scope of the play
ERROR! The field 'order' has an invalid value, which includes an undefined variable. The error was: 'run_order' is undefined
The (awkward) solution is to create the hostvars in the first play and use it in the second play. For example, the second play in the playbook below is executed in the reverse order
shell> cat pb.yml
- hosts: all
gather_facts: false
tasks:
- debug:
var: run_order
- hosts: all
gather_facts: false
serial: 1
order: "{{ hostvars.host_01.run_order }}"
tasks:
- debug:
var: run_order
gives
shell> ansible-playbook -i hosts pb.yml
PLAY [all] ***********************************************************************************
TASK [debug] *********************************************************************************
ok: [host_01] =>
run_order: reverse_sorted
ok: [host_02] =>
run_order: reverse_sorted
PLAY [all] ***********************************************************************************
TASK [debug] *********************************************************************************
ok: [host_02] =>
run_order: reverse_sorted
PLAY [all] ***********************************************************************************
TASK [debug] *********************************************************************************
ok: [host_01] =>
run_order: reverse_sorted
I'm facing a mysterious ansible behaviour.
I've set groups, hosts and groups vars in my inventory file, but the ansible-playbook command is raising Undefined Variable for all my group vars, while printing them with the ansible debug module show the variable well.
Here is my inventory.ini file:
[windows]
ad_server ansible_host=mrspw00550.mydomain.com
[windows:vars]
ansible_connection=winrm
ansible_winrm_transport=ntlm
ansible_port=5985
ansible_become_method=runas
dns_zone=mydomain.com
ptr_zone=10.in-addr.arpa
dns_server=10.0.100.100
[linux]
web_server ansible_host=10.0.101.129
[linux:vars]
ansible_connection=ssh
ansible_become=yes
ansible_become_method=sudo
dns_zone=mydomain.com
ptr_zone=10.in-addr.arpa
dns_server=10.0.100.100
linux_home_dir=/home/ldaphome
the playbook file (set-hostname-and-dns.yml):
- name: Debug vars
debug: var=hostvars[inventory_hostname]['dns_zone']
delegate_to: ad_server
- name: Set hostname and make DNS registrations
block:
- name: Set hostname
hostname:
name: "web.{{ dns_zone }}"
delegate_to: web_server
- name: Create a DNS registration of type A
win_dns_record:
name: "web"
type: "A"
value: "{{ inventory_hostname }}"
zone: "{{ dns_zone }}"
computer_name: "{{ dns_server }}"
delegate_to: "{{ ad_server }}"
- name: Create a DNS registration of type PTR
win_dns_record:
name: "{{ inventory_hostname }}"
type: "PTR"
value: "web"
zone: "{{ ptr_zone }}"
computer_name: "{{ dns_server }}"
delegate_to: "{{ ad_server }}"
Running the ansible-playbook command gives :
$ ansible-playbook playbook.yml -i inventory-files/inventory.ini
PLAY [localhost] *********************************************************************************************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************************************************************************************************
ok: [localhost]
TASK [create-linux-vm : Debug vars] **************************************************************************************************************************************************************************
ok: [localhost -> mrspw00550.mydomain.com] => {
"hostvars[inventory_hostname]['dns_zone']": "VARIABLE IS NOT DEFINED!"
}
TASK [create-linux-vm : Set web_server hostname] **************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dns_zone' is undefined\n\nThe error appears to be in 'set-hostname-and-dns.yml': line 14, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n block:\n - name: Set {{ vm_name }} hostname\n ^ here\nWe could be wrong, but this one looks like it might be an issue with\nmissing quotes. Always quote template expression brackets when they\nstart a value. For instance:\n\n with_items:\n - {{ foo }}\n\nShould be written as:\n\n with_items:\n - \"{{ foo }}\"\n"}
PLAY RECAP ***************************************************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
Running debug module:
$ ansible -m debug -i inventory-files/inventory.ini -a "var=hostvars[inventory_hostname]['dns_zone']" all
ad_server | SUCCESS => {
"hostvars[inventory_hostname]['dns_zone']": "mydomain.com"
}
web_server | SUCCESS => {
"hostvars[inventory_hostname]['dns_zone']": "mydomain.com"
}
$
So as you can see, ansible-playbook command is unable to retrieve my host vars, while ansible debug module does.
The result is the same even if I define those variables in inventory-files/group_vars/linux.yml or inventory-files/group_vars/windows.yml, the variable is still as undefined by ansible. The problem is the same if try to access any other variable than special variables from inventory running ansible-playbook
What could be wrong there?
I tried with delegate_facts set to true, it didn't work.
Then defined a local group in the inventory containing localhost and defined my local group vars there like this :
[local]
localhost
[local:vars]
ansible_connection=local
dns_zone=mydomain.com
ptr_zone=10.in-addr.arpa
dns_server=10.0.100.100
linux_home_dir=/home/ldaphome
and remove those var from remote hosts facts, as I don't really those variables there. All my tasks are executed on the localhost and delegated to the remote hosts depending to the action.
I'd also like to precise that specifically ask for var/fact like #Zeitounator mentioned above ({{ hostvars['ad_server'].ptr_zone }}) in his comment works too.
I am trying to run a playbook to create disks on VMs which are on different Vcenters.
so when I put them in an inventory file something like this:
**inv.yml:
vcenter1 datacenter=dc1 datastore=ds1 name=vm1
vcenter1 datacenter=dc1 datastore=ds1 name=vm2
vcenter1 datacenter=dc1 datastore=ds1 name=vm3
vcenter2 datacenter=dc2 datastore=ds2 name=vm4
vcenter2 datacenter=dc2 datastore=ds2 name=vm5
vcenter3 datacenter=dc3 datastore=ds3 name=vm6
vcenter3 datacenter=dc3 datastore=ds3 name=vm6**
When I run this it reads only once for each vcenter and ignores other entries.
Any way I can make Ansible read same hostname again and again.
I am able to do this with a variable file but I am trying to do this with an inventory file.
Q: "Any way I can make Ansible read same hostname again and again?"
A: No. There isn't any. Change the data instead. For example
shell> cat inv.yml
vcenter1 datacenter=dc1 datastore=ds1 name=[vm1,vm2,vm3]
vcenter2 datacenter=dc2 datastore=ds2 name=[vm4,vm5]
vcenter3 datacenter=dc3 datastore=ds3 name=[vm6]
shell> cat test.yml
- hosts: all
tasks:
- debug:
var: name
shell> ansible-playbook -i inv.yml test.yml
ok: [vcenter1] =>
name: '[vm1,vm2,vm3]'
ok: [vcenter2] =>
name: '[vm4,vm5]'
ok: [vcenter3] =>
name: '[vm6]'
I need to use variable defined for some hosts (in inventory), on another host.
Here i define it in my inventory
[mygroup:vars]
service_url=my_front_url
Where mygroup contain other groups, containing my hosts.
Then my playbook :
- name: Get variable
hosts: 127.0.0.1
tasks:
- debug:
var: hostvars[groups['{{ platform }}'][0]]['service_url']
- debug:
msg: "{{ hostvars[groups['\"{{ platform }}\"'][0]]['service_url'] }}"
Where platform is an extra-var (setting which "mygroup" to use)
and where 127.0.0.1 is my ansible host, distinct from my target hosts.
ex:
ansible-playbook test.yaml --extra-vars='platform=my-group'
TASK [debug] ********************************************************************************************************************************************************************
ok: [127.0.0.1] => {
"hostvars[groups['idi_se_prod'][0]]['service_url']": "my-front-url"
}
TASK [debug] ********************************************************************************************************************************************************************
fatal: [127.0.0.1]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute '\"{{ platform }}\"'\n\nThe error appears to have been in 'XXXX/ansible/test.yaml': line 6, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n var: hostvars[groups['{{ platform }}'][0]]['service_url']\n - debug:\n ^ here\n"}
If i set static group name in yaml, this work fine.
- name: Get variable
hosts: 127.0.0.1
tasks:
- debug:
var: hostvars[groups['{{ platform }}'][0]]['service_url']
- debug:
msg: "{{ hostvars[groups['mygroup'][0]]['service_url'] }}"
TASK [debug] ********************************************************************************************************************************************************************
ok: [127.0.0.1] => {
"hostvars[groups['my-group'][0]]['service_url']": "my-front-url"
}
TASK [debug] ********************************************************************************************************************************************************************
ok: [127.0.0.1] => {
"msg": "my_front_url"
}
It look like a syntax probleme but i tried so many ways that i think i could use some help.
Thank you
Nicolas
Everything inside {{ and }} is more or less just python, so don't use recursive templates like you have:
msg: "{{ hostvars[groups['\"{{ platform }}\"'][0]]['service_url'] }}"
instead just reference the variable like it is, a variable:
msg: "{{ hostvars[groups[platform][0]]['service_url'] }}"