Skip task if user can't sudo - ansible

I am trying to run a playbook with these tasks on a few thousand servers
- name: Check root login config
shell: "egrep -i '^PermitRootLogin' /etc/ssh/sshd_config|awk '{print $2}'"
register: config_value
async : 3
become: yes
poll: 1
- name: "config value"
debug: msg="{{ inventory_hostname }} - {{ config_value.stdout }}"
They have slightly varied configs but this should work on most of them. While running it ,ansible gets stuck somewhere in the middle on some hosts where my user doesn't have passwordless sudo or sudo privileges.
I want to skip those servers where this doesn't work.Is there a way to do that ?
ansible-playbook -i hosts playbook.yml --ask-become-pass
I tried giving a wrong password too ,but it still hangs.

Ansible continues with the rest of the hosts if one task fails on one or more hosts. You could use that behaviour by provoking it before the actual tasks. Don't set become in the playbook, do this instead:
- name: Ping or fail host
become: true
ping:
- name: Check root login config
become: true
shell: "egrep -i '^PermitRootLogin' /etc/ssh/sshd_config|awk '{print $2}'"
register: config_value
async : 3
become: yes
poll: 1
- name: "config value"
debug: msg="{{ inventory_hostname }} - {{ config_value.stdout }}"

Related

How do I avoid registering a variable when a “when” condition is Not met? [duplicate]

I have the following Ansible Playbook code:
- name: Users | Generate password for user (Debian/Ubuntu)
shell: makepasswd --chars=20
register: make_password
when: ansible_distribution in ['Debian', 'Ubuntu']
- name: Users | Generate password for user (Fedora)
shell: makepasswd -m 20 -M 20
register: make_password
when: ansible_distribution in ['Fedora', 'Amazon']
- name: Users | Generate password for user (CentOS)
shell: mkpasswd -l 20
register: make_password
when: ansible_distribution in ['CentOS']
- name: debug
debug: var=make_password
Which outputs:
TASK: [users | debug]
ok: [127.0.0.1] => {
"var": {
"make_password": {
"changed": false,
"skipped": true
}
}
}
... Because every register block gets executed regardless of the when condition.
How would I fix this so make_password doesn't get overwritten when the when condition isn't met?
Or if this is the wrong approach for what you can see that I'm trying to accomplish, let me know of a better one.
Unfortunately, this is the expected behavior. From Ansible Variables
Note
If a task fails or is skipped, the variable still is registered
with a failure or skipped status, the only way to avoid registering a
variable is using tags.
I do not know how to use tags to solve your issue.
EDIT: I found a way albeit a crude solution. Store the results so that it is not overwritten
- set_fact: mypwd="{{make_password}}"
when: make_password.changed
So your code will look like:
- name: Users | Generate password for user (Debian/Ubuntu)
shell: makepasswd --chars=20
register: make_password
when: ansible_distribution in ['Debian', 'Ubuntu']
- set_fact: mypwd="{{make_password}}"
when: make_password.changed
- name: Users | Generate password for user (Fedora)
shell: makepasswd -m 20 -M 20
register: make_password
when: ansible_distribution in ['Fedora', 'Amazon']
- set_fact: mypwd="{{make_password}}"
when: make_password.changed
- name: Users | Generate password for user (CentOS)
shell: mkpasswd -l 20
register: make_password
when: ansible_distribution in ['CentOS']
- set_fact: mypwd="{{make_password}}"
when: make_password.changed
- name: debug
debug: var=mypwd
Typically for tasks that run differently on different distros I tend to include a distro specific playbook that is then conditionally included into main.yml.
So an example might look something like this:
main.yml:
- include: tasks/Debian.yml
when: ansible_distribution in ['Debian', 'Ubuntu']
- include: tasks/Fedora.yml
when: ansible_distribution in ['Fedora', 'Amazon']
- include: tasks/Centos.yml
when: ansible_distribution in ['CentOS']
- name: debug
debug: var=make_password
Debian.yml
- name: Users | Generate password for user (Debian/Ubuntu)
shell: makepasswd --chars=20
register: make_password
And obviously repeat for the other 2 distros.
This way you keep main.yml to be only running all the generic tasks for the role that can be run on any distro but then anything that needs to be different can be in a distro specific playbook. Because the include is conditional it won't even load the task if the condition isn't met so the variable should not be registered.
how about define a dict in var file?
cat vars.yml
make_password: {
'Debian':'makepasswd --chars=20',
'Ubuntu':'makepasswd --chars=20',
'Fedora':'makepasswd -m 20 -M 20',
'Amazon':'makepasswd -m 20 -M 20',
'CentOS':'mkpasswd -l 20'
}
cat test.yml
---
- hosts: "{{ host }}"
remote_user: root
vars_files:
- vars.yml
tasks:
- name: get mkpasswd
debug: var="{{ make_password[ansible_distribution] }}"
run result:
TASK: [get mkpasswd]
ok: [10.10.10.1] => {
"mkpasswd -l 20": "mkpasswd -l 20"
}
Maybe it makes sense to put all the variants into a shell script and then just run that script instead of multiple Ansible tasks.
The script can detect the OS or simply react on a passed parameter.
#!/bin/bash
case "$1" in
"Debian" | "Ubuntu")
makepasswd --chars=20
;;
"Fedora" | "Amazon")
makepasswd -m 20 -M 20
;;
"CentOS")
mkpasswd -l 20
;;
*)
echo "Unexpected distribution" 1>&2
exit 1
;;
esac
Throw this in the scripts folder of your role as make_password.sh and then call it as:
- name: Users | Generate password for user
script: make_password.sh {{ ansible_distribution }}
register: make_password
Another idea: You seem to actually generate a password remotely, register the output and then use it later in other tasks. If you can guarantee the Ansible master host always is of the same type and not every team member uses a different distribution you could simply run the task locally.
Let's say you use Ubuntu:
- name: Users | Generate password for user
shell: makepasswd --chars=20
delegate_to: localhost
register: make_password
The tasks is executed locally on the host you ran Ansible via delegate_to.

