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.

Know More Team
January 27, 2025
4 min read
LinuxUser ManagementShell ScriptingCSV ProcessingAutomation

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

Table of Contents

Navigate the scroll
Reading Progress