Tutorial

Novas atualizações e melhorias para a Macfleet.

Aviso importante

Os exemplos de código e scripts fornecidos nestes tutoriais são apenas para fins educacionais. A Macfleet não é responsável por quaisquer problemas, danos ou vulnerabilidades de segurança que possam surgir do uso, modificação ou implementação destes exemplos. Sempre revise e teste o código em um ambiente seguro antes de usá-lo em sistemas de produção.

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

Novas atualizações e melhorias para a Macfleet.

Configurando um Runner do GitHub Actions em um Mac Mini (Apple Silicon)

Runner do GitHub Actions

GitHub Actions é uma plataforma poderosa de CI/CD que permite automatizar seus fluxos de trabalho de desenvolvimento de software. Embora o GitHub ofereça runners hospedados, runners auto-hospedados fornecem maior controle e personalização para sua configuração de CI/CD. Este tutorial o guia através da configuração e conexão de um runner auto-hospedado em um Mac mini para executar pipelines do macOS.

Pré-requisitos

Antes de começar, certifique-se de ter:

  • Um Mac mini (registre-se no Macfleet)
  • Um repositório GitHub com direitos de administrador
  • Um gerenciador de pacotes instalado (preferencialmente Homebrew)
  • Git instalado em seu sistema

Passo 1: Criar uma Conta de Usuário Dedicada

Primeiro, crie uma conta de usuário dedicada para o runner do GitHub Actions:

# Criar a conta de usuário '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

# Definir a senha para o usuário
sudo dscl . -passwd /Users/gh-runner sua_senha

# Adicionar 'gh-runner' ao grupo 'admin'
sudo dscl . -append /Groups/admin GroupMembership gh-runner

Mude para a nova conta de usuário:

su gh-runner

Passo 2: Instalar Software Necessário

Instale Git e Rosetta 2 (se estiver usando Apple Silicon):

# Instalar Git se ainda não estiver instalado
brew install git

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

Passo 3: Configurar o Runner do GitHub Actions

  1. Vá para seu repositório GitHub
  2. Navegue para Configurações > Actions > Runners

Runner do GitHub Actions

  1. Clique em "New self-hosted runner" (https://github.com/<username>/<repository>/settings/actions/runners/new)
  2. Selecione macOS como imagem do runner e ARM64 como arquitetura
  3. Siga os comandos fornecidos para baixar e configurar o runner

Runner do GitHub Actions

Crie um arquivo .env no diretório _work do runner:

# arquivo _work/.env
ImageOS=macos15
XCODE_15_DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
  1. Execute o script run.sh em seu diretório do runner para completar a configuração.
  2. Verifique se o runner está ativo e ouvindo por trabalhos no terminal e verifique as configurações do repositório GitHub para a associação do runner e status Idle.

Runner do GitHub Actions

Passo 4: Configurar Sudoers (Opcional)

Se suas ações requerem privilégios de root, configure o arquivo sudoers:

sudo visudo

Adicione a seguinte linha:

gh-runner ALL=(ALL) NOPASSWD: ALL

Passo 5: Usar o Runner em Fluxos de Trabalho

Configure seu fluxo de trabalho do GitHub Actions para usar o runner auto-hospedado:

name: Fluxo de trabalho de exemplo

on:
  workflow_dispatch:

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

O runner está autenticado em seu repositório e rotulado com self-hosted, macOS, e ARM64. Use-o em seus fluxos de trabalho especificando estes rótulos no campo runs-on:

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

Melhores Práticas

  • Mantenha seu software do runner atualizado
  • Monitore regularmente os logs do runner para problemas
  • Use rótulos específicos para diferentes tipos de runners
  • Implemente medidas de segurança adequadas
  • Considere usar múltiplos runners para balanceamento de carga

Solução de Problemas

Problemas comuns e soluções:

  1. Runner não conectando:

    • Verifique conectividade de rede
    • Verifique validade do token GitHub
    • Certifique-se de permissões adequadas
  2. Falhas de build:

    • Verifique instalação do Xcode
    • Verifique dependências necessárias
    • Revise logs do fluxo de trabalho
  3. Problemas de permissão:

    • Verifique permissões do usuário
    • Verifique configuração sudoers
    • Revise permissões do sistema de arquivos

Conclusão

Agora você tem um runner auto-hospedado do GitHub Actions configurado em seu Mac mini. Esta configuração fornece mais controle sobre seu ambiente CI/CD e permite executar fluxos de trabalho específicos do macOS de forma eficiente.

Lembre-se de manter regularmente seu runner e mantê-lo atualizado com os patches de segurança e versões de software mais recentes.

Aplicativo Nativo

Aplicativo nativo do Macfleet

Guia de Instalação do Macfleet

Macfleet é uma solução poderosa de gerenciamento de frota projetada especificamente para ambientes Mac Mini hospedados na nuvem. Como provedor de hospedagem na nuvem Mac Mini, você pode usar o Macfleet para monitorar, gerenciar e otimizar toda sua frota de instâncias Mac virtualizadas.

Este guia de instalação o conduzirá através da configuração do monitoramento do Macfleet em sistemas macOS, Windows e Linux para garantir supervisão abrangente de sua infraestrutura na nuvem.

🍎 macOS

  • Baixe o arquivo .dmg para Mac aqui
  • Clique duas vezes no arquivo .dmg baixado
  • Arraste o aplicativo Macfleet para a pasta Aplicativos
  • Ejete o arquivo .dmg
  • Abra Preferências do Sistema > Segurança e Privacidade
    • Aba Privacidade > Acessibilidade
    • Marque Macfleet para permitir monitoramento
  • Inicie o Macfleet a partir de Aplicativos
  • O rastreamento inicia automaticamente

🪟 Windows

  • Baixe o arquivo .exe para Windows aqui
  • Clique com o botão direito no arquivo .exe > "Executar como administrador"
  • Siga o assistente de instalação
  • Aceite os termos e condições
  • Permita no Windows Defender se solicitado
  • Conceda permissões de monitoramento de aplicativo
  • Inicie o Macfleet a partir do Menu Iniciar
  • O aplicativo começa o rastreamento automaticamente

🐧 Linux

  • Baixe o pacote .deb (Ubuntu/Debian) ou .rpm (CentOS/RHEL) aqui
  • Instale usando seu gerenciador de pacotes
    • Ubuntu/Debian: sudo dpkg -i Macfleet-linux.deb
    • CentOS/RHEL: sudo rpm -ivh Macfleet-linux.rpm
  • Permita permissões de acesso X11 se solicitado
  • Adicione o usuário aos grupos apropriados se necessário
  • Inicie o Macfleet a partir do menu Aplicativos
  • O aplicativo começa o rastreamento automaticamente

Nota: Após a instalação em todos os sistemas, faça login com suas credenciais do Macfleet para sincronizar dados com seu painel de controle.