Ansible: Failed to restart apache2.service: Connection timed out - ansible

I am using Ansible AWX to issue a restart command to restart an apache2 service on a host. The restart command is contained in a playbook.
---
- name: Manage Linux Services
hosts: all
tasks:
- name: Restart a linux service
command: systemctl restart '{{ service_name }}'
register: result
ignore_errors: yes
- name: Show result of task
debug:
var: result
OR
---
- name: Manage Linux Services
hosts: all
tasks:
- name: Restart a linux service
ansible.builtin.service:
name: '{{ service_name }}'
state: restarted
register: result
ignore_errors: yes
- name: Show result of task
debug:
var: result
However, when I run the command, I get the error below:
"Failed to restart apache2.service: Connection timed out",
"See system logs and 'systemctl status apache2.service' for details."
I have tried to figure out the issue, but no luck yet.

I later figured the cause of the issue.
Here's how I fixed it:
The restart command requires sudo access to run which was missing in my command.
All I have to do was to add the become: true command so that I can execute the command with root privileges.
So my playbook looked like this thereafter:
---
- name: Manage Linux Services
hosts: all
tasks:
- name: Restart a linux service
command: systemctl restart '{{ service_name }}'
become: true
register: result
ignore_errors: yes
- name: Show result of task
debug:
var: result
OR
---
- name: Manage Linux Services
hosts: all
tasks:
- name: Restart a linux service
ansible.builtin.service:
name: '{{ service_name }}'
state: restarted
become: true
register: result
ignore_errors: yes
- name: Show result of task
debug:
var: result
Another way if you want to achieve this on Ansible AWX is to tick the Privilege Escalation option in the job template.
If enabled, this runs the selected playbook in the job template as an administrator.
That's all.
I hope this helps

Restarting a service requires sudo privileges. Besides adding the 'become' directive, if you would like to prompt for the password, you can do so by passing the -K flag (note: uppercase K)
$ ansible-playbook myplay.yml -i hosts -u myname --ask-pass -K

Related

How to wait for ssh to become available on a host before installing a role?

Is there a way to wait for ssh to become available on a host before installing a role? There's wait_for_connection but I only figured out how to use it with tasks.
This particular playbook spin up servers on a cloud provider before attempting to install roles. But fails since the ssh service on the hosts isn't available yet.
How should I fix this?
---
- hosts: localhost
connection: local
tasks:
- name: Deploy vultr servers
include_tasks: create_vultr_server.yml
loop: "{{ groups['vultr_servers'] }}"
- hosts: all
gather_facts: no
become: true
tasks:
- name: wait_for_connection # This one works
wait_for_connection:
delay: 5
timeout: 600
- name: Gather facts for first time
setup:
- name: Install curl
package:
name: "curl"
state: present
roles: # How to NOT install roles UNLESS the current host is available ?
- role: apache2
vars:
doc_root: /var/www/example
message: 'Hello world!'
- common-tools
Ansible play actions start with pre_tasks, then roles, followed by tasks and finally post_tasks. Move your wait_for_connection task as the first pre_tasks and it will block everything until connection is available:
- hosts: all
gather_facts: no
become: true
pre_tasks:
- name: wait_for_connection # This one works
wait_for_connection:
delay: 5
timeout: 600
roles: ...
tasks: ...
For more info on execution order, see this title in role's documentation (paragraph just above the notes).
Note: you probably want to move all your current example tasks in that section too so that facts are gathered and curl installed prior to do anything else.

Ansible playbook example code fail to run

I'm started to try Ansible, and using example code from Ansible Documentation. After I try several examples, I get error at the beginning of the code. It says
- name: Change the hostname to Windows_Ansible
^ here(Point at name)"
Any advice would be appreciate.
I tried this one
https://docs.ansible.com/ansible/latest/modules/win_hostname_module.html#win-hostname-module
---
- name: Change the hostname to Windows_Ansible
win_hostname:
name: "Windows_Ansible"
register: res
- name: Reboot
win_reboot:
when: res.reboot_required
The below task will change the hostname of the server. Make sure you run on a test server so that it wont create issues. If you just wanted to test some playbook, use the second playbook with win_command
---
- hosts: <remote server name which needs to be added in the inventory>
tasks:
- name: Change the hostname to Windows_Ansible
win_hostname:
name: "Windows_Ansible"
register: res
- name: Reboot
win_reboot:
when: res.reboot_required
---
- hosts: <remote server name which needs to be added in the inventory>
tasks:
- name: Test
win_command: whoami
register: res

