Creación de Usuarios en Lote

Automatización empresarial para gestión masiva de usuarios y configuraciones de sistema

Módulo 12 ⏱️ 45-50 min 👥 Usuarios 🏢 Empresarial 🎯 Proyecto Final

Proyecto Final: Sistema Empresarial de Usuarios

En este módulo final del curso, desarrollaremos un sistema completo y profesional para la gestión masiva de usuarios en entornos empresariales. Este proyecto integra todos los conocimientos adquiridos: seguridad, automatización, manejo de errores, logging, y mejores prácticas de administración de sistemas.

El sistema que construiremos será capaz de procesar archivos CSV con información de empleados, crear usuarios con configuraciones específicas por departamento, establecer estructuras de directorios corporativas, configurar permisos granulares, y generar reportes detallados de la operación.

Objetivos del Proyecto Final
  • Automatización completa: Creación masiva de usuarios desde CSV
  • Seguridad robusta: Implementación de políticas de contraseñas seguras
  • Configuración por roles: Diferentes configuraciones según departamento
  • Auditoría completa: Logging detallado y reportes de cumplimiento
  • Escalabilidad: Capaz de manejar cientos o miles de usuarios
Casos de Uso Empresariales
  • Onboarding masivo de empleados nuevos
  • Migración de sistemas de autenticación
  • Reestructuración organizacional
  • Implementación de políticas de seguridad unificadas
  • Automatización de procesos de RRHH

Sistema de Gestión Masiva de Usuarios

Script Principal: Enterprise User Manager

Sistema completo y robusto para la creación y gestión masiva de usuarios empresariales.

enterprise_user_manager.sh Copiar
#!/bin/bash

# Enterprise User Management System
# Sistema profesional para gestión masiva de usuarios en entornos empresariales
# Proyecto Final - Curso Bash Scripting Avanzado

# ========================================
# CONFIGURACIÓN GLOBAL DEL SISTEMA
# ========================================

set -euo pipefail
IFS=$'\n\t'

# Variables del sistema
readonly SCRIPT_NAME="$(basename "$0")"
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_VERSION="2.0.0"
readonly SCRIPT_AUTHOR="Enterprise IT Team"

# Directorios de trabajo
readonly CONFIG_DIR="/etc/enterprise-user-manager"
readonly LOG_DIR="/var/log/enterprise-user-manager"
readonly BACKUP_DIR="/var/backups/enterprise-user-manager"
readonly TEMPLATE_DIR="$CONFIG_DIR/templates"
readonly REPORTS_DIR="/var/reports/enterprise-user-manager"

# Archivos de configuración
readonly MAIN_CONFIG="$CONFIG_DIR/config.conf"
readonly DEPARTMENTS_CONFIG="$CONFIG_DIR/departments.yaml"
readonly PASSWORD_POLICY="$CONFIG_DIR/password_policy.conf"

# Archivos de log
readonly MAIN_LOG="$LOG_DIR/main.log"
readonly AUDIT_LOG="$LOG_DIR/audit.log"
readonly ERROR_LOG="$LOG_DIR/errors.log"
readonly SECURITY_LOG="$LOG_DIR/security.log"

# Archivos temporales
readonly TEMP_DIR="/tmp/eum_$$"
readonly LOCK_FILE="/var/run/enterprise-user-manager.lock"

# Configuración de colores
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly MAGENTA='\033[0;35m'
readonly CYAN='\033[0;36m'
readonly NC='\033[0m'

# ========================================
# CONFIGURACIÓN POR DEFECTO DEL SISTEMA
# ========================================

# Configuración por defecto
readonly DEFAULT_CONFIG='
# Configuración Enterprise User Manager
COMPANY_NAME="Mi Empresa Corp"
DEFAULT_SHELL="/bin/bash"
HOME_BASE_DIR="/home"
CORPORATE_GROUPS=(employees contractors)
SUDO_GROUPS=(sysadmin devops)
PASSWORD_EXPIRY_DAYS=90
MIN_UID=2000
MAX_UID=60000
BACKUP_RETENTION_DAYS=30
EMAIL_DOMAIN="miempresa.com"
ENABLE_LDAP_SYNC=false
ENABLE_EMAIL_NOTIFICATIONS=true
ENABLE_SLACK_NOTIFICATIONS=false
'

