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.

Application Uninstall Management for macOS

Implement enterprise-grade application uninstall management across your MacFleet deployment with automated lifecycle management, security validation, residual file cleanup, and comprehensive fleet-wide application removal. This tutorial provides solutions for maintaining clean system environments while ensuring secure and efficient application lifecycle management.

Understanding macOS Application Uninstall Management

macOS provides several methods for application removal:

  • Manual Drag-to-Trash - Standard user method via Finder
  • rm command - Command-line removal of application bundles
  • Third-party Uninstallers - Specialized tools for complete removal
  • MDM Uninstall Actions - Remote uninstall capabilities
  • Package Manager Tools - Homebrew, MacPorts uninstall commands

Basic Application Uninstall

Simple Application Removal

#!/bin/bash

# Basic app uninstall
sudo rm -rf /Applications/Opera.app

Core Uninstall Command Structure

#!/bin/bash

# Generic app uninstall template
sudo rm -rf /path/to/app/files

The rm command options:

  • -r - Removes the file hierarchy (recursive)
  • -f - Removes files without prompting for confirmation (force)

Enterprise Application Uninstall Management System

Comprehensive Application Lifecycle Tool

#!/bin/bash

# MacFleet Enterprise Application Uninstall Management Tool
# Advanced application removal and lifecycle management

# Configuration
CONFIG_FILE="/etc/macfleet/app_uninstall_policy.conf"
LOG_FILE="/var/log/macfleet_app_uninstall.log"
QUARANTINE_DIR="/Library/MacFleet/QuarantinedApps"
AUDIT_LOG="/var/log/macfleet_app_audit.log"

# Create directories
mkdir -p "$(dirname "$CONFIG_FILE")" "$(dirname "$LOG_FILE")" "$QUARANTINE_DIR" "$(dirname "$AUDIT_LOG")"

# Default application uninstall policy
cat > "$CONFIG_FILE" 2>/dev/null << 'EOF' || true
# MacFleet Enterprise Application Uninstall Management Policy
# Version: 2.0

# Uninstall Policy Enforcement
ENFORCE_UNINSTALL_POLICIES=true
SAFE_UNINSTALL_MODE=true
BACKUP_BEFORE_UNINSTALL=true
VERIFY_APP_SIGNATURES=true
PREVENT_SYSTEM_APP_REMOVAL=true

# Security and Safety
REQUIRE_ADMIN_APPROVAL=false
VALIDATE_APP_DEPENDENCIES=true
CHECK_RUNNING_PROCESSES=true
QUARANTINE_SUSPICIOUS_APPS=true
MALWARE_SCAN_ENABLED=true

# Cleanup and Maintenance
COMPLETE_CLEANUP_ENABLED=true
REMOVE_PREFERENCES=true
REMOVE_SUPPORT_FILES=true
REMOVE_CACHES=true
REMOVE_LOGS=true
CLEAN_LAUNCH_SERVICES=true

# Business Rules
BUSINESS_HOURS_RESTRICTION=true
BUSINESS_HOURS_START="09:00"
BUSINESS_HOURS_END="18:00"
CRITICAL_APP_PROTECTION=true
LICENSED_APP_TRACKING=true

# Notification and Communication
USER_NOTIFICATION_ENABLED=true
ADMIN_NOTIFICATION_ENABLED=true
SLACK_WEBHOOK_URL=""
EMAIL_NOTIFICATION_ENABLED=false

# Compliance and Audit
AUDIT_ALL_UNINSTALLS=true
COMPLIANCE_REPORTING=true
SOFTWARE_INVENTORY_UPDATE=true
LICENSE_TRACKING=true
SECURITY_INCIDENT_REPORTING=true

# Recovery and Rollback
BACKUP_RETENTION_DAYS=30
ROLLBACK_CAPABILITY=true
EMERGENCY_RECOVERY=true
EOF

# Source configuration
source "$CONFIG_FILE" 2>/dev/null || true

# Logging function
log_action() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}

# Audit logging function
audit_log() {
    local action="$1"
    local app_name="$2"
    local result="$3"
    local details="$4"
    echo "$(date '+%Y-%m-%d %H:%M:%S') - ACTION:$action APP:$app_name RESULT:$result DETAILS:$details USER:$(whoami)" >> "$AUDIT_LOG"
}

