Tutorial

Nuevas actualizaciones y mejoras para Macfleet.

Aviso importante

Los ejemplos de código y scripts proporcionados en estos tutoriales son solo para propósitos educativos. Macfleet no es responsable de ningún problema, daño o vulnerabilidad de seguridad que pueda surgir del uso, modificación o implementación de estos ejemplos. Siempre revisa y prueba el código en un entorno seguro antes de usarlo en sistemas de producción.

Delete Mobile Accounts on macOS Devices

This comprehensive guide demonstrates how to delete mobile accounts on macOS devices for effective user management, device reassignment, and organizational cleanup scenarios.

Overview

Mobile accounts on macOS are user profiles designed for seamless access to resources across different devices within organizations. Unlike local accounts, mobile accounts are centrally managed by directory services like Open Directory or Active Directory, enabling administrators to control access, enforce policies, and manage user data centrally.

Common Scenarios for Mobile Account Deletion

  • Employee departures: Clean up accounts when employees leave the organization
  • Role changes: Remove old mobile accounts when users change positions
  • Device reassignment: Prepare devices for new users
  • Security compliance: Remove inactive or compromised accounts
  • Storage optimization: Free up disk space by removing unused accounts
  • Organizational restructuring: Clean up accounts during company changes

Basic Mobile Account Deletion

Simple Mobile Account Removal Script

Create a basic script to remove all mobile accounts from a macOS device:

#!/bin/bash

# Basic mobile account deletion script
# Usage: ./delete_mobile_accounts.sh

delete_mobile_accounts() {
    echo "Starting mobile account deletion process..."
    
    # Check if running as root
    if [ "$EUID" -ne 0 ]; then
        echo "Error: This script must be run as root"
        exit 1
    fi
    
    # Counter for deleted accounts
    local deleted_count=0
    
    # Loop through all users
    for username in $(dscl . -list /Users | grep -v '^_'); do
        # Check if user has mobile account authentication
        auth_authority=$(dscl . -read "/Users/$username" AuthenticationAuthority 2>/dev/null | grep "LocalCachedUser")
        
        if [ -n "$auth_authority" ]; then
            echo "Removing mobile account: $username"
            
            # Delete user from directory services
            dscl . -delete "/Users/$username"
            
            # Remove user's home directory
            rm -rf "/Users/$username"
            
            ((deleted_count++))
        fi
    done
    
    echo "Mobile accounts removal complete. Deleted $deleted_count accounts."
    echo "Note: Please restart the device for changes to take full effect."
}

# Execute deletion
delete_mobile_accounts

Enhanced Mobile Account Management Script

#!/bin/bash

# Enhanced mobile account management with safety checks and logging
# Usage: ./enhanced_mobile_account_manager.sh

enhanced_mobile_account_manager() {
    local log_file="/var/log/macfleet_mobile_accounts.log"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    local backup_dir="/var/backups/macfleet/mobile_accounts"
    
    # Check if running as root
    if [ "$EUID" -ne 0 ]; then
        echo "Error: This script must be run as root"
        exit 1
    fi
    
    # Create log directory
    mkdir -p /var/log
    mkdir -p "$backup_dir"
    
    echo "[$timestamp] Enhanced mobile account manager started" >> "$log_file"
    
    # Display current mobile accounts
    display_mobile_accounts
    
    # Confirm deletion
    echo ""
    read -p "Do you want to proceed with mobile account deletion? (y/N): " confirm
    
    if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
        create_account_backup
        delete_mobile_accounts_enhanced
    else
        echo "Operation cancelled."
        echo "[$timestamp] Mobile account deletion cancelled by user" >> "$log_file"
    fi
}

display_mobile_accounts() {
    echo "======================================="
    echo "Current Mobile Accounts on Device"
    echo "======================================="
    
    local mobile_accounts_found=false
    
    for username in $(dscl . -list /Users | grep -v '^_'); do
        auth_authority=$(dscl . -read "/Users/$username" AuthenticationAuthority 2>/dev/null | grep "LocalCachedUser")
        
        if [ -n "$auth_authority" ]; then
            mobile_accounts_found=true
            
            # Get additional user information
            local real_name=$(dscl . -read "/Users/$username" RealName 2>/dev/null | cut -d: -f2 | xargs)
            local uid=$(dscl . -read "/Users/$username" UniqueID 2>/dev/null | cut -d: -f2 | xargs)
            local home_dir=$(dscl . -read "/Users/$username" NFSHomeDirectory 2>/dev/null | cut -d: -f2 | xargs)
            local last_login=$(last -1 "$username" 2>/dev/null | head -1 | awk '{print $3, $4, $5, $6}')
            
            echo "Username: $username"
            echo "  Real Name: $real_name"
            echo "  UID: $uid"
            echo "  Home Directory: $home_dir"
            echo "  Last Login: $last_login"
            
            # Check home directory size
            if [ -d "$home_dir" ]; then
                local dir_size=$(du -sh "$home_dir" 2>/dev/null | cut -f1)
                echo "  Home Directory Size: $dir_size"
            fi
            
            echo ""
        fi
    done
    
    if [ "$mobile_accounts_found" = false ]; then
        echo "No mobile accounts found on this device."
        echo "[$timestamp] No mobile accounts found on device" >> "$log_file"
        exit 0
    fi
}

create_account_backup() {
    echo "Creating backup of mobile account information..."
    
    local backup_file="$backup_dir/mobile_accounts_backup_$(date +%Y%m%d_%H%M%S).txt"
    
    cat > "$backup_file" << EOF
MacFleet Mobile Account Backup
Generated: $(date)
Device: $(scutil --get ComputerName)
macOS Version: $(sw_vers -productVersion)

Mobile Accounts Found:
EOF
    
    for username in $(dscl . -list /Users | grep -v '^_'); do
        auth_authority=$(dscl . -read "/Users/$username" AuthenticationAuthority 2>/dev/null | grep "LocalCachedUser")
        
        if [ -n "$auth_authority" ]; then
            echo "Username: $username" >> "$backup_file"
            dscl . -read "/Users/$username" >> "$backup_file"
            echo "---" >> "$backup_file"
        fi
    done
    
    echo "Backup created: $backup_file"
    echo "[$timestamp] Mobile account backup created: $backup_file" >> "$log_file"
}