# Configuración de departamentos (YAML simplificado)
readonly DEFAULT_DEPARTMENTS='
# Configuración por Departamentos
departments:
  IT:
    groups: [employees, it, sudo]
    shell: /bin/bash
    home_quota: "10G"
    description: "Departamento de Tecnología"
  HR:
    groups: [employees, hr]
    shell: /bin/bash
    home_quota: "5G"
    description: "Recursos Humanos"
  FINANCE:
    groups: [employees, finance]
    shell: /bin/bash
    home_quota: "8G"
    description: "Departamento Financiero"
  SALES:
    groups: [employees, sales]
    shell: /bin/bash
    home_quota: "6G"
    description: "Ventas y Marketing"
  EXEC:
    groups: [employees, executives, sudo]
    shell: /bin/bash
    home_quota: "15G"
    description: "Nivel Ejecutivo"
'

# ========================================
# FUNCIONES DE UTILIDAD BÁSICAS
# ========================================

# Función de logging centralizada
log_message() {
    local level="$1"
    local message="$2"
    local component="${3:-MAIN}"
    local timestamp="$(date '+%Y-%m-%d %H:%M:%S %Z')"
    local user="$(whoami)"
    local real_user="${SUDO_USER:-$user}"
    local pid="$$"
    
    # Formato: TIMESTAMP [LEVEL] (COMPONENT) USER(REAL_USER)[PID]: MESSAGE
    local log_entry="$timestamp [$level] ($component) $user($real_user)[$pid]: $message"
    
    # Escribir a log principal
    echo "$log_entry" | tee -a "$MAIN_LOG" >/dev/null
    
    # Log específico según level
    case "$level" in
        ERROR|CRITICAL)
            echo "$log_entry" >> "$ERROR_LOG"
            ;;
        SECURITY|AUTH)
            echo "$log_entry" >> "$SECURITY_LOG"
            ;;
        AUDIT)
            echo "$log_entry" >> "$AUDIT_LOG"
            ;;
    esac
    
    # Output con colores
    case "$level" in
        ERROR)     echo -e "${RED}[ERROR]${NC} $message" >&2 ;;
        CRITICAL)  echo -e "${RED}[CRITICAL]${NC} $message" >&2 ;;
        WARNING)   echo -e "${YELLOW}[WARNING]${NC} $message" >&2 ;;
        INFO)      echo -e "${BLUE}[INFO]${NC} $message" ;;
        SUCCESS)   echo -e "${GREEN}[SUCCESS]${NC} $message" ;;
        DEBUG)     [[ "${VERBOSE:-false}" == "true" ]] && echo -e "${MAGENTA}[DEBUG]${NC} $message" ;;
        AUDIT)     echo -e "${CYAN}[AUDIT]${NC} $message" ;;
        SECURITY)  echo -e "${RED}[SECURITY]${NC} $message" ;;
    esac
    
    # Enviar a syslog también
    logger -t "$SCRIPT_NAME[$pid]" -p "user.$level" "$message"
}

# Función de cleanup y salida
cleanup() {
    local exit_code=${1:-0}
    
    log_message "INFO" "Iniciando proceso de cleanup..."
    
    # Remover archivos temporales
    [[ -d "$TEMP_DIR" ]] && rm -rf "$TEMP_DIR"
    
    # Remover lock file
    [[ -f "$LOCK_FILE" ]] && rm -f "$LOCK_FILE"
    
    # Cleanup de procesos hijo si existen
    local child_pids=$(jobs -p)
    if [[ -n "$child_pids" ]]; then
        log_message "INFO" "Terminando procesos hijo: $child_pids"
        kill $child_pids 2>/dev/null || true
    fi
    
    log_message "INFO" "Sistema finalizado con código: $exit_code"
    exit "$exit_code"
}

# Configurar traps para manejo de señales
trap 'cleanup 130' INT TERM
trap 'cleanup 1' ERR
trap cleanup EXIT

# ========================================
# FUNCIONES DE INICIALIZACIÓN
# ========================================

# Verificar prerrequisitos del sistema
check_prerequisites() {
    log_message "INFO" "Verificando prerrequisitos del sistema..."
    
    # Verificar ejecución como root
    if [[ $EUID -ne 0 ]]; then
        log_message "CRITICAL" "Este script debe ejecutarse como root"
        cleanup 1
    fi
    
    # Verificar comandos requeridos
    local required_commands=(
        "useradd" "usermod" "userdel" "groupadd" "passwd"
        "chage" "quota" "setquota" "getent" "id"
        "openssl" "pwgen" "csvtool" "jq" "sendmail"
    )
    
    for cmd in "${required_commands[@]}"; do
        if ! command -v "$cmd" &>/dev/null; then
            log_message "WARNING" "Comando recomendado no encontrado: $cmd"
        fi
    done
    
    # Verificar sistema de archivos con soporte para quotas
    if ! grep -q "usrquota" /proc/mounts; then
        log_message "WARNING" "Sistema de quotas no habilitado - funcionalidad limitada"
    fi
    
    log_message "SUCCESS" "Verificación de prerrequisitos completada"
}