# Get application information
get_app_info() {
    local app_path="$1"
    
    if [[ ! -d "$app_path" ]]; then
        echo "App not found: $app_path"
        return 1
    fi
    
    local app_name
    app_name=$(basename "$app_path")
    
    local bundle_id=""
    local version=""
    local signature_status=""
    
    # Get bundle identifier
    if [[ -f "$app_path/Contents/Info.plist" ]]; then
        bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2>/dev/null || echo "unknown")
        version=$(defaults read "$app_path/Contents/Info.plist" CFBundleShortVersionString 2>/dev/null || echo "unknown")
    fi
    
    # Check code signature
    if command -v codesign >/dev/null 2>&1; then
        if codesign -v "$app_path" >/dev/null 2>&1; then
            signature_status="valid"
        else
            signature_status="invalid"
        fi
    else
        signature_status="unknown"
    fi
    
    echo "Name: $app_name"
    echo "Bundle ID: $bundle_id"
    echo "Version: $version"
    echo "Signature: $signature_status"
    echo "Path: $app_path"
}

# Check if application can be safely uninstalled
check_uninstall_safety() {
    local app_path="$1"
    local force="${2:-false}"
    
    echo "=== Validating Application Uninstall Safety ==="
    
    local app_name
    app_name=$(basename "$app_path")
    
    # Check if app exists
    if [[ ! -d "$app_path" ]]; then
        echo "✅ App not found (already uninstalled): $app_name"
        return 0
    fi
    
    # Prevent system app removal
    if [[ "$PREVENT_SYSTEM_APP_REMOVAL" == "true" ]]; then
        local system_apps=(
            "Activity Monitor.app"
            "Console.app"
            "Disk Utility.app"
            "Finder.app"
            "System Preferences.app"
            "Terminal.app"
            "Utilities"
        )
        
        for system_app in "${system_apps[@]}"; do
            if [[ "$app_name" == "$system_app" ]]; then
                echo "❌ Cannot uninstall system application: $app_name"
                audit_log "UNINSTALL_BLOCKED" "$app_name" "SYSTEM_APP" "System app protection active"
                return 1
            fi
        done
    fi
    
    # Check if app is currently running
    if [[ "$CHECK_RUNNING_PROCESSES" == "true" ]]; then
        local app_executable
        app_executable=$(basename "$app_name" .app)
        
        if pgrep -f "$app_executable" >/dev/null 2>&1; then
            echo "⚠️  Application is currently running: $app_name"
            
            if [[ "$force" != "true" ]]; then
                echo "❌ Uninstall blocked: Application is running. Use force option to terminate and uninstall."
                audit_log "UNINSTALL_BLOCKED" "$app_name" "RUNNING" "Application is currently running"
                return 1
            else
                echo "🚨 Force uninstall: Will terminate running application"
                pkill -f "$app_executable" 2>/dev/null || true
                sleep 2
            fi
        fi
    fi
    
    # Business hours check
    if [[ "$BUSINESS_HOURS_RESTRICTION" == "true" && "$force" != "true" ]]; then
        local current_hour
        current_hour=$(date +%H)
        local start_hour
        start_hour=$(echo "$BUSINESS_HOURS_START" | cut -d':' -f1)
        local end_hour
        end_hour=$(echo "$BUSINESS_HOURS_END" | cut -d':' -f1)
        
        if [[ $current_hour -ge $start_hour && $current_hour -lt $end_hour ]]; then
            echo "⚠️  Business hours restriction active ($BUSINESS_HOURS_START - $BUSINESS_HOURS_END)"
            
            # Check if it's a critical app
            if [[ "$CRITICAL_APP_PROTECTION" == "true" ]]; then
                echo "❌ Uninstall blocked during business hours for safety"
                audit_log "UNINSTALL_BLOCKED" "$app_name" "BUSINESS_HOURS" "Business hours protection active"
                return 1
            fi
        fi
    fi
    
    # Signature verification
    if [[ "$VERIFY_APP_SIGNATURES" == "true" ]]; then
        if ! codesign -v "$app_path" >/dev/null 2>&1; then
            echo "⚠️  Warning: Application has invalid or missing signature"
            
            if [[ "$QUARANTINE_SUSPICIOUS_APPS" == "true" ]]; then
                echo "🔒 Quarantining suspicious application before removal"
                quarantine_application "$app_path"
            fi
        fi
    fi
    
    echo "✅ Application uninstall safety validated"
    audit_log "SAFETY_CHECK" "$app_name" "PASSED" "All safety checks passed"
    return 0
}

