Ansible Firefighting Hotline Series (6): Automated Inspection of Key Linux Services

Ansible Firefighting Hotline Series (6): Automated Inspection of Key Linux Services

Ansible Firefighting Hotline Series (6): Automated Inspection of Key Linux Services (Easily Customizable)

Is your server down again? Don’t panic! This Ansible inspection “robot” can pinpoint the problematic service with one click!

<span><span>#Operations</span></span><span><span>#Automation</span></span><span><span>#Ansible</span></span><span><span>#ServerInspection</span></span><span><span>#Linux</span></span><span><span>#EfficiencyTool</span></span>

πŸ’‘ Thought Upgrade: From “Functional” to “Excellent”, Why Do We Choose Roles?πŸ’‘

Before we dive into practical applications, let’s discuss a deeper topic: Why do we bother to refactor a working Playbook into a more complex structure called a Role?

The answer is simple: We pursue not just functional automation, but maintainable, scalable, and collaborative excellent automation. This is a key step from amateur to professional.

1. Embrace Simplicity ✨ A lengthy Playbook is like a book without chapters; it’s exhausting to read. Roles break down a complex task (like service inspection) into several standard parts:

β€’<span><span>tasks/</span></span>: Responsible for what to do.β€’<span><span>defaults/</span></span>: Responsible for defining adjustable variables.β€’<span><span>handlers/</span></span>: Responsible for responding to events. This way, your main playbook <span><span>site.yml</span></span> becomes extremely concise, like a senior manager who only needs to call which Role without worrying about the details. The entire automation process is clear at a glance.

2. Achieve Decoupling πŸ”— This is one of the core values of Roles. We perfectly separate “what to check” (What – variables, such as service lists) from “how to check” (How – task logic) through the <span><span>defaults/main.yml</span></span> file.

β€’When requirements change: For example, if you need to add a new service <span><span>nginx</span></span> to the inspection list, you only need to modify the variable file <span><span>defaults/main.yml</span></span>, without touching the core task logic <span><span>tasks/main.yml</span></span>. This greatly reduces the risk of introducing new errors due to modifications.

3. Pursue Reusability ♻️ A well-written Role is a reusable “automation building block”. The <span><span>service_check</span></span> Role you create today can be reused in countless scenarios:

β€’Daily health inspections.β€’Environment checks before application releases.β€’Status verification after security baseline hardening. You only need to call this complete and reliable inspection capability in any main playbook with one line of code: <span><span>roles: - service_check</span></span>.

4. Empower the Team πŸš€ When automation is confined to a single Playbook file, it is merelyan individual tool. When you start building and using Roles, it transcends intoa team asset.

β€’Knowledge Accumulation: Best practices are solidified in Roles, becoming shared, tested “standard components” for the team.β€’Lowering Barriers: New members no longer need to understand complex script details; they only need to learn how to call these standardized Roles to quickly contribute value to the team.β€’Accelerated Collaboration: Team members can collaborate, with some responsible for developing and maintaining foundational Roles (like <span><span>apache</span></span>, <span><span>mysql</span></span><code><span><span>), while others orchestrate these Roles to complete complex business deployments.</span></span><p><span><span>In summary, using Roles is our path to drive automation from</span></span><strong><span><span>individual heroism</span></span></strong><span><span> towards</span></span><strong><span><span>team-oriented, engineered, and sustainable development</span></span></strong><span><span>.</span></span></p><p><span><span>Hello to all the heroes fighting on the front lines of operations! πŸ‘‹</span></span></p><p><strong><span><span>Ansible Firefighting Hotline</span></span></strong><span><span> is back online, and this time we are addressing the most heart-pounding issueβ€”</span></span><strong><span><span>core service status inspection</span></span></strong><span><span>!</span></span></p><p><span><span>Have you ever experienced such a terrifying moment:</span></span></p><span><span>β€’</span></span><span><span>Woken up by an alarm call in the middle of the night, only to find that a critical service (like </span></span><code><span><span>sshd</span></span>) is down? πŸ˜±β€’Your boss requests a status report on services like <span><span>firewalld</span></span>, <span><span>chronyd</span></span> across hundreds of servers, and you can only log in one by one to execute <span><span>systemctl status</span></span> and manually compile the results? πŸ‘¨πŸ’»πŸ’¦β€’The system is unstable, and you want to quickly troubleshoot whether the basic services are the problem, but you don’t know where to start? πŸ€”