# Inicializar estructura de directorios
init_directory_structure() {
    log_message "INFO" "Inicializando estructura de directorios..."
    
    local directories=(
        "$CONFIG_DIR" "$LOG_DIR" "$BACKUP_DIR" 
        "$TEMPLATE_DIR" "$REPORTS_DIR" "$TEMP_DIR"
    )
    
    for dir in "${directories[@]}"; do
        if [[ ! -d "$dir" ]]; then
            mkdir -p "$dir"
            chmod 755 "$dir"
            log_message "DEBUG" "Directorio creado: $dir"
        fi
    done
    
    # Configurar permisos específicos
    chmod 700 "$LOG_DIR" "$BACKUP_DIR"
    chown root:root "$CONFIG_DIR" "$LOG_DIR" "$BACKUP_DIR"
    
    log_message "SUCCESS" "Estructura de directorios inicializada"
}

# Cargar configuración del sistema
load_configuration() {
    log_message "INFO" "Cargando configuración del sistema..."
    
    # Crear configuración por defecto si no existe
    if [[ ! -f "$MAIN_CONFIG" ]]; then
        echo "$DEFAULT_CONFIG" > "$MAIN_CONFIG"
        chmod 600 "$MAIN_CONFIG"
        log_message "INFO" "Configuración por defecto creada: $MAIN_CONFIG"
    fi
    
    # Cargar configuración
    source "$MAIN_CONFIG"
    
    # Crear configuración de departamentos si no existe
    if [[ ! -f "$DEPARTMENTS_CONFIG" ]]; then
        echo "$DEFAULT_DEPARTMENTS" > "$DEPARTMENTS_CONFIG"
        chmod 600 "$DEPARTMENTS_CONFIG"
        log_message "INFO" "Configuración de departamentos creada: $DEPARTMENTS_CONFIG"
    fi
    
    log_message "SUCCESS" "Configuración cargada exitosamente"
}

# Verificar y crear lock file
acquire_lock() {
    if [[ -f "$LOCK_FILE" ]]; then
        local lock_pid="$(cat "$LOCK_FILE" 2>/dev/null)"
        
        if [[ -n "$lock_pid" ]] && kill -0 "$lock_pid" 2>/dev/null; then
            log_message "CRITICAL" "Otra instancia del script está ejecutándose (PID: $lock_pid)"
            cleanup 1
        else
            log_message "WARNING" "Lock file obsoleto encontrado, removiendo"
            rm -f "$LOCK_FILE"
        fi
    fi
    
    echo "$$" > "$LOCK_FILE"
    log_message "DEBUG" "Lock adquirido: PID $$"
}

# ========================================
# FUNCIONES DE VALIDACIÓN
# ========================================

# Validar formato CSV
validate_csv_file() {
    local csv_file="$1"
    
    log_message "INFO" "Validando archivo CSV: $csv_file"
    
    # Verificar que el archivo existe y es legible
    if [[ ! -f "$csv_file" ]] || [[ ! -r "$csv_file" ]]; then
        log_message "ERROR" "Archivo CSV no encontrado o no legible: $csv_file"
        return 1
    fi
    
    # Verificar que no está vacío
    if [[ ! -s "$csv_file" ]]; then
        log_message "ERROR" "Archivo CSV está vacío: $csv_file"
        return 1
    fi
    
    # Verificar formato de header esperado
    local expected_header="username,first_name,last_name,email,department,position,phone,start_date"
    local actual_header=$(head -1 "$csv_file")
    
    if [[ "$actual_header" != "$expected_header" ]]; then
        log_message "ERROR" "Header CSV inválido. Esperado: $expected_header"
        log_message "ERROR" "Encontrado: $actual_header"
        return 1
    fi
    
    # Contar líneas válidas (sin header)
    local user_count=$(($(wc -l < "$csv_file") - 1))
    
    if [[ $user_count -le 0 ]]; then
        log_message "ERROR" "No hay usuarios válidos en el CSV"
        return 1
    fi
    
    log_message "SUCCESS" "CSV válido: $user_count usuarios encontrados"
    return 0
}