# Create backup before uninstall
create_app_backup() {
    local app_path="$1"
    local app_name
    app_name=$(basename "$app_path")
    
    if [[ "$BACKUP_BEFORE_UNINSTALL" != "true" ]]; then
        return 0
    fi
    
    echo "=== Creating Application Backup ==="
    
    local backup_dir="/Library/MacFleet/AppBackups"
    local backup_file="$backup_dir/${app_name%.*}_backup_$(date +%Y%m%d_%H%M%S).tar.gz"
    
    mkdir -p "$backup_dir"
    
    echo "Creating backup of $app_name..."
    if tar -czf "$backup_file" -C "$(dirname "$app_path")" "$(basename "$app_path")" 2>/dev/null; then
        echo "✅ Application backup created: $backup_file"
        log_action "Application backup created: $backup_file"
        
        # Set retention policy
        find "$backup_dir" -name "*_backup_*.tar.gz" -mtime +$BACKUP_RETENTION_DAYS -delete 2>/dev/null || true
        
        return 0
    else
        echo "⚠️  Application backup failed"
        log_action "WARNING: Application backup failed for $app_name"
        return 1
    fi
}

# Quarantine suspicious application
quarantine_application() {
    local app_path="$1"
    local app_name
    app_name=$(basename "$app_path")
    
    echo "=== Quarantining Suspicious Application ==="
    
    local quarantine_path="$QUARANTINE_DIR/$(date +%Y%m%d_%H%M%S)_$app_name"
    
    if mv "$app_path" "$quarantine_path" 2>/dev/null; then
        echo "🔒 Application quarantined: $quarantine_path"
        log_action "Application quarantined: $app_name -> $quarantine_path"
        audit_log "QUARANTINE" "$app_name" "SUCCESS" "Suspicious app quarantined"
        
        # Create quarantine record
        cat > "$quarantine_path.info" << EOF
{
  "original_path": "$app_path",
  "quarantine_time": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
  "reason": "suspicious_signature",
  "quarantined_by": "$(whoami)"
}
EOF
        
        return 0
    else
        echo "❌ Failed to quarantine application"
        return 1
    fi
}

# Find and remove application support files
cleanup_app_support_files() {
    local app_name="$1"
    local bundle_id="$2"
    
    if [[ "$COMPLETE_CLEANUP_ENABLED" != "true" ]]; then
        return 0
    fi
    
    echo "=== Cleaning Application Support Files ==="
    
    local cleanup_paths=()
    local app_base_name
    app_base_name=$(basename "$app_name" .app)
    
    # Common cleanup locations
    local common_locations=(
        "/Library/Application Support"
        "/Library/Caches"
        "/Library/Preferences"
        "/Library/Logs"
        "~/Library/Application Support"
        "~/Library/Caches"
        "~/Library/Preferences"
        "~/Library/Logs"
        "~/Library/Saved Application State"
    )
    
    for location in "${common_locations[@]}"; do
        # Expand tilde
        location="${location/#\~/$HOME}"
        
        if [[ -d "$location" ]]; then
            # Search by app name
            find "$location" -maxdepth 2 -name "*$app_base_name*" -type d 2>/dev/null | while read -r path; do
                if [[ -n "$path" ]]; then
                    cleanup_paths+=("$path")
                fi
            done
            
            # Search by bundle ID if available
            if [[ -n "$bundle_id" && "$bundle_id" != "unknown" ]]; then
                find "$location" -maxdepth 2 -name "*$bundle_id*" -type d 2>/dev/null | while read -r path; do
                    if [[ -n "$path" ]]; then
                        cleanup_paths+=("$path")
                    fi
                done
            fi
        fi
    done
    
    # Remove preference files
    if [[ "$REMOVE_PREFERENCES" == "true" && -n "$bundle_id" && "$bundle_id" != "unknown" ]]; then
        local pref_files=(
            "/Library/Preferences/$bundle_id.plist"
            "$HOME/Library/Preferences/$bundle_id.plist"
            "$HOME/Library/Preferences/ByHost/$bundle_id.*"
        )
        
        for pref_file in "${pref_files[@]}"; do
            if [[ -f "$pref_file" ]]; then
                rm -f "$pref_file" 2>/dev/null || true
                echo "🗑️  Removed preference file: $pref_file"
            fi
        done
    fi
    
    # Clean up found support files
    local cleaned_count=0
    for path in "${cleanup_paths[@]}"; do
        if [[ -d "$path" ]]; then
            rm -rf "$path" 2>/dev/null || true
            echo "🗑️  Removed support files: $path"
            ((cleaned_count++))
        fi
    done
    
    # Update Launch Services database
    if [[ "$CLEAN_LAUNCH_SERVICES" == "true" ]]; then
        echo "🔄 Updating Launch Services database..."
        /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -kill -r -domain local -domain system -domain user 2>/dev/null || true
    fi
    
    echo "✅ Cleanup completed: $cleaned_count support file locations removed"
    log_action "Application support files cleanup completed for $app_name ($cleaned_count locations)"
}