Creating an idempotent playbook that uses root then disables root server access

I'm provisioning a server and hardening it by disabling root access after creating an account with escalated privs. The tasks before the account creation require root so once root has been disabled the playbook is no longer idempotent. I've discovered one way to resolve this is to use wait_for_connection with block/rescue...
- name: Secure the server
hosts: "{{ hostvars.localhost.ipv4 }}"
gather_facts: no
tasks:
- name: Block required to leverage rescue as only way I can see of avoid an already disabled root stopping the playbook
block:
- wait_for_connection:
sleep: 2 # avoid too many requests within the timeout
timeout: 5
- name: Create the service account first so that play is idempotent, we can't rely on root being enabled as it is disabled later
user:
name: swirb
password: {{password}}
shell: /bin/bash
ssh_key_file: "{{ssh_key}}"
groups: sudo
- name: Add authorized keys for service account
authorized_key:
user: swirb
key: '{{ item }}'
with_file:
- "{{ssh_key}}"
- name: Disallow password authentication
lineinfile:
dest: /etc/ssh/sshd_config
regexp: "^[\#]PasswordAuthentication"
line: "PasswordAuthentication no"
state: present
notify: Restart ssh
- name: Disable root login
replace:
path: /etc/ssh/sshd_config
regexp: 'PermitRootLogin yes'
replace: 'PermitRootLogin no'
backup: yes
become: yes
notify: Restart ssh
rescue:
- debug:
msg: "{{error}}"
handlers:
- name: Restart ssh
service: name=ssh state=restarted
This is fine until I install fail2ban as the wait_for_connection causes too many connections to the server which jails the IP. So I created a task to add the IP address of the Ansible Controller to the jail.conf like so...
- name: Install and configure fail2ban
hosts: "{{hostvars.localhost.ipv4}}"
gather_facts: no
tasks:
- name: Install fail2ban
apt:
name: "{{ packages }}"
vars:
packages:
- fail2ban
become: yes
- name: Add the IP address to the whitelist otherwise wait_for_connection triggers jailing
lineinfile: dest=/etc/fail2ban/jail.conf
regexp="^(ignoreip = (?!.*{{hostvars.localhost.ipv4}}).*)"
line="\1 <IPv4>"
state=present
backrefs=True
notify: Restart fail2ban
become: yes
handlers:
- name: Restart fail2ban
service: name=fail2ban state=restarted
become: yes
This works but I have to hard wire the Ansible Controller IPv4. There doesn't seem to be a standard way of obtaining the IP address of the Ansible Controller.
I'm also not that keen on adding the controller to every server white list.
Is there a cleaner way of creating an idempotent provisioning playbook?
Otherwise, how do I get the Ansible Controller IP address?

ansible check non-existent service [duplicate]