# Validar datos de usuario individual
validate_user_data() {
    local username="$1"
    local first_name="$2"
    local last_name="$3"
    local email="$4"
    local department="$5"
    local position="$6"
    local phone="$7"
    local start_date="$8"
    
    # Validar username
    if [[ ! "$username" =~ ^[a-z][a-z0-9_-]{2,31}$ ]]; then
        log_message "ERROR" "Username inválido: $username"
        return 1
    fi
    
    # Verificar si el usuario ya existe
    if id "$username" &>/dev/null; then
        log_message "WARNING" "Usuario ya existe: $username"
        return 2  # Código especial para usuario existente
    fi
    
    # Validar email
    if [[ ! "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
        log_message "ERROR" "Email inválido: $email"
        return 1
    fi
    
    # Validar departamento
    if ! grep -q "^  $department:" "$DEPARTMENTS_CONFIG"; then
        log_message "ERROR" "Departamento no configurado: $department"
        return 1
    fi
    
    # Validar nombres
    if [[ -z "$first_name" ]] || [[ -z "$last_name" ]]; then
        log_message "ERROR" "Nombres no pueden estar vacíos"
        return 1
    fi
    
    return 0
}

# ========================================
# FUNCIONES DE GESTIÓN DE USUARIOS
# ========================================

# Generar contraseña segura
generate_secure_password() {
    local length="${1:-16}"
    local password=""
    
    # Verificar si pwgen está disponible
    if command -v pwgen &>/dev/null; then
        password=$(pwgen -s -B -n "$length" 1)
    else
        # Fallback usando openssl
        password=$(openssl rand -base64 "$length" | tr -d "=+/" | cut -c1-"$length")
    fi
    
    # Asegurar complejidad mínima
    if [[ ! "$password" =~ [A-Z] ]] || [[ ! "$password" =~ [a-z] ]] || [[ ! "$password" =~ [0-9] ]]; then
        # Agregar caracteres necesarios
        password="${password}Aa1"
    fi
    
    echo "$password"
}

# Obtener configuración de departamento
get_department_config() {
    local department="$1"
    local config_key="$2"
    
    # Parsear YAML simplificado (limitado pero funcional)
    awk -v dept="$department" -v key="$config_key" '
        /^  [A-Z]+:/ { current_dept = substr($1, 3, length($1)-3) }
        current_dept == dept && $1 == key":" { 
            gsub(/^ +/, "", $2); 
            gsub(/[\[\]]/, "", $2); 
            print $2 
        }
    ' "$DEPARTMENTS_CONFIG"
}

# Crear usuario individual
create_single_user() {
    local username="$1"
    local first_name="$2"
    local last_name="$3"
    local email="$4"
    local department="$5"
    local position="$6"
    local phone="$7"
    local start_date="$8"
    
    log_message "INFO" "Creando usuario: $username ($first_name $last_name)"
    log_message "AUDIT" "USER_CREATE_START: $username, $department, $email"
    
    # Obtener configuración del departamento
    local dept_groups=$(get_department_config "$department" "groups")
    local dept_shell=$(get_department_config "$department" "shell")
    local dept_quota=$(get_department_config "$department" "home_quota")
    
    # Configurar valores por defecto si no se encuentran
    dept_shell="${dept_shell:-$DEFAULT_SHELL}"
    
    # Generar contraseña segura
    local password=$(generate_secure_password)
    local password_file="$TEMP_DIR/${username}_password.txt"
    echo "$password" > "$password_file"
    chmod 600 "$password_file"
    
    # Crear usuario
    local useradd_cmd="useradd -m -s $dept_shell -c \"$first_name $last_name ($department)\""
    
    # Asignar UID automáticamente dentro del rango
    local next_uid=$(awk -F: -v min="$MIN_UID" -v max="$MAX_UID" '$3 >= min && $3 <= max {uid[$3]=1} END {for(i=min;i<=max;i++) if(!uid[i]) {print i; exit}}' /etc/passwd)
    
    if [[ -n "$next_uid" ]]; then
        useradd_cmd="$useradd_cmd -u $next_uid"
    fi
    
    if eval "$useradd_cmd $username"; then
        log_message "SUCCESS" "Usuario creado exitosamente: $username"
    else
        log_message "ERROR" "Falló la creación del usuario: $username"
        return 1
    fi
    
    # Establecer contraseña
    if echo "$username:$password" | chpasswd; then
        log_message "DEBUG" "Contraseña establecida para: $username"
    else
        log_message "ERROR" "Falló establecer contraseña para: $username"
        return 1
    fi
    
    # Forzar cambio de contraseña en primer login
    if chage -d 0 "$username"; then
        log_message "DEBUG" "Cambio de contraseña forzado para: $username"
    fi
    
    # Agregar a grupos
    if [[ -n "$dept_groups" ]]; then
        # Limpiar formato de grupos
        dept_groups=$(echo "$dept_groups" | tr -d '[]' | tr ',' ' ')
        
        for group in $dept_groups; do
            group=$(echo "$group" | xargs)  # Trim whitespace
            
            # Crear grupo si no existe
            if ! getent group "$group" >/dev/null; then
                if groupadd "$group"; then
                    log_message "INFO" "Grupo creado: $group"
                fi
            fi
            
            # Agregar usuario al grupo
            if usermod -a -G "$group" "$username"; then
                log_message "DEBUG" "Usuario $username agregado al grupo: $group"
            fi
        done
    fi
    
    # Configurar quota si está especificada
    if [[ -n "$dept_quota" ]] && command -v setquota &>/dev/null; then
        local quota_kb=$(echo "$dept_quota" | sed 's/G/*1024*1024/;s/M/*1024/' | bc 2>/dev/null || echo "5242880")
        if setquota -u "$username" "$quota_kb" "$quota_kb" 0 0 /home 2>/dev/null; then
            log_message "DEBUG" "Quota configurada para $username: $dept_quota"
        fi
    fi
    
    # Crear estructura de directorios personalizada
    create_user_directory_structure "$username" "$department"
    
    # Configurar archivos de perfil
    setup_user_profile "$username" "$department"
    
    # Generar credenciales para entrega
    generate_user_credentials "$username" "$first_name" "$last_name" "$email" "$password"
    
    log_message "AUDIT" "USER_CREATE_SUCCESS: $username, UID: $(id -u "$username"), Groups: $(groups "$username")"
    log_message "SUCCESS" "Usuario $username configurado completamente"
    
    return 0
}

# Crear estructura de directorios personalizada
create_user_directory_structure() {
    local username="$1"
    local department="$2"
    local home_dir="/home/$username"
    
    log_message "DEBUG" "Configurando estructura de directorios para: $username"
    
    # Directorios estándar
    local standard_dirs=(
        "Documents" "Downloads" "Desktop" "Projects"
        "Scripts" "Backups" ".ssh" ".config"
    )
    
    for dir in "${standard_dirs[@]}"; do
        local full_path="$home_dir/$dir"
        if [[ ! -d "$full_path" ]]; then
            mkdir -p "$full_path"
            chown "$username:$username" "$full_path"
            chmod 755 "$full_path"
        fi
    done
    
    # Configurar permisos especiales para .ssh
    chmod 700 "$home_dir/.ssh"
    
    # Crear directorio específico por departamento
    local dept_dir="$home_dir/Department_${department}"
    mkdir -p "$dept_dir"
    chown "$username:$username" "$dept_dir"
    chmod 755 "$dept_dir"
    
    # Crear README personalizado
    cat > "$home_dir/README.txt" << EOF
Bienvenido al sistema $COMPANY_NAME
===================================

Usuario: $username
Departamento: $department
Fecha de creación: $(date)

Estructura de directorios:
- Documents/: Documentos personales
- Projects/: Proyectos de trabajo
- Scripts/: Scripts personalizados
- Department_${department}/: Recursos del departamento
- .ssh/: Configuración SSH (mantener privado)

Para soporte técnico, contacta al departamento de IT.
EOF
    
    chown "$username:$username" "$home_dir/README.txt"
    chmod 644 "$home_dir/README.txt"
    
    log_message "DEBUG" "Estructura de directorios creada para: $username"
}

# Configurar perfil de usuario
setup_user_profile() {
    local username="$1"
    local department="$2"
    local home_dir="/home/$username"
    
    # Configurar .bashrc personalizado
    cat >> "$home_dir/.bashrc" << EOF

# Configuración personalizada $COMPANY_NAME
export COMPANY="$COMPANY_NAME"
export DEPARTMENT="$department"
export USER_CREATED="$(date)"

# Aliases útiles
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'
alias grep='grep --color=auto'
alias ..='cd ..'
alias ...='cd ../..'

# Configuración del prompt
PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '

# Mensaje de bienvenida
echo "Bienvenido a $COMPANY_NAME, $username"
echo "Departamento: $department"
echo "Último login: \$(date)"
EOF
    
    # Configurar .profile
    cat >> "$home_dir/.profile" << EOF
# Configuración de entorno $COMPANY_NAME
export PATH="\$HOME/Scripts:\$PATH"
export EDITOR=nano
export PAGER=less
EOF
    
    # Ajustar propietario
    chown "$username:$username" "$home_dir/.bashrc" "$home_dir/.profile"
    
    log_message "DEBUG" "Perfil configurado para: $username"
}

# Generar archivo de credenciales
generate_user_credentials() {
    local username="$1"
    local first_name="$2"
    local last_name="$3"
    local email="$4"
    local password="$5"
    
    local credentials_file="$REPORTS_DIR/credentials_${username}_$(date +%Y%m%d_%H%M%S).txt"
    
    cat > "$credentials_file" << EOF
CREDENCIALES DE ACCESO - $COMPANY_NAME
======================================

Información del Usuario:
- Nombre Completo: $first_name $last_name
- Usuario: $username
- Email: $email
- Contraseña Temporal: $password

Instrucciones de Primer Acceso:
1. Inicia sesión con las credenciales proporcionadas
2. Cambia tu contraseña cuando se te solicite
3. Lee el archivo README.txt en tu directorio home
4. Configura tu perfil según las políticas de la empresa

IMPORTANTE:
- Esta contraseña debe cambiarse en el primer acceso
- No compartas tus credenciales con terceros
- Contacta a IT para cualquier problema técnico

Fecha de generación: $(date)
Sistema: $(hostname)
EOF
    
    # Proteger el archivo de credenciales
    chmod 600 "$credentials_file"
    chown root:root "$credentials_file"
    
    log_message "DEBUG" "Credenciales generadas: $credentials_file"
    echo "$credentials_file"
}

# ========================================
# FUNCIONES DE PROCESAMIENTO MASIVO
# ========================================

# Procesar archivo CSV completo
process_csv_file() {
    local csv_file="$1"
    local dry_run="${2:-false}"
    
    log_message "INFO" "Iniciando procesamiento masivo de usuarios..."
    log_message "AUDIT" "BATCH_PROCESS_START: file=$csv_file, dry_run=$dry_run"
    
    # Validar archivo
    if ! validate_csv_file "$csv_file"; then
        log_message "CRITICAL" "Validación de CSV falló"
        return 1
    fi
    
    # Contadores
    local total_users=0
    local successful_creates=0
    local failed_creates=0
    local skipped_users=0
    
    # Procesar línea por línea (saltando header)
    while IFS=',' read -r username first_name last_name email department position phone start_date; do
        ((total_users++))
        
        log_message "DEBUG" "Procesando usuario $total_users: $username"
        
        # Validar datos del usuario
        if ! validate_user_data "$username" "$first_name" "$last_name" "$email" "$department" "$position" "$phone" "$start_date"; then
            case $? in
                1)
                    log_message "ERROR" "Datos inválidos para usuario: $username"
                    ((failed_creates++))
                    ;;
                2)
                    log_message "INFO" "Usuario ya existe, saltando: $username"
                    ((skipped_users++))
                    ;;
            esac
            continue
        fi
        
        # Modo dry-run
        if [[ "$dry_run" == "true" ]]; then
            log_message "INFO" "DRY-RUN: Crearía usuario $username ($department)"
            continue
        fi
        
        # Crear usuario
        if create_single_user "$username" "$first_name" "$last_name" "$email" "$department" "$position" "$phone" "$start_date"; then
            ((successful_creates++))
            log_message "SUCCESS" "Usuario creado exitosamente: $username"
        else
            ((failed_creates++))
            log_message "ERROR" "Falló la creación del usuario: $username"
        fi
        
        # Pausa pequeña para evitar sobrecarga del sistema
        sleep 0.1
        
    done < <(tail -n +2 "$csv_file")  # Saltar header
    
    # Generar reporte final
    generate_batch_report "$csv_file" "$total_users" "$successful_creates" "$failed_creates" "$skipped_users" "$dry_run"
    
    log_message "AUDIT" "BATCH_PROCESS_END: total=$total_users, success=$successful_creates, failed=$failed_creates, skipped=$skipped_users"
    log_message "SUCCESS" "Procesamiento completado: $successful_creates/$total_users usuarios creados exitosamente"
    
    return 0
}

