¡Bienvenido amante de la tecnología y la automatización! Hoy quiero compartir contigo una experiencia muy curiosa y, seamos sinceros, un poco irónica: Satya Nadella y su equipo en Microsoft han decidido despedirse de WSUS para la centralización de actualizaciones de Windows. ¡Sí, lo sé, es como si de repente decidieran que los unicornios son la nueva forma de actualizar sistemas! Pero, como buen geek que soy, no podía quedarme de brazos cruzados ante este cambio radical.
Decidí enfrentar este reto automatizando la búsqueda, descarga, instalación y registro de actualizaciones en servidores Windows utilizando Ansible. Y para que nadie se pierda, agregué comentarios en español en cada línea del código, explicando qué hace cada instrucción.
Playbook 1: Descargar actualizaciones sin instalarlas
---
# Inicio del playbook para verificar actualizaciones en Windows y registrarlas
- name: Verificar actualizaciones de Windows y registrarlas # Nombre del playbook
hosts: windows # Se ejecuta en los hosts del grupo "windows"
# gather_facts: yes # (Opcional) Recolecta información del sistema
tasks:
- name: Buscar y descargar actualizaciones sin instalarlas # Nombre de la tarea en español
ansible.windows.win_updates: # Utilizo el módulo de actualizaciones para Windows
state: downloaded # Estado: descarga las actualizaciones sin instalarlas
# Fin de tarea: Se buscan y descargan las actualizaciones
Es tan sencillo como escribir un “Hola Mundo” en Python, pero aplicado a las actualizaciones de Windows.
Playbook 2: Descargar actualizaciones y registrar los resultados
---
# Inicio del playbook para buscar, descargar y registrar actualizaciones
- name: Buscar y registrar actualizaciones de Windows # Nombre del playbook en español
hosts: windows # Apunta a los hosts en el grupo "windows"
gather_facts: yes # Recolecta información del sistema antes de ejecutar
tasks:
- name: Crear directorio de logs si no existe # Tarea para asegurarme de que el directorio de logs exista
win_file: # Módulo para manejar archivos y directorios en Windows
path: C:\log # Ruta donde se creará el directorio de logs
state: directory # Estado: debe existir como directorio
# Fin de tarea: Directorio de logs creado o verificado
- name: Buscar y descargar actualizaciones sin instalarlas # Tarea para descargar actualizaciones
ansible.windows.win_updates: # Utilizo el módulo de actualizaciones de Windows
state: downloaded # Estado: solo descarga las actualizaciones
register: update_result # Guardo el resultado en la variable "update_result"
# Fin de tarea: Actualizaciones descargadas y resultado almacenado
- name: Registrar resultados de actualizaciones (con actualizaciones) # Tarea para registrar actualizaciones descargadas
win_shell: | # Ejecuto un script de PowerShell en Windows
# Defino el archivo de log y preparo el resultado de actualizaciones
$logFile = "C:\log\log.txt" # Variable que define la ruta del archivo de log
$updateResult = "{{ update_result }}" # Asigno el resultado de la tarea anterior a una variable
# Si se encontraron actualizaciones, itero y registro cada una
if ($updateResult.updates.Count -gt 0) {
foreach ($update in $updateResult.updates) {
Add-Content -Path $logFile -Value ("Update Title: " + $update.title + " - KB: " + $update.kb_article_ids)
}
} else {
Add-Content -Path $logFile -Value "No updates found or downloaded."
}
when: update_result is defined and update_result.updates | length > 0 # Ejecuto esta tarea solo si se descargaron actualizaciones
# Fin de tarea: Resultados registrados en el log
- name: Registrar mensaje de ausencia de actualizaciones # Tarea para registrar que no se encontraron actualizaciones
win_shell: | # Ejecuto un script de PowerShell en Windows
Add-Content -Path C:\log\log.txt -Value "No updates found or downloaded."
when: update_result is not defined or update_result.updates | length == 0 # Ejecuto si no hay actualizaciones
# Fin de tarea: Mensaje de log para ausencia de actualizaciones
Con este playbook, incluso Cortana se sentiría orgullosa de lo bien que quedan registrados mis logs.
Playbook 3: Instalar actualizaciones sin reiniciar
---
# Inicio del playbook para instalar actualizaciones sin reiniciar el sistema
- name: Instalar actualizaciones sin reiniciar Windows # Nombre del playbook en español
hosts: windows # Se ejecuta en hosts del grupo "windows"
gather_facts: yes # Recolecta información del sistema antes de comenzar
tasks:
- name: Crear directorio de logs si no existe # Verifico o creo el directorio para guardar logs
win_file: # Módulo para manejar archivos en Windows
path: C:\log # Ruta del directorio de logs
state: directory # Aseguro que exista como directorio
# Fin de tarea: Directorio de logs verificado
- name: Instalar actualizaciones sin reiniciar # Tarea para instalar actualizaciones sin reinicio inmediato
ansible.windows.win_updates: # Utilizo el módulo de actualizaciones en Windows
state: installed # Estado: instala las actualizaciones
reboot: no # No reinicio el sistema después de la instalación
register: update_result # Guardo el resultado de la instalación en "update_result"
# Fin de tarea: Actualizaciones instaladas y resultado registrado
- name: Registrar resultados de la instalación (con actualizaciones) # Tarea para registrar cada actualización instalada
win_shell: | # Ejecuto un script de PowerShell para registrar resultados
$logFile = "C:\log\log.txt" # Defino la ruta del archivo de log
$updateResult = "{{ update_result }}" # Asigno el resultado de la instalación a una variable
if ($updateResult.updates.Count -gt 0) { # Si hay actualizaciones en el resultado
foreach ($update in $updateResult.updates) { # Itero sobre cada actualización
Add-Content -Path $logFile -Value ("Update Title: " + $update.title + " - KB: " + $update.kb_article_ids)
}
} else {
Add-Content -Path $logFile -Value "No updates found or installed."
}
when: update_result is defined and update_result.updates | length > 0 # Se ejecuta solo si existen actualizaciones
# Fin de tarea: Actualizaciones registradas en el log
- name: Registrar mensaje de ausencia de actualizaciones instaladas # Tarea para registrar cuando no se encontraron actualizaciones
win_shell: | # Ejecuto un script de PowerShell
Add-Content -Path C:\log\log.txt -Value "No updates found or installed."
when: update_result is not defined or update_result.updates | length == 0 # Solo si no hay actualizaciones disponibles
# Fin de tarea: Mensaje registrado en el log
Con este playbook, puedo instalar actualizaciones sin que mi sistema se reinicie inesperadamente, ¡ideal para esos momentos en que no puedo permitirme una pausa!
Playbook 4: Verificar reinicio previo a la instalación y registrar el proceso
---
# Inicio del playbook para gestionar el reinicio previo y la instalación de actualizaciones
- name: Verificar reinicio previo e instalar actualizaciones en Windows # Nombre del playbook en español
hosts: windows # Se ejecuta en los hosts del grupo "windows"
gather_facts: yes # Recolecto información del sistema
tasks:
- name: Comprobar si el sistema requiere reinicio # Tarea para verificar si se requiere reiniciar antes de actualizar
win_reg_stat: # Módulo para consultar el registro de Windows
path: 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update' # Ruta en el registro
name: 'RebootRequired' # Nombre de la clave que indica si se necesita reinicio
register: reboot_check # Guardo el resultado en "reboot_check"
ignore_errors: yes # No fallo si la clave no existe
# Fin de tarea: Estado de reinicio verificado
- name: Notificar que se requiere reinicio # Tarea para notificar que se necesita reinicio
debug: # Muestro un mensaje en la salida de Ansible
msg: "El sistema necesita reiniciarse antes de continuar con las actualizaciones."
when: reboot_check.exists == true # Solo se ejecuta si la clave 'RebootRequired' existe
# Fin de tarea: Se notifica la necesidad de reinicio
- name: Detener la ejecución del playbook por reinicio requerido # Tarea para detener el playbook si se necesita reinicio
meta: end_play # Detengo la ejecución del playbook
when: reboot_check.exists == true # Se ejecuta solo si se requiere reinicio
# Fin de tarea: Playbook detenido ante la necesidad de reinicio
- name: Crear directorio de logs si no existe # Tarea para crear el directorio de logs (si no se requiere reinicio)
win_file: # Módulo para gestionar archivos en Windows
path: C:\log # Ruta del directorio de logs
state: directory # Aseguro que exista como directorio
when: reboot_check.exists == false # Se ejecuta solo si no se requiere reinicio
# Fin de tarea: Directorio de logs creado o verificado
- name: Descargar actualizaciones de Windows # Tarea para buscar y descargar actualizaciones
ansible.windows.win_updates: # Utilizo el módulo de actualizaciones en Windows
state: downloaded # Estado: descarga las actualizaciones sin instalarlas
register: update_result # Guardo el resultado en "update_result"
when: reboot_check.exists == false # Se ejecuta solo si no se requiere reinicio
# Fin de tarea: Actualizaciones descargadas
- name: Registrar inicio de la instalación # Tarea para registrar el comienzo de la instalación
win_shell: | # Ejecuto un script de PowerShell
$logFile = "C:\log\log.txt" # Defino la ruta del archivo de log
Add-Content -Path $logFile -Value ("[INFO] Iniciando la instalación de actualizaciones a las " + (Get-Date).ToString())
run_once: true # Se ejecuta una sola vez (no por cada host)
when: reboot_check.exists == false # Solo si no se requiere reinicio
# Fin de tarea: Inicio de instalación registrado
- name: Instalar actualizaciones (permitiendo reinicio si es necesario) # Tarea para instalar actualizaciones
ansible.windows.win_updates: # Utilizo el módulo de actualizaciones de Windows
state: installed # Estado: instala las actualizaciones
reboot: yes # Permito reinicio si es requerido
register: install_result # Guardo el resultado de la instalación
when: reboot_check.exists == false # Se ejecuta solo si no se requiere reinicio
# Fin de tarea: Actualizaciones instaladas
- name: Registrar el estado de cada actualización instalada # Tarea para registrar el progreso de la instalación
win_shell: | # Ejecuto un script de PowerShell para cada actualización
$logFile = "C:\log\log.txt" # Defino la ruta del log
$updateTitle = "{{ item.title }}" # Extraigo el título de la actualización
$kbArticle = "{{ item.kb_article_ids }}" # Extraigo el KB asociado a la actualización
try {
Add-Content -Path $logFile -Value ("[INFO] Instalando actualización: " + $updateTitle + " - KB: " + $kbArticle)
Start-Sleep -Seconds 5 # Simulo un retraso en la instalación (opcional)
Add-Content -Path $logFile -Value ("[INFO] Actualización instalada correctamente: " + $updateTitle + " - KB: " + $kbArticle)
} catch {
Add-Content -Path $logFile -Value ("[ERROR] Falló la instalación de la actualización: " + $updateTitle + " - KB: " + $kbArticle + " Error: " + $_.Exception.Message)
}
loop: "{{ update_result.updates }}" # Itero sobre cada actualización descargada
when: update_result is defined and update_result.updates | length > 0 and reboot_check.exists == false
# Fin de tarea: Cada actualización es registrada conforme se instala
- name: Reiniciar el sistema si es requerido # Tarea para reiniciar el sistema tras la instalación
win_reboot: # Módulo que reinicia el sistema Windows
reboot_timeout: 600 # Tiempo máximo de espera para completar el reinicio (600 segundos)
when: install_result.reboot_required == true and reboot_check.exists == false # Se ejecuta solo si se requiere reinicio
# Fin de tarea: Sistema reiniciado si fue necesario
- name: Registrar mensaje de ausencia de actualizaciones # Tarea para registrar que no se encontraron actualizaciones
win_shell: | # Ejecuto un script de PowerShell
Add-Content -Path C:\log\log.txt -Value "[INFO] No se encontraron ni instalaron actualizaciones."
when: (update_result is not defined or update_result.updates | length == 0) and reboot_check.exists == false
# Fin de tarea: Mensaje registrado en el log para ausencia de actualizaciones
- name: Registrar fin de la instalación # Tarea para registrar el final del proceso de instalación
win_shell: | # Ejecuto un script de PowerShell
$logFile = "C:\log\log.txt" # Defino la ruta del archivo de log
Add-Content -Path $logFile -Value ("[INFO] Instalación de actualizaciones completada a las " + (Get-Date).ToString())
run_once: true # Se ejecuta una sola vez (no por cada host)
when: reboot_check.exists == false # Solo si no se requiere reinicio
# Fin de tarea: Fin del proceso de instalación registrado
Este playbook se ha convertido en mi navaja suiza para la actualización: verifico si se necesita reiniciar, descargo, instalo y registro todo el proceso. Así, si mi servidor decide tomarse un “descanso” (o reiniciar inesperadamente), ya tengo todo controlado.