¿Cómo mostrar la salida de comandos shell en playbooks de Ansible?

AnsibleIntermediate
Practicar Ahora

Introducción

Ansible es una poderosa herramienta de automatización de código abierto ampliamente utilizada por administradores de sistemas y profesionales de DevOps. Una de sus capacidades clave es la ejecución de comandos de shell en hosts remotos y el procesamiento de su salida. En este tutorial práctico, aprenderá a capturar, mostrar y procesar eficazmente la salida de los comandos de shell en los playbooks de Ansible. Esta habilidad es esencial para crear flujos de trabajo de automatización robustos que puedan adaptarse a diferentes condiciones del sistema y proporcionar retroalimentación útil.

Este es un Guided Lab, que proporciona instrucciones paso a paso para ayudarte a aprender y practicar. Sigue las instrucciones cuidadosamente para completar cada paso y obtener experiencia práctica. Los datos históricos muestran que este es un laboratorio de nivel intermedio con una tasa de finalización del 53%. Ha recibido una tasa de reseñas positivas del 100% por parte de los estudiantes.

Configuración de su Primer Playbook de Ansible con Comandos de Shell

En este paso, configuraremos un playbook básico de Ansible que ejecuta comandos de shell y captura su salida. Esto proporcionará la base para técnicas más avanzadas en pasos posteriores.

Instalación de Ansible

Primero, instalemos Ansible en nuestro sistema:

sudo apt update
sudo apt install -y ansible

Ahora, verifique que Ansible esté instalado correctamente:

ansible --version

Debería ver una salida similar a esta:

ansible [core 2.12.x]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/labex/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  ansible collection location = /home/labex/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.10.x (default, Mar 15 2022, 12:22:08) [GCC 11.2.0]
  jinja version = 3.0.3
  libyaml = True

Creación de un Archivo de Inventario

Ansible utiliza un archivo de inventario para saber qué hosts administrar. Para este laboratorio, crearemos un inventario simple que incluye solo la máquina local:

  1. Abra su WebIDE y cree un nuevo archivo llamado inventory.ini en el directorio /home/labex/project.
  2. Agregue el siguiente contenido al archivo:
[local]
localhost ansible_connection=local

Este inventario define un grupo llamado local que contiene solo el localhost, e indica a Ansible que se conecte directamente sin SSH.

Creación de su Primer Playbook

Ahora, creemos un playbook simple que ejecuta comandos de shell:

  1. Cree un nuevo archivo llamado first_playbook.yml en el directorio /home/labex/project.
  2. Agregue el siguiente contenido YAML al archivo:
---
- name: Shell Command Example
  hosts: local
  gather_facts: no

  tasks:
    - name: Run a simple shell command
      shell: echo "Hello from Ansible shell command"
      register: hello_output

    - name: Display the output
      debug:
        msg: "{{ hello_output.stdout }}"

Este playbook hace lo siguiente:

  • Se dirige al grupo local que definimos en nuestro inventario
  • Omite la recopilación de datos (información del sistema) para simplificar las cosas
  • Ejecuta un comando de shell que muestra un mensaje de saludo
  • Almacena la salida en una variable utilizando la palabra clave register
  • Muestra la salida utilizando el módulo debug

Ejecución de su Playbook

Ahora, ejecutemos el playbook:

ansible-playbook -i inventory.ini first_playbook.yml

Debería ver una salida similar a esta:

PLAY [Shell Command Example] **************************************************

TASK [Run a simple shell command] *********************************************
changed: [localhost]

TASK [Display the output] *****************************************************
ok: [localhost] => {
    "msg": "Hello from Ansible shell command"
}

PLAY RECAP ********************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

La salida muestra que nuestro playbook se ejecutó correctamente, ejecutando el comando de shell y mostrando su salida.

Conceptos Clave a Comprender

De este ejercicio, tenga en cuenta estos conceptos importantes:

  1. El módulo shell le permite ejecutar comandos de shell
  2. La directiva register captura la salida de una tarea en una variable
  3. El módulo debug ayuda a mostrar los valores de las variables
  4. La salida del comando de shell contiene tanto stdout (salida estándar) como stderr (salida de error)

En el siguiente paso, exploraremos cómo procesar y formatear la salida del comando de shell de manera más efectiva.

Trabajando con la Salida Estructurada de Comandos de Shell

