Guide

Nouvelles mises à jour et améliorations de Macfleet.

Gestion de Lancement Automatique d'Applications sur macOS

Optimisez la productivité d'entreprise en lançant automatiquement les applications essentielles à la connexion utilisateur sur vos appareils MacFleet. Ce tutoriel couvre la configuration LaunchAgent, la gestion d'applications spécifiques aux utilisateurs, la validation de sécurité et les stratégies de déploiement centralisées.

Comprendre les Mécanismes de Lancement d'Applications macOS

macOS fournit plusieurs mécanismes pour le lancement automatique d'applications :

  • LaunchAgents - Applications spécifiques à l'utilisateur qui se lancent à la connexion
  • LaunchDaemons - Services système qui démarrent au boot
  • Éléments de Connexion - Applications configurées par l'utilisateur dans Préférences Système
  • Configurations MDM - Configurations de lancement gérées par l'entreprise

Lancement Automatique d'Application Basique

Configuration Simple de Lancement d'Application

#!/bin/bash

# Configuration basique de lancement d'application pour une seule application
APP_NAME="Messages"
PLIST_NAME="com.${APP_NAME}.plist"
LAUNCH_AGENTS_DIR="/Library/LaunchAgents"

# Créer le plist LaunchAgent
cat > "${LAUNCH_AGENTS_DIR}/${PLIST_NAME}" << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>KeepAlive</key>
    <false/>
    <key>Label</key>
    <string>${PLIST_NAME}</string>
    <key>Program</key>
    <string>/System/Applications/${APP_NAME}.app/Contents/MacOS/${APP_NAME}</string>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>
EOF

echo "LaunchAgent créé pour ${APP_NAME}"

Lancement d'Application Amélioré avec Validation

#!/bin/bash

# Lancement d'application amélioré avec validation
setup_app_launch() {
    local app_name="$1"
    local app_path="$2"
    local plist_name="com.macfleet.${app_name}.plist"
    
    echo "Configuration du lancement automatique pour : $app_name"
    
    # Valider que l'application existe
    if [[ ! -d "$app_path" ]]; then
        echo "Erreur : Application non trouvée à $app_path"
        return 1
    fi
    
    # Valider l'exécutable
    local executable_path="${app_path}/Contents/MacOS/${app_name}"
    if [[ ! -x "$executable_path" ]]; then
        echo "Erreur : Exécutable non trouvé ou non exécutable : $executable_path"
        return 1
    fi
    
    # Créer le LaunchAgent
    cat > "/Library/LaunchAgents/${plist_name}" << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>KeepAlive</key>
    <false/>
    <key>Label</key>
    <string>${plist_name}</string>
    <key>Program</key>
    <string>${executable_path}</string>
    <key>RunAtLoad</key>
    <true/>
    <key>StandardErrorPath</key>
    <string>/var/log/macfleet_${app_name}.error.log</string>
    <key>StandardOutPath</key>
    <string>/var/log/macfleet_${app_name}.out.log</string>
</dict>
</plist>
EOF

    echo "LaunchAgent configuré avec succès pour $app_name"
    return 0
}

# Exemple d'utilisation
setup_app_launch "Messages" "/System/Applications/Messages.app"

Gestion de Lancement d'Applications d'Entreprise

#!/bin/bash

# Gestion de Lancement d'Applications d'Entreprise MacFleet
# Contrôle centralisé des lancements automatiques d'applications sur les appareils de flotte

# Configuration
LOG_FILE="/var/log/macfleet_app_launch.log"
CONFIG_DIR="/etc/macfleet/app_launch"
LAUNCH_AGENTS_DIR="/Library/LaunchAgents"
USER_LAUNCH_AGENTS_DIR="~/Library/LaunchAgents"
BACKUP_DIR="/var/backups/launch_agents"

# Base de données de configuration d'applications
declare -A ENTERPRISE_APPS=(
    ["suite_productivite"]="Slack,Microsoft Teams,Notion,1Password 7 - Password Manager"
    ["outils_developpement"]="Xcode,Terminal,Visual Studio Code,Docker Desktop"
    ["suite_design"]="Adobe Photoshop 2023,Sketch,Figma,Adobe Illustrator 2023"
    ["communication"]="Messages,Mail,Zoom,Microsoft Outlook"
    ["outils_securite"]="Little Snitch 5,1Password 7 - Password Manager,Malwarebytes Anti-Malware"
)

# Configurations de profils utilisateur
declare -A USER_PROFILES=(
    ["developpeur"]="outils_developpement,communication,outils_securite"
    ["designer"]="suite_design,communication,suite_productivite"
    ["manager"]="suite_productivite,communication"
    ["securite"]="outils_securite,communication,suite_productivite"
    ["defaut"]="communication"
)

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

# Créer les répertoires nécessaires
setup_directories() {
    for dir in "$CONFIG_DIR" "$BACKUP_DIR"; do
        if [[ ! -d "$dir" ]]; then
            mkdir -p "$dir"
            log_action "Répertoire créé : $dir"
        fi
    done
}

# Valider le chemin et l'exécutable de l'application
validate_application() {
    local app_name="$1"
    local app_path=""
    
    log_action "Validation de l'application : $app_name"
    
    # Chemins d'applications communs à vérifier
    local search_paths=(
        "/Applications"
        "/System/Applications"
        "/Applications/Utilities"
        "/System/Applications/Utilities"
    )
    
    # Trouver l'application
    for base_path in "${search_paths[@]}"; do
        local potential_path="${base_path}/${app_name}.app"
        if [[ -d "$potential_path" ]]; then
            app_path="$potential_path"
            break
        fi
    done
    
    if [[ -z "$app_path" ]]; then
        log_action "❌ Application non trouvée : $app_name"
        return 1
    fi
    
    # Valider l'exécutable
    local executable_path="${app_path}/Contents/MacOS/${app_name}"
    if [[ ! -x "$executable_path" ]]; then
        # Essayer un nom d'exécutable alternatif (certaines apps utilisent des noms différents)
        local info_plist="${app_path}/Contents/Info.plist"
        if [[ -f "$info_plist" ]]; then
            local bundle_executable
            bundle_executable=$(defaults read "$info_plist" CFBundleExecutable 2>/dev/null)
            if [[ -n "$bundle_executable" ]]; then
                executable_path="${app_path}/Contents/MacOS/${bundle_executable}"
            fi
        fi
        
        if [[ ! -x "$executable_path" ]]; then
            log_action "❌ Exécutable non trouvé ou non exécutable : $executable_path"
            return 1
        fi
    fi
    
    log_action "✅ Application validée : $app_name à $app_path"
    echo "$app_path"
    return 0
}

# Créer le plist LaunchAgent pour l'application
create_launch_agent() {
    local app_name="$1"
    local app_path="$2"
    local user_specific="${3:-false}"
    local delay="${4:-0}"
    
    local plist_name="com.macfleet.${app_name// /_}.plist"
    local target_dir="$LAUNCH_AGENTS_DIR"
    
    if [[ "$user_specific" == "true" ]]; then
        target_dir="$USER_LAUNCH_AGENTS_DIR"
    fi
    
    log_action "Création du LaunchAgent pour : $app_name"
    
    # Obtenir le chemin de l'exécutable
    local executable_path="${app_path}/Contents/MacOS/${app_name}"
    local info_plist="${app_path}/Contents/Info.plist"
    
    if [[ -f "$info_plist" ]]; then
        local bundle_executable
        bundle_executable=$(defaults read "$info_plist" CFBundleExecutable 2>/dev/null)
        if [[ -n "$bundle_executable" ]]; then
            executable_path="${app_path}/Contents/MacOS/${bundle_executable}"
        fi
    fi
    
    # Créer le contenu du plist
    cat > "${target_dir}/${plist_name}" << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>${plist_name}</string>
    <key>Program</key>
    <string>${executable_path}</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <false/>
    <key>StandardErrorPath</key>
    <string>/var/log/macfleet_$(echo "$app_name" | tr ' ' '_').error.log</string>
    <key>StandardOutPath</key>
    <string>/var/log/macfleet_$(echo "$app_name" | tr ' ' '_').out.log</string>
EOF

    # Ajouter un délai si spécifié
    if [[ "$delay" -gt 0 ]]; then
        cat >> "${target_dir}/${plist_name}" << EOF
    <key>StartInterval</key>
    <integer>$delay</integer>
EOF
    fi
    
    # Ajouter des variables d'environnement pour les applications d'entreprise
    cat >> "${target_dir}/${plist_name}" << EOF
    <key>EnvironmentVariables</key>
    <dict>
        <key>MACFLEET_MANAGED</key>
        <string>true</string>
        <key>ENTERPRISE_MODE</key>
        <string>enabled</string>
    </dict>
</dict>
</plist>
EOF

    # Définir les permissions appropriées
    chmod 644 "${target_dir}/${plist_name}"
    
    log_action "✅ LaunchAgent créé : ${plist_name}"
    return 0
}

# Supprimer le LaunchAgent pour l'application
remove_launch_agent() {
    local app_name="$1"
    local user_specific="${2:-false}"
    
    local plist_name="com.macfleet.${app_name// /_}.plist"
    local target_dir="$LAUNCH_AGENTS_DIR"
    
    if [[ "$user_specific" == "true" ]]; then
        target_dir="$USER_LAUNCH_AGENTS_DIR"
    fi
    
    local plist_path="${target_dir}/${plist_name}"
    
    if [[ -f "$plist_path" ]]; then
        # Décharger si chargé
        launchctl unload "$plist_path" 2>/dev/null || true
        
        # Supprimer le fichier
        rm -f "$plist_path"
        log_action "✅ LaunchAgent supprimé : $plist_name"
        return 0
    else
        log_action "⚠️  LaunchAgent non trouvé : $plist_name"
        return 1
    fi
}

# Déployer les applications pour le profil utilisateur
deploy_user_profile() {
    local profile_name="$1"
    local username="${2:-$(whoami)}"
    
    log_action "Déploiement du profil utilisateur : $profile_name pour l'utilisateur : $username"
    
    # Obtenir la configuration du profil
    local profile_config="${USER_PROFILES[$profile_name]}"
    if [[ -z "$profile_config" ]]; then
        log_action "❌ Profil utilisateur inconnu : $profile_name"
        return 1
    fi
    
    # Sauvegarder la configuration existante
    backup_launch_agents
    
    local success_count=0
    local total_count=0
    local failed_apps=()
    
    # Traiter chaque suite d'applications dans le profil
    IFS=',' read -ra SUITES <<< "$profile_config"
    for suite in "${SUITES[@]}"; do
        local suite_apps="${ENTERPRISE_APPS[$suite]}"
        if [[ -z "$suite_apps" ]]; then
            log_action "⚠️  Suite d'applications inconnue : $suite"
            continue
        fi
        
        # Traiter chaque application dans la suite
        IFS=',' read -ra APPS <<< "$suite_apps"
        for app in "${APPS[@]}"; do
            total_count=$((total_count + 1))
            
            # Valider et déployer l'application
            local app_path
            if app_path=$(validate_application "$app"); then
                if create_launch_agent "$app" "$app_path" false 0; then
                    success_count=$((success_count + 1))
                    log_action "✅ Déployé : $app"
                else
                    failed_apps+=("$app")
                fi
            else
                failed_apps+=("$app")
            fi
        done
    done
    
    # Générer le rapport de déploiement
    generate_deployment_report "$profile_name" "$username" "$success_count" "$total_count" "${failed_apps[@]}"
    
    log_action "Déploiement de profil terminé : $success_count/$total_count réussi"
    return $((total_count - success_count))
}

# Sauvegarder les LaunchAgents existants
backup_launch_agents() {
    local backup_timestamp
    backup_timestamp=$(date '+%Y%m%d_%H%M%S')
    local backup_file="$BACKUP_DIR/launch_agents_backup_$backup_timestamp.tar.gz"
    
    log_action "Création de la sauvegarde LaunchAgents : $backup_file"
    
    if tar -czf "$backup_file" -C /Library LaunchAgents 2>/dev/null; then
        log_action "✅ Sauvegarde créée : $backup_file"
        return 0
    else
        log_action "❌ Échec de la sauvegarde"
        return 1
    fi
}

# Générer le rapport de déploiement
generate_deployment_report() {
    local profile_name="$1"
    local username="$2"
    local success_count="$3"
    local total_count="$4"
    shift 4
    local failed_apps=("$@")
    
    local report_file="$CONFIG_DIR/deployment_report_$(date '+%Y%m%d_%H%M%S').json"
    
    log_action "Génération du rapport de déploiement : $report_file"
    
    cat > "$report_file" << EOF
{
    "deployment_metadata": {
        "timestamp": "$(date -Iseconds)",
        "profile_name": "$profile_name",
        "username": "$username",
        "hostname": "$(hostname)",
        "generator": "MacFleet App Launch Manager"
    },
    "deployment_summary": {
        "total_applications": $total_count,
        "successful_deployments": $success_count,
        "failed_deployments": $((total_count - success_count)),
        "success_rate": $(awk "BEGIN {printf \"%.2f\", ($success_count/$total_count)*100}")
    },
    "failed_applications": [
EOF

    # Ajouter les applications échouées
    local first=true
    for app in "${failed_apps[@]}"; do
        if [[ "$first" == true ]]; then
            first=false
        else
            echo "," >> "$report_file"
        fi
        echo "        \"$app\"" >> "$report_file"
    done
    
    cat >> "$report_file" << EOF
    ],
    "system_info": {
        "os_version": "$(sw_vers -productVersion)",
        "build_version": "$(sw_vers -buildVersion)",
        "serial_number": "$(system_profiler SPHardwareDataType | grep 'Serial Number' | awk '{print $4}')"
    }
}
EOF

    log_action "✅ Rapport de déploiement généré : $report_file"
    echo "$report_file"
}

# Lister tous les LaunchAgents configurés
list_configured_apps() {
    log_action "Liste des applications de lancement automatique configurées"
    
    echo "=== LaunchAgents Gérés par MacFleet ==="
    
    local agent_count=0
    for plist_file in "$LAUNCH_AGENTS_DIR"/com.macfleet.*.plist; do
        if [[ -f "$plist_file" ]]; then
            local label
            local program
            label=$(defaults read "$plist_file" Label 2>/dev/null || echo "Inconnu")
            program=$(defaults read "$plist_file" Program 2>/dev/null || echo "Inconnu")
            
            echo "Étiquette : $label"
            echo "Programme : $program"
            echo "Fichier : $plist_file"
            echo "---"
            
            agent_count=$((agent_count + 1))
        fi
    done
    
    echo "Total des LaunchAgents gérés : $agent_count"
}

# Valider toutes les applications configurées
validate_all_apps() {
    log_action "Validation de toutes les applications configurées"
    
    local validation_report="$CONFIG_DIR/validation_report_$(date '+%Y%m%d_%H%M%S').json"
    local valid_count=0
    local invalid_count=0
    local invalid_apps=()
    
    cat > "$validation_report" << EOF
{
    "validation_timestamp": "$(date -Iseconds)",
    "hostname": "$(hostname)",
    "validation_results": [
EOF

    local first=true
    for plist_file in "$LAUNCH_AGENTS_DIR"/com.macfleet.*.plist; do
        if [[ -f "$plist_file" ]]; then
            if [[ "$first" == true ]]; then
                first=false
            else
                echo "," >> "$validation_report"
            fi
            
            local label
            local program
            local exists="false"
            local executable="false"
            
            label=$(defaults read "$plist_file" Label 2>/dev/null || echo "Inconnu")
            program=$(defaults read "$plist_file" Program 2>/dev/null || echo "Inconnu")
            
            if [[ -f "$program" ]]; then
                exists="true"
                if [[ -x "$program" ]]; then
                    executable="true"
                    valid_count=$((valid_count + 1))
                else
                    invalid_count=$((invalid_count + 1))
                    invalid_apps+=("$label")
                fi
            else
                invalid_count=$((invalid_count + 1))
                invalid_apps+=("$label")
            fi
            
            cat >> "$validation_report" << EOF
        {
            "label": "$label",
            "program": "$program",
            "file_exists": $exists,
            "executable": $executable,
            "valid": $([ "$exists" = "true" ] && [ "$executable" = "true" ] && echo "true" || echo "false")
        }
EOF
        fi
    done
    
    cat >> "$validation_report" << EOF
    ],
    "validation_summary": {
        "total_agents": $((valid_count + invalid_count)),
        "valid_agents": $valid_count,
        "invalid_agents": $invalid_count
    }
}
EOF

    log_action "✅ Validation terminée : $valid_count valides, $invalid_count invalides"
    log_action "Rapport de validation : $validation_report"
    
    if [[ $invalid_count -gt 0 ]]; then
        log_action "Applications invalides trouvées : ${invalid_apps[*]}"
    fi
    
    echo "$validation_report"
}