delete_mobile_accounts_enhanced() {
    echo "======================================="
    echo "Deleting Mobile Accounts"
    echo "======================================="
    
    local deleted_count=0
    local failed_count=0
    
    for username in $(dscl . -list /Users | grep -v '^_'); do
        auth_authority=$(dscl . -read "/Users/$username" AuthenticationAuthority 2>/dev/null | grep "LocalCachedUser")
        
        if [ -n "$auth_authority" ]; then
            echo "Processing mobile account: $username"
            
            # Check if user is currently logged in
            if who | grep -q "^$username "; then
                echo "  Warning: User $username is currently logged in. Skipping..."
                echo "[$timestamp] Skipped deletion of $username - user currently logged in" >> "$log_file"
                ((failed_count++))
                continue
            fi
            
            # Delete user from directory services
            if dscl . -delete "/Users/$username" 2>/dev/null; then
                echo "  Deleted user record from directory services"
            else
                echo "  Warning: Failed to delete user record from directory services"
                echo "[$timestamp] Failed to delete user record for $username" >> "$log_file"
                ((failed_count++))
                continue
            fi
            
            # Remove user's home directory
            local home_dir=$(dscl . -read "/Users/$username" NFSHomeDirectory 2>/dev/null | cut -d: -f2 | xargs)
            if [ -d "$home_dir" ]; then
                if rm -rf "$home_dir" 2>/dev/null; then
                    echo "  Removed home directory: $home_dir"
                else
                    echo "  Warning: Failed to remove home directory: $home_dir"
                    echo "[$timestamp] Failed to remove home directory for $username: $home_dir" >> "$log_file"
                fi
            fi
            
            echo "  Successfully deleted mobile account: $username"
            echo "[$timestamp] Successfully deleted mobile account: $username" >> "$log_file"
            ((deleted_count++))
        fi
    done
    
    echo ""
    echo "======================================="
    echo "Mobile Account Deletion Summary"
    echo "======================================="
    echo "Successfully deleted: $deleted_count accounts"
    echo "Failed to delete: $failed_count accounts"
    echo "Total processed: $((deleted_count + failed_count)) accounts"
    echo ""
    echo "Note: Please restart the device for changes to take full effect."
    
    echo "[$timestamp] Mobile account deletion completed - Success: $deleted_count, Failed: $failed_count" >> "$log_file"
}

# Execute enhanced management
enhanced_mobile_account_manager

Advanced Mobile Account Management

Selective Mobile Account Management

#!/bin/bash

# Selective mobile account management system
# Usage: ./selective_mobile_account_manager.sh

selective_mobile_account_manager() {
    local log_file="/var/log/macfleet_selective_mobile_accounts.log"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    
    # Check if running as root
    if [ "$EUID" -ne 0 ]; then
        echo "Error: This script must be run as root"
        exit 1
    fi
    
    echo "[$timestamp] Selective mobile account manager started" >> "$log_file"
    
    while true; do
        display_selective_menu
        read -p "Select an option (1-7): " choice
        
        case $choice in
            1)
                list_mobile_accounts_detailed
                ;;
            2)
                delete_specific_mobile_account
                ;;
            3)
                delete_inactive_mobile_accounts
                ;;
            4)
                delete_mobile_accounts_by_criteria
                ;;
            5)
                export_mobile_account_report
                ;;
            6)
                restore_mobile_account_backup
                ;;
            7)
                echo "Exiting selective mobile account manager."
                exit 0
                ;;
            *)
                echo "Invalid option. Please try again."
                ;;
        esac
        
        echo ""
        read -p "Press Enter to continue..."
    done
}

display_selective_menu() {
    clear
    echo "======================================="
    echo "MacFleet Selective Mobile Account Manager"
    echo "======================================="
    echo ""
    echo "1. List all mobile accounts with details"
    echo "2. Delete specific mobile account"
    echo "3. Delete inactive mobile accounts"
    echo "4. Delete mobile accounts by criteria"
    echo "5. Export mobile account report"
    echo "6. Restore from backup"
    echo "7. Exit"
    echo ""
}

list_mobile_accounts_detailed() {
    echo "======================================="
    echo "Detailed Mobile Account Information"
    echo "======================================="
    
    local mobile_accounts=()
    local count=0
    
    for username in $(dscl . -list /Users | grep -v '^_'); do
        auth_authority=$(dscl . -read "/Users/$username" AuthenticationAuthority 2>/dev/null | grep "LocalCachedUser")
        
        if [ -n "$auth_authority" ]; then
            mobile_accounts+=("$username")
            ((count++))
            
            echo "[$count] Mobile Account: $username"
            
            # Get detailed information
            local real_name=$(dscl . -read "/Users/$username" RealName 2>/dev/null | cut -d: -f2 | xargs)
            local uid=$(dscl . -read "/Users/$username" UniqueID 2>/dev/null | cut -d: -f2 | xargs)
            local gid=$(dscl . -read "/Users/$username" PrimaryGroupID 2>/dev/null | cut -d: -f2 | xargs)
            local home_dir=$(dscl . -read "/Users/$username" NFSHomeDirectory 2>/dev/null | cut -d: -f2 | xargs)
            local shell=$(dscl . -read "/Users/$username" UserShell 2>/dev/null | cut -d: -f2 | xargs)
            
            echo "    Real Name: $real_name"
            echo "    UID: $uid"
            echo "    GID: $gid"
            echo "    Home Directory: $home_dir"
            echo "    Shell: $shell"
            
            # Check if user is currently logged in
            if who | grep -q "^$username "; then
                echo "    Status: Currently logged in"
            else
                echo "    Status: Not logged in"
            fi
            
            # Get last login information
            local last_login=$(last -1 "$username" 2>/dev/null | head -1)
            if [ -n "$last_login" ]; then
                echo "    Last Login: $last_login"
            else
                echo "    Last Login: Never"
            fi
            
            # Check home directory size
            if [ -d "$home_dir" ]; then
                local dir_size=$(du -sh "$home_dir" 2>/dev/null | cut -f1)
                echo "    Home Directory Size: $dir_size"
                
                # Check last modification time
                local last_modified=$(stat -f "%Sm" "$home_dir" 2>/dev/null)
                echo "    Last Modified: $last_modified"
            else
                echo "    Home Directory: Not found"
            fi
            
            echo ""
        fi
    done
    
    if [ $count -eq 0 ]; then
        echo "No mobile accounts found on this device."
    else
        echo "Total mobile accounts found: $count"
    fi
}

