Create Users from CSV: Automated User Management with Shell Scripting
Learn how to automate user creation from CSV files using shell scripting. Master secure user management, password handling, and bulk user onboarding for DevOps environments.
Create Users from CSV: Automated User Management with Shell Scripting
In modern DevOps environments, managing user accounts manually becomes impractical as teams grow and systems scale. Whether you're onboarding new employees, setting up development environments, or managing access across multiple servers, automated user creation from CSV files is an essential skill. This approach ensures consistency, reduces human error, and enables rapid deployment of user accounts with proper security policies.
Understanding User Management Automation
Why Automate User Creation?
Automated user creation provides several benefits:
- Consistency - All users are created with the same configuration
- Efficiency - Bulk operations save time and reduce manual work
- Security - Enforce password policies and security settings automatically
- Auditability - Track user creation and modifications
- Scalability - Handle large numbers of users efficiently
Security Considerations
When automating user creation, consider these security aspects:
- Password policies - Enforce strong passwords and expiration
- Access control - Limit user privileges appropriately
- Audit logging - Track all user creation activities
- Secure storage - Protect CSV files containing sensitive data
- Validation - Verify user data before creation
Basic CSV User Creation Script
Simple User Creation Script
#!/bin/bash
# create_users.sh
INPUT="users.csv"
# Check if the file exists
if [[ ! -f "$INPUT" ]]; then
echo "CSV file not found!"
exit 1
fi
# Skip header and read each line
tail -n +2 "$INPUT" | while IFS=',' read -r username password; do
# Check if user already exists
if id "$username" &>/dev/null; then
echo "User '$username' already exists. Skipping..."
continue
fi
# Create the user
useradd "$username"
# Set the password
echo "${username}:${password}" | chpasswd
# Force password change on first login
chage -d 0 "$username"
echo "User '$username' created successfully."
done
CSV File Format
username,password
alice,Password@123
bob,Secure@456
carol,DevOps@789
Advanced User Creation Script
Enhanced Script with Error Handling
#!/bin/bash
# advanced_create_users.sh
# Configuration
INPUT="users.csv"
LOG_FILE="/var/log/user_creation.log"
BACKUP_DIR="/var/backups/users"
DRY_RUN=false
# Function to log actions
log_action() {
echo "[$(date)] $1" | tee -a "$LOG_FILE"
}
# Function to validate username
validate_username() {
local username="$1"
# Check username length
if [ ${#username} -lt 3 ] || [ ${#username} -gt 32 ]; then
echo "Invalid username length: $username"
return 1
fi
# Check for valid characters
if [[ ! "$username" =~ ^[a-z][a-z0-9_-]*$ ]]; then
echo "Invalid username format: $username"
return 1
fi
return 0
}
# Function to validate password
validate_password() {
local password="$1"
# Check password length
if [ ${#password} -lt 8 ]; then
echo "Password too short"
return 1
fi
# Check for complexity
if [[ ! "$password" =~ [A-Z] ]] || [[ ! "$password" =~ [a-z] ]] || [[ ! "$password" =~ [0-9] ]]; then
echo "Password does not meet complexity requirements"
return 1
fi
return 0
}
# Function to create user
create_user() {
local username="$1"
local password="$2"
local full_name="$3"
local email="$4"
# Validate inputs
if ! validate_username "$username"; then
log_action "ERROR: Invalid username: $username"
return 1
fi
if ! validate_password "$password"; then
log_action "ERROR: Invalid password for user: $username"
return 1
fi
# Check if user already exists
if id "$username" &>/dev/null; then
log_action "WARNING: User '$username' already exists. Skipping..."
return 0
fi
# Create user with home directory
if [ "$DRY_RUN" = true ]; then
log_action "DRY RUN: Would create user: $username"
return 0
fi
# Create user
useradd -m -s /bin/bash "$username"
if [ $? -ne 0 ]; then
log_action "ERROR: Failed to create user: $username"
return 1
fi
# Set password
echo "${username}:${password}" | chpasswd
if [ $? -ne 0 ]; then
log_action "ERROR: Failed to set password for user: $username"
userdel -r "$username"
return 1
fi
# Force password change on first login
chage -d 0 "$username"
# Set additional user information
if [ -n "$full_name" ]; then
usermod -c "$full_name" "$username"
fi
# Add to appropriate groups
usermod -aG users "$username"
# Set up SSH directory
mkdir -p "/home/$username/.ssh"
chown "$username:$username" "/home/$username/.ssh"
chmod 700 "/home/$username/.ssh"
log_action "SUCCESS: Created user: $username"
return 0
}
# Main execution
main() {
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo "This script must be run as root"
exit 1
fi
# Create backup directory
mkdir -p "$BACKUP_DIR"
# Backup existing CSV
if [ -f "$INPUT" ]; then
cp "$INPUT" "$BACKUP_DIR/users_$(date +%Y%m%d_%H%M%S).csv"
fi
log_action "Starting user creation process"
# Process CSV file
while IFS=',' read -r username password full_name email; do
# Skip header line
if [ "$username" = "username" ]; then
continue
fi
# Skip empty lines
if [ -z "$username" ]; then
continue
fi
create_user "$username" "$password" "$full_name" "$email"
done < "$INPUT"
log_action "User creation process completed"
}
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
--dry-run)
DRY_RUN=true
shift
;;
--input)
INPUT="$2"
shift 2
;;
--help)
echo "Usage: $0 [--dry-run] [--input FILE]"
echo " --dry-run Show what would be done without making changes"
echo " --input Specify input CSV file (default: users.csv)"
exit 0
;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done
# Run main function
main
Enhanced CSV Format
Extended CSV with Additional Fields
username,password,full_name,email,groups
alice,Password@123,Alice Johnson,alice@company.com,developers,users
bob,Secure@456,Bob Smith,bob@company.com,admins,users
carol,DevOps@789,Carol Davis,carol@company.com,devops,users
Processing Extended CSV
#!/bin/bash
# process_extended_csv.sh
INPUT="users.csv"
while IFS=',' read -r username password full_name email groups; do
# Skip header
if [ "$username" = "username" ]; then
continue
fi
# Create user
useradd -m -c "$full_name" "$username"
echo "${username}:${password}" | chpasswd
chage -d 0 "$username"
# Add to groups
if [ -n "$groups" ]; then
IFS=';' read -ra GROUP_ARRAY <<< "$groups"
for group in "${GROUP_ARRAY[@]}"; do
# Create group if it doesn't exist
if ! getent group "$group" >/dev/null 2>&1; then
groupadd "$group"
fi
usermod -aG "$group" "$username"
done
fi
echo "Created user: $username ($full_name)"
done < "$INPUT"
Security Enhancements
Password Policy Enforcement
#!/bin/bash
# secure_user_creation.sh
# Function to generate secure password
generate_password() {
local length=${1:-12}
openssl rand -base64 32 | tr -d "=+/" | cut -c1-${length}
}
# Function to set password policy
set_password_policy() {
local username="$1"
# Set password expiration
chage -M 90 "$username" # Expire in 90 days
# Set minimum password age
chage -m 1 "$username" # Minimum 1 day between changes
# Set account expiration
chage -E $(date -d "+1 year" +%Y-%m-%d) "$username"
# Set warning period
chage -W 7 "$username" # Warn 7 days before expiration
}
# Function to create secure user
create_secure_user() {
local username="$1"
local full_name="$2"
# Generate secure password
local password=$(generate_password)
# Create user
useradd -m -c "$full_name" "$username"
echo "${username}:${password}" | chpasswd
# Apply security policies
set_password_policy "$username"
# Log password securely
echo "$username:$password" >> "/var/log/secure_passwords.log"
chmod 600 "/var/log/secure_passwords.log"
echo "Created secure user: $username"
}
SSH Key Management
#!/bin/bash
# ssh_key_setup.sh
# Function to setup SSH keys
setup_ssh_keys() {
local username="$1"
local public_key="$2"
local ssh_dir="/home/$username/.ssh"
local authorized_keys="$ssh_dir/authorized_keys"
# Create SSH directory
mkdir -p "$ssh_dir"
chown "$username:$username" "$ssh_dir"
chmod 700 "$ssh_dir"
# Add public key
if [ -n "$public_key" ]; then
echo "$public_key" >> "$authorized_keys"
chown "$username:$username" "$authorized_keys"
chmod 600 "$authorized_keys"
fi
# Disable password authentication for SSH
echo "PasswordAuthentication no" >> "$ssh_dir/config"
chown "$username:$username" "$ssh_dir/config"
chmod 600 "$ssh_dir/config"
}
Monitoring and Reporting
User Creation Reporting
#!/bin/bash
# user_creation_report.sh
LOG_FILE="/var/log/user_creation.log"
REPORT_FILE="/tmp/user_creation_report.txt"
# Generate report
echo "User Creation Report - $(date)" > "$REPORT_FILE"
echo "=================================" >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
# Count successful creations
SUCCESS_COUNT=$(grep "SUCCESS" "$LOG_FILE" | wc -l)
echo "Successful user creations: $SUCCESS_COUNT" >> "$REPORT_FILE"
# Count failures
FAILURE_COUNT=$(grep "ERROR" "$LOG_FILE" | wc -l)
echo "Failed user creations: $FAILURE_COUNT" >> "$REPORT_FILE"
# List recent users
echo "" >> "$REPORT_FILE"
echo "Recent user creations:" >> "$REPORT_FILE"
grep "SUCCESS" "$LOG_FILE" | tail -10 >> "$REPORT_FILE"
# Send report
mail -s "User Creation Report" admin@company.com < "$REPORT_FILE"
Best Practices
1. Input Validation
# Validate CSV format
validate_csv() {
local file="$1"
# Check if file exists
if [ ! -f "$file" ]; then
echo "ERROR: CSV file not found: $file"
return 1
fi
# Check if file is readable
if [ ! -r "$file" ]; then
echo "ERROR: Cannot read CSV file: $file"
return 1
fi
# Check header
local header=$(head -1 "$file")
if [[ ! "$header" =~ username,password ]]; then
echo "ERROR: Invalid CSV header: $header"
return 1
fi
return 0
}
2. Error Handling
# Function with comprehensive error handling
create_user_safe() {
local username="$1"
local password="$2"
# Validate inputs
if [ -z "$username" ] || [ -z "$password" ]; then
echo "ERROR: Username and password are required"
return 1
fi
# Check if user exists
if id "$username" &>/dev/null; then
echo "WARNING: User $username already exists"
return 0
fi
# Create user with error handling
if ! useradd -m "$username"; then
echo "ERROR: Failed to create user $username"
return 1
fi
# Set password with error handling
if ! echo "${username}:${password}" | chpasswd; then
echo "ERROR: Failed to set password for $username"
userdel -r "$username"
return 1
fi
echo "SUCCESS: Created user $username"
return 0
}
3. Logging and Audit
# Comprehensive logging
log_user_creation() {
local username="$1"
local action="$2"
local status="$3"
local details="$4"
echo "[$(date)] USER:$username ACTION:$action STATUS:$status DETAILS:$details" >> "/var/log/user_management.log"
}
Conclusion
Automated user creation from CSV files is a powerful technique for managing user accounts in Linux environments. By implementing proper validation, error handling, and security measures, you can create a robust system for user management that scales with your organization's needs.
Key takeaways:
- Validate all inputs - Check usernames, passwords, and file formats
- Implement security policies - Enforce password complexity and expiration
- Handle errors gracefully - Provide clear error messages and rollback capabilities
- Log all activities - Maintain audit trails for compliance
- Test thoroughly - Validate scripts before production use