Ahora que comprende los conceptos básicos de la ejecución de comandos de shell en Ansible, exploremos cómo trabajar con la salida estructurada de comandos y mostrarla en diferentes formatos.

Procesamiento de Información del Sistema

Creemos un playbook más práctico que recopile información del sistema y la presente de forma estructurada:

  1. Cree un nuevo archivo llamado system_info.yml en el directorio /home/labex/project.
  2. Agregue el siguiente contenido:
---
- name: Gather and Display System Information
  hosts: local
  gather_facts: no

  tasks:
    - name: Gather system information
      shell: |
        echo "OS Information: $(cat /etc/os-release | grep PRETTY_NAME | cut -d= -f2)"
        echo "Kernel Version: $(uname -r)"
        echo "CPU Information: $(grep "model name" /proc/cpuinfo | head -1 | cut -d: -f2 | xargs)"
        echo "Memory Information: $(free -h | grep Mem | awk '{print $2}')"
      register: system_info

    - name: Display raw system information
      debug:
        msg: "{{ system_info.stdout }}"

    - name: Display information as a list
      debug:
        msg: "{{ system_info.stdout_lines }}"

Este playbook:

  • Ejecuta un script de shell de varias líneas que recopila varios detalles del sistema
  • Almacena la salida en la variable system_info
  • Muestra la salida primero como una cadena sin formato y luego como una lista de líneas

Ejecute el playbook:

ansible-playbook -i inventory.ini system_info.yml

Debería ver una salida que se parece a esto:

PLAY [Gather and Display System Information] **********************************

TASK [Gather system information] **********************************************
changed: [localhost]

TASK [Display raw system information] *****************************************
ok: [localhost] => {
    "msg": "OS Information: \"Ubuntu 22.04.1 LTS\"\nKernel Version: 5.15.0-1023-azure\nCPU Information: Intel(R) Xeon(R) Platinum 8171M CPU @ 2.60GHz\nMemory Information: 4.0Gi"
}

TASK [Display information as a list] *****************************************
ok: [localhost] => {
    "msg": [
        "OS Information: \"Ubuntu 22.04.1 LTS\"",
        "Kernel Version: 5.15.0-1023-azure",
        "CPU Information: Intel(R) Xeon(R) Platinum 8171M CPU @ 2.60GHz",
        "Memory Information: 4.0Gi"
    ]
}

PLAY RECAP ********************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Observe cómo la segunda tarea de visualización muestra la salida como una lista en lugar de una cadena con saltos de línea. Este formato de lista facilita el trabajo con la salida de varias líneas.

Trabajando con Resultados de Comandos y Códigos de Retorno

Los comandos de shell en Linux devuelven códigos de salida para indicar éxito (0) o fracaso (distinto de cero). Ansible los captura en el atributo rc (código de retorno) de la variable registrada.

Creemos un playbook que demuestre cómo trabajar con códigos de retorno:

  1. Cree un nuevo archivo llamado command_results.yml en el directorio /home/labex/project.
  2. Agregue el siguiente contenido:
---
- name: Working with Command Results
  hosts: local
  gather_facts: no

  tasks:
    - name: Check if a file exists
      shell: test -f /etc/hosts
      register: file_check
      ignore_errors: yes

    - name: Show command result details
      debug:
        msg: |
          Return code: {{ file_check.rc }}
          Succeeded: {{ file_check.rc == 0 }}
          Failed: {{ file_check.rc != 0 }}

    - name: Check if a non-existent file exists
      shell: test -f /file/does/not/exist
      register: missing_file
      ignore_errors: yes

    - name: Show command result for missing file
      debug:
        msg: |
          Return code: {{ missing_file.rc }}
          Succeeded: {{ missing_file.rc == 0 }}
          Failed: {{ missing_file.rc != 0 }}

Este playbook:

  • Ejecuta dos comandos de prueba para verificar si existen archivos
  • Utiliza ignore_errors: yes para evitar que el playbook se detenga si un comando falla
  • Muestra información detallada sobre los resultados del comando, incluido el código de retorno y el estado de éxito/fracaso

Ejecute el playbook:

ansible-playbook -i inventory.ini command_results.yml

Debería ver una salida similar a esta:

PLAY [Working with Command Results] *******************************************

TASK [Check if a file exists] *************************************************
changed: [localhost]