# Generar reporte de procesamiento masivo
generate_batch_report() {
    local csv_file="$1"
    local total="$2"
    local successful="$3"
    local failed="$4"
    local skipped="$5"
    local dry_run="$6"
    
    local report_file="$REPORTS_DIR/batch_report_$(date +%Y%m%d_%H%M%S).txt"
    
    {
        echo "REPORTE DE PROCESAMIENTO MASIVO DE USUARIOS"
        echo "==========================================="
        echo ""
        echo "Información General:"
        echo "- Archivo procesado: $csv_file"
        echo "- Fecha/Hora: $(date)"
        echo "- Sistema: $(hostname)"
        echo "- Ejecutado por: $(whoami)"
        echo "- Modo: $([ "$dry_run" == "true" ] && echo "DRY RUN" || echo "PRODUCCIÓN")"
        echo ""
        echo "Estadísticas:"
        echo "- Total de usuarios procesados: $total"
        echo "- Creaciones exitosas: $successful"
        echo "- Creaciones fallidas: $failed"
        echo "- Usuarios saltados (existentes): $skipped"
        echo ""
        echo "Tasa de éxito: $(( (successful * 100) / (total > 0 ? total : 1) ))%"
        echo ""
        
        if [[ $failed -gt 0 ]]; then
            echo "ERRORES ENCONTRADOS:"
            echo "- $failed usuarios no pudieron ser creados"
            echo "- Revisar $ERROR_LOG para detalles"
            echo ""
        fi
        
        if [[ "$dry_run" != "true" ]]; then
            echo "ARCHIVOS GENERADOS:"
            echo "- Credenciales: $REPORTS_DIR/credentials_*.txt"
            echo "- Logs: $LOG_DIR/"
            echo ""
        fi
        
        echo "Para soporte técnico, contactar al administrador del sistema."
        
    } > "$report_file"
    
    log_message "SUCCESS" "Reporte generado: $report_file"
    
    # Mostrar resumen en pantalla
    echo -e "\n${GREEN}=== RESUMEN DEL PROCESAMIENTO ===${NC}"
    echo "Total procesados: $total"
    echo "Exitosos: $successful"
    echo "Fallidos: $failed"
    echo "Saltados: $skipped"
    echo "Reporte completo: $report_file"
}