delete_specific_mobile_account() {
    echo "======================================="
    echo "Delete Specific Mobile Account"
    echo "======================================="
    
    # List mobile accounts with numbers
    local mobile_accounts=()
    local count=0
    
    for username in $(dscl . -list /Users | grep -v '^_'); do
        auth_authority=$(dscl . -read "/Users/$username" AuthenticationAuthority 2>/dev/null | grep "LocalCachedUser")
        
        if [ -n "$auth_authority" ]; then
            mobile_accounts+=("$username")
            ((count++))
            echo "[$count] $username"
        fi
    done
    
    if [ $count -eq 0 ]; then
        echo "No mobile accounts found on this device."
        return
    fi
    
    echo ""
    read -p "Enter the number of the account to delete (1-$count): " selection
    
    if [[ "$selection" =~ ^[0-9]+$ ]] && [ "$selection" -ge 1 ] && [ "$selection" -le $count ]; then
        local selected_user="${mobile_accounts[$((selection-1))]}"
        
        echo ""
        echo "Selected account: $selected_user"
        
        # Show account details
        local real_name=$(dscl . -read "/Users/$selected_user" RealName 2>/dev/null | cut -d: -f2 | xargs)
        local home_dir=$(dscl . -read "/Users/$selected_user" NFSHomeDirectory 2>/dev/null | cut -d: -f2 | xargs)
        
        echo "Real Name: $real_name"
        echo "Home Directory: $home_dir"
        
        # Check if user is logged in
        if who | grep -q "^$selected_user "; then
            echo "Warning: User is currently logged in!"
            read -p "Do you want to continue anyway? (y/N): " force_delete
            if [ "$force_delete" != "y" ] && [ "$force_delete" != "Y" ]; then
                echo "Deletion cancelled."
                return
            fi
        fi
        
        echo ""
        read -p "Are you sure you want to delete this account? (y/N): " confirm
        
        if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
            delete_single_mobile_account "$selected_user"
        else
            echo "Deletion cancelled."
        fi
    else
        echo "Invalid selection."
    fi
}

delete_single_mobile_account() {
    local username="$1"
    
    echo "Deleting mobile account: $username"
    
    # Create backup of user data
    local backup_dir="/var/backups/macfleet/mobile_accounts"
    mkdir -p "$backup_dir"
    
    local backup_file="$backup_dir/${username}_backup_$(date +%Y%m%d_%H%M%S).txt"
    
    echo "Creating backup of user data..."
    dscl . -read "/Users/$username" > "$backup_file"
    
    # Delete user from directory services
    if dscl . -delete "/Users/$username" 2>/dev/null; then
        echo "User record deleted from directory services"
    else
        echo "Failed to delete user record from directory services"
        return 1
    fi
    
    # Remove user's home directory
    local home_dir=$(dscl . -read "/Users/$username" NFSHomeDirectory 2>/dev/null | cut -d: -f2 | xargs)
    if [ -d "$home_dir" ]; then
        read -p "Do you want to remove the home directory? (y/N): " remove_home
        if [ "$remove_home" = "y" ] || [ "$remove_home" = "Y" ]; then
            if rm -rf "$home_dir"; then
                echo "Home directory removed: $home_dir"
            else
                echo "Failed to remove home directory: $home_dir"
            fi
        else
            echo "Home directory preserved: $home_dir"
        fi
    fi
    
    echo "Successfully deleted mobile account: $username"
    echo "Backup created: $backup_file"
    echo "[$timestamp] Successfully deleted mobile account: $username" >> "$log_file"
}

