Automatización del aprovisionamiento de usuarios y grupos
Cree, modifique y audite cuentas en bloque mediante useradd, chage y la gestión de fragmentos de sudoers.
Automatización del aprovisionamiento de usuarios y grupos es una lección gratuita de Linux Command Line & Bash Scripting Mastery en CoddyKit. Esta es la lección 1 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Linux Command Line & Bash Scripting Mastery, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Linux Command Line & Bash Scripting Mastery incluye 4 lecciones en total.
¿Por qué automatizar el aprovisionamiento de usuarios?
Gestionar los usuarios uno a uno con useradd funciona bien para un puñado de cuentas, pero los entornos empresariales incorporan habitualmente decenas o cientos de usuarios a la vez. Los comandos manuales son propensos a errores, incoherentes y difíciles de auditar.
Los scripts de Bash le permiten:
- Crear usuarios con configuraciones estandarizadas (shell, directorio personal y política de contraseñas) en cada ocasión
- Leer un archivo CSV o de texto con las nuevas incorporaciones y aprovisionarlas en una sola ejecución
- Registrar cada acción para disponer de un rastro de auditoría con fines de cumplimiento
- Integrarse con pipelines de gestión de configuración (Ansible, Chef, Jenkins)
En esta lección se explica cómo crear desde cero un script de aprovisionamiento de usuarios preparado para producción, incluyendo useradd, chage, usermod, la gestión de grupos, los archivos drop-in de sudoers y la auditoría posterior a la ejecución.
Lectura de una lista masiva de usuarios
El formato de entrada habitual para el aprovisionamiento masivo es un archivo de texto delimitado: un registro por línea. Un CSV típico podría tener este aspecto:
username,full_name,group,shell
alice,Alice Smith,developers,/bin/bash
bob,Bob Jones,ops,/bin/zshUse IFS y read dentro de un bucle while para analizarlo de forma segura. Omitir la línea de encabezado con tail -n +2 mantiene la lógica clara.
Prácticas defensivas clave:
- Elimine los espacios en blanco iniciales y finales de cada campo
- Omita las líneas vacías y las líneas de comentarios que comiencen por
# - Compruebe que los campos obligatorios no estén vacíos antes de llamar a cualquier comando del sistema
#!/usr/bin/env bash
# parse_users.sh — safely read a CSV of users
set -euo pipefail
USER_FILE="${1:-users.csv}"
[[ -f "$USER_FILE" ]] || { echo "ERROR: $USER_FILE not found"; exit 1; }
tail -n +2 "$USER_FILE" | while IFS=',' read -r username full_name group shell; do
# trim whitespace
username="${username// /}"
[[ -z "$username" || "$username" == \#* ]] && continue
echo "Parsed -> user=$username group=$group shell=$shell"
doneCreación de usuarios con useradd
useradd es la utilidad de bajo nivel que escribe en /etc/passwd, /etc/shadow y /etc/group. Las opciones más importantes para crear scripts son:
-m— crear el directorio personal-s— establecer el shell de inicio de sesión-c— campo de comentario GECOS (nombre completo)-G— grupos suplementarios (separados por comas)-e— fecha de caducidad de la cuenta (YYYY-MM-DD)
Compruebe siempre si el usuario ya existe con id antes de llamar a useradd; ejecutarlo sobre un usuario existente devuelve el código de salida 9 y muestra un error que podría saturar los registros.
Nota: useradd requiere privilegios de root. Incluya una comprobación de privilegios al principio del script.
#!/usr/bin/env bash
# create_user.sh — idempotent single-user creation
set -euo pipefail
[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }
USERNAME="$1"
FULL_NAME="${2:-}"
GROUP="${3:-staff}"
SHELL="${4:-/bin/bash}"
if id "$USERNAME" &>/dev/null; then
echo "[SKIP] User $USERNAME already exists"
else
useradd \
--create-home \
--shell "$SHELL" \
--comment "$FULL_NAME" \
--groups "$GROUP" \
"$USERNAME"
echo "[OK] Created $USERNAME"
fiConfiguración segura de las contraseñas iniciales
No incluya nunca contraseñas directamente en los scripts. Dos métodos seguros para el aprovisionamiento masivo son:
- Generar una contraseña inicial aleatoria con
openssl rando/dev/urandom, mostrarla una sola vez y obligar al usuario a cambiarla en el primer inicio de sesión - Establecer una contraseña previamente cifrada mediante
usermod -pcon un hash SHA-512, para que el texto plano nunca aparezca en la lista de procesos
chpasswd es la herramienta recomendada para establecer contraseñas mediante scripts: lee pares username:password de stdin, por lo que la contraseña nunca aparece en los argumentos de la línea de comandos (visibles mediante ps).
Después de establecer la contraseña, use chage -d 0 para obligar a restablecerla en el siguiente inicio de sesión.
#!/usr/bin/env bash
# set_temp_password.sh
set -euo pipefail
[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }
USERNAME="$1"
# Generate a 16-char random password (alphanumeric only)
TMP_PASS=$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 16)
# Set password via chpasswd (password never in argv)
echo "${USERNAME}:${TMP_PASS}" | chpasswd
# Force password change on next login
chage -d 0 "$USERNAME"
echo "[OK] Temporary password for $USERNAME: $TMP_PASS"
echo "[OK] User must change password on first login"Gestión de la caducidad de contraseñas con chage
chage (change age) controla la política de caducidad de contraseñas almacenada en /etc/shadow. Las políticas de seguridad empresariales suelen exigir:
- Antigüedad máxima de la contraseña (por ejemplo, 90 días)
- Número mínimo de días antes de poder volver a cambiar una contraseña
- Periodo de aviso previo a la caducidad
- Bloqueo por inactividad de la cuenta tras el último uso de la contraseña
Opciones principales de chage:
-M <days>— antigüedad máxima de la contraseña-m <days>— antigüedad mínima de la contraseña-W <days>— días de aviso antes de la caducidad-I <days>— días de inactividad antes de bloquear la cuenta-E <date>— caducidad absoluta de la cuenta-l— mostrar la configuración actual de un usuario
#!/usr/bin/env bash
# apply_password_policy.sh — enforce org-wide ageing policy
set -euo pipefail
[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }
# Policy constants
MAX_AGE=90
MIN_AGE=1
WARN_DAYS=14
INACTIVE_DAYS=30
apply_policy() {
local user="$1"
chage \
-M "$MAX_AGE" \
-m "$MIN_AGE" \
-W "$WARN_DAYS" \
-I "$INACTIVE_DAYS" \
"$user"
echo "[OK] Policy applied to $user"
}
# Apply to all non-system users (UID >= 1000)
awk -F: '$3 >= 1000 && $3 < 65534 { print $1 }' /etc/passwd | while read -r user; do
apply_policy "$user"
doneGestión masiva de grupos
Los grupos son el mecanismo principal para controlar el acceso a los recursos. Un script de aprovisionamiento debe asegurarse de que los grupos necesarios existan antes de añadir usuarios a ellos; useradd -G nonexistent fallará.
Use groupadd de forma idempotente comprobando el código de salida: devuelve 9 si el grupo ya existe. La expresión getent group <name> es una alternativa portable y legible a buscar con grep en /etc/group.
gpasswd -a añade un usuario a un grupo sin sustituir sus membresías existentes (a diferencia de usermod -G, que reemplaza la lista de grupos suplementarios).
#!/usr/bin/env bash
# ensure_groups.sh — create groups if missing, then add users
set -euo pipefail
[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }
REQUIRED_GROUPS=(developers ops security auditors)
for grp in "${REQUIRED_GROUPS[@]}"; do
if getent group "$grp" &>/dev/null; then
echo "[SKIP] Group $grp already exists"
else
groupadd "$grp"
echo "[OK] Created group $grp"
fi
done
# Safely add a user to a group (append, don't replace)
add_to_group() {
local user="$1" group="$2"
gpasswd -a "$user" "$group" 2>/dev/null && echo "[OK] $user -> $group"
}Script completo de aprovisionamiento masivo
Al combinarlo todo, un solo script lee un CSV, crea usuarios y grupos, establece la política de contraseñas, registra cada acción y gestiona los errores correctamente sin detener todo el lote.
Decisiones de diseño importantes del siguiente script:
- Un
LOG_FILEcon marcas de tiempo captura todas las operaciones para su auditoría - Los errores de usuarios individuales se registran, pero no interrumpen el bucle (
|| log_error) - El script es idempotente: se puede volver a ejecutar de forma segura después de fallos parciales
- Toda la salida se dirige tanto al terminal como al archivo de registro mediante
tee
#!/usr/bin/env bash
# bulk_provision.sh — production user provisioning
set -uo pipefail
[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }
USER_FILE="${1:-users.csv}"
LOG_FILE="/var/log/user_provision_$(date +%F).log"
log() { echo "[$(date '+%F %T')] $*" | tee -a "$LOG_FILE"; }
err() { log "ERROR: $*"; }
log "=== Provisioning started from $USER_FILE ==="
tail -n +2 "$USER_FILE" | while IFS=',' read -r username fullname group shell; do
username="${username// /}"
[[ -z "$username" || "$username" == \#* ]] && continue
group="${group:-staff}"
shell="${shell:-/bin/bash}"
# Ensure group exists
getent group "$group" &>/dev/null || groupadd "$group"
# Create user idempotently
if id "$username" &>/dev/null; then
log "[SKIP] $username exists"
else
useradd -m -s "$shell" -c "$fullname" -G "$group" "$username" || { err "useradd failed for $username"; continue; }
TMP="$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 14)"
echo "${username}:${TMP}" | chpasswd
chage -M 90 -m 1 -W 14 -I 30 -d 0 "$username"
log "[OK] $username created (group=$group) tmp_pass=$TMP"
fi
done
log "=== Provisioning complete ==="Gestión de fragmentos drop-in de sudoers
Editar directamente /etc/sudoers es peligroso: un error de sintaxis puede dejar a todo el mundo sin acceso a sudo. El método seguro consiste en utilizar archivos drop-in en /etc/sudoers.d/, validando cada uno con visudo -c -f antes de instalarlo.
Prácticas recomendadas para los fragmentos de sudoers:
- Asigne a los archivos el nombre del equipo o rol cuyos permisos conceden (por ejemplo,
10-developers,20-ops) - Use reglas basadas en grupos (
%developers ALL=(ALL) NOPASSWD: /usr/bin/systemctl) en lugar de líneas para usuarios individuales - Establezca siempre los permisos en
0440y el propietario enroot:root - Valide con
visudo -c; devuelve un código distinto de cero ante cualquier error de sintaxis
#!/usr/bin/env bash
# write_sudoers_fragment.sh
set -euo pipefail
[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }
FRAGMENT_NAME="${1:-10-developers}"
SUDOERS_DIR="/etc/sudoers.d"
TMP_FILE="$(mktemp)"
# Write the fragment to a temp file first
cat > "$TMP_FILE" << 'EOF'
# Developers: restart services and view journals without full root
%developers ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart *, /usr/bin/journalctl
%ops ALL=(ALL) NOPASSWD: ALL
EOF
# Validate BEFORE installing
if visudo -c -f "$TMP_FILE"; then
install -m 0440 -o root -g root "$TMP_FILE" "${SUDOERS_DIR}/${FRAGMENT_NAME}"
echo "[OK] Installed ${SUDOERS_DIR}/${FRAGMENT_NAME}"
else
echo "[ERROR] sudoers syntax check failed — fragment NOT installed"
rm -f "$TMP_FILE"
exit 1
fi
rm -f "$TMP_FILE"Auditoría de cuentas existentes
Después del aprovisionamiento (y de forma periódica), debe auditar la base de datos de usuarios para detectar anomalías:
- Cuentas con UID 0: cualquier cuenta con UID 0 distinta de root constituye un hallazgo de seguridad crítico
- Cuentas sin contraseña: entradas con un campo de contraseña vacío o con
!en/etc/shadow - Cuentas caducadas que siguen activas: la salida de
chage -lse puede analizar de forma masiva - Usuarios con shell pero sin directorio personal: una configuración incorrecta que impide iniciar sesión
Generar un informe estructurado y enviarlo por correo al equipo de seguridad es sencillo con mail o añadiéndolo a una ruta de registros supervisada.
#!/usr/bin/env bash
# audit_users.sh — produce a security-relevant user report
set -uo pipefail
REPORT="/var/log/user_audit_$(date +%F).txt"
echo "=== User Audit Report $(date) ===" > "$REPORT"
echo "" >> "$REPORT"
echo "--- Accounts with UID 0 (should be root only) ---" >> "$REPORT"
awk -F: '$3 == 0 { print $1 }' /etc/passwd >> "$REPORT"
echo "" >> "$REPORT"
echo "--- Accounts with empty password field ---" >> "$REPORT"
awk -F: '($2 == "" || $2 == "!") && $3 >= 1000 { print $1 }' /etc/shadow 2>/dev/null >> "$REPORT" || echo " (requires root)" >> "$REPORT"
echo "" >> "$REPORT"
echo "--- Normal users (UID 1000-60000) ---" >> "$REPORT"
awk -F: '$3 >= 1000 && $3 < 60000 { printf "%-20s uid=%-6s shell=%s\n", $1, $3, $7 }' /etc/passwd >> "$REPORT"
cat "$REPORT"
echo "Report saved to $REPORT"Bloqueo, desbloqueo y eliminación de cuentas
La baja de usuarios es tan importante como su alta. Cuando un usuario abandona la organización, la secuencia correcta es:
- Bloquear la cuenta inmediatamente (
usermod -L): antepone!al hash de la contraseña en shadow, lo que impide iniciar sesión sin eliminar los datos - Revocar sudo: elimine su fragmento de sudoers si existe
- Transferir la propiedad de sus archivos a un responsable o a una cuenta de archivo
- Archivar el directorio personal como un tarball antes de eliminarlo
- Eliminar con
userdel -r: elimina el directorio personal y la cola de correo
usermod -U desbloquea una cuenta (elimina el prefijo !), lo que resulta útil para una suspensión temporal.
#!/usr/bin/env bash
# offboard_user.sh — lock, archive, then optionally delete
set -euo pipefail
[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }
USERNAME="$1"
ARCHIVE_DIR="/srv/archived-homes"
mkdir -p "$ARCHIVE_DIR"
# 1. Lock account
usermod -L "$USERNAME"
echo "[OK] Account $USERNAME locked"
# 2. Remove sudoers fragment if present
SUDOERS_FILE="/etc/sudoers.d/${USERNAME}"
[[ -f "$SUDOERS_FILE" ]] && rm -f "$SUDOERS_FILE" && echo "[OK] Removed sudoers fragment"
# 3. Archive home directory
HOME_DIR="$(getent passwd "$USERNAME" | cut -d: -f6)"
if [[ -d "$HOME_DIR" ]]; then
tar -czf "${ARCHIVE_DIR}/${USERNAME}_$(date +%F).tar.gz" -C "$(dirname "$HOME_DIR")" "$(basename "$HOME_DIR")"
echo "[OK] Home archived to ${ARCHIVE_DIR}/${USERNAME}_$(date +%F).tar.gz"
fi
echo "[NOTICE] Review archive, then run: userdel -r $USERNAME"Prueba del script con un modo de simulación
Los scripts de aprovisionamiento para producción deben poder probarse sin efectos secundarios. Implemente un modo de simulación mediante un indicador DRY_RUN que sustituya todos los comandos que modifican el sistema por stubs de echo.
El patrón es sencillo: defina una función auxiliar run() que ejecute o muestre el comando según el indicador. Este enfoque permite:
- Ejecutar todas las rutas de código durante las pruebas
- Mostrar exactamente lo que ocurriría en una ejecución real
- Validar la lógica en los pipelines de CI sin acceso root
Combine el modo de simulación con un prefijo específico para usuarios de prueba (por ejemplo, test_) que facilite la limpieza posterior a las pruebas de integración.
#!/usr/bin/env bash
# provision_with_dryrun.sh
set -euo pipefail
DRY_RUN="${DRY_RUN:-false}"
# Wrapper: execute or echo
run() {
if [[ "$DRY_RUN" == "true" ]]; then
echo "[DRY-RUN] $*"
else
"$@"
fi
}
create_user() {
local user="$1" group="$2"
if id "$user" &>/dev/null; then
echo "[SKIP] $user exists"
return
fi
run useradd -m -s /bin/bash -G "$group" "$user"
run chage -M 90 -m 1 -W 14 -d 0 "$user"
echo "[OK] $user provisioned (dry=$DRY_RUN)"
}
# Test run
DRY_RUN=true create_user testuser developers
echo "---"
create_user realuser developers 2>/dev/null || true¿Qué comando debe usar para añadir un usuario a un grupo suplementario sin eliminar sus membresías actuales?
En un script de aprovisionamiento masivo necesita asignar un usuario existente al grupo auditors. El usuario ya pertenece a developers y staff. ¿Qué comando conserva todas las membresías actuales y añade la nueva?
Repaso de la lección: automatización del aprovisionamiento de usuarios y grupos
En esta lección ha creado un kit completo de aprovisionamiento de usuarios preparado para producción. Estas son las conclusiones principales:
- Analice la entrada de forma defensiva: use bucles con
IFS/read, omita las líneas vacías y de comentarios, y valide los campos antes de cualquier llamada al sistema - Aspectos esenciales de
useradd: use siempre-m(directorio personal),-s(shell),-c(comentario) y-G(grupos); compruebe primero la existencia conidpara garantizar la idempotencia - Contraseñas: establézcalas mediante stdin de
chpasswdpara mantener el texto plano fuera de los argumentos del proceso; fuerce el restablecimiento en el primer inicio de sesión conchage -d 0 chagepara aplicar políticas: estandarice la antigüedad máxima (-M), los días de aviso (-W) y el bloqueo por inactividad (-I) en todas las cuentas que no sean del sistema- Membresía de grupos: use
gpasswd -aousermod -aG(con la opción-a) para añadir membresías en lugar de sustituirlas - Archivos drop-in de sudoers: escriba en
/etc/sudoers.d/, valide convisudo -c -fantes de instalarlos y establezca los permisos0440 root:root - Baja de usuarios: bloquee (
usermod -L), archive el directorio personal y después elimine; no omita nunca el archivado - Modo de simulación: incluya los comandos que modifican el sistema en una función auxiliar
run()para que los pipelines puedan verificar la lógica sin efectos secundarios que requieran root
La combinación de estos patrones le proporciona una capa de automatización repetible, auditable y segura para gestionar identidades Linux a cualquier escala.
Preguntas frecuentes
¿La lección «Automatización del aprovisionamiento de usuarios y grupos» es gratis?
Sí — el texto completo de «Automatización del aprovisionamiento de usuarios y grupos» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Linux Command Line & Bash Scripting Mastery, actualiza a CoddyKit PRO. El curso de Linux Command Line & Bash Scripting Mastery incluye 4 lecciones en total.
¿Qué aprenderé en «Automatización del aprovisionamiento de usuarios y grupos»?
Cree, modifique y audite cuentas en bloque mediante useradd, chage y la gestión de fragmentos de sudoers. Practicas Linux Command Line & Bash Scripting Mastery con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Linux Command Line & Bash Scripting Mastery?
No se requiere experiencia previa. Linux Command Line & Bash Scripting Mastery en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 1 de 4.
¿Cuánto tiempo toma la lección «Automatización del aprovisionamiento de usuarios y grupos»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Linux Command Line & Bash Scripting Mastery?
Sí. Cada lección de Linux Command Line & Bash Scripting Mastery incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Automatización del aprovisionamiento de usuarios y grupos
- Control de servicios systemd y escritura de archivos unit
- Automatización de discos, sistemas de archivos y montajes
- Creación de scripts de comprobación y alerta del estado del sistema