# ========================================
# FUNCIÓN PRINCIPAL Y MANEJO DE ARGUMENTOS
# ========================================

show_help() {
    cat << EOF
Enterprise User Management System v$SCRIPT_VERSION
Creado por: $SCRIPT_AUTHOR

DESCRIPCIÓN:
    Sistema profesional para gestión masiva de usuarios empresariales.
    Procesa archivos CSV para crear usuarios con configuraciones específicas
    por departamento, incluyendo permisos, quotas y estructura de directorios.

USO:
    $0 [COMANDO] [OPCIONES]

COMANDOS:
    create-batch CSV_FILE       - Crear usuarios masivamente desde CSV
    create-single              - Crear usuario individual (interactivo)
    validate CSV_FILE          - Validar archivo CSV sin crear usuarios
    list-departments           - Mostrar departamentos configurados
    generate-template          - Generar template CSV de ejemplo
    cleanup-old-users DAYS     - Limpiar usuarios antiguos
    backup-system             - Crear backup del sistema de usuarios
    restore-system BACKUP     - Restaurar desde backup
    
OPCIONES GLOBALES:
    --dry-run                  - Simular sin hacer cambios reales
    --verbose                  - Modo verbose con debug detallado
    --config FILE             - Usar archivo de configuración específico
    --log-level LEVEL         - Nivel de log: ERROR,WARN,INFO,DEBUG
    -h, --help                - Mostrar esta ayuda

FORMATO CSV ESPERADO:
    username,first_name,last_name,email,department,position,phone,start_date
    
EJEMPLOS:
    # Validar archivo CSV
    $0 validate employees.csv
    
    # Crear usuarios en modo simulación
    $0 create-batch employees.csv --dry-run
    
    # Crear usuarios en producción
    $0 create-batch employees.csv
    
    # Generar template de ejemplo
    $0 generate-template > template.csv

ARCHIVOS DE CONFIGURACIÓN:
    - Config principal: $MAIN_CONFIG
    - Departamentos: $DEPARTMENTS_CONFIG
    - Logs: $LOG_DIR/
    - Reportes: $REPORTS_DIR/

Para más información, consulta la documentación o contacta al equipo de IT.
EOF
}