Today, we bring you a powerful toolβ€”a reusable Ansible Role that acts like an intelligent inspection robot, automatically logging into all servers, checking all specified critical services, and collecting detailed reports!

🀩 Inspection Report, A Sneak Peek!

After executing this Role, you will find a dedicated report generated for each server in the <span><span>/tmp/</span></span> directory, with clear and straightforward content:

# RHEL Key Service Status Check Report for rhel9.example.com
# Generated at: 2025-06-14T12:00:00Z
# ---
[Service Status Report: sshd]
  Service Name: sshd.service
  Current Status: RUNNING
  Auto-start Configuration: ENABLED
  Service Source: systemd
[Service Status Report: firewalld]
  Service Name: firewalld.service
  Current Status: RUNNING
  Auto-start Configuration: ENABLED
  Service Source: systemd
[Service Status Report: imaginary-service]
  [CRITICAL] Service imaginary-service is not installed or recognized by systemd!
...

It clearly indicates which services are not running and which services are not installed!

πŸ”₯ Evolution! From Playbook to Professional Role!

To make this inspection tool more professional and user-friendly, we have refactored it from a standalone <span><span>.yml</span></span> file into a standard Ansible Role. The benefits of this approach are:

β€’Structured: Logic is clearer, and files serve their respective purposes.β€’Configurable: Want to check different services? Just modify the variable file without touching the core logic!β€’Reusable: This Role can be easily integrated into any other automation project!

πŸš€ Foolproof Getting Started Guide (Three Steps to Success)

Step 1: πŸ“‚ Create Role Directory Structure

First, create a standard Role directory structure. You can quickly generate it using the <span><span>ansible-galaxy init roles/service_check</span></span> command or manually create the following directories and files as shown:

.
β”œβ”€β”€ inventory                  # Your server inventory
β”œβ”€β”€ site.yml                   # Main playbook, responsible for calling Roles
└── roles/
    └── service_check/
        β”œβ”€β”€ defaults/
        β”‚   └── main.yml       # πŸ‘ˆ Define the list of services you want to check here!
        └── tasks/
            └── main.yml       # Core task logic of the Role

Step 2: ✍️ Fill in the File Content

Fill in the prepared code into the corresponding files.

1. Role Default Variables: <span><span>roles/service_check/defaults/main.yml</span></span> Here, you can customize the list of services you want to inspect!

---
# roles/service_check/defaults/main.yml
# Define the list of services you want to inspect here, feel free to add or remove! (Please ensure the service names are correct!)
critical_services_list:
  - sshd
  - firewalld
  - chronyd
  - tuned
  - NetworkManager
  - rsyslog
  - auditd
  - crond
  - dbus
  - systemd-journald

# Define the path for the report
report_base_path: "/tmp"
collection_dest_path: "/tmp/"

2. Role Core Tasks: <span><span>roles/service_check/tasks/main.yml</span></span> This is the core logic of the inspection robot.

---
# roles/service_check/tasks/main.yml
- name: Stage 1.1 - Define the complete path for the report
  ansible.builtin.set_fact:
    report_file_path: "{{ report_base_path }}/linux_service_check_{{ inventory_hostname }}.txt"

- name: Stage 1.2 - Clean up old report files on remote hosts
  ansible.builtin.file:
    path: "{{ report_file_path }}"
    state: absent

- name: Stage 1.3 - Collect necessary Facts (date and time)
  ansible.builtin.setup:
    filter:
      - ansible_date_time

- name: Stage 1.4 - Efficiently collect all service information
  ansible.builtin.service_facts:
  register: service_facts_result

- name: Stage 1.5 - Create report file on remote host and write title
  ansible.builtin.lineinfile:
    path: "{{ report_file_path }}"
    line: |
      # RHEL Key Service Status Check Report for {{ inventory_hostname }}
      # Generated at: {{ ansible_date_time.iso8601 }}
      # ---
    create: true
    mode: '0644'