Ansible become not switching user

I'm trying to create a playbook that has to complete the following tasks:
retrieve hostnames and releases from a file file
connect to those hostnames one by one
retrieve the contents of another file another_file in each host that will give us the environment (dev, qa, prod)
so my first task is to retrieve the names of the hosts I need to connect to
- name: retrieve nodes
shell: cat file | grep ";;" | grep foo | grep -v "bar" | sort | uniq
register: nodes
- adding nodes:
add_host:
name: "{{ item }}"
group: "servers"
with_items: "{{ nodes.stdout_lines }}"
the next task connects to the hosts
- hosts: "{{ nodes }}"
become: yes
become_user: user1
become_method: sudo
gather_facts: no
tasks:
- name: check remote file
slurp:
src: /xyz/directory/another_file
register: thing
- debug: msg="{{ thing['content'] | b64decode }}"
but it doesn't work, when I add verbosity I still see the task being executed with the user running the playbook (local_user)
<node> ESTABLISH SSH CONNECTION FOR USER: local_user
what am I doing wrong?
ansible version is 2.7.1 over rhel 6.1
UPDATE
I've also used remote_user: user1
- hosts: "{{ nodes }}"
remote_user: user1
gather_facts: no
tasks:
- name: check remote file
slurp:
src: /xyz/directory/another_file
register: thing
- debug: msg="{{ thing['content'] | b64decode }}"
no luck so far, same error
You need to set remote_user, which is the one that controls what username is used when connecting to the server. Only after the connection is established, become_user is used.
https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html#hosts-and-users
if you use become_user it is like using sudo command on the remote machine.
You want to connect to your machines as a specific user. And after the connection is established issue the commands with sudo.
This question has some answers that should help you.

Pause time between hosts in the Ansible Inventory

