Controlar el Estado de las Tareas con changed_when y failed_when
En este paso, obtendrá un control más preciso sobre cómo Ansible interpreta el resultado de sus tareas. Aprenderá sobre dos directivas potentes: changed_when y failed_when.
changed_when: Por defecto, módulos como ansible.builtin.command o ansible.builtin.shell casi siempre informan un estado de "cambiado", incluso si el comando que ejecutaron no alteró el sistema. changed_when le permite definir una condición personalizada que determina si una tarea debe informarse como "cambiada". Esto es crucial para escribir playbooks idempotentes y para disparar handlers de manera precisa.
failed_when: A veces, un comando puede salir con un código de estado distinto de cero (que Ansible considera un fallo) incluso cuando el resultado es aceptable. failed_when le permite anular las condiciones de fallo predeterminadas, permitiendo que su playbook continúe basándose en criterios más inteligentes, como la salida del comando o un código de salida específico.
Comencemos configurando un nuevo directorio de proyecto.
cd ~/project
mkdir control-state-lab
cd control-state-lab
Cree el archivo inventory estándar para localhost.
nano inventory
Agregue el siguiente contenido:
localhost ansible_connection=local
Guarde y salga del editor (Ctrl+X, Y, Enter).
Usando changed_when
Primero, veamos cómo se comporta una tarea de comando por defecto. Crearemos un playbook que ejecute el comando date. Este comando simplemente imprime la fecha y no cambia el sistema, pero el módulo command lo informará como un cambio.
Cree un nuevo playbook llamado playbook.yml.
nano playbook.yml
Ingrese el siguiente contenido:
---
- name: Control Task State
hosts: localhost
tasks:
- name: Check local time (default behavior)
ansible.builtin.command: date
Guarde y salga. Ahora, ejecute el playbook.
ansible-playbook -i inventory playbook.yml
Observe en la salida que la tarea se informa como changed=1, a pesar de que nada en el sistema fue modificado.
...
TASK [Check local time (default behavior)] *************************************
changed: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0 ...
Ahora, usemos changed_when para decirle a Ansible que este comando nunca cambia el sistema. Modifique playbook.yml.
nano playbook.yml
Agregue changed_when: false a la tarea.
---
- name: Control Task State
hosts: localhost
tasks:
- name: Check local time (with changed_when)
ansible.builtin.command: date
changed_when: false
Guarde y salga. Ejecute el playbook nuevamente.
ansible-playbook -i inventory playbook.yml
Esta vez, la tarea informa ok y el resumen final muestra changed=0. Ha anulado con éxito el comportamiento predeterminado.
...
TASK [Check local time (with changed_when)] ************************************
ok: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 ...
Usando failed_when
A continuación, exploremos failed_when. Crearemos una tarea que verifique la existencia de un archivo que no está allí. El comando "fallará" por defecto.
Primero, cree un archivo ficticio para buscar dentro.
echo "System is running" > status.txt
Ahora, modifique playbook.yml para buscar la palabra "ERROR" en este archivo. El comando grep saldrá con un código de estado de 1 porque la palabra no se encuentra, lo que Ansible interpreta como un fallo.
nano playbook.yml
Reemplace el contenido con lo siguiente:
---
- name: Control Task State
hosts: localhost
tasks:
- name: Check for ERROR in status file (will fail)
ansible.builtin.command: grep ERROR status.txt
Guarde y salga. Ejecute el playbook.
ansible-playbook -i inventory playbook.yml
Como se esperaba, la ejecución del playbook se detiene con un mensaje FAILED!.
...
TASK [Check for ERROR in status file (will fail)] ******************************
fatal: [localhost]: FAILED! => {"changed": true, "cmd": ["grep", "ERROR", "status.txt"], "delta": "...", "end": "...", "msg": "non-zero return code", "rc": 1, ...}
...
Esto no es lo que queremos. La ausencia de "ERROR" es una condición de éxito para nosotros. Podemos usar failed_when para redefinir qué constituye un fallo. Le diremos a Ansible que solo falle si el código de retorno del comando es mayor que 1. Un código de retorno de 1 (patrón no encontrado) ahora se considerará un éxito. También necesitamos register el resultado de la tarea para inspeccionar su código de retorno (rc).
Modifique playbook.yml una última vez.
nano playbook.yml
Actualice el playbook con register y failed_when.
---
- name: Control Task State
hosts: localhost
tasks:
- name: Check for ERROR in status file (with failed_when)
ansible.builtin.command: grep ERROR status.txt
register: grep_result
failed_when: grep_result.rc > 1
changed_when: false
También agregamos changed_when: false porque grep es una operación de solo lectura y no cambia el sistema.
Guarde y salga. Ejecute el playbook final.
ansible-playbook -i inventory playbook.yml
¡Éxito! La tarea ahora informa ok porque su código de retorno fue 1, lo que no cumple nuestra nueva condición de fallo (rc > 1). El playbook se completa con éxito.
...
TASK [Check for ERROR in status file (with failed_when)] ***********************
ok: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 ...
Ahora ha aprendido a usar changed_when y failed_when para definir con precisión los estados de éxito, cambio y fallo de sus tareas, lo que lleva a una automatización más robusta e inteligente.