# Send uninstall notification
send_uninstall_notification() {
    local app_name="$1"
    local operation="$2"
    local result="$3"
    
    if [[ "$USER_NOTIFICATION_ENABLED" != "true" ]]; then
        return 0
    fi
    
    echo "=== Sending Uninstall Notifications ==="
    
    local notification_title="MacFleet Application Management"
    local notification_message="Application $operation: $app_name ($result)"
    
    # Display notification to logged-in users
    local logged_users
    logged_users=$(who | awk '{print $1}' | sort -u)
    
    for user in $logged_users; do
        if [[ -n "$user" ]]; then
            sudo -u "$user" osascript -e "display notification \"$notification_message\" with title \"$notification_title\"" 2>/dev/null || true
            echo "📱 Notification sent to user: $user"
        fi
    done
    
    # Slack notification
    if [[ -n "$SLACK_WEBHOOK_URL" ]]; then
        local slack_payload="{\"text\":\"🖥️ *$(hostname)*: $notification_title\\n$notification_message\"}"
        curl -s -X POST -H 'Content-type: application/json' --data "$slack_payload" "$SLACK_WEBHOOK_URL" >/dev/null 2>&1 || true
        echo "📢 Slack notification sent"
    fi
    
    log_action "Uninstall notifications sent for $app_name ($operation - $result)"
}

# Enterprise application uninstall
enterprise_uninstall() {
    local app_path="$1"
    local force="${2:-false}"
    local skip_backup="${3:-false}"
    
    echo "=== Enterprise Application Uninstall ==="
    
    if [[ -z "$app_path" ]]; then
        echo "❌ Application path required"
        return 1
    fi
    
    local app_name
    app_name=$(basename "$app_path")
    
    # Get application information
    echo "Analyzing application: $app_name"
    local app_info
    app_info=$(get_app_info "$app_path")
    echo "$app_info"
    
    local bundle_id
    bundle_id=$(echo "$app_info" | grep "Bundle ID:" | cut -d':' -f2 | xargs)
    
    # Safety checks
    if ! check_uninstall_safety "$app_path" "$force"; then
        return 1
    fi
    
    # Create backup if enabled
    if [[ "$skip_backup" != "true" ]]; then
        create_app_backup "$app_path"
    fi
    
    log_action "ENTERPRISE UNINSTALL: Starting uninstall of $app_name"
    audit_log "UNINSTALL" "$app_name" "INITIATED" "Path: $app_path Bundle: $bundle_id Force: $force"
    
    # Perform the uninstall
    echo "🗑️  Removing application bundle..."
    if sudo rm -rf "$app_path"; then
        echo "✅ Application bundle removed successfully"
        
        # Complete cleanup
        cleanup_app_support_files "$app_name" "$bundle_id"
        
        # Send notifications
        send_uninstall_notification "$app_name" "uninstall" "success"
        
        log_action "Application uninstalled successfully: $app_name"
        audit_log "UNINSTALL" "$app_name" "SUCCESS" "Complete removal including support files"
        
        return 0
    else
        echo "❌ Failed to remove application bundle"
        send_uninstall_notification "$app_name" "uninstall" "failed"
        log_action "FAILED: Could not remove application: $app_name"
        audit_log "UNINSTALL" "$app_name" "FAILED" "Bundle removal failed"
        return 1
    fi
}