# Función principal
main() {
    local command="${1:-help}"
    local verbose=false
    local dry_run=false
    
    # Procesar opciones globales
    while [[ $# -gt 0 ]]; do
        case $1 in
            --verbose)
                verbose=true
                export VERBOSE=true
                shift
                ;;
            --dry-run)
                dry_run=true
                shift
                ;;
            --help|-h|help)
                show_help
                exit 0
                ;;
            *)
                break
                ;;
        esac
    done
    
    # Re-capturar comando después de procesar opciones
    command="${1:-help}"
    
    # Inicialización del sistema
    check_prerequisites
    init_directory_structure
    load_configuration
    acquire_lock
    
    log_message "INFO" "=== Enterprise User Manager v$SCRIPT_VERSION iniciado ==="
    log_message "INFO" "Comando: $command, Verbose: $verbose, Dry-run: $dry_run"
    
    # Ejecutar comando solicitado
    case "$command" in
        create-batch)
            [[ -z "${2:-}" ]] && { log_message "ERROR" "Archivo CSV requerido"; show_help; cleanup 1; }
            process_csv_file "$2" "$dry_run"
            ;;
        validate)
            [[ -z "${2:-}" ]] && { log_message "ERROR" "Archivo CSV requerido"; cleanup 1; }
            validate_csv_file "$2"
            ;;
        generate-template)
            echo "username,first_name,last_name,email,department,position,phone,start_date"
            echo "jsmith,John,Smith,[email protected],IT,Developer,555-1234,2024-01-15"
            echo "mdoe,Mary,Doe,[email protected],HR,Manager,555-5678,2024-01-16"
            ;;
        list-departments)
            grep "^  [A-Z]*:" "$DEPARTMENTS_CONFIG" | sed 's/://g' | awk '{print $1}'
            ;;
        help|--help|-h)
            show_help
            ;;
        *)
            log_message "ERROR" "Comando desconocido: $command"
            show_help
            cleanup 1
            ;;
    esac
    
    cleanup 0
}