# Nettoyer les LaunchAgents invalides ou orphelins
cleanup_launch_agents() {
    log_action "Nettoyage des LaunchAgents invalides"
    
    local cleanup_count=0
    local cleanup_report="$CONFIG_DIR/cleanup_report_$(date '+%Y%m%d_%H%M%S').txt"
    
    echo "Rapport de Nettoyage LaunchAgent MacFleet" > "$cleanup_report"
    echo "Généré : $(date)" >> "$cleanup_report"
    echo "======================================" >> "$cleanup_report"
    
    for plist_file in "$LAUNCH_AGENTS_DIR"/com.macfleet.*.plist; do
        if [[ -f "$plist_file" ]]; then
            local program
            program=$(defaults read "$plist_file" Program 2>/dev/null || echo "")
            
            if [[ -n "$program" ]] && [[ ! -x "$program" ]]; then
                local label
                label=$(defaults read "$plist_file" Label 2>/dev/null || echo "Inconnu")
                
                log_action "Suppression du LaunchAgent invalide : $label"
                echo "Supprimé : $label ($program non trouvé/exécutable)" >> "$cleanup_report"
                
                # Décharger si chargé
                launchctl unload "$plist_file" 2>/dev/null || true
                
                # Supprimer le fichier
                rm -f "$plist_file"
                cleanup_count=$((cleanup_count + 1))
            fi
        fi
    done
    
    echo "Total nettoyé : $cleanup_count" >> "$cleanup_report"
    log_action "✅ Nettoyage terminé : $cleanup_count agents supprimés"
    echo "$cleanup_report"
}

# Tester le lancement d'application
test_app_launch() {
    local app_name="$1"
    local plist_name="com.macfleet.${app_name// /_}.plist"
    local plist_path="$LAUNCH_AGENTS_DIR/$plist_name"
    
    if [[ ! -f "$plist_path" ]]; then
        log_action "❌ LaunchAgent non trouvé : $plist_name"
        return 1
    fi
    
    log_action "Test du lancement d'application : $app_name"
    
    # Charger le LaunchAgent
    if launchctl load "$plist_path" 2>/dev/null; then
        log_action "✅ LaunchAgent chargé avec succès"
        
        # Attendre un moment et vérifier si le processus fonctionne
        sleep 2
        if pgrep -f "$app_name" >/dev/null; then
            log_action "✅ L'application fonctionne : $app_name"
            return 0
        else
            log_action "⚠️  L'application n'a peut-être pas démarré correctement"
            return 1
        fi
    else
        log_action "❌ Échec du chargement du LaunchAgent"
        return 1
    fi
}

# Fonction d'exécution principale
main() {
    local action="${1:-list}"
    local profile_name="$2"
    local username="$3"
    
    log_action "=== Démarrage de la Gestion de Lancement d'Applications MacFleet ==="
    log_action "Action : $action"
    log_action "Profil : ${profile_name:-N/A}"
    log_action "Utilisateur : ${username:-$(whoami)}"
    
    # Configuration
    setup_directories
    
    case "$action" in
        "deploy")
            if [[ -z "$profile_name" ]]; then
                echo "Profils disponibles :"
                for profile in "${!USER_PROFILES[@]}"; do
                    echo "  - $profile : ${USER_PROFILES[$profile]}"
                done
                echo ""
                echo "Usage : $0 deploy <nom_profil> [nom_utilisateur]"
                exit 1
            fi
            deploy_user_profile "$profile_name" "$username"
            ;;
        "list")
            list_configured_apps
            ;;
        "validate")
            validate_all_apps
            ;;
        "cleanup")
            cleanup_launch_agents
            ;;
        "test")
            if [[ -z "$profile_name" ]]; then
                echo "Usage : $0 test <nom_app>"
                exit 1
            fi
            test_app_launch "$profile_name"
            ;;
        "remove")
            if [[ -z "$profile_name" ]]; then
                echo "Usage : $0 remove <nom_app>"
                exit 1
            fi
            remove_launch_agent "$profile_name"
            ;;
        "backup")
            backup_launch_agents
            ;;
        *)
            echo "Usage : $0 {deploy|list|validate|cleanup|test|remove|backup}"
            echo "  deploy   - Déployer les applications de profil utilisateur"
            echo "  list     - Lister les LaunchAgents configurés"
            echo "  validate - Valider toutes les applications configurées"
            echo "  cleanup  - Supprimer les LaunchAgents invalides"
            echo "  test     - Tester le lancement d'application spécifique"
            echo "  remove   - Supprimer un LaunchAgent spécifique"
            echo "  backup   - Sauvegarder les LaunchAgents actuels"
            exit 1
            ;;
    esac
    
    log_action "=== Gestion de lancement d'applications terminée ==="
}

# Exécuter la fonction principale
main "$@"

Configuration LaunchAgent Avancée

Lancement d'Application Conditionnel

#!/bin/bash

# Créer un LaunchAgent conditionnel basé sur l'état du système
create_conditional_launch_agent() {
    local app_name="$1"
    local app_path="$2"
    local condition="$3"  # network, power, time
    
    local plist_name="com.macfleet.conditional.${app_name// /_}.plist"
    
    cat > "/Library/LaunchAgents/${plist_name}" << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>${plist_name}</string>
    <key>Program</key>
    <string>${app_path}/Contents/MacOS/${app_name}</string>
    <key>RunAtLoad</key>
    <true/>
EOF

    case "$condition" in
        "network")
            cat >> "/Library/LaunchAgents/${plist_name}" << EOF
    <key>KeepAlive</key>
    <dict>
        <key>NetworkState</key>
        <true/>
    </dict>
EOF
            ;;
        "power")
            cat >> "/Library/LaunchAgents/${plist_name}" << EOF
    <key>KeepAlive</key>
    <dict>
        <key>PowerManagement</key>
        <true/>
    </dict>
EOF
            ;;
        "time")
            cat >> "/Library/LaunchAgents/${plist_name}" << EOF
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>9</integer>
        <key>Minute</key>
        <integer>0</integer>
    </dict>
EOF
            ;;
    esac
    
    cat >> "/Library/LaunchAgents/${plist_name}" << EOF
</dict>
</plist>
EOF

    echo "LaunchAgent conditionnel créé : $plist_name"
}

Lancement d'Application Conscient des Ressources

#!/bin/bash

# Créer un LaunchAgent conscient des ressources avec limitation
create_resource_aware_agent() {
    local app_name="$1"
    local app_path="$2"
    local cpu_limit="$3"    # pourcentage
    local memory_limit="$4" # MB
    
    local plist_name="com.macfleet.resource.${app_name// /_}.plist"
    
    cat > "/Library/LaunchAgents/${plist_name}" << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>${plist_name}</string>
    <key>Program</key>
    <string>${app_path}/Contents/MacOS/${app_name}</string>
    <key>RunAtLoad</key>
    <true/>
    <key>ThrottleInterval</key>
    <integer>60</integer>
    <key>SoftResourceLimits</key>
    <dict>
        <key>CPU</key>
        <integer>$cpu_limit</integer>
        <key>Mem</key>
        <integer>$((memory_limit * 1024 * 1024))</integer>
    </dict>
    <key>HardResourceLimits</key>
    <dict>
        <key>CPU</key>
        <integer>$((cpu_limit + 10))</integer>
        <key>Mem</key>
        <integer>$((memory_limit * 1024 * 1024 * 2))</integer>
    </dict>
</dict>
</plist>
EOF

    echo "LaunchAgent conscient des ressources créé : $plist_name"
}

Gestion de Profils Utilisateur

Attribution Dynamique de Profil

#!/bin/bash

# Attribuer un profil basé sur les attributs utilisateur
assign_user_profile() {
    local username="$1"
    
    # Obtenir les informations utilisateur
    local user_groups
    user_groups=$(groups "$username" 2>/dev/null)
    
    # Déterminer le profil basé sur l'appartenance au groupe
    if [[ "$user_groups" =~ developer|engineering ]]; then
        echo "developpeur"
    elif [[ "$user_groups" =~ design|creative ]]; then
        echo "designer"
    elif [[ "$user_groups" =~ manager|executive ]]; then
        echo "manager"
    elif [[ "$user_groups" =~ security|admin ]]; then
        echo "securite"
    else
        echo "defaut"
    fi
}

# Auto-configurer l'utilisateur à la première connexion
auto_configure_user() {
    local username="$(whoami)"
    local assigned_profile
    assigned_profile=$(assign_user_profile "$username")
    
    echo "Auto-configuration de l'utilisateur : $username"
    echo "Profil attribué : $assigned_profile"
    
    # Déployer le profil
    deploy_user_profile "$assigned_profile" "$username"
}

Modèles de Profil

#!/bin/bash

# Créer un modèle de profil personnalisé
create_profile_template() {
    local template_name="$1"
    local template_file="$CONFIG_DIR/profiles/${template_name}.json"
    
    mkdir -p "$(dirname "$template_file")"
    
    cat > "$template_file" << EOF
{
    "profile_name": "$template_name",
    "description": "Modèle de profil personnalisé",
    "applications": [
        {
            "name": "Nom de l'Application",
            "path": "/Applications/App.app",
            "required": true,
            "delay": 0,
            "conditions": []
        }
    ],
    "settings": {
        "auto_update": true,
        "notifications": true,
        "logging": true
    }
}
EOF

    echo "Modèle de profil créé : $template_file"
}

Sécurité et Validation

Vérification de Sécurité d'Application

#!/bin/bash

# Vérifier la sécurité de l'application avant le lancement
verify_app_security() {
    local app_path="$1"
    
    echo "=== Vérification de Sécurité d'Application ==="
    
    # Vérifier la signature de code
    if codesign -v "$app_path" 2>/dev/null; then
        echo "✅ Signature de code valide"
    else
        echo "❌ Signature de code invalide ou manquante"
        return 1
    fi
    
    # Vérifier la notarisation
    if spctl -a -v "$app_path" 2>/dev/null | grep -q "accepted"; then
        echo "✅ Application notarisée"
    else
        echo "⚠️  Application non notarisée"
    fi
    
    # Vérifier les attributs de quarantaine
    if xattr -l "$app_path" | grep -q "com.apple.quarantine"; then
        echo "⚠️  L'application a des attributs de quarantaine"
    else
        echo "✅ Pas d'attributs de quarantaine"
    fi
    
    # Vérifier les caractéristiques suspectes
    local info_plist="${app_path}/Contents/Info.plist"
    if [[ -f "$info_plist" ]]; then
        local bundle_id
        bundle_id=$(defaults read "$info_plist" CFBundleIdentifier 2>/dev/null)
        echo "Bundle ID : $bundle_id"
        
        # Vérifier les identifiants de bundle suspects
        if [[ "$bundle_id" =~ (malware|hack|crack) ]]; then
            echo "❌ Identifiant de bundle suspect détecté"
            return 1
        fi
    fi
    
    echo "✅ Vérification de sécurité terminée"
    return 0
}

Vérifications de Conformité d'Entreprise

#!/bin/bash

# Vérifier les exigences de conformité d'entreprise
check_enterprise_compliance() {
    local app_path="$1"
    
    echo "=== Vérification de Conformité d'Entreprise ==="
    
    # Vérifier si l'application est dans la liste approuvée
    local approved_apps_file="/etc/macfleet/approved_apps.txt"
    if [[ -f "$approved_apps_file" ]]; then
        local app_name
        app_name=$(basename "$app_path" .app)
        
        if grep -q "^$app_name$" "$approved_apps_file"; then
            echo "✅ L'application est dans la liste approuvée"
        else
            echo "❌ L'application n'est pas dans la liste approuvée"
            return 1
        fi
    fi
    
    # Vérifier les certificats d'entreprise
    local cert_info
    cert_info=$(codesign -dv "$app_path" 2>&1)
    
    if echo "$cert_info" | grep -q "Developer ID\|Mac App Store"; then
        echo "✅ Certificat développeur valide"
    else
        echo "⚠️  Pas de certificat développeur valide"
    fi
    
    return 0
}

Surveillance et Maintenance

Surveillance du Succès de Lancement

#!/bin/bash

# Surveiller le succès de lancement d'applications
monitor_launch_success() {
    local monitoring_log="/var/log/macfleet_launch_monitoring.log"
    
    echo "=== Surveillance du Succès de Lancement ===" | tee -a "$monitoring_log"
    echo "Horodatage : $(date)" | tee -a "$monitoring_log"
    
    # Vérifier chaque LaunchAgent configuré
    for plist_file in "$LAUNCH_AGENTS_DIR"/com.macfleet.*.plist; do
        if [[ -f "$plist_file" ]]; then
            local label
            local program
            label=$(defaults read "$plist_file" Label 2>/dev/null)
            program=$(defaults read "$plist_file" Program 2>/dev/null)
            
            # Vérifier si l'application fonctionne
            local app_name
            app_name=$(basename "$program")
            
            if pgrep -f "$app_name" >/dev/null; then
                echo "✅ $label : En cours d'exécution" | tee -a "$monitoring_log"
            else
                echo "❌ $label : Pas en cours d'exécution" | tee -a "$monitoring_log"
                
                # Essayer de redémarrer
                launchctl unload "$plist_file" 2>/dev/null
                launchctl load "$plist_file" 2>/dev/null
                echo "🔄 Tentative de redémarrage pour $label" | tee -a "$monitoring_log"
            fi
        fi
    done
}

Maintenance Automatisée

#!/bin/bash

# Configurer la planification de maintenance automatisée
setup_maintenance_schedule() {
    local maintenance_script="/usr/local/bin/macfleet_app_maintenance.sh"
    local launchd_plist="/Library/LaunchDaemons/com.macfleet.app.maintenance.plist"
    
    # Créer le script de maintenance
    cat > "$maintenance_script" << 'EOF'
#!/bin/bash
LOG_FILE="/var/log/macfleet_app_maintenance.log"
exec > >(tee -a "$LOG_FILE") 2>&1

echo "$(date): Démarrage de la maintenance d'applications"

# Valider toutes les applications
/usr/local/bin/macfleet_app_manager.sh validate

# Surveiller le succès de lancement
monitor_launch_success

# Nettoyer les agents invalides
/usr/local/bin/macfleet_app_manager.sh cleanup

echo "$(date): Maintenance d'applications terminée"
EOF

    chmod +x "$maintenance_script"
    
    # Créer le LaunchDaemon pour la maintenance programmée
    cat > "$launchd_plist" << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.macfleet.app.maintenance</string>
    <key>ProgramArguments</key>
    <array>
        <string>$maintenance_script</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>2</integer>
        <key>Minute</key>
        <integer>0</integer>
    </dict>
    <key>RunAtLoad</key>
    <false/>
</dict>
</plist>
EOF

    # Charger le LaunchDaemon
    sudo launchctl load "$launchd_plist"
    
    echo "Maintenance automatisée configurée"
    echo "Script : $maintenance_script"
    echo "Planification : Quotidien à 2h00"
}

Meilleures Pratiques

🚀 Optimisation des Performances

  • Échelonnez les lancements d'applications pour éviter la contention de ressources
  • Utilisez des limites de ressources pour éviter la surcharge du système
  • Surveillez le succès de lancement et implémentez le redémarrage automatique
  • Validez les applications avant le déploiement

🔐 Directives de Sécurité

  • Vérifiez les signatures de code avant d'autoriser le lancement automatique
  • Maintenez des listes d'applications approuvées pour la conformité d'entreprise
  • Utilisez des chemins sécurisés et évitez les emplacements accessibles en écriture par l'utilisateur
  • Audits de sécurité réguliers des applications configurées

📋 Meilleures Pratiques de Gestion

  • Déploiement basé sur les profils pour différents types d'utilisateurs
  • Gestion de configuration centralisée
  • Sauvegarde régulière des configurations LaunchAgent
  • Surveillance et maintenance automatisées

🔍 Conseils de Dépannage

  • Vérifiez les chemins d'applications et les exécutables
  • Vérifiez la syntaxe LaunchAgent en utilisant plutil
  • Surveillez les journaux système pour les erreurs de lancement
  • Testez les configurations avant le déploiement de flotte

Notes Importantes

  • Les LaunchAgents fonctionnent par utilisateur tandis que les LaunchDaemons fonctionnent à l'échelle du système
  • Les applications doivent être correctement signées pour le déploiement d'entreprise
  • Les limites de ressources aident à prévenir les problèmes de performance du système
  • La validation régulière assure le fonctionnement continu
  • Sauvegardez les configurations avant de faire des changements