I have an Ansible playbook for deploying a Java app as an init.d daemon.
Being a beginner in both Ansible and Linux I'm having trouble to conditionally execute tasks on a host based on the host's status.
Namely I have some hosts having the service already present and running where I want to stop it before doing anything else. And then there might be new hosts, which don't have the service yet. So I can't simply use service: name={{service_name}} state=stopped, because this will fail on new hosts.
How I can I achieve this? Here's what I have so far:
- name: Check if Service Exists
shell: "if chkconfig --list | grep -q my_service; then echo true; else echo false; fi;"
register: service_exists
# This should only execute on hosts where the service is present
- name: Stop Service
service: name={{service_name}} state=stopped
when: service_exists
register: service_stopped
# This too
- name: Remove Old App Folder
command: rm -rf {{app_target_folder}}
when: service_exists
# This should be executed on all hosts, but only after the service has stopped, if it was present
- name: Unpack App Archive
unarchive: src=../target/{{app_tar_name}} dest=/opt
See the service_facts module, new in Ansible 2.5.
- name: Populate service facts
service_facts:
- debug:
msg: Docker installed!
when: "'docker' in services"
Of course I could also just check if the wrapper script exists in /etc/init.d. So this is what I ended up with:
- name: Check if Service Exists
stat: path=/etc/init.d/{{service_name}}
register: service_status
- name: Stop Service
service: name={{service_name}} state=stopped
when: service_status.stat.exists
register: service_stopped
I modified Florian's answer to only use the return code of the service command (this worked on Mint 18.2)
- name: Check if Logstash service exist
shell: service logstash status
register: logstash_status
failed_when: not(logstash_status.rc == 3 or logstash_status.rc == 0)
- name: Check if Logstash service exist
service:
name: logstash
state: stopped
when: logstash_status.rc == 0
It would be nice if the "service" module could handle "unrecognized service" errors.
This is my approach, using the service command instead of checking for an init script:
- name: check for apache
shell: "service apache2 status"
register: _svc_apache
failed_when: >
_svc_apache.rc != 0 and ("unrecognized service" not in _svc_apache.stderr)
- name: disable apache
service: name=apache2 state=stopped enabled=no
when: "_svc_apache.rc == 0"
check the exit code of "service status" and accept the exit code 0 when the output contains "unrecognized service"
if the exit code was 0, that service is installed (stopped or running)
Another approach for systemd (from Jakuje):
- name: Check if cups-browsed service exists
command: systemctl cat cups-browsed
check_mode: no
register: cups_browsed_exists
changed_when: False
failed_when: cups_browsed_exists.rc not in [0, 1]
- name: Stop cups-browsed service
systemd:
name: cups-browsed
state: stopped
when: cups_browsed_exists.rc == 0
Building on #Maciej's answer for RedHat 8, and combining it with the comments made on it.
This is how I managed to stop Celery only if it has already been installed:
- name: Populate service facts
service_facts:
- debug:
msg: httpd installed!
when: ansible_facts.services['httpd.service'] is defined
- name: Stop celery.service
service:
name: celery.service
state: stopped
enabled: true
when: ansible_facts.services['celery.service'] is defined
You can drop the debug statement--it's there just to confirm that ansible_facts is working.
This way using only the service module has worked for us:
- name: Disable *service_name*
service:
name: *service_name*
enabled: no
state: stopped
register: service_command_output
failed_when: >
service_command_output|failed
and 'unrecognized service' not in service_command_output.msg
and 'Could not find the requested service' not in service_command_output.msg
My few cents. The same approach as above but for kubernetes
Check if kublete service is running
- name: "Obtain state of kublet service"
command: systemctl status kubelet.service
register: kubelet_status
failed_when: kubelet_status.rc > 3
Display debug message if kublet service is not running
- debug:
msg: "{{ kubelet_status.stdout }}"
when: "'running' not in kubelet_status.stdout"
You can use the service_facts module since Ansible 2.5. But you need to know that the output are the real name of the service like docker.service or somthing#.service. So you have different options like:
- name: Populate service facts
service_facts:
- debug:
msg: Docker installed!
when: "'docker.service' in services"
Or you can search for the string beginning with the service name; that is much more reliable, because the service names are different between the distributions:
- name: Populate service facts
service_facts:
- debug:
msg: Docker installed!
when: "services.keys()|list|select('search', '^docker')|length >0"

Ansible Playbook not resolving variable correctly

I am using ansible and having issues with running the playbook
site.yml:
---
- name: Test Ansible Playbook
hosts: '{{ myhosts }}'
sudo: no
roles:
- myRole
And the myRole file:
---
- name: Node script
hosts: '{{ myhosts }}'
sudo: no
tasks:
- name: Start Tomcat
service: name=tomcat state=started enabled=yes
And when I try running the command: ansible-playbook "-e 'myhosts=myHostName'" site.yml
I get the following error:
ERROR: hosts is not a legal parameter in an Ansible task or handler
ERROR: hosts is not a legal parameter in an Ansible task or handler
And that is the problem. Inside a tasks file of a role you may define tasks - nothing else. Which hosts these tasks will be executed on is defined in the playbook.
Your tasks file should only contain this:
---
- name: Start Tomcat
service: name=tomcat
state=started
enabled=yes
...

Resources