# Bulk application uninstall
bulk_uninstall() {
    local app_list_file="$1"
    local force="${2:-false}"
    
    echo "=== Bulk Application Uninstall ==="
    
    if [[ ! -f "$app_list_file" ]]; then
        echo "❌ Application list file not found: $app_list_file"
        return 1
    fi
    
    local success_count=0
    local failure_count=0
    local total_count=0
    
    while IFS= read -r app_path; do
        # Skip empty lines and comments
        if [[ -z "$app_path" || "$app_path" == \#* ]]; then
            continue
        fi
        
        ((total_count++))
        echo "Processing ($total_count): $app_path"
        
        if enterprise_uninstall "$app_path" "$force" "false"; then
            ((success_count++))
        else
            ((failure_count++))
        fi
        
        echo "---"
    done < "$app_list_file"
    
    echo "=== Bulk Uninstall Summary ==="
    echo "Total applications: $total_count"
    echo "Successful uninstalls: $success_count"
    echo "Failed uninstalls: $failure_count"
    
    log_action "Bulk uninstall completed: $success_count/$total_count successful"
    audit_log "BULK_UNINSTALL" "MULTIPLE" "COMPLETED" "Success: $success_count Failed: $failure_count Total: $total_count"
}

# Malware application removal
remove_malware_app() {
    local app_path="$1"
    local threat_level="${2:-medium}"
    
    echo "🚨 MALWARE APPLICATION REMOVAL 🚨"
    
    local app_name
    app_name=$(basename "$app_path")
    
    log_action "MALWARE REMOVAL: Removing malicious application: $app_name (Threat: $threat_level)"
    audit_log "MALWARE_REMOVAL" "$app_name" "INITIATED" "Threat level: $threat_level"
    
    # Quarantine first for analysis
    if [[ "$threat_level" == "high" ]]; then
        echo "🔒 High threat level - quarantining for security analysis"
        quarantine_application "$app_path"
    else
        # Force removal without backup for malware
        echo "🗑️  Performing emergency malware removal..."
        enterprise_uninstall "$app_path" "true" "true"
    fi
    
    # Security incident reporting
    if [[ "$SECURITY_INCIDENT_REPORTING" == "true" ]]; then
        local incident_file="/var/log/macfleet_security_incidents.log"
        echo "$(date '+%Y-%m-%d %H:%M:%S') - MALWARE_REMOVAL: $app_name (Threat: $threat_level) User: $(whoami) Device: $(hostname)" >> "$incident_file"
    fi
    
    send_uninstall_notification "$app_name" "malware removal" "completed"
    audit_log "MALWARE_REMOVAL" "$app_name" "COMPLETED" "Emergency removal completed"
}

# List installed applications
list_applications() {
    local filter="$1"
    
    echo "=== Installed Applications ==="
    
    local app_list=()
    
    # Find all .app bundles
    while IFS= read -r -d '' app_path; do
        local app_name
        app_name=$(basename "$app_path")
        
        if [[ -n "$filter" ]]; then
            if [[ "$app_name" == *"$filter"* ]]; then
                app_list+=("$app_path")
            fi
        else
            app_list+=("$app_path")
        fi
    done < <(find /Applications -name "*.app" -type d -print0 2>/dev/null)
    
    # Sort and display
    printf '%s\n' "${app_list[@]}" | sort
    
    echo "Total applications found: ${#app_list[@]}"
}

# Generate uninstall report
generate_uninstall_report() {
    local report_file="/Library/MacFleet/Reports/app_uninstall_report_$(date +%Y%m%d_%H%M%S).json"
    
    echo "=== Generating Application Uninstall Report ==="
    
    mkdir -p "$(dirname "$report_file")"
    
    # Count recent operations from audit log
    local recent_uninstalls=0
    local recent_quarantines=0
    local recent_malware_removals=0
    
    if [[ -f "$AUDIT_LOG" ]]; then
        recent_uninstalls=$(grep -c "ACTION:UNINSTALL.*RESULT:SUCCESS" "$AUDIT_LOG" 2>/dev/null || echo 0)
        recent_quarantines=$(grep -c "ACTION:QUARANTINE" "$AUDIT_LOG" 2>/dev/null || echo 0)
        recent_malware_removals=$(grep -c "ACTION:MALWARE_REMOVAL" "$AUDIT_LOG" 2>/dev/null || echo 0)
    fi
    
    # Get current application count
    local total_apps
    total_apps=$(find /Applications -name "*.app" -type d 2>/dev/null | wc -l)
    
    # Create JSON report
    cat > "$report_file" << EOF
{
  "report_type": "application_uninstall_management",
  "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
  "device_info": {
    "hostname": "$(hostname)",
    "os_version": "$(sw_vers -productVersion)",
    "serial_number": "$(system_profiler SPHardwareDataType | grep "Serial Number" | awk -F: '{print $2}' | xargs)"
  },
  "application_statistics": {
    "total_installed_applications": $total_apps,
    "recent_uninstalls": $recent_uninstalls,
    "quarantined_applications": $recent_quarantines,
    "malware_removals": $recent_malware_removals
  },
  "policy_configuration": {
    "safe_uninstall_mode": $SAFE_UNINSTALL_MODE,
    "backup_before_uninstall": $BACKUP_BEFORE_UNINSTALL,
    "complete_cleanup_enabled": $COMPLETE_CLEANUP_ENABLED,
    "business_hours_restriction": $BUSINESS_HOURS_RESTRICTION
  },
  "security_status": {
    "signature_verification": $VERIFY_APP_SIGNATURES,
    "quarantine_enabled": $QUARANTINE_SUSPICIOUS_APPS,
    "malware_scan_enabled": $MALWARE_SCAN_ENABLED,
    "security_incident_reporting": $SECURITY_INCIDENT_REPORTING
  }
}
EOF
    
    echo "Application uninstall report saved to: $report_file"
    log_action "Application uninstall management report generated: $report_file"
}

# Main function with argument handling
main() {
    log_action "=== MacFleet Application Uninstall Management Tool Started ==="
    
    case "${1:-help}" in
        "uninstall")
            enterprise_uninstall "$2" "$3" "$4"
            ;;
        "bulk")
            bulk_uninstall "$2" "$3"
            ;;
        "malware")
            remove_malware_app "$2" "$3"
            ;;
        "list")
            list_applications "$2"
            ;;
        "info")
            get_app_info "$2"
            ;;
        "report")
            generate_uninstall_report
            ;;
        *)
            echo "MacFleet Enterprise Application Uninstall Management Tool"
            echo "Usage: $0 [command] [options]"
            echo ""
            echo "Commands:"
            echo "  uninstall [app_path] [force] [skip_backup]  - Uninstall application with safety checks"
            echo "  bulk [list_file] [force]                    - Bulk uninstall from file list"
            echo "  malware [app_path] [threat_level]           - Remove malicious application"
            echo "  list [filter]                               - List installed applications"
            echo "  info [app_path]                             - Get application information"
            echo "  report                                      - Generate uninstall management report"
            echo ""
            echo "Examples:"
            echo "  $0 uninstall /Applications/Opera.app       - Safe uninstall with backup"
            echo "  $0 uninstall /Applications/App.app true    - Force uninstall (skip safety checks)"
            echo "  $0 bulk apps_to_remove.txt                 - Bulk uninstall from list"
            echo "  $0 malware /Applications/Malware.app high  - Emergency malware removal"
            echo "  $0 list Safari                             - List apps containing 'Safari'"
            ;;
    esac
    
    log_action "=== Application uninstall management operation completed ==="
}

