I'm an Ansible AWX newbie. I'm trying to pass a variable between two tasks in a playbook. The playbook looks like this:
---
- name: start
hosts: all
gather_facts: no
tasks:
- name: Check Debian disk space
command: df --output=avail {{ a7_pcap_data_path }}
become: true
become_method: sudo
register: df_output
- name: Show Debian free space
vars:
a7_free_space_mb: "{{ (df_output.stdout_lines[1]|int / 1024)|round(0,'floor') }}"
a7_spare_space_mb: "{{ a7_free_space_mb|int - (a7_capture_volume_mb|int * 1.1) }}" # Allow 10% for safety
debug:
msg: "Spare space: {{ a7_spare_space_mb }} MB"
- name: Pass/fail check
debug:
msg: "Spare space: {{ a7_spare_space_mb }} MB"
The a7_pcap_data_pathand a7_capture_volume_mb are passed in as AWX variables. When I run this from the Job Template in AWX, I get this:
Identity added: /tmp/awx_57_PesTa2/credential_3 (/tmp/awx_57_PesTa2/credential_3)
SSH password:
PLAY [start] *******************************************************************
TASK [Check Debian disk space] *************************************************
changed: [ip-172-31-14-43.eu-west-1.compute.internal]
TASK [Show Debian free space] **************************************************
ok: [ip-172-31-14-43.eu-west-1.compute.internal] => {
"msg": "Spare space: 3020.0 MB"
}
TASK [Pass/fail check] *********************************************************
fatal: [ip-172-31-14-43.eu-west-1.compute.internal]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'a7_spare_space_mb' is undefined\n\nThe error appears to have been in '/var/lib/awx/projects/pre_checks/test.yml': line 21, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Pass/fail check\n ^ here\n"}
PLAY RECAP *********************************************************************
ip-172-31-14-43.eu-west-1.compute.internal : ok=2 changed=1 unreachable=0 failed=1
The variable a7_spare_space_mb obviously is available in the task Show Debian free space but not in the subsequent task.
It appears that the scope of the a7_spare_space_mb variable is only within the task in which it's defined, but from what I've read, the scope should be the entire playbook.
What am I doing wrong?
Thanks and regards...Paul
An option would be to use set_fact
- set_fact:
a7_free_space_mb: "{{ (df_output.stdout_lines[1]|int / 1024)|round(0,'floor') }}"
a7_spare_space_mb: "{{ a7_free_space_mb|int - (a7_capture_volume_mb|int * 1.1) }}" # Allow 10% for safety
- name: Show Debian free space
debug:
msg: "Spare space: {{ a7_spare_space_mb }} MB"
- name: Pass/fail check
debug:
msg: "Spare space: {{ a7_spare_space_mb }} MB"
Related
I am trying to create a csv file that can be used to review certain systems details. One of these items is the system uptime, which is reflected in unix seconds. But in the os.csv output file I would like to see it as days, HH:MM:SS.
Below my yaml script:
---
- name: playbook query system and output to file
hosts: OEL7_systems
vars:
output_file: os.csv
tasks:
- block:
# For permisison setup.
- name: get current user
command: whoami
register: whoami
run_once: yes
- name: clean_file
copy:
dest: "{{ output_file }}"
content: 'hostname,distribution,version,release,uptime'
owner: "{{ whoami.stdout }}"
run_once: yes
- name: fill os information
lineinfile:
path: "{{ output_file }}"
line: "{{ ansible_hostname }},\
{{ ansible_distribution }},\
{{ ansible_distribution_version }},\
{{ ansible_distribution_release }},\
{{ ansible_uptime_seconds }}"
# Tries to prevent concurrent writes.
throttle: 1
delegate_to: localhost
Any help is appreciated.
tried several conversions but can't get it to work.
There is actually a (somewhat hard to find) example in the official documentation on complex data manipulations doing exactly what you are looking for (check at the bottom of the page).
Here is a full example playbook to run it on localhost
---
- hosts: localhost
tasks:
- name: Show the uptime in days/hours/minutes/seconds
ansible.builtin.debug:
msg: Uptime {{ now().replace(microsecond=0) - now().fromtimestamp(now(fmt='%s') | int - ansible_uptime_seconds) }}
which gives on my machine:
PLAY [localhost] ************************************************************************************************************************************************
TASK [Gathering Facts] ******************************************************************************************************************************************
ok: [localhost]
TASK [Show the uptime in days/hours/minutes/seconds] ************************************************************************************************************
ok: [localhost] => {
"msg": "Uptime 1 day, 3:56:34"
}
PLAY RECAP ******************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
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'm trying to evaluate the condition
hostvars[inventory_hostname].external_ip == ansible_facts['ipify_ip']
The external_ip is set on the host_variable . I have set the ipify_ip on the custom facts and trying to evaluate this condition
when: hostvars[inventory_hostname].external_ip == ansible_facts['ipify_ip']
I have tried this option also but it failed there too
when: hostvars[inventory_hostname].external_ip == {{ ipify_ip }}
Here is the complete playbook file
- name: Get public ip for the host
ipify_facts:
api_url: https://api.ipify.org/
timeout: 20
tags: always
- name: Set fact
set_fact:
ipify_ip: "{{ ipify_public_ip }}"
tags: always
- name: ipify_ip External IP
debug:
var: ipify_ip
tags: always
- name: is extrnal_ip in hostvars is same with ipify_ip
debug:
var=hostvars[inventory_hostname].external_ip
when: hostvars[inventory_hostname].external_ip == ansible_facts['ipify_ip']
tags: always
The error
fatal: [localhost]: FAILED! => {"msg": "The conditional check 'hostvars[inventory_hostname].external_ip == ansible_facts['ipify_ip']' failed. The error was: error while evaluating conditional (hostvars[inventory_hostname].external_ip == ansible_facts['ipify_ip']): 'dict object' has no attribute 'ipify_ip'\n\nThe error appears to be in '/home/anish/playbook/prereqs.yml': line 29, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: extrnal_ip on hostvars\n ^ here\n"}
What is the right way to do this ??
Simply use the variables. For example, given the inventory
shell> cat hosts
host01 external_ip=1.2.3.4
and the playbook
shell> cat playbook.yml
- hosts: host01
tasks:
- set_fact:
ipify_ip: 1.2.3.4
- debug:
var: ipify_ip
- debug:
var: external_ip
- debug:
msg: "{{ external_ip }} is equal to {{ ipify_ip }}"
when: external_ip == ipify_ip
gives
shell> ansible-playbook -i hosts playbook.yml
PLAY [host01] *******************************
TASK [set_fact] *****************************
ok: [host01]
TASK [debug] ********************************
ok: [host01] =>
ipify_ip: 1.2.3.4
TASK [debug] ********************************
ok: [host01] =>
external_ip: 1.2.3.4
TASK [debug] ********************************
ok: [host01] =>
msg: 1.2.3.4 is equal to 1.2.3.4
PLAY RECAP **********************************
host01: ok=4 changed=0 unreachable=0 failed=0 ...
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 following the redhat ansible course online and I followed the below steps to use multi-valued variable in a playbook. But I am hitting an error stating there's an undefined variable
Below is my arrays.yaml file
---
- name: show arrays
hosts: ansible1.example.com
vars_files:
- vars/users
tasks:
- name: print array values
debug:
msg: "User {{ item.username }} has homedirectory {{ item.homedir }} and shell {{ item.shell }}"
with_items: "{{ users }}"
And below is vars/users file
users:
linda:
username: linda
homedir: /home/linda
shell: /bin/bash
gokul:
username: gokul
homedir: /home/gokul
shell: /bin/bash
saha:
username: saha
homedir: /home/gokul/saha
shell: /bin/bash
And below is the error that I am hitting
ansible-playbook arrays.yaml
PLAY [show arrays] *****************************************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************************************************************************************************************************************************
ok: [ansible1.example.com]
TASK [print array values] **********************************************************************************************************************************************************************************************************************************
fatal: [ansible1.example.com]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'username'\n\nThe error appears to be in '/home/ansible/install/arrays.yaml': line 7, 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: print array values\n ^ here\n"}
PLAY RECAP *************************************************************************************************************************************************************************************************************************************************
ansible1.example.com : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
However, when I try to refer individual values like below in the playbook, it works fine.
---
- name: show arrays
hosts: ansible1.example.com
vars_files:
- vars/users
tasks:
- name: print array values
debug:
msg: "User {{ users.gokul.username }} has homedirectory {{ users.gokul.homedir }} and shell {{ users.gokul.shell }}"
It's not possible to iterate a dictionary. Either the code or the data should be changed.
1. Change data
Use a list of users instead of a dictionary. For example, the list below
users:
- username: linda
homedir: /home/linda
shell: /bin/bash
- username: gokul
homedir: /home/gokul
shell: /bin/bash
- username: saha
homedir: /home/gokul/saha
shell: /bin/bash
would work as expected
- name: print array values
debug:
msg: "User {{ item.username }}
has homedirectory {{ item.homedir }}
and shell {{ item.shell }}"
loop: "{{ users }}"
2. Change code
It's possible to use the users dictionary with the filter dict2items. For example, the task below would give the same result
- name: print array values
debug:
msg: "User {{ item.value.username }}
has homedirectory {{ item.value.homedir }}
and shell {{ item.value.shell }}"
loop: "{{ users|dict2items }}"