Gestion Entreprise de la Désinstallation d'Applications pour macOS

Implémentez une gestion de désinstallation d'applications de niveau entreprise sur votre déploiement MacFleet avec la gestion automatisée du cycle de vie, la validation de sécurité, le nettoyage des fichiers résiduels et la suppression complète d'applications. Ce tutoriel fournit des solutions pour maintenir des environnements système propres tout en assurant une gestion sécurisée et efficace du cycle de vie des applications.

Comprendre la Gestion de Désinstallation d'Applications macOS

macOS fournit plusieurs méthodes pour la suppression d'applications :

  • Glisser-Déposer vers la Corbeille - Méthode utilisateur standard via Finder
  • Commande rm - Suppression en ligne de commande des bundles d'applications
  • Désinstallateurs Tiers - Outils spécialisés pour la suppression complète
  • Actions de Désinstallation MDM - Capacités de désinstallation à distance
  • Outils de Gestionnaire de Paquets - Commandes de désinstallation Homebrew, MacPorts

Désinstallation d'Application de Base

Suppression Simple d'Application

#!/bin/bash

# Désinstallation d'app de base
sudo rm -rf /Applications/Opera.app

Structure de Commande de Désinstallation Principale

#!/bin/bash

# Modèle générique de désinstallation d'app
sudo rm -rf /chemin/vers/fichiers/app

Les options de la commande rm :

  • -r - Supprime la hiérarchie de fichiers (récursif)
  • -f - Supprime les fichiers sans demander confirmation (force)

Système de Gestion de Désinstallation d'Applications Entreprise

Outil Complet de Cycle de Vie d'Applications

#!/bin/bash

# Outil de Gestion de Désinstallation d'Applications Entreprise MacFleet
# Suppression d'applications avancée et gestion du cycle de vie

# 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"

# Créer les répertoires
mkdir -p "$(dirname "$CONFIG_FILE")" "$(dirname "$LOG_FILE")" "$QUARANTINE_DIR" "$(dirname "$AUDIT_LOG")"

# Politique de désinstallation d'applications par défaut
cat > "$CONFIG_FILE" 2>/dev/null << 'EOF' || true
# Politique de Gestion de Désinstallation d'Applications Entreprise MacFleet
# Version : 2.0

# Application des Politiques de Désinstallation
ENFORCE_UNINSTALL_POLICIES=true
SAFE_UNINSTALL_MODE=true
BACKUP_BEFORE_UNINSTALL=true
VERIFY_APP_SIGNATURES=true
PREVENT_SYSTEM_APP_REMOVAL=true

# Sécurité et Sûreté
REQUIRE_ADMIN_APPROVAL=false
VALIDATE_APP_DEPENDENCIES=true
CHECK_RUNNING_PROCESSES=true
QUARANTINE_SUSPICIOUS_APPS=true
MALWARE_SCAN_ENABLED=true

# Nettoyage et Maintenance
COMPLETE_CLEANUP_ENABLED=true
REMOVE_PREFERENCES=true
REMOVE_SUPPORT_FILES=true
REMOVE_CACHES=true
REMOVE_LOGS=true
CLEAN_LAUNCH_SERVICES=true

# Règles Métier
BUSINESS_HOURS_RESTRICTION=true
BUSINESS_HOURS_START="09:00"
BUSINESS_HOURS_END="18:00"
CRITICAL_APP_PROTECTION=true
LICENSED_APP_TRACKING=true

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

# Conformité et Audit
AUDIT_ALL_UNINSTALLS=true
COMPLIANCE_REPORTING=true
SOFTWARE_INVENTORY_UPDATE=true
LICENSE_TRACKING=true
SECURITY_INCIDENT_REPORTING=true

# Récupération et Restauration
BACKUP_RETENTION_DAYS=30
ROLLBACK_CAPABILITY=true
EMERGENCY_RECOVERY=true
EOF

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

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

# Fonction de journalisation d'audit
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"
}

# Obtenir les informations sur l'application
get_app_info() {
    local app_path="$1"
    
    if [[ ! -d "$app_path" ]]; then
        echo "Application introuvable : $app_path"
        return 1
    fi
    
    local app_name
    app_name=$(basename "$app_path")
    
    local bundle_id=""
    local version=""
    local signature_status=""
    
    # Obtenir l'identifiant de bundle
    if [[ -f "$app_path/Contents/Info.plist" ]]; then
        bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2>/dev/null || echo "inconnu")
        version=$(defaults read "$app_path/Contents/Info.plist" CFBundleShortVersionString 2>/dev/null || echo "inconnu")
    fi
    
    # Vérifier la signature de code
    if command -v codesign >/dev/null 2>&1; then
        if codesign -v "$app_path" >/dev/null 2>&1; then
            signature_status="valide"
        else
            signature_status="invalide"
        fi
    else
        signature_status="inconnu"
    fi
    
    echo "Nom : $app_name"
    echo "ID Bundle : $bundle_id"
    echo "Version : $version"
    echo "Signature : $signature_status"
    echo "Chemin : $app_path"
}

# Vérifier si l'application peut être désinstallée en sécurité
check_uninstall_safety() {
    local app_path="$1"
    local force="${2:-false}"
    
    echo "=== Validation de la Sécurité de Désinstallation d'Application ==="
    
    local app_name
    app_name=$(basename "$app_path")
    
    # Vérifier si l'app existe
    if [[ ! -d "$app_path" ]]; then
        echo "✅ Application introuvable (déjà désinstallée) : $app_name"
        return 0
    fi
    
    # Empêcher la suppression d'apps système
    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 "❌ Impossible de désinstaller l'application système : $app_name"
                audit_log "UNINSTALL_BLOCKED" "$app_name" "SYSTEM_APP" "Protection d'app système active"
                return 1
            fi
        done
    fi
    
    # Vérifier si l'app est en cours d'exécution
    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 "⚠️  L'application est actuellement en cours d'exécution : $app_name"
            
            if [[ "$force" != "true" ]]; then
                echo "❌ Désinstallation bloquée : L'application est en cours d'exécution. Utilisez l'option force pour terminer et désinstaller."
                audit_log "UNINSTALL_BLOCKED" "$app_name" "RUNNING" "L'application est actuellement en cours d'exécution"
                return 1
            else
                echo "🚨 Désinstallation forcée : Terminera l'application en cours d'exécution"
                pkill -f "$app_executable" 2>/dev/null || true
                sleep 2
            fi
        fi
    fi
    
    # Vérification des heures de bureau
    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 "⚠️  Restriction des heures de bureau active ($BUSINESS_HOURS_START - $BUSINESS_HOURS_END)"
            
            # Vérifier s'il s'agit d'une app critique
            if [[ "$CRITICAL_APP_PROTECTION" == "true" ]]; then
                echo "❌ Désinstallation bloquée pendant les heures de bureau pour sécurité"
                audit_log "UNINSTALL_BLOCKED" "$app_name" "BUSINESS_HOURS" "Protection des heures de bureau active"
                return 1
            fi
        fi
    fi
    
    # Vérification de signature
    if [[ "$VERIFY_APP_SIGNATURES" == "true" ]]; then
        if ! codesign -v "$app_path" >/dev/null 2>&1; then
            echo "⚠️  Avertissement : L'application a une signature invalide ou manquante"
            
            if [[ "$QUARANTINE_SUSPICIOUS_APPS" == "true" ]]; then
                echo "🔒 Mise en quarantaine de l'application suspecte avant suppression"
                quarantine_application "$app_path"
            fi
        fi
    fi
    
    echo "✅ Sécurité de désinstallation d'application validée"
    audit_log "SAFETY_CHECK" "$app_name" "PASSED" "Toutes les vérifications de sécurité réussies"
    return 0
}

# Créer une sauvegarde avant désinstallation
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 "=== Création de Sauvegarde d'Application ==="
    
    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 "Création de sauvegarde de $app_name..."
    if tar -czf "$backup_file" -C "$(dirname "$app_path")" "$(basename "$app_path")" 2>/dev/null; then
        echo "✅ Sauvegarde d'application créée : $backup_file"
        log_action "Sauvegarde d'application créée : $backup_file"
        
        # Définir la politique de rétention
        find "$backup_dir" -name "*_backup_*.tar.gz" -mtime +$BACKUP_RETENTION_DAYS -delete 2>/dev/null || true
        
        return 0
    else
        echo "⚠️  Échec de la sauvegarde d'application"
        log_action "AVERTISSEMENT : Échec de la sauvegarde d'application pour $app_name"
        return 1
    fi
}

# Mettre en quarantaine une application suspecte
quarantine_application() {
    local app_path="$1"
    local app_name
    app_name=$(basename "$app_path")
    
    echo "=== Mise en Quarantaine d'Application Suspecte ==="
    
    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 mise en quarantaine : $quarantine_path"
        log_action "Application mise en quarantaine : $app_name -> $quarantine_path"
        audit_log "QUARANTINE" "$app_name" "SUCCESS" "App suspecte mise en quarantaine"
        
        # Créer un enregistrement de quarantaine
        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 "❌ Échec de la mise en quarantaine de l'application"
        return 1
    fi
}

# Trouver et supprimer les fichiers de support d'application
cleanup_app_support_files() {
    local app_name="$1"
    local bundle_id="$2"
    
    if [[ "$COMPLETE_CLEANUP_ENABLED" != "true" ]]; then
        return 0
    fi
    
    echo "=== Nettoyage des Fichiers de Support d'Application ==="
    
    local cleanup_paths=()
    local app_base_name
    app_base_name=$(basename "$app_name" .app)
    
    # Emplacements de nettoyage communs
    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
        # Étendre le tilde
        location="${location/#\~/$HOME}"
        
        if [[ -d "$location" ]]; then
            # Rechercher par nom d'app
            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
            
            # Rechercher par ID de bundle si disponible
            if [[ -n "$bundle_id" && "$bundle_id" != "inconnu" ]]; 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
    
    # Supprimer les fichiers de préférences
    if [[ "$REMOVE_PREFERENCES" == "true" && -n "$bundle_id" && "$bundle_id" != "inconnu" ]]; 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 "🗑️  Fichier de préférences supprimé : $pref_file"
            fi
        done
    fi
    
    # Nettoyer les fichiers de support trouvés
    local cleaned_count=0
    for path in "${cleanup_paths[@]}"; do
        if [[ -d "$path" ]]; then
            rm -rf "$path" 2>/dev/null || true
            echo "🗑️  Fichiers de support supprimés : $path"
            ((cleaned_count++))
        fi
    done
    
    # Mettre à jour la base de données Launch Services
    if [[ "$CLEAN_LAUNCH_SERVICES" == "true" ]]; then
        echo "🔄 Mise à jour de la base de données Launch Services..."
        /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -kill -r -domain local -domain system -domain user 2>/dev/null || true
    fi
    
    echo "✅ Nettoyage terminé : $cleaned_count emplacements de fichiers de support supprimés"
    log_action "Nettoyage des fichiers de support d'application terminé pour $app_name ($cleaned_count emplacements)"
}

# Envoyer une notification de désinstallation
send_uninstall_notification() {
    local app_name="$1"
    local operation="$2"
    local result="$3"
    
    if [[ "$USER_NOTIFICATION_ENABLED" != "true" ]]; then
        return 0
    fi
    
    echo "=== Envoi des Notifications de Désinstallation ==="
    
    local notification_title="Gestion d'Applications MacFleet"
    local notification_message="Application $operation : $app_name ($result)"
    
    # Afficher la notification aux utilisateurs connectés
    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 envoyée à l'utilisateur : $user"
        fi
    done
    
    log_action "Notifications de désinstallation envoyées pour $app_name ($operation - $result)"
}

# Désinstallation d'application entreprise
enterprise_uninstall() {
    local app_path="$1"
    local force="${2:-false}"
    local skip_backup="${3:-false}"
    
    echo "=== Désinstallation d'Application Entreprise ==="
    
    if [[ -z "$app_path" ]]; then
        echo "❌ Chemin d'application requis"
        return 1
    fi
    
    local app_name
    app_name=$(basename "$app_path")
    
    # Obtenir les informations sur l'application
    echo "Analyse de l'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 "ID Bundle:" | cut -d':' -f2 | xargs)
    
    # Vérifications de sécurité
    if ! check_uninstall_safety "$app_path" "$force"; then
        return 1
    fi
    
    # Créer une sauvegarde si activé
    if [[ "$skip_backup" != "true" ]]; then
        create_app_backup "$app_path"
    fi
    
    log_action "DÉSINSTALLATION ENTREPRISE : Début de désinstallation de $app_name"
    audit_log "UNINSTALL" "$app_name" "INITIATED" "Chemin : $app_path Bundle : $bundle_id Force : $force"
    
    # Effectuer la désinstallation
    echo "🗑️  Suppression du bundle d'application..."
    if sudo rm -rf "$app_path"; then
        echo "✅ Bundle d'application supprimé avec succès"
        
        # Nettoyage complet
        cleanup_app_support_files "$app_name" "$bundle_id"
        
        # Envoyer des notifications
        send_uninstall_notification "$app_name" "désinstallation" "succès"
        
        log_action "Application désinstallée avec succès : $app_name"
        audit_log "UNINSTALL" "$app_name" "SUCCESS" "Suppression complète incluant les fichiers de support"
        
        return 0
    else
        echo "❌ Échec de la suppression du bundle d'application"
        send_uninstall_notification "$app_name" "désinstallation" "échec"
        log_action "ÉCHEC : Impossible de supprimer l'application : $app_name"
        audit_log "UNINSTALL" "$app_name" "FAILED" "Échec de la suppression du bundle"
        return 1
    fi
}

# Désinstallation en masse d'applications
bulk_uninstall() {
    local app_list_file="$1"
    local force="${2:-false}"
    
    echo "=== Désinstallation en Masse d'Applications ==="
    
    if [[ ! -f "$app_list_file" ]]; then
        echo "❌ Fichier de liste d'applications introuvable : $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
        # Ignorer les lignes vides et les commentaires
        if [[ -z "$app_path" || "$app_path" == \#* ]]; then
            continue
        fi
        
        ((total_count++))
        echo "Traitement ($total_count) : $app_path"
        
        if enterprise_uninstall "$app_path" "$force" "false"; then
            ((success_count++))
        else
            ((failure_count++))
        fi
        
        echo "---"
    done < "$app_list_file"
    
    echo "=== Résumé de Désinstallation en Masse ==="
    echo "Total des applications : $total_count"
    echo "Désinstallations réussies : $success_count"
    echo "Désinstallations échouées : $failure_count"
    
    log_action "Désinstallation en masse terminée : $success_count/$total_count réussies"
    audit_log "BULK_UNINSTALL" "MULTIPLE" "COMPLETED" "Succès : $success_count Échecs : $failure_count Total : $total_count"
}

# Suppression d'application malveillante
remove_malware_app() {
    local app_path="$1"
    local threat_level="${2:-medium}"
    
    echo "🚨 SUPPRESSION D'APPLICATION MALVEILLANTE 🚨"
    
    local app_name
    app_name=$(basename "$app_path")
    
    log_action "SUPPRESSION MALWARE : Suppression d'application malveillante : $app_name (Menace : $threat_level)"
    audit_log "MALWARE_REMOVAL" "$app_name" "INITIATED" "Niveau de menace : $threat_level"
    
    # Mettre en quarantaine d'abord pour analyse
    if [[ "$threat_level" == "high" ]]; then
        echo "🔒 Niveau de menace élevé - mise en quarantaine pour analyse de sécurité"
        quarantine_application "$app_path"
    else
        # Suppression forcée sans sauvegarde pour les logiciels malveillants
        echo "🗑️  Exécution de la suppression d'urgence de malware..."
        enterprise_uninstall "$app_path" "true" "true"
    fi
    
    send_uninstall_notification "$app_name" "suppression malware" "terminée"
    audit_log "MALWARE_REMOVAL" "$app_name" "COMPLETED" "Suppression d'urgence terminée"
}

# Lister les applications installées
list_applications() {
    local filter="$1"
    
    echo "=== Applications Installées ==="
    
    local app_list=()
    
    # Trouver tous les bundles .app
    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)
    
    # Trier et afficher
    printf '%s\n' "${app_list[@]}" | sort
    
    echo "Total des applications trouvées : ${#app_list[@]}"
}

# Fonction principale avec gestion d'arguments
main() {
    log_action "=== Outil de Gestion de Désinstallation d'Applications MacFleet Démarré ==="
    
    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"
            ;;
        *)
            echo "Outil de Gestion de Désinstallation d'Applications Entreprise MacFleet"
            echo "Usage : $0 [commande] [options]"
            echo ""
            echo "Commandes :"
            echo "  uninstall [chemin_app] [force] [ignorer_sauvegarde]  - Désinstaller l'application avec vérifications de sécurité"
            echo "  bulk [fichier_liste] [force]                        - Désinstallation en masse depuis fichier liste"
            echo "  malware [chemin_app] [niveau_menace]                 - Supprimer l'application malveillante"
            echo "  list [filtre]                                        - Lister les applications installées"
            echo "  info [chemin_app]                                    - Obtenir les informations sur l'application"
            echo ""
            echo "Exemples :"
            echo "  $0 uninstall /Applications/Opera.app       - Désinstallation sécurisée avec sauvegarde"
            echo "  $0 uninstall /Applications/App.app true    - Désinstallation forcée (ignorer vérifications sécurité)"
            echo "  $0 bulk apps_to_remove.txt                 - Désinstallation en masse depuis liste"
            echo "  $0 malware /Applications/Malware.app high  - Suppression d'urgence de malware"
            echo "  $0 list Safari                             - Lister les apps contenant 'Safari'"
            ;;
    esac
    
    log_action "=== Opération de gestion de désinstallation d'application terminée ==="
}

