debug module is not showing the expected output - ansible

I wrote a simple playbook to check the crontab entries for a bunch of servers, which I'm passing in an inventory file.
- hosts: all
ignore_unreachable: yes
vars_files:
- ansible-vault-file
become: true
remote_user: userID
tasks:
- name: checkingCron
shell: crontab -l | grep abcd
become: true
register: crondata
ignore_errors: yes
- name: show cron results
debug: msg="{{ crondata.stdout_lines }}"
ignore_errors: yes
And then I run the playbook using ansile-playbook cronCheck.yml -i serversfile
I expect it to show the crontab entries for all servers. However, all it shows is this:
PLAY [all] ***
TASK [Gathering Facts] ***
ok: [server1]
ok: [server2]
ok: [server3]
and so on...
TASK [debug] *******
ok: [server_1] => {
"msg": "0"
}
ok: [server_2] => {
"msg": "0"
and so on...
Looks like I'm missing something here.
The requirement is straight forward and I've done something like this before. I can't figure out, what I'm doing wrong this time.

Related

How to create a one-time user prompt input in ansible?

I have a the following oversimplified ansible playbook:
- name: Prepare worker nodes
hosts: "{{ hosts }}"
serial:
- 1
- 3
remote_user: root
any_errors_fatal: true
vars:
hosts: nodes
reboot: false
tasks:
- pause:
prompt: "Reboot server(s) to make sure things are working during setup? (Y/n)"
echo: true
register: confirm_reboot
tags: [ untagged, hostname, netplan, firewalld ]
- set_fact:
reboot: "{{ (confirm_reboot.user_input == '' or confirm_reboot.user_input == 'Y' or confirm_reboot.user_input == 'y' ) | ternary('True', 'False') }}"
tags: [ untagged, hostname, netplan, firewalld, firewalld-install, firewalld-config ]
- debug:
msg: "{{ reboot }}"
It asks for the user's input so it can decide on some reboot policies.
This works just fine when you have just one node, but when you have multiple nodes it will prompt for each one. Suppose you have 42 nodes -- it will ask you 42 times.
I'm trying to figure out if there is an easy way to make the prompt appear just once and share the result among the nodes. Maybe I have missed something in the docs?
Given the inventory
shell> cat hosts
[test]
host1
host2
host3
host4
host5
the playbook
shell> cat playbook.yml
---
- hosts: test
serial:
- 1
- 3
gather_facts: false
tasks:
- pause:
prompt: "Reboot? (Y/n)"
echo: true
register: confirm_reboot
run_once: true
- debug:
msg: "Reboot {{ inventory_hostname }}"
when: confirm_reboot.user_input|lower == 'y'
works as expected
shell> ansible-playbook -i hosts playbook.yml
PLAY [test] *********************************
TASK [pause] ********************************
[pause]
Reboot? (Y/n):
ok: [host1]
TASK [debug] ********************************
ok: [host1] =>
msg: Reboot host1
PLAY [test] *********************************
TASK [pause] ********************************
[pause]
Reboot? (Y/n):
ok: [host2]
TASK [debug] ********************************
ok: [host2] =>
msg: Reboot host2
ok: [host3] =>
msg: Reboot host3
ok: [host4] =>
msg: Reboot host4
PLAY [test] *********************************
TASK [pause] ********************************
[pause]
Reboot? (Y/n):
ok: [host5]
TASK [debug] ********************************
ok: [host5] =>
msg: Reboot host5
Q: "Require the input just once for the entire playbook and be propagated to all hosts."
A: Split the playbook, e.g.
shell> cat playbook.yml
---
- hosts: test
gather_facts: false
tasks:
- pause:
prompt: "Reboot? (Y/n)"
echo: true
register: confirm_reboot
run_once: true
- hosts: test
serial:
- 1
- 3
gather_facts: false
tasks:
- debug:
msg: "Reboot {{ inventory_hostname }}"
when: confirm_reboot.user_input|lower == 'y'
the variable from the first play will be shared among all hosts in the second play
shell> ansible-playbook -i hosts playbook.yml
PLAY [test] *********************************
TASK [pause] ********************************
[pause]
Reboot? (Y/n):
ok: [host1]
PLAY [test] *********************************
TASK [debug] ********************************
ok: [host1] =>
msg: Reboot host1
PLAY [test] *********************************
TASK [debug] ********************************
ok: [host3] =>
msg: Reboot host3
ok: [host2] =>
msg: Reboot host2
ok: [host4] =>
msg: Reboot host4
PLAY [test] *********************************
TASK [debug] ********************************
ok: [host5] =>
msg: Reboot host5
It looks like the only way this will work is by using delegate_to and delegate_facts. I came up with something like this:
- name: Prepare worker nodes
hosts: "{{ hosts }}"
serial:
- 1
- 3
remote_user: root
any_errors_fatal: true
vars:
hosts: nodes
reboot: true
pre_tasks:
- pause:
prompt: "Reboot server(s) to make sure things are working during setup? (Y/n)"
echo: true
register: confirm_reboot
run_once: true
delegate_to: localhost
delegate_facts: true
tags: [ untagged, hostname, netplan, firewalld, firewalld-install, firewalld-config ]
when: "'reboot' not in hostvars['localhost']"
- set_fact:
reboot: "{{ (confirm_reboot.user_input == '' or confirm_reboot.user_input == 'Y' or confirm_reboot.user_input == 'y' ) | ternary('True', 'False') }}"
run_once: true
delegate_to: localhost
delegate_facts: true
tags: [ untagged, hostname, netplan, firewalld, firewalld-install, firewalld-config ]
when: "'reboot' not in hostvars['localhost']"
- set_fact:
reboot: "{{ hostvars['localhost']['reboot'] }}"
run_once: true
tasks:
- debug:
msg: "{{ hostvars['localhost'] }}"
tags: [ untagged, hostname, netplan, firewalld, firewalld-install, firewalld-config ]
- debug:
msg: "{{ reboot }}"
tags: [ untagged, hostname, netplan, firewalld, firewalld-install, firewalld-config ]
This works by delegating the fact to the localhost (control node) and then it uses it by reference that seems to be kept between the different nodes. It is a hackish workaround to me, but since I don't have that much time to dig deeper into the "why", it'll have to do for now.
If anybody figures out a better way - feel free to post your answer.

Or condition is not working with set_facts in my playbook

I created one playbook to run the tasks based on the test case so I have created like below
Here when I pass the ansible-playbook playbook.yml -e stage=1, it's skipping all the tasks, and when I debug the test_case* values I could see both are in a false state, So can some help me to work this code.
---
- name: test
hosts: localhost
tasks:
- name: setting the level
set_fact:
test_case_1: "{{ stage == 1 }}"
test_case_2: "{{ stage == 1 or stage == 2 }}"
- name: "running ls command"
shell: "ls -l"
register: testing
when:
- test_case_1 == true
- debug:
msg: "{{ testing.stdout_lines }}"
when:
- test_case_1 == true
- name: "kickoff"
shell: "df -Th"
register: kick
when:
- test_case_2 == true
- name: "printing kickoff"
debug:
msg: "{{ kick.stdout_lines }}"
when:
- test_case_2 == true
Below is the error results which I am getting
[root#server ~]# ansible-playbook playbook.yml -e stage=1
PLAY [test] ***********************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************
ok: [localhost]
TASK [setting the level] **********************************************************************************************
ok: [localhost]
TASK [running ls command] *********************************************************************************************
skipping: [localhost]
TASK [debug] **********************************************************************************************************
skipping: [localhost]
TASK [kickoff] ********************************************************************************************************
skipping: [localhost]
TASK [printing kickoff] ***********************************************************************************************
skipping: [localhost]
PLAY RECAP ************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=4 rescued=0 ignored=0
[root#server ~]#
expected results should be, it should execute all the tasks from the play.
Your problem is that you are performing an integer comparison (stage == 1), but when you provide a value on the command line via -e stage=1, you are setting a string value.
You probably want to case the value of stage to an integer using the int filter.
---
- name: test
hosts: localhost
tasks:
- name: setting the level
set_fact:
test_case_1: "{{ stage|int == 1 }}"
test_case_2: "{{ stage|int == 1 or stage|int == 2 }}"
With this change, things seem to work as expected.
Unrelated to your question, but you could rewrite the second test like this:
{{ stage|int in [1, 2] }}
That simplifies things a bit.

Condition based execution in ansible

I have a scenario if first condition is successful then execute condition two and if condition two is successful then execute condition three.
Below is what i tried.
vi testme.yml
---
- hosts: all
tasks:
- name: run this command and ignore the result
shell: "grep -ci Hello /tmp/data.out"
register: pingout
ignore_errors: yes
- debug: msg="{{ pingout.rc }}"
- name: run the server if Hello is found in the above task
command: echo "The server is UP since `uptime`"
register: output1
when: pingout.rc == 0
- debug: "{{ output1.stdout }}"
When the string is found I was expecting to see this executed and shown in the output: The server is UP since uptime
However, I do not see this printed in the output.
ansible-playbook -i /tmp/myhost /root/testme.yml
Output:
PLAY [all] ******************************************************************************************************************************************************************
TASK [Gathering Facts] ******************************************************************************************************************************************************
ok: [95.76.121.113]
TASK [run this command and ignore the result] *******************************************************************************************************************************
changed: [95.76.121.113]
TASK [debug] ****************************************************************************************************************************************************************
ok: [95.76.121.113] => {
"msg": "0"
}
TASK [run the server if Hello is found in the above task] *******************************************************************************************************************
changed: [95.76.121.113]
TASK [debug] ****************************************************************************************************************************************************************
ok: [95.76.121.113] => {
"msg": "Hello world!"
}
PLAY RECAP ******************************************************************************************************************************************************************
95.76.121.113 : ok=5 changed=2 unreachable=0 failed=0
You do not need to check rc. Ansible knows, when a command failed.
---
- hosts: localhost
connection: local
tasks:
- name: First
shell: "true"
register: first
ignore_errors: yes
- name: Second
shell: "true"
register: second
ignore_errors: yes
when: first is not failed
- name: Third
shell: "true"
register: third
ignore_errors: yes
when: second is not failed
Correct syntax is
- debug: msg="{{ output1.stdout }}"
, or
- debug: var=output1.stdout

Using lookup plugin to check SHA512 password hash

In my Ansible git repo, I have a var file with contents like this
vault_users:
alex:
password: $6$PwhqORmvn$tXctAkh9RLs60ZFhn9Cxz/eLZEx1UhQkbDIoM6xWsk7M18TApDd9/b8CHJnEiaiQE2YJ8mqu6kvsGuImDt4dy/
danny:
password: $6$PwhqORmvn$tXctAkh9RLs60ZFhn9Cxz/eLZEx1UhQkbDIoM6xWsk7M18TApDd9/b8CHJnEiaiQE2YJ8mqu6kvsGuImDt4dy/
gary:
password: $6$PwhqORmvn$tXctAkh9RLs60ZFhn9Cxz/eLZEx1UhQkbDIoM6xWsk7M18TApDd9/b8CHJnEiaiQE2YJ8mqu6kvsGuImDt4dy/
Now, I want to check if the password hashes from this var file matches the ones from the /etc/shadow file on a remote server. I know it is possible to mix Ansible and a bash/python script to get what I want. I would like to know if it is possible to do this using pure Ansible playbooks only (no bash/python scripts) using the lookup plugin or some other Ansible feature.
You can use line in file to check if line has changed, register result and store it in another variable if lineinfile module returned "changed".
Unfortunately, due to this bug you can't simply use with_items and backrefs in lineinfile module to check if strings are valid, so i used a little include hack.
So we have a playbook called playbook.yml and task called checkpasswords.yml, let's explain each of them.
playbook.yml
- hosts: localhost
tasks:
# execute checkpasswords.yml for each user in vault_users dict
# and pass each user (or item) as {{ user }} variable to included task
- include: checkpasswords.yml user="{{ item }}"
with_items: "{{ vault_users }}"
- debug: msg="{{ changed_users|default([]) }}"
checkpasswords.yml
- name: check for user and hash
lineinfile:
dest: /etc/shadow
regexp: '{{ user }}:([^:]+):(.*)'
# replace sting with user:hashed_password:everything_that_remains
line: '{{ user }}:{{ vault_users[user].password }}:\2'
state: present
backrefs: yes
register: u
- name: changed users
set_fact:
# set changed_users list to [] if not present and add [user] element
# when user password has changed
changed_users: "{{ changed_users|default([]) + [user] }}"
when: u.changed
hashvars.yml
vault_users:
root:
password: "nothing to see here"
my_user:
password: "nothing here"
I included variables to hashvars.yml file and changed hashes for my_user and root inside it. So the result of executing this playbook will be something like output below, don't forget --check!
ansible-playbook playbook.yml -e #hashvars.yml --check
PLAY [localhost] ***************************************************************
TASK [setup] *******************************************************************
ok: [localhost]
TASK [include] *****************************************************************
included: /home/my_user/workspace/so/checkpasswords.yml for localhost
included: /home/my_user/workspace/so/checkpasswords.yml for localhost
TASK [check for user and hash] *************************************************
changed: [localhost]
TASK [changed users] ***********************************************************
ok: [localhost]
TASK [check for user and hash] *************************************************
changed: [localhost]
TASK [changed users] ***********************************************************
ok: [localhost]
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": [
"my_user",
"root"
]
}
PLAY RECAP *********************************************************************
localhost : ok=8 changed=2 unreachable=0 failed=0

Ansible increment variable globally for all hosts

I have two servers in my inventory (hosts)
[server]
10.23.12.33
10.23.12.40
and playbook (play.yml)
---
- hosts: all
roles:
web
Inside web role in vars directory i have main.yml
---
file_number : 0
Inside web role in tasks directory i have main.yml
---
- name: Increment variable
set_fact: file_number={{ file_number | int + 1 }}
- name: create file
command: 'touch file{{ file_number }}'
Now i expect that in first machine i will have file1 and in second machine i will have file2 but in both machines i have file1
So this variable is local for every machine, how could i make it global for all machines.
My file structure is:
hosts
play.yml
roles/
web/
tasks/
main.yml
vars/
main.yml
Now i expect that in first machine i will have file1 and in second machine i will have file2 but in both machines i have file1
You need to keep in mind that variables in Ansible aren't global. Variables (aka 'facts') are applied uniquely to each host, so file_number for host1 is different than file_number for host2. Here's an example based loosely on what you posted:
roles/test/vars/main.yml:
---
file_number: 0
roles/test/tasks/main.yml:
---
- name: Increment variable
set_fact: file_number={{ file_number | int + 1 }}
- name: debug
debug: msg="file_number is {{ file_number }} on host {{ inventory_hostname }}"
Now suppose you have just two hosts defined, and you run this role multiple times in a playbook that looks like this:
---
- hosts: all
roles:
- { role: test }
- hosts: host1
roles:
- { role: test }
- hosts: all
roles:
- { role: test }
So in the first play the role is applied to both host1 & host2. In the second play it's only run against host1, and in the third play it's again run against both host1 & host2. The output of this playbook is:
PLAY [all] ********************************************************************
TASK: [test | Increment variable] *********************************************
ok: [host1]
ok: [host2]
TASK: [test | debug] **********************************************************
ok: [host1] => {
"msg": "file_number is 1 on host host1"
}
ok: [host2] => {
"msg": "file_number is 1 on host host2"
}
PLAY [host1] **************************************************
TASK: [test | Increment variable] *********************************************
ok: [host1]
TASK: [test | debug] **********************************************************
ok: [host1] => {
"msg": "file_number is 2 on host host1"
}
PLAY [all] ********************************************************************
TASK: [test | Increment variable] *********************************************
ok: [host1]
ok: [host2]
TASK: [test | debug] **********************************************************
ok: [host1] => {
"msg": "file_number is 3 on host host1"
}
ok: [host2] => {
"msg": "file_number is 2 on host host2"
}
So as you can see, the value of file_number is different for host1 and host2 since the role that increments the value ran against host1 more times than it did host2.
Unfortunately there really isn't a clean way making a variable global within Ansible. The entire nature of Ansible's ability to run tasks in parallel against large numbers of hosts makes something like this very tricky. Unless you're extremely careful with global variables in a parallel environment you can easily trigger a race condition, which will likely result in unpredictable (inconsistent) results.
I haven't found a solution with ansible although i made work around using shell to make global variable for all hosts.
Create temporary file in /tmp in localhost and place in it the starting count
Read the file for every host and increment the number inside the file
I created the file and initialized it in the playbook (play.yml)
- name: Manage localhost working area
hosts: 127.0.0.1
connection: local
tasks:
- name: Create localhost tmp file
file: path={{ item.path }} state={{ item.state }}
with_items:
- { path: '/tmp/file_num', state: 'absent' }
- { path: '/tmp/file_num', state: 'touch' }
- name: Managing tmp files
lineinfile: dest=/tmp/file_num line='0'
Then in web role in main.tml task i read the file and increment it.
- name: Get file number
local_action: shell file=$((`cat /tmp/file_num` + 1)); echo $file | tee /tmp/file_num
register: file_num
- name: Set file name
command: 'touch file{{ file_num.stdout }}'
Now i have in first host file1 and in second host file2
You can use Matt Martz's solution from here.
Basically your task would be like:
- name: Set file name
command: 'touch file{{ play_hosts.index(inventory_hostname) }}'
And you can remove all that code for maintaining global var and external file.

Resources