delete_inactive_mobile_accounts() {
    echo "======================================="
    echo "Delete Inactive Mobile Accounts"
    echo "======================================="
    
    read -p "Enter number of days since last login to consider inactive (default: 30): " days_inactive
    days_inactive=${days_inactive:-30}
    
    echo "Searching for mobile accounts inactive for more than $days_inactive days..."
    
    local inactive_accounts=()
    local current_timestamp=$(date +%s)
    local cutoff_timestamp=$((current_timestamp - (days_inactive * 24 * 60 * 60)))
    
    for username in $(dscl . -list /Users | grep -v '^_'); do
        auth_authority=$(dscl . -read "/Users/$username" AuthenticationAuthority 2>/dev/null | grep "LocalCachedUser")
        
        if [ -n "$auth_authority" ]; then
            # Check last login
            local last_login=$(last -1 "$username" 2>/dev/null | head -1 | awk '{print $3, $4, $5, $6}')
            
            if [ -n "$last_login" ]; then
                local last_login_timestamp=$(date -j -f "%a %b %d %H:%M:%S %Y" "$last_login" +%s 2>/dev/null)
                
                if [ -n "$last_login_timestamp" ] && [ "$last_login_timestamp" -lt "$cutoff_timestamp" ]; then
                    inactive_accounts+=("$username")
                    echo "Inactive account found: $username (last login: $last_login)"
                fi
            else
                # No login record found - consider as inactive
                inactive_accounts+=("$username")
                echo "Inactive account found: $username (no login record)"
            fi
        fi
    done
    
    if [ ${#inactive_accounts[@]} -eq 0 ]; then
        echo "No inactive mobile accounts found."
        return
    fi
    
    echo ""
    echo "Found ${#inactive_accounts[@]} inactive mobile accounts."
    read -p "Do you want to delete all inactive accounts? (y/N): " confirm
    
    if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
        for username in "${inactive_accounts[@]}"; do
            echo "Deleting inactive account: $username"
            delete_single_mobile_account "$username"
        done
        echo "Inactive mobile accounts deletion completed."
    else
        echo "Deletion cancelled."
    fi
}

delete_mobile_accounts_by_criteria() {
    echo "======================================="
    echo "Delete Mobile Accounts by Criteria"
    echo "======================================="
    
    echo "Available criteria:"
    echo "1. By UID range"
    echo "2. By home directory pattern"
    echo "3. By real name pattern"
    echo "4. By last login date"
    echo ""
    read -p "Select criteria (1-4): " criteria
    
    case $criteria in
        1)
            delete_by_uid_range
            ;;
        2)
            delete_by_home_pattern
            ;;
        3)
            delete_by_name_pattern
            ;;
        4)
            delete_by_login_date
            ;;
        *)
            echo "Invalid criteria selected."
            ;;
    esac
}