# ========================================
# PUNTO DE ENTRADA DEL SCRIPT
# ========================================

# Verificar ejecución directa
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    main "$@"
fi

Archivo CSV de Ejemplo

Formato estándar para la carga masiva de usuarios empresariales.

employees_sample.csv Copiar
username,first_name,last_name,email,department,position,phone,start_date
jsmith,John,Smith,[email protected],IT,Senior Developer,555-0101,2024-01-15
mdoe,Mary,Doe,[email protected],HR,HR Manager,555-0102,2024-01-15
rjohnson,Robert,Johnson,[email protected],FINANCE,Financial Analyst,555-0103,2024-01-16
swilliams,Susan,Williams,[email protected],SALES,Sales Representative,555-0104,2024-01-16
dbrown,David,Brown,[email protected],IT,DevOps Engineer,555-0105,2024-01-17
ljones,Lisa,Jones,[email protected],HR,Recruiter,555-0106,2024-01-17
mgarcia,Michael,Garcia,[email protected],SALES,Sales Manager,555-0107,2024-01-18
krodriguez,Karen,Rodriguez,[email protected],FINANCE,Controller,555-0108,2024-01-18
twilson,Thomas,Wilson,[email protected],EXEC,VP Engineering,555-0109,2024-01-19
amartinez,Anna,Martinez,[email protected],IT,Security Specialist,555-0110,2024-01-19

Demostración y Casos de Uso

# Generar template CSV
$ ./enterprise_user_manager.sh generate-template > new_employees.csv

# Validar archivo antes de procesamiento
$ ./enterprise_user_manager.sh validate new_employees.csv
[SUCCESS] CSV válido: 10 usuarios encontrados

# Simulación de creación masiva (dry-run)
$ ./enterprise_user_manager.sh create-batch new_employees.csv --dry-run
[INFO] DRY-RUN: Crearía usuario jsmith (IT)
[INFO] DRY-RUN: Crearía usuario mdoe (HR)
...

# Procesamiento real
$ ./enterprise_user_manager.sh create-batch new_employees.csv
[SUCCESS] Usuario creado exitosamente: jsmith
[SUCCESS] Usuario creado exitosamente: mdoe
[SUCCESS] Procesamiento completado: 10/10 usuarios creados exitosamente

# Verificar usuarios creados
$ tail -10 /etc/passwd
jsmith:x:2001:2001:John Smith (IT):/home/jsmith:/bin/bash
mdoe:x:2002:2002:Mary Doe (HR):/home/mdoe:/bin/bash
...
Funcionalidades Implementadas
  • ✅ Validación exhaustiva de datos CSV
  • ✅ Creación masiva con configuración por departamento
  • ✅ Generación automática de contraseñas seguras
  • ✅ Configuración de grupos y permisos granulares
  • ✅ Estructura de directorios personalizada
  • ✅ Sistema completo de auditoría y logging
  • ✅ Reportes detallados de procesamiento
  • ✅ Modo dry-run para testing
Ejercicio Final Integrador

Implementa un sistema completo que incluya:

  1. Carga y validación de empleados desde CSV
  2. Configuración automática según organigrama empresarial
  3. Integración con sistema de directorio LDAP (opcional)
  4. Notificaciones automáticas por email/Slack
  5. Dashboard web para monitoreo (bonus)
  6. Sistema de backup y recuperación
Evaluación del Proyecto Final

Tu implementación será evaluada en:

  • Funcionalidad (40%): Cumplimiento de todos los requisitos
  • Seguridad (25%): Implementación de mejores prácticas
  • Código (20%): Calidad, documentación y mantenibilidad
  • Escalabilidad (15%): Capacidad de manejar grandes volúmenes