- name: Stage 1.6 - Loop through and check service status, writing to report
  ansible.builtin.lineinfile:
    path: "{{ report_file_path }}"
    insertafter: EOF
    create: false
    line: |
      {% set service_full_name = item + '.service' %}
      {% if service_full_name in service_facts_result.ansible_facts.services %}
      [Service Status Report: {{ item }}]
        Service Name: {{ service_facts_result.ansible_facts.services[service_full_name].name | default('N/A') }}
        Current Status: {{ service_facts_result.ansible_facts.services[service_full_name].state | upper | default('N/A') }}
        Auto-start Configuration: {{ service_facts_result.ansible_facts.services[service_full_name].status | upper | default('N/A') }}
        Service Source: {{ service_facts_result.ansible_facts.services[service_full_name].source | default('N/A') }}
      {% else %}
      [Service Status Report: {{ item }}]
        [CRITICAL] Service {{ item }} is not installed or recognized by systemd!
      {% endif %}
  loop: "{{ critical_services_list }}"

3. Main Playbook: <span><span>site.yml</span></span> This file is now super simple; it only needs to “issue commands”.

---
- name: Play 1 - Execute service inspection on all hosts and generate reports
  hosts: all
  become: true
  gather_facts: false # Role will collect as needed
  roles:
    - service_check

- name: Play 2 - Collect reports back to control node
  hosts: all
  gather_facts: false
  # 🚨 Bug fix: Define the variables needed for this Play, as this Play does not load the Role and cannot know the variables defined in the Role
  vars:
    report_base_path: "/tmp"
    collection_dest_path: "/tmp/"
  tasks:
    - name: Pull report files from each target host
      ansible.builtin.fetch:
        src: "{{ report_base_path }}/linux_service_check_{{ inventory_hostname }}.txt"
        dest: "{{ collection_dest_path }}"
        flat: yes

- name: Play 3 - Final Confirmation
  hosts: localhost
  connection: local
  gather_facts: false
  tasks:
    - name: Display final summary information on console
      ansible.builtin.debug:
        msg:
          - "βœ… All inspection reports have been successfully collected!"
          - "Reports are stored in the /tmp/ directory of the controller."

Step 3: πŸš€ Run!

In the terminal, navigate to the project root directory and run the final command:

[root@ansible25 service_check]# ansible-playbook -i inventory site.yml
PLAY [Play 1 - Execute service inspection on all hosts and generate reports] 
************************************************
TASK [service_check : Stage 1.1 - Define the complete path for the report] 
ok: [Test-RHEL-7.9-1]
************************************************
TASK [service_check : Stage 1.2 - Clean up old report files on remote hosts] 
changed: [Test-RHEL-7.9-1]
************************************************
TASK [service_check : Stage 1.3 - Collect necessary Facts (date and time)] 
ok: [Test-RHEL-7.9-1]
************************************************
TASK [service_check : Stage 1.4 - Efficiently collect all service information] 
ok: [Test-RHEL-7.9-1]
************************************************
TASK [service_check : Stage 1.5 - Create report file on remote host and write title] 
changed: [Test-RHEL-7.9-1]
************************************************
TASK [service_check : Stage 1.6 - Loop through and check service status, writing to report] 
************************************************
changed: [Test-RHEL-7.9-1] => (item=sshd)
changed: [Test-RHEL-7.9-1] => (item=firewalld)
changed: [Test-RHEL-7.9-1] => (item=chronyd)
changed: [Test-RHEL-7.9-1] => (item=tuned)
changed: [Test-RHEL-7.9-1] => (item=NetworkManager)
changed: [Test-RHEL-7.9-1] => (item=rsyslog)
changed: [Test-RHEL-7.9-1] => (item=auditd)
changed: [Test-RHEL-7.9-1] => (item=crond)
changed: [Test-RHEL-7.9-1] => (item=dbus)
changed: [Test-RHEL-7.9-1] => (item=systemd-journald)
************************************************
PLAY [Play 2 - Collect reports back to control node] 
************************************************
TASK [Pull report files from each target host] 
changed: [Test-RHEL-7.9-1]
************************************************
PLAY [Play 3 - Final Confirmation] 
************************************************
TASK [Display final summary information on console] 
ok: [localhost] =>
  msg:
  - "βœ… All inspection reports have been successfully collected!"
  - "Reports are stored in the /tmp/ directory of the controller."