delete_by_uid_range() {
    read -p "Enter minimum UID: " min_uid
    read -p "Enter maximum UID: " max_uid
    
    echo "Searching for mobile accounts with UID between $min_uid and $max_uid..."
    
    local matching_accounts=()
    
    for username in $(dscl . -list /Users | grep -v '^_'); do
        auth_authority=$(dscl . -read "/Users/$username" AuthenticationAuthority 2>/dev/null | grep "LocalCachedUser")
        
        if [ -n "$auth_authority" ]; then
            local uid=$(dscl . -read "/Users/$username" UniqueID 2>/dev/null | cut -d: -f2 | xargs)
            
            if [ "$uid" -ge "$min_uid" ] && [ "$uid" -le "$max_uid" ]; then
                matching_accounts+=("$username")
                echo "Found matching account: $username (UID: $uid)"
            fi
        fi
    done
    
    if [ ${#matching_accounts[@]} -eq 0 ]; then
        echo "No mobile accounts found matching the UID criteria."
        return
    fi
    
    echo ""
    echo "Found ${#matching_accounts[@]} matching mobile accounts."
    read -p "Do you want to delete all matching accounts? (y/N): " confirm
    
    if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
        for username in "${matching_accounts[@]}"; do
            delete_single_mobile_account "$username"
        done
        echo "Mobile accounts deletion by UID range completed."
    else
        echo "Deletion cancelled."
    fi
}

delete_by_home_pattern() {
    read -p "Enter home directory pattern (e.g., /Users/temp*): " pattern
    
    echo "Searching for mobile accounts with home directory matching pattern: $pattern"
    
    local matching_accounts=()
    
    for username in $(dscl . -list /Users | grep -v '^_'); do
        auth_authority=$(dscl . -read "/Users/$username" AuthenticationAuthority 2>/dev/null | grep "LocalCachedUser")
        
        if [ -n "$auth_authority" ]; then
            local home_dir=$(dscl . -read "/Users/$username" NFSHomeDirectory 2>/dev/null | cut -d: -f2 | xargs)
            
            if [[ "$home_dir" == $pattern ]]; then
                matching_accounts+=("$username")
                echo "Found matching account: $username (Home: $home_dir)"
            fi
        fi
    done
    
    if [ ${#matching_accounts[@]} -eq 0 ]; then
        echo "No mobile accounts found matching the home directory pattern."
        return
    fi
    
    echo ""
    echo "Found ${#matching_accounts[@]} matching mobile accounts."
    read -p "Do you want to delete all matching accounts? (y/N): " confirm
    
    if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
        for username in "${matching_accounts[@]}"; do
            delete_single_mobile_account "$username"
        done
        echo "Mobile accounts deletion by home directory pattern completed."
    else
        echo "Deletion cancelled."
    fi
}

delete_by_name_pattern() {
    read -p "Enter real name pattern (e.g., *Test*): " pattern
    
    echo "Searching for mobile accounts with real name matching pattern: $pattern"
    
    local matching_accounts=()
    
    for username in $(dscl . -list /Users | grep -v '^_'); do
        auth_authority=$(dscl . -read "/Users/$username" AuthenticationAuthority 2>/dev/null | grep "LocalCachedUser")
        
        if [ -n "$auth_authority" ]; then
            local real_name=$(dscl . -read "/Users/$username" RealName 2>/dev/null | cut -d: -f2 | xargs)
            
            if [[ "$real_name" == $pattern ]]; then
                matching_accounts+=("$username")
                echo "Found matching account: $username (Real Name: $real_name)"
            fi
        fi
    done
    
    if [ ${#matching_accounts[@]} -eq 0 ]; then
        echo "No mobile accounts found matching the real name pattern."
        return
    fi
    
    echo ""
    echo "Found ${#matching_accounts[@]} matching mobile accounts."
    read -p "Do you want to delete all matching accounts? (y/N): " confirm
    
    if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
        for username in "${matching_accounts[@]}"; do
            delete_single_mobile_account "$username"
        done
        echo "Mobile accounts deletion by real name pattern completed."
    else
        echo "Deletion cancelled."
    fi
}

delete_by_login_date() {
    read -p "Enter cutoff date (YYYY-MM-DD): " cutoff_date
    
    local cutoff_timestamp=$(date -j -f "%Y-%m-%d" "$cutoff_date" +%s 2>/dev/null)
    
    if [ -z "$cutoff_timestamp" ]; then
        echo "Invalid date format. Please use YYYY-MM-DD format."
        return
    fi
    
    echo "Searching for mobile accounts with last login before $cutoff_date..."
    
    local matching_accounts=()
    
    for username in $(dscl . -list /Users | grep -v '^_'); do
        auth_authority=$(dscl . -read "/Users/$username" AuthenticationAuthority 2>/dev/null | grep "LocalCachedUser")
        
        if [ -n "$auth_authority" ]; then
            local last_login=$(last -1 "$username" 2>/dev/null | head -1 | awk '{print $3, $4, $5, $6}')
            
            if [ -n "$last_login" ]; then
                local last_login_timestamp=$(date -j -f "%a %b %d %H:%M:%S %Y" "$last_login" +%s 2>/dev/null)
                
                if [ -n "$last_login_timestamp" ] && [ "$last_login_timestamp" -lt "$cutoff_timestamp" ]; then
                    matching_accounts+=("$username")
                    echo "Found matching account: $username (last login: $last_login)"
                fi
            else
                # No login record - consider as before cutoff
                matching_accounts+=("$username")
                echo "Found matching account: $username (no login record)"
            fi
        fi
    done
    
    if [ ${#matching_accounts[@]} -eq 0 ]; then
        echo "No mobile accounts found with last login before $cutoff_date."
        return
    fi
    
    echo ""
    echo "Found ${#matching_accounts[@]} matching mobile accounts."
    read -p "Do you want to delete all matching accounts? (y/N): " confirm
    
    if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
        for username in "${matching_accounts[@]}"; do
            delete_single_mobile_account "$username"
        done
        echo "Mobile accounts deletion by login date completed."
    else
        echo "Deletion cancelled."
    fi
}

export_mobile_account_report() {
    echo "======================================="
    echo "Export Mobile Account Report"
    echo "======================================="
    
    local report_file="/tmp/mobile_accounts_report_$(date +%Y%m%d_%H%M%S).txt"
    
    cat > "$report_file" << EOF
MacFleet Mobile Account Report
Generated: $(date)
Device: $(scutil --get ComputerName)
macOS Version: $(sw_vers -productVersion)

Mobile Accounts Summary:
EOF
    
    local count=0
    for username in $(dscl . -list /Users | grep -v '^_'); do
        auth_authority=$(dscl . -read "/Users/$username" AuthenticationAuthority 2>/dev/null | grep "LocalCachedUser")
        
        if [ -n "$auth_authority" ]; then
            ((count++))
            
            echo "" >> "$report_file"
            echo "[$count] Username: $username" >> "$report_file"
            
            # Get detailed information
            local real_name=$(dscl . -read "/Users/$username" RealName 2>/dev/null | cut -d: -f2 | xargs)
            local uid=$(dscl . -read "/Users/$username" UniqueID 2>/dev/null | cut -d: -f2 | xargs)
            local home_dir=$(dscl . -read "/Users/$username" NFSHomeDirectory 2>/dev/null | cut -d: -f2 | xargs)
            local last_login=$(last -1 "$username" 2>/dev/null | head -1)
            
            echo "    Real Name: $real_name" >> "$report_file"
            echo "    UID: $uid" >> "$report_file"
            echo "    Home Directory: $home_dir" >> "$report_file"
            echo "    Last Login: $last_login" >> "$report_file"
            
            # Check if user is logged in
            if who | grep -q "^$username "; then
                echo "    Status: Currently logged in" >> "$report_file"
            else
                echo "    Status: Not logged in" >> "$report_file"
            fi
            
            # Check home directory size
            if [ -d "$home_dir" ]; then
                local dir_size=$(du -sh "$home_dir" 2>/dev/null | cut -f1)
                echo "    Home Directory Size: $dir_size" >> "$report_file"
            fi
        fi
    done
    
    echo "" >> "$report_file"
    echo "Total Mobile Accounts: $count" >> "$report_file"
    
    echo "Mobile account report exported to: $report_file"
}

restore_mobile_account_backup() {
    echo "======================================="
    echo "Restore Mobile Account Backup"
    echo "======================================="
    
    local backup_dir="/var/backups/macfleet/mobile_accounts"
    
    if [ ! -d "$backup_dir" ]; then
        echo "No backup directory found: $backup_dir"
        return
    fi
    
    echo "Available backups:"
    ls -la "$backup_dir"/*.txt 2>/dev/null || echo "No backup files found."
    
    echo ""
    read -p "Enter the backup filename to restore: " backup_file
    
    if [ -f "$backup_dir/$backup_file" ]; then
        echo "Backup file found: $backup_dir/$backup_file"
        echo "Note: This feature shows backup content for reference."
        echo "Manual restoration may be required for complex scenarios."
        echo ""
        cat "$backup_dir/$backup_file"
    else
        echo "Backup file not found: $backup_dir/$backup_file"
    fi
}

# Execute selective management
selective_mobile_account_manager

Enterprise Mobile Account Management

Automated Mobile Account Cleanup

#!/bin/bash

# Automated mobile account cleanup for enterprise environments
# Usage: ./automated_mobile_cleanup.sh

automated_mobile_cleanup() {
    local config_file="/etc/macfleet/mobile_cleanup_config.conf"
    local log_file="/var/log/macfleet_automated_cleanup.log"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    
    # Check if running as root
    if [ "$EUID" -ne 0 ]; then
        echo "Error: This script must be run as root"
        exit 1
    fi
    
    # Create configuration if it doesn't exist
    if [ ! -f "$config_file" ]; then
        create_cleanup_config
    fi
    
    source "$config_file"
    
    echo "[$timestamp] Automated mobile account cleanup started" >> "$log_file"
    
    # Execute cleanup based on configuration
    execute_automated_cleanup
}

create_cleanup_config() {
    mkdir -p /etc/macfleet
    
    cat > "/etc/macfleet/mobile_cleanup_config.conf" << 'EOF'
# MacFleet Automated Mobile Account Cleanup Configuration

# Organization settings
ORGANIZATION_NAME="MacFleet Organization"
IT_CONTACT="support@macfleet.com"

# Cleanup criteria
CLEANUP_INACTIVE_DAYS="30"
CLEANUP_ENABLED="true"
PRESERVE_ADMIN_ACCOUNTS="true"

# Safety settings
REQUIRE_CONFIRMATION="true"
CREATE_BACKUP="true"
PRESERVE_HOME_DATA="false"
SKIP_LOGGED_IN_USERS="true"

# Notification settings
SEND_NOTIFICATIONS="true"
NOTIFICATION_EMAIL="admin@macfleet.com"

# Scheduling
ENABLE_SCHEDULED_CLEANUP="false"
CLEANUP_SCHEDULE="weekly"
CLEANUP_TIME="02:00"

# Logging
ENABLE_DETAILED_LOGGING="true"
LOG_RETENTION_DAYS="90"
EOF
    
    echo "Cleanup configuration created at /etc/macfleet/mobile_cleanup_config.conf"
}

execute_automated_cleanup() {
    echo "[$timestamp] Executing automated mobile account cleanup" >> "$log_file"
    
    # Check if cleanup is enabled
    if [ "$CLEANUP_ENABLED" != "true" ]; then
        echo "[$timestamp] Automated cleanup is disabled" >> "$log_file"
        echo "Automated cleanup is disabled in configuration."
        return
    fi
    
    # Display cleanup configuration
    echo "======================================="
    echo "MacFleet Automated Mobile Account Cleanup"
    echo "======================================="
    echo "Organization: $ORGANIZATION_NAME"
    echo "Cleanup inactive accounts older than: $CLEANUP_INACTIVE_DAYS days"
    echo "Preserve admin accounts: $PRESERVE_ADMIN_ACCOUNTS"
    echo "Create backup: $CREATE_BACKUP"
    echo "Skip logged in users: $SKIP_LOGGED_IN_USERS"
    echo ""
    
    # Require confirmation if enabled
    if [ "$REQUIRE_CONFIRMATION" = "true" ]; then
        read -p "Do you want to proceed with automated cleanup? (y/N): " confirm
        if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
            echo "[$timestamp] Automated cleanup cancelled by user" >> "$log_file"
            echo "Automated cleanup cancelled."
            return
        fi
    fi
    
    # Find accounts to cleanup
    local accounts_to_cleanup=()
    local current_timestamp=$(date +%s)
    local cutoff_timestamp=$((current_timestamp - (CLEANUP_INACTIVE_DAYS * 24 * 60 * 60)))
    
    echo "Scanning for mobile accounts to cleanup..."
    
    for username in $(dscl . -list /Users | grep -v '^_'); do
        auth_authority=$(dscl . -read "/Users/$username" AuthenticationAuthority 2>/dev/null | grep "LocalCachedUser")
        
        if [ -n "$auth_authority" ]; then
            local should_cleanup=true
            
            # Check if user is admin (if preservation is enabled)
            if [ "$PRESERVE_ADMIN_ACCOUNTS" = "true" ]; then
                if dscl . -read "/Groups/admin" GroupMembership 2>/dev/null | grep -q "$username"; then
                    echo "Skipping admin account: $username"
                    echo "[$timestamp] Skipped admin account: $username" >> "$log_file"
                    should_cleanup=false
                fi
            fi
            
            # Check if user is logged in (if skip is enabled)
            if [ "$SKIP_LOGGED_IN_USERS" = "true" ] && who | grep -q "^$username "; then
                echo "Skipping logged in user: $username"
                echo "[$timestamp] Skipped logged in user: $username" >> "$log_file"
                should_cleanup=false
            fi
            
            # Check last login date
            if [ "$should_cleanup" = true ]; then
                local last_login=$(last -1 "$username" 2>/dev/null | head -1 | awk '{print $3, $4, $5, $6}')
                
                if [ -n "$last_login" ]; then
                    local last_login_timestamp=$(date -j -f "%a %b %d %H:%M:%S %Y" "$last_login" +%s 2>/dev/null)
                    
                    if [ -n "$last_login_timestamp" ] && [ "$last_login_timestamp" -lt "$cutoff_timestamp" ]; then
                        accounts_to_cleanup+=("$username")
                        echo "Account marked for cleanup: $username (last login: $last_login)"
                    fi
                else
                    # No login record - consider for cleanup
                    accounts_to_cleanup+=("$username")
                    echo "Account marked for cleanup: $username (no login record)"
                fi
            fi
        fi
    done
    
    if [ ${#accounts_to_cleanup[@]} -eq 0 ]; then
        echo "No mobile accounts found that meet cleanup criteria."
        echo "[$timestamp] No mobile accounts found for cleanup" >> "$log_file"
        return
    fi
    
    echo ""
    echo "Found ${#accounts_to_cleanup[@]} mobile accounts for cleanup:"
    for account in "${accounts_to_cleanup[@]}"; do
        echo "  - $account"
    done
    
    # Create backup if enabled
    if [ "$CREATE_BACKUP" = "true" ]; then
        create_automated_backup "${accounts_to_cleanup[@]}"
    fi
    
    # Cleanup accounts
    local success_count=0
    local failure_count=0
    
    echo ""
    echo "Starting automated cleanup..."
    
    for username in "${accounts_to_cleanup[@]}"; do
        echo "Processing: $username"
        
        # Delete user from directory services
        if dscl . -delete "/Users/$username" 2>/dev/null; then
            echo "  Deleted user record from directory services"
            
            # Handle home directory based on configuration
            local home_dir=$(dscl . -read "/Users/$username" NFSHomeDirectory 2>/dev/null | cut -d: -f2 | xargs)
            
            if [ -d "$home_dir" ]; then
                if [ "$PRESERVE_HOME_DATA" = "true" ]; then
                    echo "  Preserved home directory: $home_dir"
                else
                    if rm -rf "$home_dir" 2>/dev/null; then
                        echo "  Removed home directory: $home_dir"
                    else
                        echo "  Warning: Failed to remove home directory: $home_dir"
                    fi
                fi
            fi
            
            echo "  Successfully cleaned up account: $username"
            echo "[$timestamp] Successfully cleaned up account: $username" >> "$log_file"
            ((success_count++))
        else
            echo "  Failed to cleanup account: $username"
            echo "[$timestamp] Failed to cleanup account: $username" >> "$log_file"
            ((failure_count++))
        fi
    done
    
    # Generate cleanup summary
    echo ""
    echo "======================================="
    echo "Automated Cleanup Summary"
    echo "======================================="
    echo "Total accounts processed: ${#accounts_to_cleanup[@]}"
    echo "Successfully cleaned up: $success_count"
    echo "Failed to cleanup: $failure_count"
    echo "Cleanup completed at: $(date)"
    
    echo "[$timestamp] Automated cleanup completed - Success: $success_count, Failed: $failure_count" >> "$log_file"
    
    # Send notification if enabled
    if [ "$SEND_NOTIFICATIONS" = "true" ]; then
        send_cleanup_notification "$success_count" "$failure_count"
    fi
    
    echo ""
    echo "Note: Please restart the device for changes to take full effect."
}

create_automated_backup() {
    local accounts=("$@")
    local backup_dir="/var/backups/macfleet/automated_cleanup"
    local backup_file="$backup_dir/cleanup_backup_$(date +%Y%m%d_%H%M%S).txt"
    
    mkdir -p "$backup_dir"
    
    echo "Creating backup of accounts to be cleaned up..."
    
    cat > "$backup_file" << EOF
MacFleet Automated Cleanup Backup
Generated: $(date)
Device: $(scutil --get ComputerName)
Organization: $ORGANIZATION_NAME
Cleanup Criteria: Inactive for $CLEANUP_INACTIVE_DAYS days

Accounts to be cleaned up:
EOF
    
    for username in "${accounts[@]}"; do
        echo "" >> "$backup_file"
        echo "Username: $username" >> "$backup_file"
        dscl . -read "/Users/$username" >> "$backup_file" 2>/dev/null
        echo "---" >> "$backup_file"
    done
    
    echo "Backup created: $backup_file"
    echo "[$timestamp] Automated cleanup backup created: $backup_file" >> "$log_file"
}

send_cleanup_notification() {
    local success_count=$1
    local failure_count=$2
    
    local notification_subject="MacFleet Mobile Account Cleanup Report"
    local notification_body="Automated mobile account cleanup completed.

Device: $(scutil --get ComputerName)
Organization: $ORGANIZATION_NAME
Cleanup Date: $(date)

Results:
- Successfully cleaned up: $success_count accounts
- Failed to cleanup: $failure_count accounts
- Total processed: $((success_count + failure_count)) accounts

For detailed logs, check: /var/log/macfleet_automated_cleanup.log

Contact: $IT_CONTACT"
    
    # Use osascript to display notification (for local notifications)
    osascript -e "display notification \"Cleanup completed: $success_count success, $failure_count failed\" with title \"MacFleet Mobile Account Cleanup\""
    
    echo "[$timestamp] Cleanup notification sent" >> "$log_file"
}

# Execute automated cleanup
automated_mobile_cleanup

Troubleshooting and Best Practices

Common Issues and Solutions

  1. Permission Errors: Always run scripts as root using sudo
  2. Logged-in Users: Check for active sessions before deletion
  3. Directory Services: Verify mobile account identification using LocalCachedUser attribute
  4. Home Directory Protection: Create backups before removing home directories
  5. Network Accounts: Ensure network connectivity for directory service operations

Best Practices

  • Test First: Always test scripts in controlled environments
  • Create Backups: Maintain backups of user data before deletion
  • Log Operations: Keep detailed logs of all account operations
  • User Communication: Notify users before account cleanup
  • Schedule Maintenance: Perform cleanup during maintenance windows

Conclusion

Mobile account management is crucial for maintaining clean and secure macOS environments. These scripts provide comprehensive solutions for deleting mobile accounts, from simple cleanup to enterprise-grade automation. Always ensure proper testing and backup procedures before implementing in production environments.

Tutorial

Nuevas actualizaciones y mejoras para Macfleet.

Configurando un Runner de GitHub Actions en un Mac Mini (Apple Silicon)

Runner de GitHub Actions

GitHub Actions es una plataforma poderosa de CI/CD que te permite automatizar tus flujos de trabajo de desarrollo de software. Aunque GitHub ofrece runners hospedados, los runners auto-hospedados proporcionan mayor control y personalización para tu configuración de CI/CD. Este tutorial te guía a través de la configuración y conexión de un runner auto-hospedado en un Mac mini para ejecutar pipelines de macOS.

Prerrequisitos

Antes de comenzar, asegúrate de tener:

  • Un Mac mini (regístrate en Macfleet)
  • Un repositorio de GitHub con derechos de administrador
  • Un gestor de paquetes instalado (preferiblemente Homebrew)
  • Git instalado en tu sistema

Paso 1: Crear una Cuenta de Usuario Dedicada

Primero, crea una cuenta de usuario dedicada para el runner de GitHub Actions:

# Crear la cuenta de usuario 'gh-runner'
sudo dscl . -create /Users/gh-runner
sudo dscl . -create /Users/gh-runner UserShell /bin/bash
sudo dscl . -create /Users/gh-runner RealName "GitHub runner"
sudo dscl . -create /Users/gh-runner UniqueID "1001"
sudo dscl . -create /Users/gh-runner PrimaryGroupID 20
sudo dscl . -create /Users/gh-runner NFSHomeDirectory /Users/gh-runner

# Establecer la contraseña para el usuario
sudo dscl . -passwd /Users/gh-runner tu_contraseña

# Agregar 'gh-runner' al grupo 'admin'
sudo dscl . -append /Groups/admin GroupMembership gh-runner

Cambia a la nueva cuenta de usuario:

su gh-runner

Paso 2: Instalar Software Requerido

Instala Git y Rosetta 2 (si usas Apple Silicon):

# Instalar Git si no está ya instalado
brew install git

# Instalar Rosetta 2 para Macs Apple Silicon
softwareupdate --install-rosetta

Paso 3: Configurar el Runner de GitHub Actions

  1. Ve a tu repositorio de GitHub
  2. Navega a Configuración > Actions > Runners

Runner de GitHub Actions

  1. Haz clic en "New self-hosted runner" (https://github.com/<username>/<repository>/settings/actions/runners/new)
  2. Selecciona macOS como imagen del runner y ARM64 como arquitectura
  3. Sigue los comandos proporcionados para descargar y configurar el runner

Runner de GitHub Actions

Crea un archivo .env en el directorio _work del runner:

# archivo _work/.env
ImageOS=macos15
XCODE_15_DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
  1. Ejecuta el script run.sh en tu directorio del runner para completar la configuración.
  2. Verifica que el runner esté activo y escuchando trabajos en la terminal y revisa la configuración del repositorio de GitHub para la asociación del runner y el estado Idle.

Runner de GitHub Actions

Paso 4: Configurar Sudoers (Opcional)

Si tus acciones requieren privilegios de root, configura el archivo sudoers:

sudo visudo

Agrega la siguiente línea:

gh-runner ALL=(ALL) NOPASSWD: ALL

Paso 5: Usar el Runner en Flujos de Trabajo

Configura tu flujo de trabajo de GitHub Actions para usar el runner auto-hospedado:

name: Flujo de trabajo de muestra

on:
  workflow_dispatch:

jobs:
  build:
    runs-on: [self-hosted, macOS, ARM64]
    steps:
      - name: Instalar NodeJS
        run: brew install node

El runner está autenticado en tu repositorio y etiquetado con self-hosted, macOS, y ARM64. Úsalo en tus flujos de trabajo especificando estas etiquetas en el campo runs-on:

runs-on: [self-hosted, macOS, ARM64]

Mejores Prácticas

  • Mantén tu software del runner actualizado
  • Monitorea regularmente los logs del runner para problemas
  • Usa etiquetas específicas para diferentes tipos de runners
  • Implementa medidas de seguridad apropiadas
  • Considera usar múltiples runners para balanceo de carga

Solución de Problemas

Problemas comunes y soluciones:

  1. Runner no conectando:

    • Verifica conectividad de red
    • Verifica validez del token de GitHub
    • Asegúrate de permisos apropiados
  2. Fallas de construcción:

    • Verifica instalación de Xcode
    • Verifica dependencias requeridas
    • Revisa logs del flujo de trabajo
  3. Problemas de permisos:

    • Verifica permisos de usuario
    • Verifica configuración de sudoers
    • Revisa permisos del sistema de archivos

Conclusión

Ahora tienes un runner auto-hospedado de GitHub Actions configurado en tu Mac mini. Esta configuración te proporciona más control sobre tu entorno de CI/CD y te permite ejecutar flujos de trabajo específicos de macOS de manera eficiente.

Recuerda mantener regularmente tu runner y mantenerlo actualizado con los últimos parches de seguridad y versiones de software.

Aplicación Nativa

Aplicación nativa de Macfleet

Guía de Instalación de Macfleet

Macfleet es una solución poderosa de gestión de flota diseñada específicamente para entornos de Mac Mini alojados en la nube. Como proveedor de hosting en la nube de Mac Mini, puedes usar Macfleet para monitorear, gestionar y optimizar toda tu flota de instancias Mac virtualizadas.

Esta guía de instalación te llevará a través de la configuración del monitoreo de Macfleet en sistemas macOS, Windows y Linux para asegurar una supervisión integral de tu infraestructura en la nube.

🍎 macOS

  • Descarga el archivo .dmg para Mac aquí
  • Haz doble clic en el archivo .dmg descargado
  • Arrastra la aplicación Macfleet a la carpeta Aplicaciones
  • Expulsa el archivo .dmg
  • Abre Preferencias del Sistema > Seguridad y Privacidad
    • Pestaña Privacidad > Accesibilidad
    • Marca Macfleet para permitir el monitoreo
  • Inicia Macfleet desde Aplicaciones
  • El seguimiento comienza automáticamente

🪟 Windows

  • Descarga el archivo .exe para Windows aquí
  • Haz clic derecho en el archivo .exe > "Ejecutar como administrador"
  • Sigue el asistente de instalación
  • Acepta los términos y condiciones
  • Permite en Windows Defender si se solicita
  • Concede permisos de monitoreo de aplicaciones
  • Inicia Macfleet desde el Menú Inicio
  • La aplicación comienza el seguimiento automáticamente

🐧 Linux

  • Descarga el paquete .deb (Ubuntu/Debian) o .rpm (CentOS/RHEL) aquí
  • Instala usando tu gestor de paquetes
    • Ubuntu/Debian: sudo dpkg -i Macfleet-linux.deb
    • CentOS/RHEL: sudo rpm -ivh Macfleet-linux.rpm
  • Permite permisos de acceso X11 si se solicita
  • Agrega el usuario a los grupos apropiados si es necesario
  • Inicia Macfleet desde el menú de Aplicaciones
  • La aplicación comienza el seguimiento automáticamente

Nota: Después de la instalación en todos los sistemas, inicia sesión con tus credenciales de Macfleet para sincronizar datos con tu panel de control.