I am trying the below task in my playbook. but the pause is not executed. i want the play should be paused for 30 sec once after the each host is deleted.
name: delete host from the NagiosXI
shell: curl -k -XDELETE "https://10.000.00.00/nagiosxi/api/v1/config/host?apikey=qdjcwc&pretty=1&host_name={{ item }}&applyconfig=1"
- pause:
seconds: 120
ignore_error: yes
with_items:
- "{{ groups['grp1'] }}"
can someone suggest if this is the right way if doing or propose me the right way. i also used serial=1 module but its still not working.
You can use pause under your loop:
- name: Pause
hosts: all
gather_facts: False
tasks:
- name: delete host from the NagiosXI
shell: curl -k -XDELETE "https://10.000.00.00/nagiosxi/api/v1/config/host?apikey=qdjcwc&pretty=1&host_name={{ item }}&applyconfig=1"
ignore_errors: True
with_items:
- "{{ groups['grp1'] }}"
loop_control:
pause: 120
Unfortunately, applying multiple tasks to with_items is not possible in Ansible at the moment, but is still doable with the include directive. As example,
The main play file would be
---
- hosts: localhost
connection: local
gather_facts: no
remote_user: me
tasks:
- include: sub_play.yml nagios_host={{ item }}
with_items:
- host1
- host2
- host3
The sub_play yml which is included in the main play would be,
---
- shell: echo "{{ nagios_host }}"
- pause:
prompt: "Waiting for {{ nagios_host }}"
seconds: 5
In this case, the include statement is executed over a loop which executes all the tasks in the sub_task yml.

Ansible writing output from multiple task to a single file

In Ansible, I have written an Yaml playbook that takes list of host name and the executes command for each host. I have registered a variable for these task and at the end of executing a task I append output of each command to a single file.
But every time I try to append to my output file, only the last record is getting persisted.
---
- hosts: list_of_hosts
become_user: some user
vars:
output: []
tasks:
- name: some name
command: some command
register: output
failed_when: "'FAILED' in output"
- debug: msg="{{output | to_nice_json}}"
- local_action: copy content='{{output | to_nice_json}}' dest="/path/to/my/local/file"
I even tried to append using lineinfile using insertafter parameter yet was not successful.
Anything that I am missing?
You can try something like this:
- name: dummy
hosts: myhosts
serial: 1
tasks:
- name: create file
file:
dest: /tmp/foo
state: touch
delegate_to: localhost
- name: run cmd
shell: echo "{{ inventory_hostname }}"
register: op
- name: append
lineinfile:
dest: /tmp/foo
line: "{{ op }}"
insertafter: EOF
delegate_to: localhost
I have used serial: 1 as I am not sure if lineinfile tasks running in parallel will garble the output file.
Ansible doc recommend use copy:
- name: get jstack
shell: "/usr/lib/jvm/java/bin/jstack -l {{PID_JAVA_APP}}"
args:
executable: /bin/bash
register: jstackOut
- name: write jstack
copy:
content: "{{jstackOut.stdout}}"
dest: "tmp/jstack.txt"
If you want write local file, add this:
delegate_to: localhost
Why to complicate things ?
I did this like that and it worked:
ansible-playbook your_playbook.yml >> /file/you/want/to/redirect/output.txt
you can also try some parsing with grep or some other stuff with tee -a.

Ansible: How do I avoid registering a variable when a "when" condition is *not* met?

