¡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.

Vídeo

error: ooops!