# Execute main function
main "$@"

Advanced Application Management Features

Application Dependency Analysis

#!/bin/bash

# Check application dependencies before uninstall
check_app_dependencies() {
    local app_path="$1"
    local app_name
    app_name=$(basename "$app_path")
    
    echo "=== Analyzing Application Dependencies ==="
    
    # Check for shared frameworks
    local frameworks_dir="$app_path/Contents/Frameworks"
    if [[ -d "$frameworks_dir" ]]; then
        echo "📦 Found embedded frameworks:"
        ls -la "$frameworks_dir" | grep -v "^total" | awk '{print $9}' | grep -v "^$"
    fi
    
    # Check for plugins and extensions
    local plugins_dir="$app_path/Contents/PlugIns"
    if [[ -d "$plugins_dir" ]]; then
        echo "🔌 Found plugins/extensions:"
        find "$plugins_dir" -name "*.appex" -o -name "*.plugin" | while read -r plugin; do
            echo "  - $(basename "$plugin")"
        done
    fi
    
    # Check for launch agents/daemons
    local plist_locations=(
        "/Library/LaunchAgents"
        "/Library/LaunchDaemons"
        "$HOME/Library/LaunchAgents"
    )
    
    echo "🔄 Checking for launch services:"
    for location in "${plist_locations[@]}"; do
        if [[ -d "$location" ]]; then
            find "$location" -name "*.plist" | while read -r plist; do
                if grep -q "$app_name" "$plist" 2>/dev/null; then
                    echo "  - Found reference in: $(basename "$plist")"
                fi
            done
        fi
    done
}