************************************************
PLAY RECAP
Test-RHEL-7.9-1 : ok=7 changed=4 unreachable=0 failed=0 ...


[root@ansible25 service_check]# more /tmp/linux_service_check_Test-RHEL-7.9-1.txt
# RHEL Key Service Status Check Report for Test-RHEL-7.9-1
# Generated at: 2025-07-27T23:02:20Z
# ---

[Service Status Report: sshd]
  Service Name: sshd.service
  Current Status: RUNNING
  Auto-start Configuration: ENABLED
  Service Source: systemd

[Service Status Report: firewalld]
  Service Name: firewalld.service
  Current Status: INACTIVE
  Auto-start Configuration: DISABLED
  Service Source: systemd

[Service Status Report: chronyd]
  Service Name: chronyd.service
  Current Status: RUNNING
  Auto-start Configuration: ENABLED
  Service Source: systemd

[Service Status Report: tuned]
  Service Name: tuned.service
  Current Status: RUNNING
  Auto-start Configuration: ENABLED
  Service Source: systemd

[Service Status Report: NetworkManager]
  Service Name: NetworkManager.service
  Current Status: RUNNING
  Auto-start Configuration: ENABLED
  Service Source: systemd

[Service Status Report: rsyslog]
  Service Name: rsyslog.service
  Current Status: RUNNING
  Auto-start Configuration: ENABLED
  Service Source: systemd

[Service Status Report: auditd]
  Service Name: auditd.service
  Current Status: RUNNING
  Auto-start Configuration: ENABLED
  Service Source: systemd

[Service Status Report: crond]
  Service Name: crond.service
  Current Status: RUNNING
  Auto-start Configuration: ENABLED
  Service Source: systemd

[Service Status Report: dbus]
  Service Name: dbus.service
  Current Status: RUNNING
  Auto-start Configuration: STATIC
  Service Source: systemd

[Service Status Report: systemd-journald]
  Service Name: systemd-journald.service
  Current Status: RUNNING
  Auto-start Configuration: STATIC
  Service Source: systemd

All done! Now, your automated inspection system is fully set up!

πŸš€ Summary Moment: The “Treasure Insights” of Operations Personnel

This refactoring from Playbook to Role is not just a relocation of code, but an elevation of the philosophy of operational automation:

1Embrace Roles, Embrace the Future: Roles are the essence of Ansible. They make your automation code modular, configurable, and shareable. A Role you write today may become the cornerstone of another major project for you or your colleagues tomorrow.2Defaults (<span><span>defaults</span></span>) are Best Practices: Placing variable parts (like service lists) in <span><span>defaults/main.yml</span></span><span><span> is the golden rule of Role design. It provides users with great flexibility while maintaining the stability of core logic.</span></span><span><span>3</span></span><strong><code><span><span>service_facts</span></span> is the Inspection Tool: This module can retrieve the status of all services at once, which is much more efficient than calling the <span><span>systemd</span></span><span><span> module one by one in a loop, exemplifying performance optimization.</span></span><span><span>4</span></span><strong><span><span>Phased Execution, Clear Logic</span></span></strong><span><span>: Retaining the three-part playbook structure of "Generate -> Collect -> Confirm" ensures clarity and reliability in the workflow. Let all nodes complete their tasks first, then collect the results uniformly; this is a classic model for distributed task processing.</span></span><p><span><span>Do you feel like you’ve grasped the correct way to use enterprise-level Ansible?</span></span></p><p><span><span>I have packaged this ready-to-use, structurally complete </span></span><strong><span><span>Ansible Role</span></span></strong><span><span> for you!</span></span></p><p><span><span>πŸ‘‡πŸ‘‡πŸ‘‡</span></span></p><p><strong><span><span>Click on the "Read Original" below to get the complete Role source package!</span></span></strong></p><p><span><span>To access the original text, please scroll to the bottom of the page.</span></span></p><p><span><span>Hurry up and deploy a tireless automated inspection robot for you and your team! See you next time! πŸ˜‰</span></span></p>

Leave a Comment