# Exécuter la fonction principale
main "$@"

Notes de Configuration Importantes

Compréhension de la Structure d'Applications macOS

  • Bundles d'Applications - Répertoires .app contenant l'exécutable et les ressources
  • Identifiants de Bundle - Identifiants uniques pour le suivi d'applications
  • Signatures de Code - Validation de sécurité pour l'authenticité des applications
  • Fichiers de Support - Préférences, caches, journaux dans divers emplacements Library

Points d'Intégration Entreprise

  • Gestion d'Actifs Logiciels - Intégration avec les plateformes SAM
  • Conformité de Licence - Suivi des suppressions d'applications sous licence
  • Gestion d'Information et d'Événements de Sécurité (SIEM) - Rapport d'incidents de sécurité
  • Systèmes de Gestion des Changements - Flux de documentation et d'approbation

Bonnes Pratiques pour la Gestion d'Applications Entreprise

  1. Sécurité et Validation

    • Toujours vérifier les signatures d'applications avant suppression
    • Vérifier les processus en cours et les dépendances
    • Implémenter des restrictions d'heures de bureau pour les applications critiques
    • Maintenir des pistes d'audit complètes
  2. Protection des Données

    • Créer des sauvegardes avant désinstallation quand requis
    • Préserver les données utilisateur et préférences quand approprié
    • Implémenter des procédures de quarantaine pour les applications suspectes
    • Suivre les politiques de rétention des données
  3. Propreté du Système

    • Supprimer tous les fichiers de support et préférences associés
    • Nettoyer les caches et fichiers temporaires
    • Mettre à jour la base de données Launch Services après suppression
    • Vérifier la suppression complète via des analyses système
  4. Sécurité et Conformité

    • Suivre la conformité de licence pour les applications supprimées
    • Rapporter les incidents de sécurité pour la suppression de malware
    • Maintenir des journaux d'audit détaillés pour la conformité
    • Implémenter des contrôles d'accès basés sur les rôles

Dépannage des Problèmes Courants

  • Erreurs de permissions - S'assurer des privilèges administrateur pour la suppression d'applications
  • Apps en cours d'utilisation - Terminer les processus en cours avant désinstallation
  • Suppression incomplète - Utiliser des procédures de nettoyage complètes
  • Apps système - Implémenter une protection contre la suppression d'apps système critiques
  • Conflits de dépendances - Analyser les dépendances avant les suppressions en masse

N'oubliez pas de tester minutieusement les procédures de désinstallation dans un environnement contrôlé avant l'implémentation sur l'ensemble de votre MacFleet pour assurer la stabilité du système et la continuité opérationnelle.

Gestion Entreprise du Lancement d'Applications pour macOS

Implémentez une gestion de lancement d'applications de niveau entreprise sur votre déploiement MacFleet avec le déploiement automatisé d'apps, la validation de sécurité, l'exécution à distance et la gestion complète du cycle de vie des applications. Ce tutoriel fournit des solutions pour maintenir la disponibilité des applications tout en assurant un lancement sécurisé et efficace.

Comprendre la Gestion de Lancement d'Applications macOS

macOS fournit plusieurs méthodes pour le lancement d'applications :

  • open - Commande système pour ouvrir fichiers, répertoires et applications
  • open -a - Lancer des applications par nom
  • open -b - Lancer des applications par identifiant bundle
  • Finder - Lancement d'applications basé sur GUI
  • Spotlight - Lancement d'applications basé sur recherche

Opérations de Lancement d'Applications de Base

Lancement Simple d'Application

#!/bin/bash

# Lancement d'app de base
open -a "Nom App"

Lancement par Identifiant Bundle

#!/bin/bash

# Lancement utilisant l'ID bundle
open -b com.apple.AppStore

Lancement d'Application en Arrière-Plan

#!/bin/bash

# Lancer app en arrière-plan
open -g -a "App Store"

Lancement d'Instance Multiple

#!/bin/bash

# Ouvrir nouvelle instance
open -n -a "App Store"

Lancement d'Application Cachée

#!/bin/bash

# Lancer app cachée
open -j -a "App Store"

Système de Gestion de Lancement d'Applications Entreprise

#!/bin/bash

# Outil de Gestion de Lancement d'Applications Entreprise MacFleet
# Déploiement d'applications avancé et gestion du cycle de vie

# Configuration
CONFIG_FILE="/etc/macfleet/app_launch_policy.conf"
LOG_FILE="/var/log/macfleet_app_launch.log"
APP_REGISTRY="/Library/MacFleet/AppRegistry"
DEPLOYMENT_LOG="/var/log/macfleet_deployment_audit.log"

# Créer les répertoires
mkdir -p "$(dirname "$CONFIG_FILE")" "$(dirname "$LOG_FILE")" "$APP_REGISTRY" "$(dirname "$DEPLOYMENT_LOG")"

# Politique de gestion de lancement d'applications par défaut
cat > "$CONFIG_FILE" 2>/dev/null << 'EOF' || true
# Politique de Gestion de Lancement d'Applications Entreprise MacFleet
# Version : 2.0

# Application des Politiques de Lancement
ENFORCE_LAUNCH_POLICIES=true
VALIDATE_APP_SIGNATURES=true
CHECK_APP_PERMISSIONS=true
QUARANTINE_UNKNOWN_APPS=true
BUSINESS_HOURS_ENFORCEMENT=true

# Sécurité et Validation
REQUIRE_ADMIN_APPROVAL=false
WHITELIST_ENFORCEMENT=true
MALWARE_SCANNING=true
SECURITY_ASSESSMENT=true
CERTIFICATE_VALIDATION=true

# Gestion d'Applications
AUTO_UPDATE_DETECTION=true
LICENSE_VALIDATION=true
DEPENDENCY_CHECKING=true
RESOURCE_MONITORING=true
PERFORMANCE_TRACKING=true

# Contrôles de Déploiement
STAGED_DEPLOYMENT=true
ROLLBACK_CAPABILITY=true
A_B_TESTING=false
CANARY_RELEASES=false
EMERGENCY_DEPLOYMENT=true

# Expérience Utilisateur
USER_NOTIFICATION_ENABLED=true
LAUNCH_TIMEOUT=30
RETRY_ATTEMPTS=3
GRACEFUL_FAILURE=true
CONTEXT_AWARENESS=true

# Conformité et Audit
AUDIT_ALL_LAUNCHES=true
COMPLIANCE_REPORTING=true
LAUNCH_ANALYTICS=true
USAGE_TRACKING=true
INCIDENT_LOGGING=true
EOF

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

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

# Fonction de journalisation d'audit
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)" >> "$DEPLOYMENT_LOG"
}

# Valider l'application avant le lancement
validate_application() {
    local app_identifier="$1"
    local app_path=""
    
    echo "=== Validation d'Application ==="
    
    # Déterminer le chemin de l'app
    if [[ "$app_identifier" == *".app" ]]; then
        # Nom d'app direct
        app_path="/Applications/$app_identifier"
    elif [[ "$app_identifier" == com.* ]]; then
        # Identifiant bundle
        app_path=$(mdfind "kMDItemCFBundleIdentifier == '$app_identifier'" | head -1)
    else
        # Rechercher l'app
        app_path="/Applications/$app_identifier.app"
    fi
    
    if [[ ! -d "$app_path" ]]; then
        echo "❌ Application introuvable : $app_identifier"
        audit_log "VALIDATION" "$app_identifier" "NOT_FOUND" "Chemin d'application introuvable"
        return 1
    fi
    
    echo "✅ Application trouvée : $app_path"
    
    # Valider la signature de code
    if [[ "$VALIDATE_APP_SIGNATURES" == "true" ]]; then
        echo "🔐 Validation de la signature de code..."
        if codesign -v "$app_path" 2>/dev/null; then
            echo "✅ Signature de code valide"
        else
            echo "❌ Signature de code invalide"
            audit_log "VALIDATION" "$app_identifier" "SIGNATURE_INVALID" "Validation de signature de code échouée"
            return 1
        fi
    fi
    
    # Vérifier le statut de quarantaine
    if [[ "$QUARANTINE_UNKNOWN_APPS" == "true" ]]; then
        if xattr -l "$app_path" 2>/dev/null | grep -q "com.apple.quarantine"; then
            echo "⚠️  Application en quarantaine"
            log_action "Tentative de lancement d'application en quarantaine : $app_identifier"
            audit_log "VALIDATION" "$app_identifier" "QUARANTINED" "Application sous quarantaine"
            return 1
        fi
    fi
    
    # Validation de liste blanche
    if [[ "$WHITELIST_ENFORCEMENT" == "true" ]]; then
        if ! check_whitelist "$app_identifier"; then
            echo "❌ Application pas dans la liste blanche"
            audit_log "VALIDATION" "$app_identifier" "NOT_WHITELISTED" "Application pas dans la liste blanche approuvée"
            return 1
        fi
    fi
    
    audit_log "VALIDATION" "$app_identifier" "PASSED" "Toutes les vérifications de validation réussies"
    return 0
}

# Vérifier la liste blanche d'applications
check_whitelist() {
    local app_identifier="$1"
    local whitelist_file="$APP_REGISTRY/whitelist.txt"
    
    if [[ ! -f "$whitelist_file" ]]; then
        # Créer la liste blanche par défaut
        cat > "$whitelist_file" << 'EOF'
# Liste Blanche d'Applications Approuvées MacFleet
Safari
Google Chrome
Microsoft Word
Microsoft Excel
Microsoft PowerPoint
Slack
Microsoft Teams
Zoom
Finder
Préférences Système
Moniteur d'Activité
Terminal
TextEdit
Calculatrice
Calendrier
Contacts
Mail
Notes
Rappels
Plans
App Store
com.apple.Safari
com.google.Chrome
com.microsoft.Word
com.microsoft.Excel
com.microsoft.PowerPoint
com.tinyspeck.slackmacgap
com.microsoft.teams
us.zoom.xos
com.apple.finder
com.apple.systempreferences
EOF
    fi
    
    # Vérifier si l'app est dans la liste blanche
    if grep -qi "^$app_identifier$" "$whitelist_file" 2>/dev/null; then
        return 0
    fi
    
    return 1
}

# Application des heures de bureau
check_business_hours() {
    local app_identifier="$1"
    
    if [[ "$BUSINESS_HOURS_ENFORCEMENT" != "true" ]]; then
        return 0
    fi
    
    local current_hour
    current_hour=$(date +%H)
    local current_day
    current_day=$(date +%u)  # 1=Lundi, 7=Dimanche
    
    # Heures de bureau : 9h à 18h, lundi à vendredi
    if [[ $current_day -ge 1 && $current_day -le 5 ]]; then
        if [[ $current_hour -ge 9 && $current_hour -lt 18 ]]; then
            # Pendant les heures de bureau - vérifier les apps restreintes
            local restricted_apps=(
                "Steam"
                "Epic Games"
                "Discord"
                "Spotify"
                "Netflix"
                "YouTube"
            )
            
            for restricted_app in "${restricted_apps[@]}"; do
                if [[ "$app_identifier" == *"$restricted_app"* ]]; then
                    echo "⚠️  Application restreinte pendant les heures de bureau : $app_identifier"
                    audit_log "BUSINESS_HOURS" "$app_identifier" "RESTRICTED" "Application bloquée pendant les heures de bureau"
                    return 1
                fi
            done
        fi
    fi
    
    return 0
}

# Vérifier les dépendances d'applications
check_dependencies() {
    local app_identifier="$1"
    
    if [[ "$DEPENDENCY_CHECKING" != "true" ]]; then
        return 0
    fi
    
    echo "=== Vérification des Dépendances ==="
    
    # Dépendances connues
    case "$app_identifier" in
        *"Microsoft"*)
            if ! check_microsoft_office_license; then
                echo "❌ Licence Microsoft Office requise"
                return 1
            fi
            ;;
        *"Adobe"*)
            if ! check_adobe_license; then
                echo "❌ Licence Adobe requise"
                return 1
            fi
            ;;
        *"Xcode"*)
            if ! check_developer_tools; then
                echo "❌ Outils de développement requis"
                return 1
            fi
            ;;
    esac
    
    return 0
}

# Lancement d'application entreprise avec gestion complète
enterprise_app_launch() {
    local app_identifier="$1"
    local options="${2:-}"
    local deployment_mode="${3:-standard}"
    
    echo "=== Lancement d'Application Entreprise ==="
    
    if [[ -z "$app_identifier" ]]; then
        echo "❌ Identifiant d'application requis"
        return 1
    fi
    
    log_action "LANCEMENT APP ENTREPRISE : Début du lancement de $app_identifier"
    
    # Validation pré-lancement
    if ! validate_application "$app_identifier"; then
        log_action "ÉCHEC : Validation d'application échouée pour $app_identifier"
        return 1
    fi
    
    # Vérification des heures de bureau
    if ! check_business_hours "$app_identifier"; then
        log_action "BLOQUÉ : Restriction d'heures de bureau pour $app_identifier"
        return 1
    fi
    
    # Vérification des dépendances
    if ! check_dependencies "$app_identifier"; then
        log_action "ÉCHEC : Vérification des dépendances échouée pour $app_identifier"
        return 1
    fi
    
    # Vérification de disponibilité des ressources
    check_system_resources
    
    # Lancer l'application selon le type
    local launch_command=""
    local launch_result=""
    
    case "$deployment_mode" in
        "background"|"arriere-plan")
            launch_command="open -g"
            ;;
        "hidden"|"cache")
            launch_command="open -j"
            ;;
        "new-instance"|"nouvelle-instance")
            launch_command="open -n"
            ;;
        *)
            launch_command="open"
            ;;
    esac
    
    # Déterminer la méthode de lancement
    if [[ "$app_identifier" == com.* ]]; then
        # Identifiant bundle
        echo "🚀 Lancement par identifiant bundle : $app_identifier"
        audit_log "LAUNCH" "$app_identifier" "BUNDLE_ID" "Lancement par identifiant bundle"
        
        timeout "$LAUNCH_TIMEOUT" $launch_command -b "$app_identifier" $options 2>/dev/null
        launch_result=$?
    else
        # Nom d'application
        echo "🚀 Lancement par nom d'application : $app_identifier"
        audit_log "LAUNCH" "$app_identifier" "APP_NAME" "Lancement par nom d'application"
        
        timeout "$LAUNCH_TIMEOUT" $launch_command -a "$app_identifier" $options 2>/dev/null
        launch_result=$?
    fi
    
    # Gérer le résultat du lancement
    if [[ $launch_result -eq 0 ]]; then
        echo "✅ Application lancée avec succès : $app_identifier"
        post_launch_monitoring "$app_identifier"
        audit_log "LAUNCH" "$app_identifier" "SUCCESS" "Application lancée avec succès"
        
        # Mettre à jour les statistiques d'utilisation
        update_usage_stats "$app_identifier"
        
        log_action "SUCCÈS : Application lancée avec succès : $app_identifier"
        return 0
    else
        echo "❌ Échec du lancement d'application : $app_identifier"
        
        # Logique de nouvelle tentative
        if [[ "$RETRY_ATTEMPTS" -gt 0 ]]; then
            echo "🔄 Nouvelle tentative de lancement..."
            retry_launch "$app_identifier" "$options" "$deployment_mode"
        fi
        
        audit_log "LAUNCH" "$app_identifier" "FAILED" "Lancement d'application échoué"
        log_action "ÉCHEC : Impossible de lancer l'application : $app_identifier"
        return 1
    fi
}