I have the following Ansible Playbook code:
- name: Users | Generate password for user (Debian/Ubuntu)
shell: makepasswd --chars=20
register: make_password
when: ansible_distribution in ['Debian', 'Ubuntu']
- name: Users | Generate password for user (Fedora)
shell: makepasswd -m 20 -M 20
register: make_password
when: ansible_distribution in ['Fedora', 'Amazon']
- name: Users | Generate password for user (CentOS)
shell: mkpasswd -l 20
register: make_password
when: ansible_distribution in ['CentOS']
- name: debug
debug: var=make_password
Which outputs:
TASK: [users | debug]
ok: [127.0.0.1] => {
"var": {
"make_password": {
"changed": false,
"skipped": true
}
}
}
... Because every register block gets executed regardless of the when condition.
How would I fix this so make_password doesn't get overwritten when the when condition isn't met?
Or if this is the wrong approach for what you can see that I'm trying to accomplish, let me know of a better one.
Unfortunately, this is the expected behavior. From Ansible Variables
Note
If a task fails or is skipped, the variable still is registered
with a failure or skipped status, the only way to avoid registering a
variable is using tags.
I do not know how to use tags to solve your issue.
EDIT: I found a way albeit a crude solution. Store the results so that it is not overwritten
- set_fact: mypwd="{{make_password}}"
when: make_password.changed
So your code will look like:
- name: Users | Generate password for user (Debian/Ubuntu)
shell: makepasswd --chars=20
register: make_password
when: ansible_distribution in ['Debian', 'Ubuntu']
- set_fact: mypwd="{{make_password}}"
when: make_password.changed
- name: Users | Generate password for user (Fedora)
shell: makepasswd -m 20 -M 20
register: make_password
when: ansible_distribution in ['Fedora', 'Amazon']
- set_fact: mypwd="{{make_password}}"
when: make_password.changed
- name: Users | Generate password for user (CentOS)
shell: mkpasswd -l 20
register: make_password
when: ansible_distribution in ['CentOS']
- set_fact: mypwd="{{make_password}}"
when: make_password.changed
- name: debug
debug: var=mypwd
Typically for tasks that run differently on different distros I tend to include a distro specific playbook that is then conditionally included into main.yml.
So an example might look something like this:
main.yml:
- include: tasks/Debian.yml
when: ansible_distribution in ['Debian', 'Ubuntu']
- include: tasks/Fedora.yml
when: ansible_distribution in ['Fedora', 'Amazon']
- include: tasks/Centos.yml
when: ansible_distribution in ['CentOS']
- name: debug
debug: var=make_password
Debian.yml
- name: Users | Generate password for user (Debian/Ubuntu)
shell: makepasswd --chars=20
register: make_password
And obviously repeat for the other 2 distros.
This way you keep main.yml to be only running all the generic tasks for the role that can be run on any distro but then anything that needs to be different can be in a distro specific playbook. Because the include is conditional it won't even load the task if the condition isn't met so the variable should not be registered.
how about define a dict in var file?
cat vars.yml
make_password: {
'Debian':'makepasswd --chars=20',
'Ubuntu':'makepasswd --chars=20',
'Fedora':'makepasswd -m 20 -M 20',
'Amazon':'makepasswd -m 20 -M 20',
'CentOS':'mkpasswd -l 20'
}
cat test.yml
---
- hosts: "{{ host }}"
remote_user: root
vars_files:
- vars.yml
tasks:
- name: get mkpasswd
debug: var="{{ make_password[ansible_distribution] }}"
run result:
TASK: [get mkpasswd]
ok: [10.10.10.1] => {
"mkpasswd -l 20": "mkpasswd -l 20"
}
Maybe it makes sense to put all the variants into a shell script and then just run that script instead of multiple Ansible tasks.
The script can detect the OS or simply react on a passed parameter.
#!/bin/bash
case "$1" in
"Debian" | "Ubuntu")
makepasswd --chars=20
;;
"Fedora" | "Amazon")
makepasswd -m 20 -M 20
;;
"CentOS")
mkpasswd -l 20
;;
*)
echo "Unexpected distribution" 1>&2
exit 1
;;
esac
Throw this in the scripts folder of your role as make_password.sh and then call it as:
- name: Users | Generate password for user
script: make_password.sh {{ ansible_distribution }}
register: make_password
Another idea: You seem to actually generate a password remotely, register the output and then use it later in other tasks. If you can guarantee the Ansible master host always is of the same type and not every team member uses a different distribution you could simply run the task locally.
Let's say you use Ubuntu:
- name: Users | Generate password for user
shell: makepasswd --chars=20
delegate_to: localhost
register: make_password
The tasks is executed locally on the host you ran Ansible via delegate_to.

Resources