Los paquetes de Adobe Experience Manager (AEM) son los héroes anónimos de la gestión de contenido: contenedores potentes que agrupan todo, desde código y configuraciones hasta contenido crítico. Pero seamos realistas: crear, configurar y descargar manualmente estos paquetes puede parecer una tediosa danza de clics.
¿Qué pasaría si pudieras automatizar este proceso con solo presionar unas cuantas teclas, garantizando consistencia, velocidad, confiabilidad y menos trabajo pesado?
Te mostraré un script de Bash que cambia el guión (¡juego de palabras intencionado!) sobre cómo los desarrolladores y administradores de AEM trabajan con la API del Administrador de paquetes. Piensa en crear paquetes en segundos, personalizar filtros sobre la marcha y obtener copias de seguridad con precisión quirúrgica, todo antes de que tu café se enfríe a esa temperatura perfecta para beber. ☕
Antes de empezar, una nota rápida : este artículo es un análisis profundo, meticulosamente detallado y sin complejos técnicos. Analizaremos la lógica del script, exploraremos las complejidades de la API de AEM y solucionaremos problemas en casos extremos. Los desarrolladores que deseen comenzar a trabajar directamente con el código pueden ir directamente al final del artículo. Pero si están aquí para comprender el cómo y el por qué de la automatización, abróchense los cinturones, porque vamos a adentrarnos en el tema. 🕳️
El script create-remote-aem-pkg.sh
automatiza las interacciones con la API del Administrador de paquetes de AEM, lo que ofrece un enfoque estructurado para la creación, configuración y distribución de paquetes. Diseñado para desarrolladores y administradores, reemplaza los flujos de trabajo manuales con un proceso impulsado por la línea de comandos que enfatiza la coherencia y la confiabilidad.
/content/dam
, /apps
) para incluir en el paquete.curl
./etc
o /apps
antes de aplicar actualizaciones del sistema. ./create-remote-aem-pkg.sh admin securepass123 localhost 4502 backup-group "Content Backup" /backups /content/dam /etc/clientlibs
Este comando crea un paquete llamado “Content Backup” bajo el grupo backup-group
, que incluye /content/dam
y /etc/clientlibs
, y guarda la salida en el directorio /backups
.
Analicemos el script create-remote-aem-pkg.sh
(puede encontrarlo al final del artículo) para comprender cómo organiza la administración de paquetes de AEM. Nos centraremos en su estructura, funciones clave y lógica de flujo de trabajo, ideal para desarrolladores que buscan personalizar o depurar la herramienta.
_log()
: una función de utilidad que antepone marcas de tiempo a los mensajes para dejar registros de auditoría claros. _log () { echo "[$(date +%Y.%m.%d-%H:%M:%S)] $1" }
Por qué es importante : garantiza que cada acción (por ejemplo, “Paquete creado”) se registre con contexto, lo que simplifica la resolución de problemas.
check_last_exec()
: valida el éxito de los comandos anteriores verificando los códigos de salida y las respuestas de la API. check_last_exec () { # Checks $? (exit status) and $CURL_OUTPUT for errors if [ "$status" -ne 0 ] || [[ $output =~ .*success\":false* ]]; then _log "Error detected!"; exit 1; fi }
Por qué es importante : evita fallas silenciosas al detener la ejecución en caso de errores críticos, como problemas de autenticación o rutas no válidas.
El script acepta siete argumentos posicionales seguidos de filtros dinámicos:
USR="$1" # AEM username PWD="$2" # AEM password SVR="$3" # Server host (eg, localhost) PORT="$4" # Port (eg, 4502) PKG_GROUP="$5" # Package group (eg, "backups") PKG_NAME="$6" # Package name (eg, "dam-backup") BK_FOLDER="$7" # Backup directory (eg, "/backups") shift 7 # Remaining arguments become filters (eg, "/content/dam")
Los argumentos posicionales garantizan la simplicidad, mientras que shift
maneja rutas de filtro variables de manera flexible.
PKG_NAME
con guiones bajos para evitar problemas con las URL. PKG_NAME=${PKG_NAME// /_}
curl
para enumerar paquetes a través de la API de AEM, evitando creaciones redundantes. if [ $(curl ... | grep "$PKG_NAME.zip" | wc -l) -eq 1 ]; then _log "Package exists—skipping creation." else curl -X POST ... # Creates the package fi
Construye una matriz JSON de filtros a partir de rutas de entrada:
FILTERS_PARAM="" for i in "${!FILTERS[@]}"; do FILTERS_PARAM+="{\"root\": \"${FILTERS[$i]}\", \"rules\": []}" # Adds commas between entries, but not after the last done
Ejemplo de salida :
[{"root": "/content/dam"}, {"root": "/apps"}]
Este JSON se inyecta en la definición del paquete a través del punto final /crx/packmgr/update.jsp
de AEM.
build
de AEM: curl -X POST … -F "cmd=build"
Nota : El script espera a que se complete la compilación antes de continuar.
curl
para obtener el .zip
y guardarlo con un nombre de archivo con marca de tiempo: BK_FILE="$PKG_NAME-$(date +%Y%m%d-%H%M%S).zip" curl -o "$BK_FOLDER/$BK_FILE" ...
El manejo y registro de errores sólidos son fundamentales para scripts sin supervisión como create-remote-aem-pkg.sh
, ya que garantizan que las fallas se detecten de manera temprana y se registren con claridad. A continuación, se muestra cómo el script protege contra problemas inesperados y brinda información útil.
_log
antepone a cada mensaje una marca de tiempo [YYYY.MM.DD-HH:MM:SS]
, lo que crea un registro de auditoría para la depuración: _log "Starting backup process..." # Output: [2023.10.25-14:30:45] Starting backup process...
Por qué es importante : Las marcas de tiempo ayudan a correlacionar la actividad del script con los registros del servidor AEM o eventos externos (por ejemplo, programaciones de trabajos cron).
Comprobaciones previas al vuelo :
BK_FOLDER
) antes de continuar: if [ ! -d "$BK_FOLDER" ]; then _log "Backup folder '$BK_FOLDER' does not exist!" && exit 1 fi
PKG_NAME
para evitar problemas de URL (por ejemplo, espacios reemplazados por guiones bajos).
Validación de respuesta de API :
La función check_last_exec
examina tanto los códigos de salida del shell ( $?
) como las respuestas de la API de AEM:
check_last_exec "Error message" "$CURL_OUTPUT" $CURL_STATUS
curl
) activan salidas inmediatas.
success\":false
o cadenas "HTTP ERROR" en la salida AEM.
3.3 Verificación del estado HTTP : al descargar el paquete, el script busca un código de estado 200
:
if [ "$(curl -w "%{http_code}" ...)" -eq "200" ]; then # Proceed if download succeeds else _log "Error downloading the package!" && exit 1 fi
check_last_exec
detecta respuestas 401 Unauthorized
y sale con un mensaje de error claro.success:false
, el script registra "Error al agregar filtros" y finaliza.BK_FILE
, se verifica el tamaño del archivo con el indicador -s
y se emite una alerta antes de salir.curl
sale con un código distinto de cero, el script registra "Error al crear el paquete".curl -k
para simplificar, lo que omite la verificación SSL. Recomendación para producción : reemplácelo con --cacert
para especificar un paquete de CA.
$AEM_PASSWORD
).set -x
al inicio del script para imprimir los comandos ejecutados.curl
críticos fuera del script ./create-remote-aem-pkg.sh ... >> /var/log/aem_backup.log 2>&1
El script create-remote-aem-pkg.sh
está diseñado para ser un punto de partida, una base que puede modificar para alinearla con las necesidades de su equipo. A continuación, se incluyen personalizaciones comunes, junto con una guía de implementación, para ampliar su funcionalidad o adaptarla a casos de uso específicos.
El nombre de archivo predeterminado utiliza una marca de tiempo ( $PKG_NAME-$(date +%Y%m%d-%H%M%S).zip
). Modifíquelo para incluir nombres de entorno, identificaciones de proyecto o versiones semánticas:
# Example: Include environment (eg, "dev", "prod") BK_FILE="${PKG_NAME}-${ENV}-$(date +%Y%m%d).zip" # Example: Add Git commit SHA for traceability COMMIT_SHA=$(git rev-parse --short HEAD) BK_FILE="${PKG_NAME}-${COMMIT_SHA}.zip"
Consejo : asegúrese de que los formatos de fecha y hora eviten caracteres prohibidos en los nombres de archivo (por ejemplo, dos puntos :
en Windows).
El script acepta rutas dinámicas como filtros, pero también puede codificar rutas utilizadas con frecuencia o agregar exclusiones:
# Hardcode essential paths (eg, "/var/audit") DEFAULT_FILTERS=("/content/dam" "/apps" "/var/audit") FILTERS=("${DEFAULT_FILTERS[@]}" "${@}") # Merge with command-line inputs # Add exclusion rules (requires AEM API support) FILTERS_PARAM+="{\"root\": \"${FILTERS[$i]}\", \"rules\": [{\"modifier\": \"exclude\", \"pattern\": \".*/test/*\"}]}"
Evite las contraseñas de texto simple :
Utilice variables de entorno o un administrador de secretos para inyectar credenciales:
# Fetch password from environment variable PWD="$AEM_PASSWORD" # Use AWS Secrets Manager (example) PWD=$(aws secretsmanager get-secret-value --secret-id aem/prod/password --query SecretString --output text)
Aplicar validación SSL :
Reemplace curl -k
(inseguro) con un certificado CA confiable:
curl --cacert /path/to/ca-bundle.crt -u "$USR":"$PWD" ...
Amplíe el script para activar procesos posteriores después de una descarga exitosa:
# Example: Upload to cloud storage aws s3 cp "$BK_FOLDER/$BK_FILE" s3://my-backup-bucket/ # Example: Validate package integrity CHECKSUM=$(sha256sum "$BK_FOLDER/$BK_FILE" | cut -d ' ' -f 1) _log "SHA-256 checksum: $CHECKSUM" # Example: Clean up old backups (retain last 7 days) find "$BK_FOLDER" -name "*.zip" -mtime +7 -exec rm {} \;
Notifique a los equipos sobre el éxito o el fracaso a través de Slack, correo electrónico o herramientas de monitoreo:
# Post to Slack on failure curl -X POST -H 'Content-type: application/json' \ --data "{\"text\":\"🚨 AEM backup failed: $(hostname)\"}" \ https://hooks.slack.com/services/YOUR/WEBHOOK/URL # Send email via sendmail if [ $? -ne 0 ]; then echo "Subject: Backup Failed" | sendmail [email protected] fi
La gestión de paquetes de AEM no tiene por qué ser una tarea manual propensa a errores. Con el script create-remote-aem-pkg.sh
, puede transformar la creación, el filtrado y la distribución de paquetes en un proceso optimizado y repetible. Esta herramienta no solo sirve para ahorrar tiempo, sino para permitir la coherencia, la fiabilidad y la escalabilidad en sus operaciones de AEM.
La automatización gana : al eliminar las interacciones repetitivas de la GUI, el script reduce el error humano y libera a los equipos para que se concentren en tareas de mayor valor.
La flexibilidad es importante : ya sea para realizar copias de seguridad de contenido crítico, sincronizar entornos o prepararse para actualizaciones, el script se adapta a diversos casos de uso con ajustes mínimos.
La resiliencia es clave : el registro integrado, las verificaciones de errores y las consideraciones de seguridad garantizan que el script se comporte de manera predecible, incluso cuando las cosas salen mal.
Las grandes herramientas surgen de los desafíos del mundo real. Este script es un punto de partida; considérelo como una base sobre la que construir a medida que las necesidades de su equipo crezcan. Ya sea que sea un desarrollador individual o parte de un gran equipo de DevOps, la automatización como esta ejemplifica cómo pequeñas inversiones en código pueden generar enormes ganancias en productividad y tranquilidad.
¿Listo para dar el siguiente paso?
Gracias por seguirnos. ¡Ahora siga adelante y automatice! 🚀
#!/bin/bash set -eo pipefail # The script will create a package thought the package manager api: # - The package is created, if not already present # - Package filters are populated accordingly to specified paths # - Package is builded # - Package is download to the specified folder _log () { echo "[$(date +%Y.%m.%d-%H:%M:%S)] $1" } check_last_exec () { local message="$1" local output="$2" local status=$3 if [ "$status" -ne 0 ]; then echo && echo "$message" && echo exit 1 fi if [[ $output =~ .*success\":false* ]] || [[ $output =~ .*"HTTP ERROR"* ]]; then _log "$message" exit 1 fi } USR="$1" PWD="$2" SVR="$3" PORT="$4" PKG_GROUP="$5" PKG_NAME="$6" BK_FOLDER="$7" shift 7 # The following paths will be included in the package FILTERS=($@) BK_FILE=$PKG_NAME"-"$(date +%Y%m%d-%H%M%S).zip _log "Starting backup process..." echo "AEM instance: '$SVR':'$PORT' AEM User: '$USR' Package group: $PKG_GROUP Package name: '$PKG_NAME' Destination folder: $BK_FOLDER Destination file: '$BK_FILE' Filter paths: " printf '\t%s\n\n' "${FILTERS[@]}" if [ ! -d "$BK_FOLDER" ]; then _log "Backup folder '$BK_FOLDER' does not exist!" && echo exit 1 fi PKG_NAME=${PKG_NAME// /_} check_last_exec "Error replacing white space chars from package name!" "" $? || exit 1 _log "Removed whitespaces from package name: '$PKG_NAME'" BK_FILE=$PKG_NAME.zip _log "Backup file: '$BK_FILE'" _log "Creating the package..." if [ $(curl -k -u "$USR":"$PWD" "$SVR:$PORT/crx/packmgr/service.jsp?cmd=ls" 2>/dev/null | grep "$PKG_NAME.zip" | wc -l) -eq 1 ]; then _log " Package '$PKG_GROUP/$PKG_NAME' is already present: skipping creation." else curl -k --silent -u "$USR":"$PWD" -X POST \ "$SVR:$PORT/crx/packmgr/service/.json/etc/packages/$PKG_GROUP/$PKG_NAME?cmd=create" \ -d packageName="$PKG_NAME" -d groupName="$PKG_GROUP" check_last_exec " Error creating the package!" "" $? _log " Package created" fi # create filters variable FILTERS_PARAM="" ARR_LEN="${#FILTERS[@]}" for i in "${!FILTERS[@]}"; do FILTERS_PARAM=$FILTERS_PARAM"{\"root\": \"${FILTERS[$i]}\", \"rules\": []}" T=$((i+1)) if [ $T -ne $ARR_LEN ]; then FILTERS_PARAM=$FILTERS_PARAM", " fi done # add filters _log "Adding filters to the package..." CURL_OUTPUT=$(curl -k --silent -u "$USR":"$PWD" -X POST "$SVR:$PORT/crx/packmgr/update.jsp" \ -F path=/etc/packages/"$PKG_GROUP"/"$PKG_NAME".zip -F packageName="$PKG_NAME" \ -F groupName="$PKG_GROUP" \ -F filter="[$FILTERS_PARAM]" \ -F "_charset_=UTF-8") CURL_STATUS=$? # Pass the status to the check_last_exec function check_last_exec "Error adding filters to the package!" "$CURL_OUTPUT" $CURL_STATUS _log " Package filters updated successfully." # build package _log "Building the package..." CURL_OUTPUT=$(curl -k -u "$USR":"$PWD" -X POST \ "$SVR:$PORT/crx/packmgr/service/script.html/etc/packages/$PKG_GROUP/$PKG_NAME.zip" \ -F "cmd=build") check_last_exec " Error building the package!" "$CURL_OUTPUT" $? _log " Package built." # download package _log "Downloading the package..." if [ "$(curl -w "%{http_code}" -o "$BK_FOLDER/$BK_FILE" -k --silent -u "$USR":"$PWD" "$SVR:$PORT/etc/packages/$PKG_GROUP/$PKG_NAME.zip")" -eq "200" ]; then if [ -f "$BK_FOLDER/$BK_FILE" ] && [ -s "$BK_FOLDER/$BK_FILE" ]; then _log " Package $BK_FILE downloaded in $BK_FOLDER." exit 0 fi fi _log " Error downloading the package!" exit 1
[¹] Omitir la verificación SSL con curl -k
es útil para realizar pruebas, pero necesitarás algo más robusto en producción (por ejemplo --cacert
).