# Surveillance post-lancement
post_launch_monitoring() {
    local app_identifier="$1"
    
    echo "=== Surveillance Post-Lancement ==="
    
    sleep 5  # Permettre à l'app de se lancer complètement
    
    # Vérifier si l'application fonctionne
    if pgrep -f "$app_identifier" >/dev/null 2>&1; then
        echo "✅ Application en cours d'exécution : $app_identifier"
        
        # Surveiller l'utilisation des ressources
        local pid
        pid=$(pgrep -f "$app_identifier" | head -1)
        
        if [[ -n "$pid" ]]; then
            local cpu_usage
            local mem_usage
            cpu_usage=$(ps -p "$pid" -o pcpu= | xargs)
            mem_usage=$(ps -p "$pid" -o pmem= | xargs)
            
            echo "📊 Utilisation des ressources - CPU : ${cpu_usage}%, Mémoire : ${mem_usage}%"
            log_action "Surveillance post-lancement : $app_identifier (PID : $pid, CPU : $cpu_usage%, Mém : $mem_usage%)"
        fi
    else
        echo "⚠️  Application non détectée comme en cours d'exécution : $app_identifier"
        log_action "AVERTISSEMENT : Lancement d'application réussi mais non détectée en cours d'exécution : $app_identifier"
    fi
}

# Déploiement d'applications en masse
bulk_app_deployment() {
    local app_list_file="$1"
    local deployment_mode="${2:-standard}"
    
    echo "=== Déploiement d'Applications en Masse ==="
    
    if [[ ! -f "$app_list_file" ]]; then
        echo "❌ Fichier de liste d'applications introuvable : $app_list_file"
        return 1
    fi
    
    local success_count=0
    local failure_count=0
    local total_count=0
    
    while IFS= read -r app_line; do
        # Ignorer les lignes vides et les commentaires
        if [[ -z "$app_line" || "$app_line" == \#* ]]; then
            continue
        fi
        
        # Analyser l'identifiant d'app et les options
        local app_identifier
        local app_options
        read -r app_identifier app_options <<< "$app_line"
        
        ((total_count++))
        echo "Déploiement ($total_count) : $app_identifier"
        
        if enterprise_app_launch "$app_identifier" "$app_options" "$deployment_mode"; then
            ((success_count++))
        else
            ((failure_count++))
        fi
        
        echo "---"
        sleep 2  # Brève pause entre les déploiements
    done < "$app_list_file"
    
    echo "=== Résumé de Déploiement en Masse ==="
    echo "Total des applications : $total_count"
    echo "Lancements réussis : $success_count"
    echo "Lancements échoués : $failure_count"
    
    log_action "Déploiement en masse terminé : $success_count/$total_count réussis"
    audit_log "BULK_DEPLOYMENT" "MULTIPLE" "COMPLETED" "Succès : $success_count Échecs : $failure_count Total : $total_count"
}

# Gestion du cycle de vie des applications
manage_app_lifecycle() {
    local action="$1"
    local app_identifier="$2"
    
    case "$action" in
        "install"|"installer")
            install_application "$app_identifier"
            ;;
        "update"|"mettre-a-jour")
            update_application "$app_identifier"
            ;;
        "remove"|"supprimer")
            remove_application "$app_identifier"
            ;;
        "status"|"statut")
            check_app_status "$app_identifier"
            ;;
        *)
            echo "Action de cycle de vie invalide : $action"
            return 1
            ;;
    esac
}

# Vérifier les ressources système avant le lancement
check_system_resources() {
    echo "=== Vérification des Ressources Système ==="
    
    # Vérifier la mémoire disponible
    local available_memory
    available_memory=$(vm_stat | grep "Pages free" | awk '{print $3}' | tr -d '.')
    available_memory=$((available_memory * 4096 / 1024 / 1024))  # Convertir en MB
    
    echo "Mémoire Disponible : ${available_memory}MB"
    
    if [[ $available_memory -lt 512 ]]; then
        echo "⚠️  Avertissement mémoire faible : ${available_memory}MB disponible"
        log_action "Avertissement mémoire faible pendant le lancement d'app : ${available_memory}MB"
    fi
    
    # Vérifier la charge CPU
    local load_average
    load_average=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | tr -d ',')
    
    echo "Charge Système : $load_average"
    
    if (( $(echo "$load_average > 5.0" | bc -l) )); then
        echo "⚠️  Charge système élevée : $load_average"
        log_action "Charge système élevée pendant le lancement d'app : $load_average"
    fi
}

# Générer un rapport de déploiement d'applications
generate_deployment_report() {
    local report_file="/Library/MacFleet/Reports/app_deployment_report_$(date +%Y%m%d_%H%M%S).json"
    
    echo "=== Génération du Rapport de Déploiement d'Applications ==="
    
    mkdir -p "$(dirname "$report_file")"
    
    # Compter les déploiements récents du journal d'audit
    local recent_launches=0
    local successful_launches=0
    local failed_launches=0
    
    if [[ -f "$DEPLOYMENT_LOG" ]]; then
        recent_launches=$(grep -c "ACTION:LAUNCH" "$DEPLOYMENT_LOG" 2>/dev/null || echo 0)
        successful_launches=$(grep -c "ACTION:LAUNCH.*RESULT:SUCCESS" "$DEPLOYMENT_LOG" 2>/dev/null || echo 0)
        failed_launches=$(grep -c "ACTION:LAUNCH.*RESULT:FAILED" "$DEPLOYMENT_LOG" 2>/dev/null || echo 0)
    fi
    
    # Obtenir les informations système
    local total_apps
    total_apps=$(find /Applications -name "*.app" -maxdepth 1 | wc -l)
    
    cat > "$report_file" << EOF
{
  "report_type": "application_deployment",
  "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)"
  },
  "deployment_statistics": {
    "total_applications": $total_apps,
    "recent_launches": $recent_launches,
    "successful_launches": $successful_launches,
    "failed_launches": $failed_launches,
    "success_rate": "$(echo "scale=2; $successful_launches * 100 / ($successful_launches + $failed_launches + 1)" | bc)%"
  },
  "policy_configuration": {
    "whitelist_enforcement": $WHITELIST_ENFORCEMENT,
    "signature_validation": $VALIDATE_APP_SIGNATURES,
    "business_hours_enforcement": $BUSINESS_HOURS_ENFORCEMENT,
    "dependency_checking": $DEPENDENCY_CHECKING
  },
  "security_status": {
    "quarantine_protection": $QUARANTINE_UNKNOWN_APPS,
    "malware_scanning": $MALWARE_SCANNING,
    "certificate_validation": $CERTIFICATE_VALIDATION,
    "audit_enabled": $AUDIT_ALL_LAUNCHES
  }
}
EOF
    
    echo "Rapport de déploiement d'applications sauvegardé dans : $report_file"
    log_action "Rapport de déploiement généré : $report_file"
}

# Fonction principale avec gestion d'arguments
main() {
    log_action "=== Outil de Gestion de Lancement d'Applications MacFleet Démarré ==="
    
    case "${1:-help}" in
        "launch"|"lancer")
            enterprise_app_launch "$2" "$3" "$4"
            ;;
        "bulk"|"masse")
            bulk_app_deployment "$2" "$3"
            ;;
        "lifecycle"|"cycle-de-vie")
            manage_app_lifecycle "$2" "$3"
            ;;
        "report"|"rapport")
            generate_deployment_report
            ;;
        *)
            echo "Outil de Gestion de Lancement d'Applications Entreprise MacFleet"
            echo "Usage : $0 [commande] [options]"
            echo ""
            echo "Commandes :"
            echo "  launch [nom_app/bundle_id] [options] [mode]  - Lancer application avec contrôles entreprise"
            echo "  bulk [fichier_liste] [mode]                 - Déploiement en masse depuis fichier liste"
            echo "  lifecycle [action] [app_identifier]         - Gérer le cycle de vie d'application"
            echo "  report                                      - Générer rapport de déploiement"
            echo ""
            echo "Modes de Lancement :"
            echo "  standard        - Lancement d'application normal"
            echo "  background      - Lancement en arrière-plan"
            echo "  hidden          - Lancement caché"
            echo "  new-instance    - Forcer nouvelle instance"
            echo ""
            echo "Exemples :"
            echo "  $0 launch \"Safari\"                         - Lancer Safari normalement"
            echo "  $0 launch com.apple.Safari \"\" background   - Lancer Safari en arrière-plan"
            echo "  $0 bulk essential_apps.txt                  - Déployer apps depuis liste"
            echo "  $0 lifecycle status \"Microsoft Word\"       - Vérifier statut d'installation Word"
            ;;
    esac
    
    log_action "=== Opération de gestion de lancement d'applications terminée ==="
}

# Exécuter la fonction principale
main "$@"

Notes de Configuration Importantes

Commandes de Lancement d'Applications macOS

  • open -a - Lancer par nom d'application
  • open -b - Lancer par identifiant bundle
  • open -g - Lancer en arrière-plan
  • open -n - Ouvrir nouvelle instance
  • open -j - Lancer caché

Points d'Intégration Entreprise

  • Gestion de Catalogue d'Applications - Répertoire d'applications centralisé
  • Systèmes de Gestion de Licences - Validation automatisée de licences
  • Plateformes de Sécurité - Intégration avec protection des terminaux
  • Surveillance de Performance - Intégration de plateforme APM

Bonnes Pratiques pour le Lancement d'Apps Entreprise

  1. Sécurité et Validation

    • Valider les signatures de code avant le lancement
    • Appliquer les listes blanches d'applications
    • Vérifier les malwares et le statut de quarantaine
    • Surveiller les comportements suspects
  2. Expérience Utilisateur

    • Fournir un retour clair sur le statut de lancement
    • Implémenter le pré-chargement intelligent
    • Gérer les échecs avec élégance
    • Supporter les scénarios hors ligne
  3. Performance et Ressources

    • Surveiller les ressources système avant le lancement
    • Implémenter l'équilibrage de charge pour les apps gourmandes en ressources
    • Suivre les métriques de performance d'applications
    • Optimiser les séquences de lancement

N'oubliez pas de tester minutieusement les procédures de lancement d'applications dans un environnement contrôlé avant l'implémentation sur l'ensemble de votre MacFleet pour assurer la stabilité système et la productivité utilisateur.

Gestion des Langues d'Applications sur macOS

Gérez les paramètres de langue spécifiques aux applications sur vos appareils MacFleet à l'aide d'outils en ligne de commande. Ce tutoriel couvre la découverte de langues, la configuration d'applications individuelles, la gestion en bloc et l'application de politiques de langue à l'échelle de l'entreprise pour les déploiements internationaux.

Comprendre la Gestion des Langues sur macOS

macOS permet de définir différentes langues pour les applications individuelles sans changer la langue du système :

Niveaux de Configuration de Langue

  • Langue Système - Langue globale de l'interface macOS
  • Langue Application - Remplacement de langue par application
  • Paramètres Régionaux - Formats de date, heure, nombres, devises
  • Saisie Clavier - Préférences de méthode et disposition de saisie

Outils de Gestion des Langues

  • defaults - Modifier les préférences d'applications et paramètres de langue
  • languagesetup - Afficher les langues système disponibles
  • locale - Afficher les informations de locale actuelles
  • Préférences Système - Interface de gestion des langues en mode graphique

Découverte et Analyse des Langues

Découvrir les Langues Disponibles

#!/bin/bash

# Afficher toutes les langues supportées par l'appareil
echo "🌍 Découverte des langues disponibles sur l'appareil..."

# Obtenir les langues système supportées
echo "=== Langues Système Supportées ==="
languagesetup

echo -e "\n=== Informations de Locale Actuelles ==="
locale

