Qué es un módulo en Ansible
Un módulo en Ansible es una pieza de código que ejecuta una tarea específica en los nodos administrados. Estos módulos son el núcleo de Ansible y permiten realizar acciones como gestionar archivos, instalar paquetes, configurar servicios o ejecutar comandos. En resumen, son bloques de construcción que podemos usar dentro de playbooks o como comandos ad-hoc. Además, Ansible incluye una amplia biblioteca de módulos predefinidos, pero también podemos crear los nuestros personalizados si necesitamos funcionalidades más específicas.
En este post vamos a ver los módulos dedicados a:
Paquetes
En esta sección, veremos cómo gestionar paquetes en sistemas basados en Red Hat usando el módulo yum
de Ansible. Este módulo es ideal para automatizar tareas comunes relacionadas con la instalación, desinstalación y actualización de paquetes.
Cuando hagamos uso de este módulo vamos a tener los siguientes estados (state):
-
present: Asegura que el paquete está presente o instalado en el sistema.
-
absent: Asegura que el paquete está ausente o desinstalado del sistema
-
latest: Asegura que el paquete está instalado y actualizado a la última versión disponible.
-
removed: Asegura que el paquete esté desinstalado.
Recomendación: Usar absent en lugar de removed, ya que está más aceptado y entendido por los módulos de Ansible.
A continuación, se presentan ejemplos detallados que ilustran diferentes estados, así como escenarios específicos como la instalación de un archivo RPM, la eliminación de versiones anteriores de paquetes, y la instalación de paquetes específicos.
NOTA: Aunque los ejemplos de esta sección se centran en el uso del módulo yum
para distribuciones basadas en Red Hat, es importante mencionar que en sistemas basados en Debian, como Ubuntu, se utiliza el gestor de paquetes apt
. Este cuenta con su propio módulo en Ansible llamado apt, que permite realizar operaciones similares como instalar, actualizar o eliminar paquetes.
1 - Instalar el paquete httpd en web1 usando el módulo yum.
---
- name: Install httpd package
hosts: web1
become: yes
tasks:
- name: Install httpd
yum:
name: httpd
state: present
2 - Instalar el rpm (https://mirror.stream.centos.org/9-stream/AppStream/x86_64/os/Packages/wget-1.21.1-8.el9.x86_64.rpm) en web1 usando el módulo yum
---
- name: Download and install package
hosts: web1
become: yes
tasks:
- name: Download the package
get_url:
url: "https://mirror.stream.centos.org/9-stream/AppStream/x86_64/os/Packages/wget-1.21.1-8.el9.x86_64.rpm"
dest: "/tmp/wget-1.21.1-8.el9.x86_64.rpm"
- name: Install the package
yum:
name: "/tmp/wget-1.21.1-8.el9.x86_64.rpm"
state: present
- name: Remove the package file
file:
path: "/tmp/wget-1.21.1-8.el9.x86_64.rpm"
state: absent
3 - Instalar el paquete unzip-5.52 en web1.
---
- hosts: web1
tasks:
- name: Install unzip package
yum:
name: unzip-5.52
state: present
4 - Instalar la última versión de iotop.
---
- hosts: all
tasks:
- name: Install iotop package
yum:
name: iotop
state: latest
5 - Instalar en web1 la última versión de sudo y poner la version vsftpd 3.0.3, ya está presente vsftpd v3.0.5 que deseamos eliminar.
---
- name: Install multi-pkgs
hosts: web1
become: yes
tasks:
- name: Install sudo latest
yum:
name: sudo
state: latest
- name: Install vsftpd v3.0.3
yum:
name: vsftpd-3.0.3
state: present
allow_downgrade: yes
Servicios
En este punto, vamos a ver los estados disponibles para gestionar servicios en Ansible utilizando los módulos service
y systemd
. Estos módulos permiten controlar servicios de manera eficaz, automatizando tareas comunes como iniciar, detener, reiniciar o habilitar servicios en sistemas Linux.
Para el módulo service, tendremos estos estados:
-
started: Inicia el servicio si no está ya en ejecución.
-
stopped: Detiene el servicio si está en ejecución.
-
restarted: Reinicia el servicio, primero lo detiene y luego lo inicia.
-
reloaded: Recarga la configuración del servicio sin detenerlo.
En el caso del módulo systemd, además de los anteriores, tenemos estos estados adicionales:
-
enabled: Habilita el servicio para que se inicie en el arranque del sistema
-
disabled: Deshabilita el servicio para que no se inicie automáticamente en el arranque del sistema
-
masked: Se usa para impedir que un servicio sea iniciado, tanto manualmente como por dependencias. Cuando un servicio está enmascarado (masked), systemd crea un enlace simbólico (symlink) que apunta a /dev/null, esencialmente bloqueando cualquier intento de iniciar el servicio.
-
unmasked: Quita la máscara del servicio, permitiendo que sea iniciado manualmente o por dependencias
-
static: Indica que el servicio no tiene archivos de unidades habilitadas (generalmente definido por systemctl is-enabled).
-
indirect: Similar a static, pero puede ser iniciado por dependencias.
Ejemplos:
1 - Asegurarse de que el servicio httpd se inicia en el nodo web1.
---
- name: Start httpd
hosts: web1
gather_facts: no
tasks:
- name: Start httpd service
service:
name: httpd
state: started
2 - Copiar un archivo con un mensaje de bienvenida bajo la raíz de documentos del servidor httpd en el nodo web1. Recargar el servicio sin reiniciar el servidor.
---
- hosts: web1
gather_facts: no
tasks:
- name: Copy Apache welcome file
copy:
src: index.html
dest: /var/www/html/index.html
- name: Reload Apache service
service:
name: httpd
state: reloaded
enabled: yes
3 - El servicio httpd en el nodo web1 se inicie siempre automáticamente después de reiniciar el sistema.
---
- name: Start httpd
hosts: web1
gather_facts: no
tasks:
- name: Start httpd service
service:
name: httpd
state: started
enabled: true
4 - Habilitar el puerto 443 para httpd en el nodo web1 en lugar del puero 80.
---
- hosts: web1
gather_facts: no
tasks:
- name: Make changes in Apache config
replace:
path: /etc/httpd/conf/httpd.conf
regexp: "^Listen 80"
replace: "Listen 443"
- name: Restart Apache
service:
name: httpd
state: restarted
5 - Instalar nginx en el nodo web1 y asegurarnos de que el servicio nginx se inicia. Debe iniciarse siempre, incluso después de reiniciar el sistema.
---
- name: Install NGINX
hosts: web1
become: yes
tasks:
- name: Install nginx
yum:
name: nginx
state: present
- name: Start and enable nginx
service:
name: nginx
state: started
enabled: yes
Reglas Firewall
En los ejemplos siguientes vamos a ver como administrar tanto firewalld cómo UFW. Firewalld es más común en distribuciones basadas en Red Hat (como CentOS y Fedora), mientras que UFW (Uncomplicated Firewall) es común en distribuciones basadas en Debian (como Ubuntu). Esto permitirá cubrir las configuraciones de cortafuegos en una amplia variedad de sistemas operativos Linux.
1 - Instalar firewalld en el nodo web1, iniciar y habilitar su servicio también.
---
- name: Install and configure firewalld
hosts: web1
become: yes
tasks:
- name: Install firewalld
yum:
name: firewalld
state: present
- name: Start firewalld service
systemd:
name: firewalld
state: started
enabled: yes
2 - En el nodo web1, poner en lista blanca la dirección IP 172.20.1.101 del nodo web2 en el cortafuegos. Añadir la IP en la zona interna.
---
- name: Whitelist IP address in firewalld
hosts: web1
become: yes
tasks:
- name: Ensure firewalld is running
systemd:
name: firewalld
state: started
enabled: yes
- name: Add web2 IP to trusted zone
firewalld:
zone: internal
source: 172.20.1.101
permanent: yes
state: enabled
- name: Reload firewalld to apply changes
command: firewall-cmd --reload
3 - Bloquear el puerto 161/udp en el nodo web1 permanentemente. Usar zona: block
---
- name: Block 161/udp port in firewalld
hosts: web1
become: yes
tasks:
- name: Ensure firewalld is running
systemd:
name: firewalld
state: started
enabled: yes
- name: Add 161/udp port to block zone
firewalld:
zone: block
port: 161/udp
permanent: yes
state: enabled
- name: Reload firewalld to apply changes
command: firewall-cmd --reload
4 - En el nodo web1 añadir una regla de cortafuegos en la zona interna para permitir la conexión https desde la máquina controladora de Ansible y asegurarnos de que la regla persiste incluso después de reiniciar el sistema. La dirección IP del controlador ansible es 172.20.1.2.
---
- name: Add HTTPS firewall rule in internal zone
hosts: web1
become: yes
tasks:
- name: Add firewall source rule for Ansible controller IP
firewalld:
zone: internal
source: 172.20.1.2
permanent: yes
immediate: yes
state: enabled
- name: Add HTTPS service rule in internal zone
firewalld:
zone: internal
service: https
permanent: yes
immediate: yes
state: enabled
- name: Reload firewalld to apply changes
command: firewall-cmd --reload
5 - Instalar el paquete firewalld e iniciar/habilitar su servicio. Como ahora Apache escucha en el puerto 8082, agregar una regla de firewall en la zona publica para que Apache pueda permitir todo el trafico entrante.
---
- hosts: web2
tasks:
- name: Install pkgs
yum:
name: httpd, firewalld
state: present
- name: Start/Enable services
service:
name: ""
state: started
enabled: yes
with_items:
- httpd
- firewalld
- name: Change Apache port
replace:
path: /etc/httpd/conf/httpd.conf
regexp: '^(Listen)\s+80\s*$'
replace: "Listen 8082"
- name: restart Apache
service:
name: httpd
state: restarted
- name: Add firewall rule for Apache
firewalld:
port: 8082/tcp
zone: public
permanent: yes
state: enabled
immediate: true
6 - Permitir puertos 22,80 y 443 usando UFW
---
- name: Configure firewall rules
hosts: localhost
become: yes
tasks:
- name: Allow port 22
ufw:
rule: allow
port: 22
- name: Allow port 80
ufw:
rule: allow
port: 80
- name: Allow port 443
ufw:
rule: allow
port: 443
- name: Verify firewall rules
command: ufw status
register: firewall_status
- name: Display firewall status
debug:
var: firewall_status.stdout_lines
Contenido de archivo
En este punto, exploraremos cómo realizar diversas tareas administrativas en el nodo web1. Cada sección contiene instrucciones específicas, desde la creación y configuración de archivos hasta la modificación de puertos y el uso de módulos como file
, lineinfile
, blockinfile
, y replace
. Estos ejemplos nos muestran el uso práctico de Ansible en la automatización de tareas cotidianas en servidores.
1 - Crear un archivo en blanco /opt/data/perm.txt con permisos 0640 en el nodo web1.
---
- name: Create blank file /opt/data/perm.txt with 0640 permisions
hosts: web1
tasks:
- name: Create file with permissions
file:
path: /opt/data/perm.txt
mode: '0640'
state: touch
2 - Crear /var/www/html/index.html en web1 con este contenido This line was added using Ansible lineinfile module!.
---
- name: Create /var/www/html/index.html and add content
hosts: web1
tasks:
- name: Create file index.html and add content
lineinfile:
path: /var/www/html/index.html
line: 'This line was added using Ansible lineinfile module!'
create: yes
3 - Buscar recursivamente ficheros en el directorio /opt/data con una antigüedad superior a 2 minutos y un tamaño igual o superior a 1 megabyte. También copia esos ficheros en el directorio /opt.
---
- hosts: web1
tasks:
- name: Find files
find:
paths: /opt/data
age: 2m
size: 1m
recurse: yes
register: file
- name: Copy files
command: "cp /opt"
with_items: ""
4 - En el archivo /var/www/html/index.html en el nodo web1 añadir un contenido adicional usando el módulo blockinfile. A continuación se muestra el contenido:
¡Bienvenido a KodeKloud!
Esto es Ansible Lab.
El usuario propietario y el grupo propietario del archivo debe ser apache. También hay que asegurarse de que el bloque se añade al principio del archivo.
---
- name: Add block to index.html
hosts: web1
tasks:
- blockinfile:
owner: apache
group: apache
insertbefore: BOF
path: /var/www/html/index.html
block: |
Welcome to KodeKloud!
This is Ansible Lab.
5 - En el nodo web1 queremos ejecutar nuestro servidor httpd en el puerto 8080. Hay que cambiar el puerto 80 a 8080 en el archivo /etc/httpd/conf/httpd.conf usando el módulo replace. También asegurarse de que Ansible reinicia el servicio httpd después de hacer el cambio.
Listen 80 es el parámetro que necesita ser cambiado en /etc/httpd/conf/httpd.conf
---
- name: replace port 80 to 8080
hosts: web1
tasks:
- replace:
path: /etc/httpd/conf/httpd.conf
regexp: '^(Listen)\s+80\s*$'
replace: 'Listen 8080'
- service: name=httpd state=restarted
Archivado
A continuación, vamos a ver unos ejemplos de tareas para gestionar la compresión, descompresión y configuración de archivos, así como la instalación y personalización de servicios como Nginx. Además, se hace uso de módulos específicos como community.general.archive
, ansible.builtin.unarchive
, y otros útiles para escenarios prácticos de administración de sistemas.
Parámetros Comunes community.general.archive (Usado para comprimir)
-
path: Ruta del archivo o directorio de origen.
-
dest: Ruta destino del archivo comprimido.
-
format: Tipo de compresión a usar (por ejemplo, gz, bz2, zip).
-
remove: Indica si eliminar los archivos de origen después de comprimido
NOTA: Asegurarse de tener instalada la colección community.general
en tu entorno Ansible. Puedes instalarla usando el siguiente comando: ansible-galaxy collection install community.general
1 - Crear archivo opt.zip del directorio /opt en el nodo web1 y guardarlo en el directorio /root del propio nodo web1.
---
- name: Archive files
hosts: web1
tasks:
- name: Create a compressed archive of files
community.general.archive:
path: /opt
dest: /root/opt.zip
format: zip
remove: true
2 - En el controlador Ansible, tenemos un archivo local.zip. Queremos extraer su contenido en web1 en el directorio /tmp.
---
- name: Extract local.zip on web1
hosts: web1
tasks:
- name: Unarchive the file on the remote host
ansible.builtin.unarchive:
src: /home/thor/playbooks/local.zip
dest: /tmp/
remote_src: no
Para descomprimir usamos el módulo ansible.builtin.unarchive
-
src: Ruta del fichero que deseamos descomprimir.
-
dest: Directorio de destino donde se descomprimirá el contenido del archivo.
-
remote_src: Indica si el archivo fuente está en el host remoto (yes) o en el controlador local (no).
3 - En el nodo web1 tenemos un archivo data.tar.gz bajo el directorio /root, extraerlo en /srv
Asegurarse de que el archivo data.tar.gz es eliminado después de eso.
---
- name: Extract local.zip on web1
hosts: web1
tasks:
- name: Unarchive the file on the remote host
ansible.builtin.unarchive:
src: /root/data.tar.gz
dest: /srv/
remote_src: yes
- name: Remove the archive after extraction
ansible.builtin.file:
path: /root/data.tar.gz
state: absent
4 - Descargar y extraer el archivo zip https://github.com/kodekloudhub/Hello-World/archive/master.zip en el directorio /root del nodo web1.
---
- name: Download and extract master.zip on web1
hosts: web1
tasks:
- name: Download the zip file from GitHub
ansible.builtin.get_url:
url: https://github.com/kodekloudhub/Hello-World/archive/master.zip
dest: /root/master.zip
- name: Extract the zip file
ansible.builtin.unarchive:
src: /root/master.zip
dest: /root/
remote_src: yes
5 - Tenemos tres ficheros en el nodo web1 /root/file1.txt, /usr/local/share/file2.txt y /var/log/lastlog. Crea un archivo bz2 de todos estos ficheros y guárdalo en el directorio /root, nombra el archivo como ficheros.tar.bz2.
---
- name: Create bz2 archive of specific files
hosts: web1
tasks:
- name: Create a bz2 archive of the specified files
community.general.archive:
path:
- /root/file1.txt
- /usr/local/share/file2.txt
- /var/log/lastlog
dest: /root/files.tar.bz2
format: bz2
6 - Queremos configurar nginx en el nodo web1 con algún código html de ejemplo. A continuación se presentan los detalles acerca de la tarea:
-
Instalar el paquete nginx e iniciar/activar su servicio.
-
Extrae el archivo /root/nginx.zip del directorio /usr/share/nginx/html.
-
Dentro de /usr/share/nginx/html/index.html sustituye la línea This is sample html code por la línea This is KodeKloud Ansible lab.
---
- name: Setup nginx on web1 with sample html code
hosts: web1
tasks:
- name: Install nginx
yum:
name: nginx
state: present
become: yes
- name: Start and enable nginx service
service:
name: nginx
state: started
enabled: yes
become: yes
- name: Extract nginx.zip archive
unarchive:
src: /root/nginx.zip
dest: /usr/share/nginx/html
remote_src: yes
become: yes
- name: Replace line in index.html
lineinfile:
path: /usr/share/nginx/html/index.html
regexp: 'This is sample html code'
line: 'This is KodeKloud Ansible lab'
become: yes
Tareas Programadas
Los siguientes ejemplos, muestran cómo utilizar Ansible para gestionar tareas de cron en el nodo node00. Las tareas incluyen limpiar archivos de registros, programar la ejecución de scripts y comandos específicos, eliminar crons previos, realizar limpieza automática después del reinicio, y configurar actualizaciones automáticas de paquetes. Los módulos de Ansible, como cron
, permiten automatizar estas operaciones esenciales de manera eficiente y reproducible.
1 - Cree un playbook ~/playbooks/lastlog.yml para añadir una tarea cron Clear Lastlog en node00 para vaciar el archivo de registros /var/log/lastlog. La tarea debe ejecutarse todos los días a las 12 de la mañana.
Puede utilizar el comando echo «» > /var/log/lastlog
para vaciar el archivo lastlog y el horario debe ser 0 0 * * *
.
---
- name: Add a cron job to clear lastlog
hosts: node00
tasks:
- name: Ensure the cron job is present
cron:
name: "Clear Lastlog"
minute: "0"
hour: "0"
job: 'echo "" > /var/log/lastlog'
2 - Tenemos un script /root/free.sh en node00 que se utiliza para comprobar la memoria libre del sistema.Nos gustaría crear un cron Free Memory Check para ejecutar este script cada 2 horas (es decir, 12am, 2am, 4am etc), el comando para ejecutar el script es sh /root/free.sh y el horario debe ser 0 */2 * * *.
Puede crear un playbook ~/playbooks/script_cron.yml para esto.
---
- name: Add a cron job to check free system memory
hosts: node00
tasks:
- name: Add the cron job
cron:
name: "Free Memory Check"
minute: "0"
hour: "*/2"
job: 'sh /root/free.sh'
3 - Teníamos un cron anterior con el nombre Check Memory, para ejecutar un script diferente - /root/free.sh en node00.
Ese trabajo estaba configurado para ejecutarse cada 1 hora. Sin embargo, como ahora tenemos un nuevo Cronjob configurado vamos a deshacernos del antiguo. Crea un playbook ~/playbooks/remove_cron.yml para eliminar este cron del nodo00.
---
- name: remove cron job from node00
hosts: node00
tasks:
- name: Remove cron job
cron:
name: "Check Memory"
state: absent
4 - Debido a algunas limitaciones de espacio en disco, queremos limpiar la ubicación /tmp en el nodo00 después de cada reinicio.
Cree un playbook ~/playbooks/reboot.yml para añadir un cron llamado cleanup en node00 que se ejecutará después de cada reinicio y limpiará la ubicación /tmp.
El comando debe ser rm -rf /tmp/*
.
---
- name: Cleanup /tmp after every reboot
hosts: node00
tasks:
- cron:
name: cleanup
job: rm -rf /tmp/*
special_time: reboot
5 - En node00 queremos mantener actualizados los paquetes instalados, por lo que nos gustaría ejecutar actualizaciones de yum regularmente.
Cree un playbook ~/playbooks/yum_update.yml y cree una tarea cron como se describe a continuación:
-
No añada cron directamente usando crontab en su lugar cree un archivo cron /etc/cron.d/ansible_yum.
-
El cron debe ejecutarse todos los domingos a las 8:05 am.
-
El nombre del cron debe ser yum update.
-
El cron debe ser agregado para el usuario root
Utilice el comando yum -y update
---
- name: Create cron for yum
hosts: node00
gather_facts: no
tasks:
- name: Creates a cron
cron:
name: yum update
weekday: 0
minute: 5
hour: 8
user: root
job: "yum -y update"
cron_file: ansible_yum
Usuarios y grupos
En este último punto, veremos cómo gestionar usuarios y grupos en sistemas Linux. Veremos cómo crear usuarios con permisos especiales, asociarlos a grupos, configurar la expiración de cuentas, y también cómo eliminar usuarios y grupos de manera eficiente. Estas tareas son esenciales para gestionar de forma segura y automatizada los accesos en servidores remotos.
1 - Crear usuario javier y añadirlo a sudoers
---
- name: Create user
user:
name: javier
state: present
- name: Add user to sudoers
lineinfile:
path: /etc/sudoers
line: "javier ALL=(ALL) NOPASSWD: ALL"
state: present
2 - Crear grupo admin y un usuario admin con grupo admin y uid 2048
---
- hosts: all
gather_facts: no
tasks:
- name: Ensure group 'admin' exists
group:
name: 'admin'
state: present
- name: Create user 'admin' with specific UID and group
user:
name: 'admin'
uid: 2048
group: 'admin'
3 - Supongamos que Sabin Nepal se unió a su equipo el primer día de 2020 como contratista especial para trabajar durante un período de 3 años, es decir, hasta el final del año 2023.
Él necesita sus cuentas en los hosts remotos hasta su período de trabajo.
Escribe un playbook add_user.yml para crear su cuenta de usuario con el nombre de usuario neymarsabin que expirará después de 3 años. La opción expires en el módulo users está en la época. Así que domingo, 31 de diciembre de 2023 11:59:59 PM GMT== 1704067199 como tiempo de época
Recuerda: tu playbook debe estar dentro de /home/thor/playbooks y usar el archivo de inventario allí.
---
- hosts: all
gather_facts: no
tasks:
- name: Create user 'neymarsabin' with expiration
user:
name: 'neymarsabin'
expires: 1704067199
4 - Eliminar en todos los servidores el usuario y grupo admin
---
- hosts: all
gather_facts: no
tasks:
- name: Remove user 'admin'
user:
name: 'admin'
state: absent
- name: Remove group 'admin'
group:
name: 'admin'
state: absent