# Create application removal checklist
create_removal_checklist() {
    local app_path="$1"
    local checklist_file="/tmp/app_removal_checklist_$(date +%Y%m%d_%H%M%S).txt"
    
    cat > "$checklist_file" << EOF
Application Removal Checklist
=============================
Application: $(basename "$app_path")
Date: $(date)
Operator: $(whoami)

Pre-Removal Checks:
[ ] Application is not currently running
[ ] No dependent applications identified
[ ] Backup created (if required)
[ ] User notification sent
[ ] Business hours compliance verified

Removal Steps:
[ ] Main application bundle removed
[ ] Preferences files cleaned
[ ] Support files removed
[ ] Cache files cleared
[ ] Launch services updated
[ ] System integrity verified

Post-Removal Verification:
[ ] Application no longer appears in Applications folder
[ ] No residual files remain
[ ] System stability confirmed
[ ] License tracking updated
[ ] Audit log entry created

Notes:
_________________________________________________
_________________________________________________
_________________________________________________

Operator Signature: ________________________
Date Completed: ____________________________
EOF
    
    echo "Removal checklist created: $checklist_file"
}

Important Configuration Notes

macOS Application Structure Understanding

  • Application Bundles - .app directories containing executable and resources
  • Bundle Identifiers - Unique identifiers for application tracking
  • Code Signatures - Security validation for application authenticity
  • Support Files - Preferences, caches, logs in various Library locations

Enterprise Integration Points

  • Software Asset Management - Integration with SAM platforms
  • License Compliance - Tracking of licensed application removals
  • Security Information and Event Management (SIEM) - Security incident reporting
  • Change Management Systems - Documentation and approval workflows

Best Practices for Enterprise Application Management

  1. Safety and Validation

    • Always verify application signatures before removal
    • Check for running processes and dependencies
    • Implement business hours restrictions for critical applications
    • Maintain comprehensive audit trails
  2. Data Protection

    • Create backups before uninstallation when required
    • Preserve user data and preferences when appropriate
    • Implement quarantine procedures for suspicious applications
    • Follow data retention policies
  3. System Cleanliness

    • Remove all associated support files and preferences
    • Clean up caches and temporary files
    • Update Launch Services database after removal
    • Verify complete removal through system scans
  4. Security and Compliance

    • Track license compliance for removed applications
    • Report security incidents for malware removal
    • Maintain detailed audit logs for compliance
    • Implement role-based access controls

Troubleshooting Common Issues

  • Permission errors - Ensure admin privileges for application removal
  • Apps in use - Terminate running processes before uninstallation
  • Incomplete removal - Use comprehensive cleanup procedures
  • System apps - Implement protection against critical system app removal
  • Dependency conflicts - Analyze dependencies before bulk removals

Remember to test uninstallation procedures thoroughly in a controlled environment before implementing across your entire MacFleet to ensure system stability and operational continuity.

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.