echo -e "\n=== Codes de Langue Disponibles ==="
ls /System/Library/CoreServices/SystemUIServer.app/Contents/Resources/*.lproj | \
    sed 's/.*\/\(.*\)\.lproj/\1/' | sort

echo "✅ Découverte des langues terminée avec succès"

Obtenir la Langue Système Actuelle

#!/bin/bash

# Afficher les paramètres de langue système et d'applications actuels
echo "🔍 Analyse de la configuration de langue actuelle..."

# Langue système
echo "Langue Système : $(defaults read -g AppleLanguages | head -1 | tr -d '[:space:],"()')"

# Paramètres régionaux
echo "Région : $(defaults read -g AppleLocale)"

# Utilisateur actuel
CURRENT_USER=$(stat -f%Su /dev/console)
echo "Utilisateur Actuel : $CURRENT_USER"

# Sources de saisie
echo -e "\nSources de Saisie :"
defaults read com.apple.HIToolbox AppleEnabledInputSources | grep -E 'InputSourceKind|KeyboardLayout Name'

echo "✅ Analyse de langue terminée"

Gestion des Langues d'Applications Individuelles

Définir la Langue d'Application

#!/bin/bash

# Définir la langue pour une application spécifique
APP_NAME="$1"
LANGUAGE_CODE="$2"

if [[ -z "$APP_NAME" || -z "$LANGUAGE_CODE" ]]; then
    echo "❌ Utilisation : $0 <NomApp> <CodeLangue>"
    echo "Exemple : $0 Calendar de"
    echo "Exemple : $0 Safari fr"
    exit 1
fi

# Obtenir l'utilisateur actuel
CURRENT_USER=$(stat -f%Su /dev/console)
echo "🌐 Définition de la langue pour $APP_NAME en $LANGUAGE_CODE"

# Valider le code de langue
if ! languagesetup | grep -q "$LANGUAGE_CODE"; then
    echo "⚠️ Attention : Le code de langue '$LANGUAGE_CODE' pourrait ne pas être supporté"
fi

# Définir la langue de l'application
if sudo su "$CURRENT_USER" -c "defaults write -app '$APP_NAME' AppleLanguages -array '$LANGUAGE_CODE'"; then
    echo "✅ Langue définie avec succès pour $APP_NAME"
    echo "ℹ️ Redémarrez $APP_NAME pour voir les changements"
else
    echo "❌ Échec de définition de langue pour $APP_NAME"
    exit 1
fi

Vérifier la Langue d'Application

#!/bin/bash

# Vérifier le paramètre de langue actuel pour une application
APP_NAME="$1"

if [[ -z "$APP_NAME" ]]; then
    echo "❌ Utilisation : $0 <NomApp>"
    echo "Exemple : $0 Calendar"
    exit 1
fi

CURRENT_USER=$(stat -f%Su /dev/console)
echo "🔍 Vérification du paramètre de langue pour $APP_NAME..."

# Lire le paramètre de langue actuel
CURRENT_LANG=$(sudo su "$CURRENT_USER" -c "defaults read -app '$APP_NAME' AppleLanguages 2>/dev/null" | head -1 | tr -d '[:space:],"()')

if [[ -n "$CURRENT_LANG" ]]; then
    echo "Langue actuelle pour $APP_NAME : $CURRENT_LANG"
else
    echo "Aucune langue spécifique définie pour $APP_NAME (utilise la langue système par défaut)"
fi

Réinitialiser la Langue d'Application

#!/bin/bash

# Réinitialiser la langue d'application à la langue système par défaut
APP_NAME="$1"

if [[ -z "$APP_NAME" ]]; then
    echo "❌ Utilisation : $0 <NomApp>"
    exit 1
fi

CURRENT_USER=$(stat -f%Su /dev/console)
echo "🔄 Réinitialisation de la langue pour $APP_NAME à la langue système par défaut..."

# Supprimer le paramètre de langue spécifique à l'application
if sudo su "$CURRENT_USER" -c "defaults delete -app '$APP_NAME' AppleLanguages 2>/dev/null"; then
    echo "✅ Langue réinitialisée avec succès pour $APP_NAME"
else
    echo "ℹ️ Aucun paramètre de langue personnalisé trouvé pour $APP_NAME"
fi

Gestion de Langues en Bloc

Définir Plusieurs Applications

#!/bin/bash

# Définir la langue pour plusieurs applications
LANGUAGE_CODE="$1"
shift
APPS=("$@")

if [[ -z "$LANGUAGE_CODE" || ${#APPS[@]} -eq 0 ]]; then
    echo "❌ Utilisation : $0 <CodeLangue> <App1> <App2> <App3>..."
    echo "Exemple : $0 fr Safari Calendar Mail"
    exit 1
fi

CURRENT_USER=$(stat -f%Su /dev/console)
echo "🌐 Définition de la langue '$LANGUAGE_CODE' pour ${#APPS[@]} applications..."

SUCCESS_COUNT=0
FAILED_APPS=()

for app in "${APPS[@]}"; do
    echo "Traitement : $app"
    
    if sudo su "$CURRENT_USER" -c "defaults write -app '$app' AppleLanguages -array '$LANGUAGE_CODE'" 2>/dev/null; then
        echo "  ✅ Succès : $app"
        ((SUCCESS_COUNT++))
    else
        echo "  ❌ Échec : $app"
        FAILED_APPS+=("$app")
    fi
done

echo -e "\n📊 Résumé :"
echo "Configurées avec succès : $SUCCESS_COUNT applications"
echo "Échecs : ${#FAILED_APPS[@]} applications"

if [[ ${#FAILED_APPS[@]} -gt 0 ]]; then
    echo "Applications en échec : ${FAILED_APPS[*]}"
fi

Configuration d'Applications Communes

#!/bin/bash

# Définir la langue pour les applications macOS communes
LANGUAGE_CODE="$1"

if [[ -z "$LANGUAGE_CODE" ]]; then
    echo "❌ Utilisation : $0 <CodeLangue>"
    echo "Codes disponibles : en, fr, de, es, it, ja, ko, zh-Hans, zh-Hant, etc."
    exit 1
fi

CURRENT_USER=$(stat -f%Su /dev/console)
echo "🌍 Configuration des applications communes pour la langue : $LANGUAGE_CODE"

# Définir les applications communes
COMMON_APPS=(
    "Safari"
    "Mail"
    "Calendar"
    "Contacts"
    "Notes"
    "Reminders"
    "Pages"
    "Numbers"
    "Keynote"
    "TextEdit"
    "Preview"
    "Finder"
    "System Preferences"
)

SUCCESS_COUNT=0
CONFIGURED_APPS=()

echo "Configuration de ${#COMMON_APPS[@]} applications communes..."

for app in "${COMMON_APPS[@]}"; do
    echo -n "  $app : "
    
    if sudo su "$CURRENT_USER" -c "defaults write -app '$app' AppleLanguages -array '$LANGUAGE_CODE'" 2>/dev/null; then
        echo "✅"
        CONFIGURED_APPS+=("$app")
        ((SUCCESS_COUNT++))
    else
        echo "❌"
    fi
done

echo -e "\n📊 Résumé de Configuration :"
echo "Configurées avec succès : $SUCCESS_COUNT applications"
echo "Applications configurées : ${CONFIGURED_APPS[*]}"

Script de Gestion de Langues d'Entreprise

#!/bin/bash

# Outil de Gestion des Langues d'Applications MacFleet
# Gestion complète des langues pour les environnements d'entreprise

# Configuration
LOG_FILE="/var/log/macfleet_language.log"
BACKUP_DIR="/var/backups/macfleet/language"
REPORT_DIR="/var/reports/macfleet/language"
CONFIG_FILE="/etc/macfleet/language_policy.conf"

# Paramètres de politique de langue par défaut
DEFAULT_LANGUAGE="en"
ENFORCE_LANGUAGE_POLICY=false
ALLOWED_LANGUAGES=("en" "fr" "de" "es" "it" "ja" "ko" "zh-Hans" "zh-Hant")
RESTRICTED_APPS=()

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

# Configurer les répertoires
setup_directories() {
    for dir in "$BACKUP_DIR" "$REPORT_DIR" "$(dirname "$CONFIG_FILE")"; do
        if [[ ! -d "$dir" ]]; then
            sudo mkdir -p "$dir"
            log_action "Répertoire créé : $dir"
        fi
    done
}

# Obtenir l'utilisateur actuel
get_current_user() {
    stat -f%Su /dev/console
}

# Découvrir les langues disponibles
discover_languages() {
    echo "🌍 Découverte des langues disponibles..."
    
    {
        echo "=== Découverte des Langues Système ==="
        echo "Date : $(date)"
        echo "Appareil : $(hostname)"
        echo ""
        
        echo "=== Langues Disponibles ==="
        languagesetup
        echo ""
        
        echo "=== Paramètres Système Actuels ==="
        echo "Langue Système : $(defaults read -g AppleLanguages | head -1 | tr -d '[:space:],"()')"
        echo "Région : $(defaults read -g AppleLocale)"
        echo "Devise : $(defaults read -g AppleCurrencies | head -1 | tr -d '[:space:],"()')"
        echo ""
        
        echo "=== Répertoires de Ressources de Langues ==="
        find /System/Library/CoreServices -name "*.lproj" -type d | head -20
        
    } > "$REPORT_DIR/decouverte_langues_$(date +%Y%m%d_%H%M%S).txt"
    
    log_action "Découverte des langues terminée"
}

# Sauvegarder les paramètres de langue actuels
backup_language_settings() {
    local backup_file="$BACKUP_DIR/sauvegarde_langue_$(date +%Y%m%d_%H%M%S).txt"
    local current_user=$(get_current_user)
    
    echo "💾 Sauvegarde des paramètres de langue actuels..."
    
    {
        echo "=== Sauvegarde des Paramètres de Langue MacFleet ==="
        echo "Date : $(date)"
        echo "Utilisateur : $current_user"
        echo "Appareil : $(hostname)"
        echo ""
        
        echo "=== Paramètres de Langue Système ==="
        echo "AppleLanguages : $(defaults read -g AppleLanguages 2>/dev/null || echo 'Non défini')"
        echo "AppleLocale : $(defaults read -g AppleLocale 2>/dev/null || echo 'Non défini')"
        echo ""
        
        echo "=== Langues Spécifiques aux Applications ==="
        
        # Obtenir la liste des applications avec paramètres de langue personnalisés
        local apps_with_custom_lang=()
        
        # Vérifier les applications communes
        local check_apps=(
            "Safari" "Mail" "Calendar" "Contacts" "Notes" "Reminders"
            "Pages" "Numbers" "Keynote" "TextEdit" "Preview" "Finder"
            "System Preferences" "Photos" "Music" "TV" "Books"
        )
        
        for app in "${check_apps[@]}"; do
            local app_lang
            app_lang=$(sudo su "$current_user" -c "defaults read -app '$app' AppleLanguages 2>/dev/null" | head -1 | tr -d '[:space:],"()')
            if [[ -n "$app_lang" ]]; then
                echo "$app : $app_lang"
                apps_with_custom_lang+=("$app")
            fi
        done
        
        if [[ ${#apps_with_custom_lang[@]} -eq 0 ]]; then
            echo "Aucune application avec paramètres de langue personnalisés trouvée"
        fi
        
    } > "$backup_file"
    
    echo "✅ Sauvegarde enregistrée dans : $backup_file"
    log_action "Paramètres de langue sauvegardés dans : $backup_file"
}

# Générer un rapport d'audit de langue
generate_language_report() {
    local report_file="$REPORT_DIR/audit_langue_$(date +%Y%m%d_%H%M%S).txt"
    local current_user=$(get_current_user)
    
    echo "📊 Génération du rapport d'audit de langue..."
    
    {
        echo "Rapport d'Audit de Langue MacFleet"
        echo "Généré : $(date)"
        echo "Appareil : $(hostname)"
        echo "Utilisateur : $current_user"
        echo "================================="
        echo ""
        
        echo "=== Configuration de Langue Système ==="
        local sys_lang=$(defaults read -g AppleLanguages | head -1 | tr -d '[:space:],"()')
        echo "Langue Principale : $sys_lang"
        echo "Région : $(defaults read -g AppleLocale)"
        echo "Devise : $(defaults read -g AppleCurrencies | head -1 | tr -d '[:space:],"()')"
        echo ""
        
        echo "=== Langues Disponibles ==="
        languagesetup | grep -v "Language setup tool" | head -20
        echo ""
        
        echo "=== Audit des Langues d'Applications ==="
        local total_apps=0
        local custom_lang_apps=0
        
        # Auditer les applications communes
        local audit_apps=(
            "Safari" "Mail" "Calendar" "Contacts" "Notes" "Reminders"
            "Pages" "Numbers" "Keynote" "TextEdit" "Preview" "Finder"
            "System Preferences" "Photos" "Music" "TV" "Books" "Maps"
            "FaceTime" "Messages" "Calculator" "Clock mini" "Dictionary"
        )
        
        for app in "${audit_apps[@]}"; do
            ((total_apps++))
            local app_lang
            app_lang=$(sudo su "$current_user" -c "defaults read -app '$app' AppleLanguages 2>/dev/null" | head -1 | tr -d '[:space:],"()')
            
            if [[ -n "$app_lang" ]]; then
                echo "$app : $app_lang (personnalisé)"
                ((custom_lang_apps++))
            else
                echo "$app : $sys_lang (système par défaut)"
            fi
        done
        
        echo ""
        echo "=== Résumé ==="
        echo "Total d'applications auditées : $total_apps"
        echo "Applications avec langue personnalisée : $custom_lang_apps"
        echo "Applications utilisant la langue système par défaut : $((total_apps - custom_lang_apps))"
        
        # Vérifier la conformité
        if [[ "$ENFORCE_LANGUAGE_POLICY" == "true" ]]; then
            echo ""
            echo "=== Conformité aux Politiques ==="
            echo "Application de politique de langue : ACTIVÉE"
            echo "Langue requise par défaut : $DEFAULT_LANGUAGE"
            echo "Langues autorisées : ${ALLOWED_LANGUAGES[*]}"
            
            local non_compliant_apps=()
            for app in "${audit_apps[@]}"; do
                local app_lang
                app_lang=$(sudo su "$current_user" -c "defaults read -app '$app' AppleLanguages 2>/dev/null" | head -1 | tr -d '[:space:],"()')
                
                if [[ -n "$app_lang" ]]; then
                    # Vérifier si la langue est dans la liste autorisée
                    local is_allowed=false
                    for allowed_lang in "${ALLOWED_LANGUAGES[@]}"; do
                        if [[ "$app_lang" == "$allowed_lang" ]]; then
                            is_allowed=true
                            break
                        fi
                    done
                    
                    if [[ "$is_allowed" == "false" ]]; then
                        non_compliant_apps+=("$app:$app_lang")
                    fi
                fi
            done
            
            if [[ ${#non_compliant_apps[@]} -gt 0 ]]; then
                echo "Applications non conformes :"
                for app_info in "${non_compliant_apps[@]}"; do
                    echo "  - ${app_info//:/ (langue: }")"
                done
            else
                echo "Toutes les applications sont conformes à la politique de langue"
            fi
        fi
        
    } > "$report_file"
    
    echo "📊 Rapport sauvegardé dans : $report_file"
    log_action "Rapport d'audit de langue généré : $report_file"
}

# Appliquer la politique de langue
enforce_language_policy() {
    local target_language="${1:-$DEFAULT_LANGUAGE}"
    local current_user=$(get_current_user)
    
    echo "🔒 Application de la politique de langue : $target_language"
    log_action "Application de politique de langue démarrée : $target_language"
    
    # Valider la langue cible
    local is_allowed=false
    for allowed_lang in "${ALLOWED_LANGUAGES[@]}"; do
        if [[ "$target_language" == "$allowed_lang" ]]; then
            is_allowed=true
            break
        fi
    done
    
    if [[ "$is_allowed" == "false" ]]; then
        echo "❌ La langue '$target_language' n'est pas dans la liste des langues autorisées"
        echo "Langues autorisées : ${ALLOWED_LANGUAGES[*]}"
        return 1
    fi
    
    # Applications sur lesquelles appliquer la politique
    local enforce_apps=(
        "Safari" "Mail" "Calendar" "Contacts" "Notes" "Reminders"
        "Pages" "Numbers" "Keynote" "TextEdit" "Preview"
        "System Preferences" "Photos" "Music" "TV" "Books"
    )
    
    local success_count=0
    local failed_apps=()
    
    for app in "${enforce_apps[@]}"; do
        # Ignorer les applications restreintes
        local is_restricted=false
        for restricted_app in "${RESTRICTED_APPS[@]}"; do
            if [[ "$app" == "$restricted_app" ]]; then
                is_restricted=true
                break
            fi
        done
        
        if [[ "$is_restricted" == "true" ]]; then
            echo "  Ignorer l'application restreinte : $app"
            continue
        fi
        
        echo -n "  Configuration $app : "
        
        if sudo su "$current_user" -c "defaults write -app '$app' AppleLanguages -array '$target_language'" 2>/dev/null; then
            echo "✅"
            ((success_count++))
        else
            echo "❌"
            failed_apps+=("$app")
        fi
    done
    
    echo -e "\n📊 Résumé d'Application de Politique :"
    echo "Configurées avec succès : $success_count applications"
    echo "Échecs : ${#failed_apps[@]} applications"
    
    if [[ ${#failed_apps[@]} -gt 0 ]]; then
        echo "Applications en échec : ${failed_apps[*]}"
    fi
    
    log_action "Application de politique de langue terminée : $success_count succès, ${#failed_apps[@]} échecs"
}

# Fonction d'exécution principale
main() {
    local action="${1:-aide}"
    local target="${2:-}"
    local apps_or_backup=("${@:3}")
    
    log_action "=== Gestion des Langues MacFleet Démarrée ==="
    
    setup_directories
    
    case "$action" in
        "decouvrir"|"langues")
            discover_languages
            ;;
        "audit"|"rapport")
            generate_language_report
            ;;
        "sauvegarder")
            backup_language_settings
            ;;
        "definir")
            if [[ -n "$target" && ${#apps_or_backup[@]} -gt 0 ]]; then
                # Définir la langue pour des applications spécifiques
                local language_code="$target"
                local apps=("${apps_or_backup[@]}")
                local current_user=$(get_current_user)
                
                echo "🌐 Définition de la langue '$language_code' pour les applications spécifiées..."
                
                local success_count=0
                for app in "${apps[@]}"; do
                    echo -n "  $app : "
                    if sudo su "$current_user" -c "defaults write -app '$app' AppleLanguages -array '$language_code'" 2>/dev/null; then
                        echo ""
                        ((success_count++))
                    else
                        echo ""
                    fi
                done
                
                echo "✅ $success_count applications configurées"
            else
                echo "❌ Utilisation : $0 definir <code_langue> <app1> <app2> ..."
                echo "Exemple : $0 definir fr Safari Mail Calendar"
            fi
            ;;
        "appliquer")
            local target_lang="${target:-$DEFAULT_LANGUAGE}"
            enforce_language_policy "$target_lang"
            ;;
        "reinitialiser")
            if [[ -n "$target" ]]; then
                local current_user=$(get_current_user)
                echo "🔄 Réinitialisation de la langue pour $target à la langue système par défaut..."
                
                if sudo su "$current_user" -c "defaults delete -app '$target' AppleLanguages" 2>/dev/null; then
                    echo "✅ Langue réinitialisée pour $target"
                else
                    echo "ℹ️ Aucun paramètre de langue personnalisé trouvé pour $target"
                fi
            else
                echo "❌ Utilisation : $0 reinitialiser <nom_app>"
            fi
            ;;
        "verifier")
            if [[ -n "$target" ]]; then
                local current_user=$(get_current_user)
                local app_lang
                app_lang=$(sudo su "$current_user" -c "defaults read -app '$target' AppleLanguages 2>/dev/null" | head -1 | tr -d '[:space:],"()')
                
                if [[ -n "$app_lang" ]]; then
                    echo "Langue pour $target : $app_lang"
                else
                    echo "Aucune langue personnalisée définie pour $target (utilise la langue système par défaut)"
                fi
            else
                echo "❌ Utilisation : $0 verifier <nom_app>"
            fi
            ;;
        "aide"|*)
            echo "Outil de Gestion des Langues d'Applications MacFleet"
            echo "Utilisation : $0 [action] [options]"
            echo ""
            echo "Actions :"
            echo "  decouvrir             - Découvrir les langues disponibles sur l'appareil"
            echo "  audit                 - Générer un rapport d'audit de langue"
            echo "  sauvegarder           - Sauvegarder les paramètres de langue actuels"
            echo "  definir <lang> <apps...> - Définir la langue pour des applications spécifiques"
            echo "  appliquer [langue]    - Appliquer la politique de langue sur les applications"
            echo "  reinitialiser <app>   - Réinitialiser l'application à la langue système par défaut"
            echo "  verifier <app>        - Vérifier le paramètre de langue actuel pour l'application"
            echo "  aide                  - Afficher ce message d'aide"
            echo ""
            echo "Codes de Langue :"
            echo "  en        - Anglais"
            echo "  fr        - Français"
            echo "  de        - Allemand"
            echo "  es        - Espagnol"
            echo "  it        - Italien"
            echo "  ja        - Japonais"
            echo "  ko        - Coréen"
            echo "  zh-Hans   - Chinois (Simplifié)"
            echo "  zh-Hant   - Chinois (Traditionnel)"
            ;;
    esac
    
    log_action "=== Gestion des Langues MacFleet Terminée ==="
}

# Exécuter la fonction principale
main "$@"

Référence des Codes de Langue

Codes de Langue Courants

LangueCodeRégion
AnglaisenInternational
FrançaisfrFrance
AllemanddeAllemagne
EspagnolesEspagne
ItalienitItalie
PortugaisptPortugal
Portugais (Brésil)pt-BRBrésil
JaponaisjaJapon
CoréenkoCorée
Chinois (Simplifié)zh-HansChine
Chinois (Traditionnel)zh-HantTaiwan/Hong Kong
RusseruRussie
ArabearMoyen-Orient
NéerlandaisnlPays-Bas
SuédoissvSuède
NorvégiennoNorvège
DanoisdaDanemark
FinnoisfiFinlande

Variantes Régionales

# Codes de langue régionaux courants
en-US    # Anglais (États-Unis)
en-GB    # Anglais (Royaume-Uni)
en-CA    # Anglais (Canada)
en-AU    # Anglais (Australie)
fr-FR    # Français (France)
fr-CA    # Français (Canada)
de-DE    # Allemand (Allemagne)
de-CH    # Allemand (Suisse)
es-ES    # Espagnol (Espagne)
es-MX    # Espagnol (Mexique)

Exemples Spécifiques aux Applications

Applications macOS Populaires

# Définir le français pour les applications de productivité
./gestionnaire_langue.sh definir fr "Pages" "Numbers" "Keynote"

# Définir l'allemand pour les applications de communication
./gestionnaire_langue.sh definir de "Mail" "Messages" "FaceTime"

# Définir le japonais pour les applications créatives
./gestionnaire_langue.sh definir ja "Photos" "GarageBand" "iMovie"

# Définir l'espagnol pour les applications système
./gestionnaire_langue.sh definir es "Calendar" "Contacts" "Reminders"

Configuration d'Applications d'Entreprise

# Configurer Microsoft Office
defaults write -app "Microsoft Word" AppleLanguages -array "fr"
defaults write -app "Microsoft Excel" AppleLanguages -array "fr"
defaults write -app "Microsoft PowerPoint" AppleLanguages -array "fr"

# Configurer Adobe Creative Suite
defaults write -app "Adobe Photoshop 2024" AppleLanguages -array "de"
defaults write -app "Adobe Illustrator 2024" AppleLanguages -array "de"
defaults write -app "Adobe InDesign 2024" AppleLanguages -array "de"

Gestion des Politiques de Langue

Application de Langue d'Organisation

# Créer une politique de langue d'organisation
ORGANIZATION_LANGUAGE="en"
DEPARTMENT_LANGUAGES=("en" "fr" "de")  # Langues autorisées

# Appliquer sur toutes les applications standard
for app in Safari Mail Calendar Contacts Notes; do
    defaults write -app "$app" AppleLanguages -array "$ORGANIZATION_LANGUAGE"
done

Vérification de Conformité

# Vérifier la conformité avec la politique de langue
check_language_compliance() {
    local required_lang="$1"
    local non_compliant_apps=()
    
    for app in Safari Mail Calendar Contacts Notes Reminders; do
        local current_lang=$(defaults read -app "$app" AppleLanguages 2>/dev/null | head -1 | tr -d '[:space:],"()')
        
        if [[ -n "$current_lang" && "$current_lang" != "$required_lang" ]]; then
            non_compliant_apps+=("$app:$current_lang")
        fi
    done
    
    if [[ ${#non_compliant_apps[@]} -gt 0 ]]; then
        echo "⚠️ Applications non conformes trouvées :"
        printf '%s\n' "${non_compliant_apps[@]}"
        return 1
    else
        echo "✅ Toutes les applications sont conformes à la politique de langue"
        return 0
    fi
}

Dépannage

Problèmes Courants

# Vérifier si l'application existe
app_exists() {
    local app_name="$1"
    if mdfind "kMDItemKind == 'Application'" | grep -q "$app_name.app"; then
        return 0
    else
        echo "❌ Application '$app_name' non trouvée"
        return 1
    fi
}

# Forcer le redémarrage de l'application
restart_application() {
    local app_name="$1"
    echo "🔄 Redémarrage de $app_name..."
    
    # Arrêter l'application si elle fonctionne
    pkill -f "$app_name" 2>/dev/null
    
    # Attendre et relancer
    sleep 2
    open -a "$app_name" 2>/dev/null
}

Vérification du Changement de Langue

# Vérifier que le changement de langue a pris effet
verify_language_change() {
    local app_name="$1"
    local expected_lang="$2"
    
    local actual_lang=$(defaults read -app "$app_name" AppleLanguages 2>/dev/null | head -1 | tr -d '[:space:],"()')
    
    if [[ "$actual_lang" == "$expected_lang" ]]; then
        echo "✅ Langue de $app_name vérifiée : $expected_lang"
        return 0
    else
        echo "❌ Décalage de langue pour $app_name. Attendu : $expected_lang, Actuel : $actual_lang"
        return 1
    fi
}

Notes Importantes

  • Redémarrage d'Application Requis - La plupart des applications doivent être redémarrées pour appliquer les changements de langue
  • Langue Système vs Application - Les paramètres spécifiques aux applications remplacent la langue système
  • Contexte Utilisateur - Les paramètres de langue s'appliquent uniquement à l'utilisateur actuel
  • Sauvegarde Avant Changements - Toujours sauvegarder les paramètres actuels avant les modifications en bloc
  • Tester les Applications Individuelles - Vérifier que les changements de langue fonctionnent pour des applications spécifiques
  • Considérations Régionales - Certaines applications peuvent nécessiter des codes de langue régionaux spécifiques

Gestion Entreprise de la Fermeture Forcée d'Applications pour macOS

Implémentez une gestion de fermeture forcée d'applications de niveau entreprise sur votre déploiement MacFleet avec la terminaison automatisée de processus, la validation de sécurité, les diagnostics de dépannage et le contrôle complet d'applications. Ce tutoriel fournit des solutions pour maintenir la stabilité système tout en assurant une gestion sécurisée et efficace des processus d'applications.

Comprendre la Gestion de Processus d'Applications macOS

macOS fournit plusieurs méthodes pour la terminaison de processus :

  • killall - Terminer toutes les instances d'un processus nommé
  • kill - Terminer des processus spécifiques par PID
  • pkill - Terminaison de processus basée sur des motifs
  • Moniteur d'Activité - Gestion de processus basée sur GUI
  • Forcer à Quitter - Terminaison d'application au niveau système

Opérations de Fermeture Forcée de Base

Fermeture Forcée Simple

#!/bin/bash

# Fermeture forcée de base
killall "nomapp"

Fermeture Forcée avec Sortie Verbeuse

#!/bin/bash

# Fermeture forcée avec sortie PID
killall -v "Notes"

Informations Détaillées de Processus

#!/bin/bash

# Obtenir les infos détaillées de processus sans tuer
killall -d "nomapp"

Lister les Signaux Disponibles

#!/bin/bash

# Lister les signaux disponibles
killall -l "nomapp"

Fermeture Forcée avec Signal Spécifique

#!/bin/bash

# Fermeture forcée avec signal ABRT
killall -abrt "Pages"

Système de Gestion de Fermeture Forcée d'Applications Entreprise

Outil de Contrôle de Processus Complet

#!/bin/bash

# Outil de Gestion de Fermeture Forcée d'Applications Entreprise MacFleet
# Terminaison de processus avancée et contrôle d'applications

# Configuration
CONFIG_FILE="/etc/macfleet/force_close_policy.conf"
LOG_FILE="/var/log/macfleet_force_close.log"
QUARANTINE_DIR="/Library/MacFleet/TerminatedApps"
AUDIT_LOG="/var/log/macfleet_process_audit.log"

# Créer les répertoires
mkdir -p "$(dirname "$CONFIG_FILE")" "$(dirname "$LOG_FILE")" "$QUARANTINE_DIR" "$(dirname "$AUDIT_LOG")"

# Politique de gestion de fermeture forcée par défaut
cat > "$CONFIG_FILE" 2>/dev/null << 'EOF' || true
# Politique de Gestion de Fermeture Forcée d'Applications Entreprise MacFleet
# Version : 2.0

# Application des Politiques de Fermeture Forcée
ENFORCE_FORCE_CLOSE_POLICIES=true
GRACEFUL_TERMINATION_FIRST=true
ESCALATION_TIMEOUT=10
SAVE_WORK_BEFORE_TERMINATION=true
PREVENT_SYSTEM_PROCESS_TERMINATION=true

# Sécurité et Sûreté
REQUIRE_ADMIN_APPROVAL=false
VALIDATE_PROCESS_LEGITIMACY=true
CHECK_PROCESS_DEPENDENCIES=true
MALWARE_DETECTION_ENABLED=true
SUSPICIOUS_BEHAVIOR_MONITORING=true

# Règles Métier
BUSINESS_HOURS_PROTECTION=true
BUSINESS_HOURS_START="09:00"
BUSINESS_HOURS_END="18:00"
CRITICAL_APP_PROTECTION=true
PRODUCTIVITY_APP_MONITORING=true

# Diagnostic et Dépannage
DIAGNOSTIC_LOGGING=true
CRASH_REPORT_COLLECTION=true
PERFORMANCE_MONITORING=true
RESOURCE_USAGE_TRACKING=true
HANG_DETECTION_ENABLED=true

# Notification et Communication
USER_NOTIFICATION_ENABLED=true
ADMIN_NOTIFICATION_ENABLED=true
SLACK_WEBHOOK_URL=""
EMAIL_NOTIFICATION_ENABLED=false
FORCE_CLOSE_WARNINGS=true

# Conformité et Audit
AUDIT_ALL_TERMINATIONS=true
COMPLIANCE_REPORTING=true
INCIDENT_DOCUMENTATION=true
ESCALATION_PROCEDURES=true
AUTOMATED_RECOVERY=true

# Récupération et Redémarrage
AUTO_RESTART_CRITICAL_APPS=true
RESTART_DELAY=5
CRASH_RECOVERY_ENABLED=true
STATE_PRESERVATION=true
USER_SESSION_PROTECTION=true
EOF

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

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

# Fonction de journalisation d'audit
audit_log() {
    local action="$1"
    local process_name="$2"
    local result="$3"
    local details="$4"
    echo "$(date '+%Y-%m-%d %H:%M:%S') - ACTION:$action PROCESS:$process_name RESULT:$result DETAILS:$details USER:$(whoami)" >> "$AUDIT_LOG"
}

# Obtenir des informations complètes sur le processus
get_process_info() {
    local process_name="$1"
    
    echo "=== Analyse d'Informations de Processus ==="
    
    # Vérifier si le processus existe
    if ! pgrep -f "$process_name" >/dev/null 2>&1; then
        echo "Processus introuvable : $process_name"
        return 1
    fi
    
    # Obtenir les détails du processus
    echo "Nom du Processus : $process_name"
    
    # Obtenir les PIDs
    local pids
    pids=$(pgrep -f "$process_name" | tr '\n' ' ')
    echo "IDs de Processus : $pids"
    
    # Obtenir l'utilisation CPU et mémoire
    echo "Utilisation des Ressources :"
    ps -p $(pgrep -f "$process_name" | head -1) -o pid,pcpu,pmem,time,command 2>/dev/null | tail -1
    
    # Obtenir l'arbre de processus
    echo "Arbre de Processus :"
    pstree $(pgrep -f "$process_name" | head -1) 2>/dev/null || echo "  Arbre de processus non disponible"
    
    # Obtenir le nombre de fichiers ouverts
    echo "Nombre de Fichiers Ouverts :"
    lsof -p $(pgrep -f "$process_name" | head -1) 2>/dev/null | wc -l || echo "  Impossible à déterminer"
    
    # Obtenir les connexions réseau
    echo "Connexions Réseau :"
    lsof -i -p $(pgrep -f "$process_name" | head -1) 2>/dev/null | wc -l || echo "  0"
    
    # Vérifier si le processus est bloqué
    local cpu_usage
    cpu_usage=$(ps -p $(pgrep -f "$process_name" | head -1) -o pcpu= 2>/dev/null | xargs)
    if [[ -n "$cpu_usage" ]] && (( $(echo "$cpu_usage < 0.1" | bc -l) )); then
        echo "Statut : Potentiellement bloqué (utilisation CPU très faible)"
    else
        echo "Statut : Actif"
    fi
}

# Vérifier si la terminaison de processus est sûre
check_termination_safety() {
    local process_name="$1"
    local force="${2:-false}"
    
    echo "=== Validation de la Sécurité de Terminaison de Processus ==="
    
    # Vérifier si le processus existe
    if ! pgrep -f "$process_name" >/dev/null 2>&1; then
        echo "✅ Processus non en cours d'exécution : $process_name"
        return 0
    fi
    
    # Empêcher la terminaison de processus système
    if [[ "$PREVENT_SYSTEM_PROCESS_TERMINATION" == "true" ]]; then
        local system_processes=(
            "kernel"
            "launchd"
            "WindowServer"
            "loginwindow"
            "SystemUIServer"
            "Dock"
            "Finder"
            "cfprefsd"
            "CoreServicesUIAgent"
        )
        
        for system_process in "${system_processes[@]}"; do
            if [[ "$process_name" == "$system_process" ]]; then
                echo "❌ Impossible de terminer le processus système critique : $process_name"
                audit_log "TERMINATION_BLOCKED" "$process_name" "SYSTEM_PROCESS" "Protection de processus système active"
                return 1
            fi
        done
    fi
    
    # Protection des heures de bureau pour les apps critiques
    if [[ "$BUSINESS_HOURS_PROTECTION" == "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
            local critical_apps=(
                "Microsoft Word"
                "Microsoft Excel"
                "Microsoft PowerPoint"
                "Slack"
                "Microsoft Teams"
                "Zoom"
                "Safari"
                "Google Chrome"
            )
            
            for critical_app in "${critical_apps[@]}"; do
                if [[ "$process_name" == *"$critical_app"* ]]; then
                    echo "❌ Terminaison bloquée : App de productivité critique pendant les heures de bureau"
                    audit_log "TERMINATION_BLOCKED" "$process_name" "BUSINESS_HOURS" "Protection d'app critique pendant les heures de bureau"
                    return 1
                fi
            done
        fi
    fi
    
    # Vérifier le travail non sauvegardé
    if [[ "$SAVE_WORK_BEFORE_TERMINATION" == "true" ]]; then
        echo "⚠️  Vérification du travail non sauvegardé..."
        
        # Vérifier les apps de documents avec des changements potentiellement non sauvegardés
        local document_apps=(
            "TextEdit"
            "Pages"
            "Numbers"
            "Keynote"
            "Microsoft Word"
            "Microsoft Excel"
            "Microsoft PowerPoint"
        )
        
        for doc_app in "${document_apps[@]}"; do
            if [[ "$process_name" == *"$doc_app"* ]]; then
                echo "📝 Application de document détectée - tentative de sauvegarde du travail"
                try_save_work "$process_name"
                break
            fi
        done
    fi
    
    # Vérifier les dépendances de processus
    if [[ "$CHECK_PROCESS_DEPENDENCIES" == "true" ]]; then
        local child_processes
        child_processes=$(pgrep -P $(pgrep -f "$process_name" | head -1) 2>/dev/null | wc -l)
        
        if [[ $child_processes -gt 0 ]]; then
            echo "⚠️  Le processus a $child_processes processus enfants"
            if [[ "$force" != "true" ]]; then
                echo "❌ Terminaison bloquée : Le processus a des dépendances. Utilisez l'option force pour continuer."
                audit_log "TERMINATION_BLOCKED" "$process_name" "DEPENDENCIES" "Le processus a $child_processes processus enfants"
                return 1
            fi
        fi
    fi
    
    echo "✅ Sécurité de terminaison de processus validée"
    audit_log "SAFETY_CHECK" "$process_name" "PASSED" "Toutes les vérifications de sécurité réussies"
    return 0
}

# Essayer de sauvegarder le travail dans les applications de documents
try_save_work() {
    local process_name="$1"
    
    echo "=== Tentative de Sauvegarde du Travail ==="
    
    # Envoyer la commande de sauvegarde via AppleScript pour les applications courantes
    local save_commands=(
        "osascript -e 'tell application \"System Events\" to keystroke \"s\" using command down'"
        "osascript -e 'tell application \"$process_name\" to save every document'"
    )
    
    for cmd in "${save_commands[@]}"; do
        eval "$cmd" 2>/dev/null || true
        sleep 1
    done
    
    # Attendre un moment pour que les opérations de sauvegarde se terminent
    sleep 3
    
    echo "💾 Tentative de sauvegarde terminée"
    log_action "Tentative de sauvegarde du travail terminée pour $process_name"
}

# Détecter les processus suspects ou malveillants
detect_suspicious_process() {
    local process_name="$1"
    
    if [[ "$MALWARE_DETECTION_ENABLED" != "true" ]]; then
        return 0
    fi
    
    echo "=== Détection de Processus Suspects ==="
    
    local suspicious_indicators=(
        "crypto"
        "miner"
        "trojan"
        "virus"
        "malware"
        "ransomware"
        "keylogger"
        "backdoor"
    )
    
    # Vérifier le nom de processus pour des mots-clés suspects
    for indicator in "${suspicious_indicators[@]}"; do
        if [[ "$process_name" == *"$indicator"* ]]; then
            echo "🚨 Processus suspect détecté : $process_name (correspond à : $indicator)"
            log_action "ALERTE SÉCURITÉ : Processus suspect détecté : $process_name"
            audit_log "SUSPICIOUS_PROCESS" "$process_name" "DETECTED" "Correspond à l'indicateur : $indicator"
            
            # Mettre en quarantaine les informations de processus
            quarantine_process_info "$process_name"
            return 1
        fi
    done
    
    # Vérifier la consommation de ressources inhabituelle
    local cpu_usage
    cpu_usage=$(ps -p $(pgrep -f "$process_name" | head -1) -o pcpu= 2>/dev/null | xargs)
    
    if [[ -n "$cpu_usage" ]] && (( $(echo "$cpu_usage > 80" | bc -l) )); then
        echo "⚠️  Utilisation CPU élevée détectée : ${cpu_usage}%"
        log_action "Utilisation CPU élevée détectée pour $process_name : ${cpu_usage}%"
    fi
    
    return 0
}

# Mettre en quarantaine les informations de processus suspects
quarantine_process_info() {
    local process_name="$1"
    
    echo "=== Mise en Quarantaine des Informations de Processus ==="
    
    local quarantine_file="$QUARANTINE_DIR/suspicious_process_$(date +%Y%m%d_%H%M%S).json"
    
    mkdir -p "$QUARANTINE_DIR"
    
    # Rassembler des informations complètes sur le processus
    local pid
    pid=$(pgrep -f "$process_name" | head -1)
    
    cat > "$quarantine_file" << EOF
{
  "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
  "process_name": "$process_name",
  "pid": "$pid",
  "detection_reason": "suspicious_indicators",
  "system_info": {
    "hostname": "$(hostname)",
    "user": "$(whoami)",
    "os_version": "$(sw_vers -productVersion)"
  },
  "process_details": {
    "command_line": "$(ps -p $pid -o command= 2>/dev/null || echo 'inconnu')",
    "cpu_usage": "$(ps -p $pid -o pcpu= 2>/dev/null || echo 'inconnu')",
    "memory_usage": "$(ps -p $pid -o pmem= 2>/dev/null || echo 'inconnu')",
    "start_time": "$(ps -p $pid -o lstart= 2>/dev/null || echo 'inconnu')"
  }
}
EOF
    
    echo "🔒 Informations de processus mises en quarantaine : $quarantine_file"
    log_action "Informations de processus suspects mises en quarantaine : $quarantine_file"
}

# Envoyer une notification de terminaison
send_termination_notification() {
    local process_name="$1"
    local operation="$2"
    local result="$3"
    local reason="$4"
    
    if [[ "$USER_NOTIFICATION_ENABLED" != "true" ]]; then
        return 0
    fi
    
    echo "=== Envoi des Notifications de Terminaison ==="
    
    local notification_title="Gestion de Processus MacFleet"
    local notification_message="Processus $operation : $process_name ($result)"
    
    if [[ -n "$reason" ]]; then
        notification_message="$notification_message - Raison : $reason"
    fi
    
    # Afficher la notification aux utilisateurs connectés
    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 envoyée à l'utilisateur : $user"
        fi
    done
    
    log_action "Notifications de terminaison envoyées pour $process_name ($operation - $result)"
}

# Fermeture forcée entreprise avec escalade
enterprise_force_close() {
    local process_name="$1"
    local force="${2:-false}"
    local signal="${3:-TERM}"
    
    echo "=== Fermeture Forcée Entreprise Initiée ==="
    
    if [[ -z "$process_name" ]]; then
        echo "❌ Nom de processus requis"
        return 1
    fi
    
    # Obtenir les informations sur le processus
    echo "Analyse du processus : $process_name"
    get_process_info "$process_name"
    
    # Détecter les processus suspects
    if detect_suspicious_process "$process_name"; then
        echo "⚠️  Processus suspect détecté - procédure avec prudence"
    fi
    
    # Vérifications de sécurité
    if ! check_termination_safety "$process_name" "$force"; then
        return 1
    fi
    
    log_action "FERMETURE FORCÉE ENTREPRISE : Début de terminaison de $process_name"
    audit_log "FORCE_CLOSE" "$process_name" "INITIATED" "Signal : $signal Force : $force"
    
    # Terminaison gracieuse d'abord (si activée)
    if [[ "$GRACEFUL_TERMINATION_FIRST" == "true" && "$force" != "true" ]]; then
        echo "🔄 Tentative de terminaison gracieuse d'abord..."
        
        if killall -TERM "$process_name" 2>/dev/null; then
            echo "Signal de terminaison gracieuse envoyé, attente ${ESCALATION_TIMEOUT}s..."
            sleep "$ESCALATION_TIMEOUT"
            
            # Vérifier si le processus est toujours en cours d'exécution
            if ! pgrep -f "$process_name" >/dev/null 2>&1; then
                echo "✅ Processus terminé gracieusement"
                send_termination_notification "$process_name" "terminaison gracieuse" "succès"
                audit_log "FORCE_CLOSE" "$process_name" "SUCCESS" "Terminaison gracieuse réussie"
                return 0
            else
                echo "⚠️  Terminaison gracieuse échouée, escalade vers terminaison forcée"
            fi
        fi
    fi
    
    # Terminaison forcée
    echo "🚨 Exécution de la terminaison forcée..."
    
    case "$signal" in
        "TERM")
            if killall "$process_name" 2>/dev/null; then
                echo "✅ Processus fermé de force avec succès avec signal TERM"
                termination_result="succès"
            else
                echo "❌ Échec de la fermeture forcée de processus avec signal TERM"
                termination_result="échec"
            fi
            ;;
        "KILL")
            if killall -KILL "$process_name" 2>/dev/null; then
                echo "✅ Processus tué de force avec succès avec signal KILL"
                termination_result="succès"
            else
                echo "❌ Échec du kill forcé de processus avec signal KILL"
                termination_result="échec"
            fi
            ;;
        "ABRT")
            if killall -ABRT "$process_name" 2>/dev/null; then
                echo "✅ Processus abandonné avec succès avec signal ABRT"
                termination_result="succès"
            else
                echo "❌ Échec de l'abandon de processus avec signal ABRT"
                termination_result="échec"
            fi
            ;;
        *)
            if killall -"$signal" "$process_name" 2>/dev/null; then
                echo "✅ Processus terminé avec succès avec signal $signal"
                termination_result="succès"
            else
                echo "❌ Échec de la terminaison de processus avec signal $signal"
                termination_result="échec"
            fi
            ;;
    esac
    
    # Envoyer des notifications
    send_termination_notification "$process_name" "fermeture forcée" "$termination_result" "Signal : $signal"
    
    # Redémarrage automatique des applications critiques si configuré
    if [[ "$AUTO_RESTART_CRITICAL_APPS" == "true" && "$termination_result" == "succès" ]]; then
        restart_if_critical "$process_name"
    fi
    
    if [[ "$termination_result" == "succès" ]]; then
        log_action "Processus fermé de force avec succès : $process_name"
        audit_log "FORCE_CLOSE" "$process_name" "SUCCESS" "Signal : $signal"
        return 0
    else
        log_action "ÉCHEC : Impossible de fermer de force le processus : $process_name"
        audit_log "FORCE_CLOSE" "$process_name" "FAILED" "Signal : $signal"
        return 1
    fi
}

# Redémarrer les applications critiques
restart_if_critical() {
    local process_name="$1"
    
    local critical_apps=(
        "Finder"
        "Dock"
        "SystemUIServer"
        "Spotlight"
    )
    
    for critical_app in "${critical_apps[@]}"; do
        if [[ "$process_name" == *"$critical_app"* ]]; then
            echo "🔄 Redémarrage de l'application critique : $critical_app"
            log_action "Redémarrage automatique de l'application critique : $critical_app"
            
            sleep "$RESTART_DELAY"
            
            case "$critical_app" in
                "Finder")
                    open -a Finder &
                    ;;
                "Dock")
                    killall Dock &
                    ;;
                "SystemUIServer")
                    killall SystemUIServer &
                    ;;
                "Spotlight")
                    mdutil -E / &
                    ;;
            esac
            
            break
        fi
    done
}

# Opérations de fermeture forcée en masse
bulk_force_close() {
    local process_list_file="$1"
    local force="${2:-false}"
    local signal="${3:-TERM}"
    
    echo "=== Opérations de Fermeture Forcée en Masse ==="
    
    if [[ ! -f "$process_list_file" ]]; then
        echo "❌ Fichier de liste de processus introuvable : $process_list_file"
        return 1
    fi
    
    local success_count=0
    local failure_count=0
    local total_count=0
    
    while IFS= read -r process_name; do
        # Ignorer les lignes vides et les commentaires
        if [[ -z "$process_name" || "$process_name" == \#* ]]; then
            continue
        fi
        
        ((total_count++))
        echo "Traitement ($total_count) : $process_name"
        
        if enterprise_force_close "$process_name" "$force" "$signal"; then
            ((success_count++))
        else
            ((failure_count++))
        fi
        
        echo "---"
        sleep 1  # Brève pause entre les opérations
    done < "$process_list_file"
    
    echo "=== Résumé de Fermeture Forcée en Masse ==="
    echo "Total des processus : $total_count"
    echo "Terminaisons réussies : $success_count"
    echo "Terminaisons échouées : $failure_count"
    
    log_action "Fermeture forcée en masse terminée : $success_count/$total_count réussies"
    audit_log "BULK_FORCE_CLOSE" "MULTIPLE" "COMPLETED" "Succès : $success_count Échecs : $failure_count Total : $total_count Signal : $signal"
}

# Détection de blocage et récupération automatique
detect_and_recover_hangs() {
    echo "=== Détection de Blocage et Récupération ==="
    
    if [[ "$HANG_DETECTION_ENABLED" != "true" ]]; then
        echo "La détection de blocage est désactivée"
        return 0
    fi
    
    local hang_threshold=0.1  # Utilisation CPU en dessous de ceci pour une durée prolongée indique un blocage
    local hang_duration=30    # Secondes d'activité faible pour considérer comme bloqué
    
    # Trouver les processus avec une utilisation CPU très faible
    local potentially_hung
    potentially_hung=$(ps -A -o pid,pcpu,time,command | awk -v threshold="$hang_threshold" '$2 < threshold && $2 > 0 {print $1 ":" $4}')
    
    if [[ -n "$potentially_hung" ]]; then
        echo "Processus potentiellement bloqués détectés :"
        echo "$potentially_hung" | while IFS=':' read -r pid command; do
            echo "  PID $pid : $command"
            
            # Vérifier si c'est une application utilisateur (pas un processus système)
            if [[ "$command" == *".app"* ]]; then
                local app_name
                app_name=$(basename "$command")
                echo "🔄 Tentative de récupération d'application bloquée : $app_name"
                
                log_action "Blocage détecté et récupération tentée : $app_name (PID : $pid)"
                audit_log "HANG_RECOVERY" "$app_name" "ATTEMPTED" "PID : $pid"
                
                # Essayer la récupération gracieuse d'abord
                kill -USR1 "$pid" 2>/dev/null || true
                sleep 5
                
                # Si toujours bloqué, fermer de force
                if ps -p "$pid" >/dev/null 2>&1; then
                    enterprise_force_close "$app_name" "true" "TERM"
                fi
            fi
        done
    else
        echo "Aucun processus bloqué détecté"
    fi
}

# Fonction principale avec gestion d'arguments
main() {
    log_action "=== Outil de Gestion de Processus MacFleet Démarré ==="
    
    case "${1:-help}" in
        "close")
            enterprise_force_close "$2" "$3" "$4"
            ;;
        "bulk")
            bulk_force_close "$2" "$3" "$4"
            ;;
        "info")
            get_process_info "$2"
            ;;
        "detect-hangs")
            detect_and_recover_hangs
            ;;
        *)
            echo "Outil de Gestion de Fermeture Forcée d'Applications Entreprise MacFleet"
            echo "Usage : $0 [commande] [options]"
            echo ""
            echo "Commandes :"
            echo "  close [nom_processus] [force] [signal]    - Fermer de force l'application avec vérifications de sécurité"
            echo "  bulk [fichier_liste] [force] [signal]    - Fermeture forcée en masse depuis fichier liste"
            echo "  info [nom_processus]                     - Obtenir les informations détaillées du processus"
            echo "  detect-hangs                             - Détecter et récupérer les applications bloquées"
            echo ""
            echo "Signaux :"
            echo "  TERM (défaut)  - Signal de terminaison gracieuse"
            echo "  KILL           - Terminaison immédiate (ne peut être ignorée)"
            echo "  ABRT           - Abandon avec génération de core dump"
            echo "  HUP            - Signal de raccrochage"
            echo "  INT            - Signal d'interruption"
            echo ""
            echo "Exemples :"
            echo "  $0 close \"Safari\"                      - Fermeture forcée sécurisée de Safari"
            echo "  $0 close \"Notes\" true KILL             - Kill forcé de Notes (contourner sécurité)"
            echo "  $0 bulk hung_processes.txt              - Fermeture en masse depuis liste"
            echo "  $0 info \"Google Chrome\"                - Obtenir les infos de processus Chrome"
            echo "  $0 detect-hangs                         - Scanner et récupérer les apps bloquées"
            ;;
    esac
    
    log_action "=== Opération de gestion de processus terminée ==="
}

# Exécuter la fonction principale
main "$@"

Notes de Configuration Importantes

Commandes de Contrôle de Processus macOS

  • killall - Terminer tous les processus par nom (sensible à la casse)
  • kill - Terminer un processus spécifique par PID
  • pkill - Terminaison de processus basée sur des motifs
  • Signaux - TERM (gracieux), KILL (immédiat), ABRT (avec core dump)

Points d'Intégration Entreprise

  • Gestion d'Information et d'Événements de Sécurité (SIEM) - Journalisation de terminaison de processus
  • Surveillance de Performance d'Applications - Intégration avec plateformes APM
  • Systèmes de Gestion d'Incidents - Création automatique de tickets pour blocages
  • Gestion des Changements - Documentation des interventions de processus

Bonnes Pratiques pour la Gestion de Processus Entreprise

  1. Sécurité et Validation

    • Toujours tenter la terminaison gracieuse avant le kill forcé
    • Protéger les processus système critiques de la terminaison accidentelle
    • Implémenter des restrictions d'heures de bureau pour les applications de productivité
    • Maintenir des pistes d'audit complètes
  2. Expérience Utilisateur

    • Tenter de sauvegarder le travail utilisateur avant de terminer les applications
    • Fournir des notifications claires sur les terminaisons de processus
    • Redémarrer automatiquement les applications critiques quand possible
    • Minimiser les perturbations pendant les heures de bureau
  3. Sécurité et Surveillance

    • Détecter et mettre en quarantaine les processus suspects
    • Surveiller les motifs de consommation de ressources inhabituels
    • Implémenter la détection de blocage et la récupération automatique
    • Suivre le comportement des processus pour l'analyse de sécurité
  4. Dépannage et Diagnostics

    • Collecter des informations détaillées sur les processus avant terminaison
    • Générer des rapports de crash pour analyse
    • Surveiller l'impact sur les performances système
    • Fournir des capacités de rapport complètes

Dépannage des Problèmes Courants

  • Le processus ne se termine pas - Escalader à travers la hiérarchie de signaux (TERM → KILL)
  • Problèmes de stabilité système - Éviter de terminer les processus système critiques
  • Préoccupations de perte de données - Implémenter des procédures de sauvegarde de travail avant terminaison
  • Fuites de ressources - Surveiller l'utilisation de mémoire et de descripteurs de fichiers
  • Faux positifs de détection de blocage - Ajuster les seuils de détection basés sur le comportement d'application

N'oubliez pas de tester minutieusement les procédures de gestion de processus dans un environnement contrôlé avant l'implémentation sur l'ensemble de votre MacFleet pour assurer la stabilité système et la productivité utilisateur.