TASK [Show command result details] ********************************************
ok: [localhost] => {
    "msg": "Return code: 0\nSucceeded: True\nFailed: False\n"
}

TASK [Check if a non-existent file exists] ************************************
fatal: [localhost]: FAILED! => {"changed": true, "cmd": "test -f /file/does/not/exist", "delta": "0:00:00.003183", "end": "2023-07-14 15:24:33.931406", "msg": "non-zero return code", "rc": 1, "start": "2023-07-14 15:24:33.928223", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
...ignoring

TASK [Show command result for missing file] ***********************************
ok: [localhost] => {
    "msg": "Return code: 1\nSucceeded: False\nFailed: True\n"
}

PLAY RECAP ********************************************************************
localhost                  : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=1

Observe cómo el código de retorno es 0 para el archivo existente y 1 para el archivo no existente. Esto demuestra cómo puede usar los códigos de retorno para tomar decisiones en sus playbooks.

Comprensión de la Estructura de la Variable Registrada

La variable registrada desde un comando de shell contiene varios atributos útiles:

  • stdout: La salida estándar como una sola cadena
  • stdout_lines: La salida estándar dividida en una lista de líneas
  • stderr: La salida de error estándar como una sola cadena
  • stderr_lines: La salida de error estándar dividida en una lista de líneas
  • rc: El código de retorno (0 para éxito, distinto de cero para fracaso)
  • cmd: El comando que se ejecutó
  • start y end: Marcas de tiempo para cuando el comando comenzó y terminó
  • delta: La duración de la ejecución del comando

Comprender esta estructura es crucial para trabajar eficazmente con la salida de comandos de shell en Ansible.

Ejecución Condicional Basada en la Salida del Shell

Una de las capacidades más poderosas de Ansible es la capacidad de tomar decisiones basadas en la salida de los comandos de shell. En este paso, aprenderemos a usar condiciones y filtros para procesar la salida de los comandos de shell y hacer que los playbooks sean más dinámicos.

Uso de Condiciones con la Salida del Shell

Creemos un playbook que tome decisiones basadas en la salida del comando de shell:

  1. Cree un nuevo archivo llamado conditional_playbook.yml en el directorio /home/labex/project.
  2. Agregue el siguiente contenido:
---
- name: Conditional Tasks Based on Command Output
  hosts: local
  gather_facts: no

  tasks:
    - name: Check disk space
      shell: df -h / | grep -v Filesystem | awk '{print $5}' | sed 's/%//'
      register: disk_usage

    - name: Display disk usage
      debug:
        msg: "Current disk usage: {{ disk_usage.stdout }}%"

    - name: Disk usage warning
      debug:
        msg: "WARNING: Disk usage is high"
      when: disk_usage.stdout|int > 50

    - name: Disk usage normal
      debug:
        msg: "Disk usage is normal"
      when: disk_usage.stdout|int <= 50

Este playbook:

  • Ejecuta un comando de shell para verificar el porcentaje de uso del disco en el sistema de archivos raíz
  • Utiliza la condición when basada en la salida del comando
  • Utiliza el filtro int para convertir la salida de la cadena en un entero para la comparación

Ejecute el playbook:

ansible-playbook -i inventory.ini conditional_playbook.yml

La salida variará según el uso real de su disco, pero se verá algo como esto:

PLAY [Conditional Tasks Based on Command Output] ******************************

TASK [Check disk space] *******************************************************
changed: [localhost]

TASK [Display disk usage] *****************************************************
ok: [localhost] => {
    "msg": "Current disk usage: 38%"
}

TASK [Disk usage warning] *****************************************************
skipped: [localhost]

TASK [Disk usage normal] ******************************************************
ok: [localhost] => {
    "msg": "Disk usage is normal"
}

PLAY RECAP ********************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

Observe cómo Ansible ejecutó solo una de las tareas condicionales en función del valor real del uso del disco.

Manejo de la Salida JSON de los Comandos

Muchas herramientas CLI modernas devuelven datos en formato JSON. Ansible tiene capacidades integradas para manejar la salida JSON:

  1. Cree un nuevo archivo llamado json_output.yml en el directorio /home/labex/project.
  2. Agregue el siguiente contenido:
---
- name: Handling JSON Output
  hosts: local
  gather_facts: no

  tasks:
    - name: Create a JSON file for testing
      copy:
        dest: /tmp/services.json
        content: |
          {
            "services": [
              {
                "name": "web",
                "status": "running",
                "port": 80
              },
              {
                "name": "database",
                "status": "stopped",
                "port": 5432
              },
              {
                "name": "cache",
                "status": "running",
                "port": 6379
              }
            ]
          }

    - name: Read JSON file with shell
      shell: cat /tmp/services.json
      register: json_output

    - name: Parse and display JSON content
      debug:
        msg: "{{ json_output.stdout | from_json }}"

    - name: Extract and display service information
      debug:
        msg: "Service: {{ item.name }}, Status: {{ item.status }}, Port: {{ item.port }}"
      loop: "{{ (json_output.stdout | from_json).services }}"

    - name: Show only running services
      debug:
        msg: "Running service: {{ item.name }} on port {{ item.port }}"
      loop: "{{ (json_output.stdout | from_json).services }}"
      when: item.status == "running"

Este playbook:

  • Crea un archivo JSON de muestra para la demostración
  • Lee el archivo JSON con un comando de shell
  • Utiliza el filtro from_json para analizar la cadena JSON en una estructura de datos
  • Itera a través de la estructura de datos para mostrar información
  • Utiliza la lógica condicional para filtrar solo los servicios en ejecución

Ejecute el playbook:

ansible-playbook -i inventory.ini json_output.yml

Debería ver una salida similar a esta:

PLAY [Handling JSON Output] ***************************************************

TASK [Create a JSON file for testing] *****************************************
changed: [localhost]

TASK [Read JSON file with shell] **********************************************
changed: [localhost]

TASK [Parse and display JSON content] *****************************************
ok: [localhost] => {
    "msg": {
        "services": [
            {
                "name": "web",
                "port": 80,
                "status": "running"
            },
            {
                "name": "database",
                "port": 5432,
                "status": "stopped"
            },
            {
                "name": "cache",
                "port": 6379,
                "status": "running"
            }
        ]
    }
}

TASK [Extract and display service information] ********************************
ok: [localhost] => (item={'name': 'web', 'status': 'running', 'port': 80}) => {
    "msg": "Service: web, Status: running, Port: 80"
}
ok: [localhost] => (item={'name': 'database', 'status': 'stopped', 'port': 5432}) => {
    "msg": "Service: database, Status: stopped, Port: 5432"
}
ok: [localhost] => (item={'name': 'cache', 'status': 'running', 'port': 6379}) => {
    "msg": "Service: cache, Status: running, Port: 6379"
}

TASK [Show only running services] *********************************************
ok: [localhost] => (item={'name': 'web', 'status': 'running', 'port': 80}) => {
    "msg": "Running service: web on port 80"
}
skipped: [localhost] => (item={'name': 'database', 'status': 'stopped', 'port': 5432})
ok: [localhost] => (item={'name': 'cache', 'status': 'running', 'port': 6379}) => {
    "msg": "Running service: cache on port 6379"
}

PLAY RECAP ********************************************************************
localhost                  : ok=5    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Observe cómo el playbook analiza el JSON, extrae información específica y filtra los datos en función de las condiciones.

Manejo de Errores con Comandos de Shell

Al ejecutar comandos de shell, es importante manejar los posibles errores con elegancia:

  1. Cree un nuevo archivo llamado error_handling.yml en el directorio /home/labex/project.
  2. Agregue el siguiente contenido:
---
- name: Error Handling with Shell Commands
  hosts: local
  gather_facts: no

  tasks:
    - name: Run a potentially failing command
      shell: grep "nonexistent_pattern" /etc/passwd
      register: command_result
      ignore_errors: yes

    - name: Display success or failure
      debug:
        msg: "Command {{ 'succeeded' if command_result.rc == 0 else 'failed with return code ' + command_result.rc|string }}"

    - name: Run a custom failing command
      shell: exit 3
      register: exit_command
      ignore_errors: yes

    - name: Display detailed error information
      debug:
        msg: |
          Return code: {{ exit_command.rc }}
          Error message: {{ exit_command.stderr if exit_command.stderr else 'No error message' }}

Este playbook:

  • Ejecuta comandos que se espera que fallen
  • Utiliza ignore_errors: yes para continuar la ejecución del playbook incluso cuando los comandos fallan
  • Muestra diferentes métodos para manejar y mostrar información de error

Ejecute el playbook:

ansible-playbook -i inventory.ini error_handling.yml

Debería ver una salida similar a esta:

PLAY [Error Handling with Shell Commands] *************************************

TASK [Run a potentially failing command] **************************************
fatal: [localhost]: FAILED! => {"changed": true, "cmd": "grep \"nonexistent_pattern\" /etc/passwd", "delta": "0:00:00.002916", "end": "2023-07-14 16:10:23.671519", "msg": "non-zero return code", "rc": 1, "start": "2023-07-14 16:10:23.668603", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
...ignoring

TASK [Display success or failure] *********************************************
ok: [localhost] => {
    "msg": "Command failed with return code 1"
}

TASK [Run a custom failing command] *******************************************
fatal: [localhost]: FAILED! => {"changed": true, "cmd": "exit 3", "delta": "0:00:00.002447", "end": "2023-07-14 16:10:23.906121", "msg": "non-zero return code", "rc": 3, "start": "2023-07-14 16:10:23.903674", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
...ignoring

TASK [Display detailed error information] *************************************
ok: [localhost] => {
    "msg": "Return code: 3\nError message: No error message\n"
}

PLAY RECAP ********************************************************************
localhost                  : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=2

Esto demuestra cómo capturar y responder a diferentes condiciones de error al ejecutar comandos de shell.

Creación de una Herramienta Práctica de Procesamiento de Salida de Shell

En este paso final, reuniremos todo lo que hemos aprendido para crear un playbook de Ansible práctico que recopila información del sistema, la procesa y genera un informe. Esto representa un escenario del mundo real donde las capacidades de procesamiento de comandos de shell de Ansible pueden ser extremadamente útiles.

Construyendo una Herramienta de Informes de Información del Sistema

Creemos una herramienta completa de recopilación de información del sistema:

  1. Cree un nuevo archivo llamado system_report.yml en el directorio /home/labex/project.
  2. Agregue el siguiente contenido:
---
- name: Comprehensive System Report
  hosts: local
  gather_facts: no

  vars:
    report_file: /tmp/system_report.txt

  tasks:
    - name: Collect basic system information
      shell: |
        echo "SYSTEM REPORT" > {{ report_file }}
        echo "=============" >> {{ report_file }}
        echo "" >> {{ report_file }}

        echo "HOSTNAME: $(hostname)" >> {{ report_file }}
        echo "TIMESTAMP: $(date)" >> {{ report_file }}
        echo "" >> {{ report_file }}

        echo "SYSTEM INFORMATION" >> {{ report_file }}
        echo "------------------" >> {{ report_file }}
        echo "OS: $(cat /etc/os-release | grep PRETTY_NAME | cut -d= -f2)" >> {{ report_file }}
        echo "KERNEL: $(uname -r)" >> {{ report_file }}
        echo "UPTIME: $(uptime -p)" >> {{ report_file }}
        echo "" >> {{ report_file }}

        echo "RESOURCE UTILIZATION" >> {{ report_file }}
        echo "-------------------" >> {{ report_file }}
        echo "CPU LOAD: $(uptime | awk -F'load average:' '{print $2}')" >> {{ report_file }}
        echo "MEMORY USAGE:" >> {{ report_file }}
        free -h >> {{ report_file }}
        echo "" >> {{ report_file }}
        echo "DISK USAGE:" >> {{ report_file }}
        df -h >> {{ report_file }}
        echo "" >> {{ report_file }}

        echo "NETWORK INFORMATION" >> {{ report_file }}
        echo "-------------------" >> {{ report_file }}
        echo "IP ADDRESSES:" >> {{ report_file }}
        ip addr | grep "inet " | awk '{print $2}' >> {{ report_file }}
        echo "" >> {{ report_file }}

        echo "PROCESS INFORMATION" >> {{ report_file }}
        echo "-------------------" >> {{ report_file }}
        echo "TOP 5 CPU CONSUMING PROCESSES:" >> {{ report_file }}
        ps aux --sort=-%cpu | head -6 >> {{ report_file }}
        echo "" >> {{ report_file }}
        echo "TOP 5 MEMORY CONSUMING PROCESSES:" >> {{ report_file }}
        ps aux --sort=-%mem | head -6 >> {{ report_file }}
      register: report_generation

    - name: Check if report was generated successfully
      stat:
        path: "{{ report_file }}"
      register: report_stat

    - name: Display report generation status
      debug:
        msg: "Report generated successfully at {{ report_file }}"
      when: report_stat.stat.exists

    - name: Display report content
      shell: cat {{ report_file }}
      register: report_content
      when: report_stat.stat.exists

    - name: Show report content
      debug:
        msg: "{{ report_content.stdout_lines }}"
      when: report_stat.stat.exists

    - name: Analyze disk usage
      shell: df -h / | grep -v Filesystem | awk '{print $5}' | sed 's/%//'
      register: disk_usage
      when: report_stat.stat.exists

    - name: Generate disk usage alert if needed
      debug:
        msg: "ALERT: Disk usage on / is {{ disk_usage.stdout }}% which exceeds the 80% threshold!"
      when:
        - report_stat.stat.exists
        - disk_usage.stdout|int > 80

    - name: Generate disk usage warning if needed
      debug:
        msg: "WARNING: Disk usage on / is {{ disk_usage.stdout }}% which exceeds the 60% threshold."
      when:
        - report_stat.stat.exists
        - disk_usage.stdout|int > 60
        - disk_usage.stdout|int <= 80

    - name: Confirm normal disk usage
      debug:
        msg: "Disk usage on / is normal at {{ disk_usage.stdout }}%."
      when:
        - report_stat.stat.exists
        - disk_usage.stdout|int <= 60

Este playbook:

  • Recopila información completa del sistema utilizando una serie de comandos de shell
  • Escribe la información en un archivo de informe
  • Verifica que el informe se creó correctamente
  • Muestra el contenido del informe
  • Analiza los datos de uso del disco
  • Genera las alertas apropiadas en función del análisis

Ejecute el playbook:

ansible-playbook -i inventory.ini system_report.yml

Verá una salida completa que muestra la ejecución del playbook y el informe completo del sistema. La salida es bastante larga, por lo que aquí hay solo una muestra de lo que podría ver:

PLAY [Comprehensive System Report] ********************************************

TASK [Collect basic system information] ***************************************
changed: [localhost]

TASK [Check if report was generated successfully] *****************************
ok: [localhost]

TASK [Display report generation status] ***************************************
ok: [localhost] => {
    "msg": "Report generated successfully at /tmp/system_report.txt"
}

TASK [Display report content] *************************************************
changed: [localhost]

TASK [Show report content] ****************************************************
ok: [localhost] => {
    "msg": [
        "SYSTEM REPORT",
        "=============",
        "",
        "HOSTNAME: ubuntu-vm",
        "TIMESTAMP: Fri Jul 14 16:35:42 UTC 2023",
        "",
        "SYSTEM INFORMATION",
        "------------------",
        "OS: \"Ubuntu 22.04.1 LTS\"",
        "KERNEL: 5.15.0-1023-azure",
        "UPTIME: up 3 hours, 25 minutes",
        ...

Examinando el Informe

Examinemos el informe del sistema que generamos:

cat /tmp/system_report.txt

Esto mostrará el informe completo que fue generado por nuestro playbook.

Creación de un Script de Shell Personalizado y Llamada desde Ansible

Para operaciones más complejas, a veces es más fácil crear un script de shell dedicado y llamarlo desde Ansible:

  1. Cree un nuevo archivo llamado disk_analyzer.sh en el directorio /home/labex/project.
  2. Agregue el siguiente contenido:
#!/bin/bash

## disk_analyzer.sh - A simple script to analyze disk usage

echo "DISK USAGE ANALYSIS"
echo "------------------"

## Get overall disk usage
ROOT_USAGE=$(df -h / | grep -v Filesystem | awk '{print $5}' | sed 's/%//')
echo "Root filesystem usage: ${ROOT_USAGE}%"

## Categorize the usage
if [ $ROOT_USAGE -gt 80 ]; then
  echo "STATUS: CRITICAL - Immediate action required"
elif [ $ROOT_USAGE -gt 60 ]; then
  echo "STATUS: WARNING - Consider cleaning up disk space"
else
  echo "STATUS: OK - Disk usage is within normal parameters"
fi

echo ""

## Find largest directories
echo "Top 5 largest directories in /var:"
du -h /var --max-depth=1 2> /dev/null | sort -hr | head -5

echo ""

## Find largest files
echo "Top 5 largest files in /var/log:"
find /var/log -type f -exec du -h {} \; 2> /dev/null | sort -hr | head -5

exit 0
  1. Haga que el script sea ejecutable:
chmod +x /home/labex/project/disk_analyzer.sh
  1. Cree un nuevo playbook para llamar a este script:
touch /home/labex/project/call_script.yml
  1. Agregue el siguiente contenido al playbook:
---
- name: Call Custom Shell Script
  hosts: local
  gather_facts: no

  tasks:
    - name: Run disk analyzer script
      shell: /home/labex/project/disk_analyzer.sh
      register: script_output

    - name: Display script output
      debug:
        msg: "{{ script_output.stdout_lines }}"

    - name: Check for critical status
      debug:
        msg: "CRITICAL DISK USAGE DETECTED! Immediate action required."
      when: script_output.stdout is search("STATUS: CRITICAL")
  1. Ejecute el playbook:
ansible-playbook -i inventory.ini call_script.yml

Debería ver una salida similar a esta:

PLAY [Call Custom Shell Script] ***********************************************

TASK [Run disk analyzer script] ***********************************************
changed: [localhost]

TASK [Display script output] **************************************************
ok: [localhost] => {
    "msg": [
        "DISK USAGE ANALYSIS",
        "------------------",
        "Root filesystem usage: 38%",
        "STATUS: OK - Disk usage is within normal parameters",
        "",
        "Top 5 largest directories in /var:",
        "60M\t/var/lib",
        "60M\t/var/cache",
        "12M\t/var/log",
        "4.0K\t/var/tmp",
        "4.0K\t/var/mail",
        "",
        "Top 5 largest files in /var/log:",
        "4.0M\t/var/log/journal/c75af53674ce472fb9654a1d5cf8cc37/system.journal",
        "2.3M\t/var/log/auth.log",
        "1.3M\t/var/log/syslog",
        "724K\t/var/log/kern.log",
        "428K\t/var/log/cloud-init.log"
    ]
}

TASK [Check for critical status] **********************************************
skipped: [localhost]

PLAY RECAP ********************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

Este enfoque combina el poder de los scripts de shell con las capacidades de automatización de Ansible. El script de shell maneja la lógica compleja para el análisis del disco, mientras que Ansible gestiona la ejecución y el procesamiento posterior de los resultados.

Conclusiones Clave

A través de este laboratorio, ha aprendido varias técnicas importantes para trabajar con la salida de comandos de shell en Ansible:

  1. Cómo ejecutar comandos de shell y capturar su salida
  2. Cómo procesar y formatear la salida de comandos para mostrarla
  3. Cómo utilizar la ejecución condicional basada en los resultados de los comandos
  4. Cómo manejar la salida JSON y las condiciones de error
  5. Cómo crear herramientas prácticas que combinan comandos de shell con las capacidades de automatización de Ansible

Estas habilidades serán invaluables a medida que construya soluciones de automatización más complejas con Ansible.

Resumen

En este laboratorio, aprendió a trabajar eficazmente con la salida de comandos de shell en los playbooks de Ansible. Comenzando con los conceptos básicos de la ejecución de comandos de shell y la captura de su salida, progresó a técnicas más avanzadas como la ejecución condicional, el manejo de errores y el procesamiento de formatos de datos estructurados como JSON.

Ha dominado varias habilidades clave:

  • Ejecutar comandos de shell en los playbooks de Ansible utilizando el módulo shell
  • Capturar la salida del comando con la directiva register
  • Mostrar la salida utilizando el módulo debug
  • Procesar la salida con filtros y condicionales de Jinja2
  • Crear herramientas de automatización prácticas que combinan Ansible con scripts de shell

Estas técnicas le permiten crear flujos de trabajo de automatización más dinámicos y receptivos que pueden adaptarse a diferentes condiciones del sistema y proporcionar comentarios útiles sobre las operaciones que se están realizando.

A medida que continúa su viaje con Ansible, recuerde que, si bien los comandos de shell brindan una gran flexibilidad, los módulos integrados de Ansible son a menudo una solución más robusta y portátil para las tareas comunes. Use comandos de shell cuando necesite aprovechar los scripts de shell existentes o realizar operaciones complejas que no se manejan fácilmente con los módulos de Ansible.