Guide

Nouvelles mises à jour et améliorations de Roundfleet.

Gestion d'Entreprise du Bluetooth sur macOS

Contrôlez et gérez les paramètres Bluetooth sur vos appareils Roundfleet en utilisant des outils en ligne de commande avancés. Ce tutoriel couvre la surveillance du statut Bluetooth, la gestion des états d'alimentation, et le contrôle de flotte de niveau entreprise avec journalisation complète et fonctionnalités de sécurité.

Comprendre la Gestion Bluetooth de macOS

macOS fournit un contrôle Bluetooth complet via plusieurs mécanismes :

  • defaults - Manipulation directe des préférences Bluetooth via les fichiers plist
  • bluetoothd - Le daemon Bluetooth central qui gère toutes les opérations Bluetooth
  • launchctl - Gestion des services pour démarrer/arrêter les services Bluetooth
  • blued - Nom du daemon Bluetooth hérité utilisé dans les anciennes versions de macOS

Le fichier de configuration principal est situé à /Library/Preferences/com.apple.Bluetooth avec la clé ControllerPowerState contrôlant l'état d'alimentation.

Gestion de Base du Statut Bluetooth

Vérifier le Statut Bluetooth

#!/bin/bash

# Vérifier l'état d'alimentation Bluetooth actuel
check_bluetooth_status() {
    local status
    status=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
    
    if [[ "$status" == "1" ]]; then
        echo "Bluetooth est ACTIVÉ"
        return 0
    elif [[ "$status" == "0" ]]; then
        echo "Bluetooth est DÉSACTIVÉ"
        return 1
    else
        echo "Statut Bluetooth inconnu ou non configuré"
        return 2
    fi
}

# Exécuter la vérification du statut
check_bluetooth_status

Activer le Bluetooth

#!/bin/bash

# Activer le Bluetooth en utilisant defaults et redémarrage du daemon
turn_bluetooth_on() {
    echo "Activation du Bluetooth..."
    
    # Définir l'état d'alimentation Bluetooth sur ACTIVÉ
    sudo defaults write /Library/Preferences/com.apple.Bluetooth ControllerPowerState -int 1
    
    # Redémarrer le daemon Bluetooth (essayer les noms de daemon moderne et hérité)
    if pgrep bluetoothd >/dev/null 2>&1; then
        sudo killall -HUP bluetoothd
        echo "Daemon Bluetooth (bluetoothd) redémarré"
    elif pgrep blued >/dev/null 2>&1; then
        sudo killall -HUP blued
        echo "Daemon Bluetooth (blued) redémarré"
    else
        echo "Attention : Daemon Bluetooth non trouvé"
    fi
    
    # Attendre que les changements prennent effet
    sleep 3
    
    # Vérifier le changement
    if check_bluetooth_status >/dev/null; then
        echo "✅ Bluetooth activé avec succès"
        return 0
    else
        echo "❌ Échec de l'activation du Bluetooth"
        return 1
    fi
}

# Fonction d'aide pour la vérification du statut
check_bluetooth_status() {
    local status
    status=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
    [[ "$status" == "1" ]]
}

# Exécuter
turn_bluetooth_on

Désactiver le Bluetooth

#!/bin/bash

# Désactiver le Bluetooth en utilisant defaults et redémarrage du daemon
turn_bluetooth_off() {
    echo "Désactivation du Bluetooth..."
    
    # Définir l'état d'alimentation Bluetooth sur DÉSACTIVÉ
    sudo defaults write /Library/Preferences/com.apple.Bluetooth ControllerPowerState -int 0
    
    # Redémarrer le daemon Bluetooth
    if pgrep bluetoothd >/dev/null 2>&1; then
        sudo killall -HUP bluetoothd
        echo "Daemon Bluetooth (bluetoothd) redémarré"
    elif pgrep blued >/dev/null 2>&1; then
        sudo killall -HUP blued
        echo "Daemon Bluetooth (blued) redémarré"
    else
        echo "Attention : Daemon Bluetooth non trouvé"
    fi
    
    # Attendre que les changements prennent effet
    sleep 3
    
    # Vérifier le changement
    if ! check_bluetooth_status >/dev/null; then
        echo "✅ Bluetooth désactivé avec succès"
        return 0
    else
        echo "❌ Échec de la désactivation du Bluetooth"
        return 1
    fi
}

# Fonction d'aide pour la vérification du statut
check_bluetooth_status() {
    local status
    status=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
    [[ "$status" == "1" ]]
}

# Exécuter
turn_bluetooth_off

Gestion Avancée du Bluetooth avec launchctl

Utilisation de launchctl pour la Gestion des Services

#!/bin/bash

# Contrôle Bluetooth avancé utilisant la gestion des services launchctl
bluetooth_service_control() {
    local action="$1"  # on, off, restart, status
    local daemon_name
    
    # Déterminer le nom du daemon correct basé sur la version macOS
    if launchctl list | grep -q "com.apple.bluetoothd"; then
        daemon_name="com.apple.bluetoothd"
    elif launchctl list | grep -q "com.apple.blued"; then
        daemon_name="com.apple.blued"
    else
        echo "❌ Daemon Bluetooth non trouvé dans launchctl"
        return 1
    fi
    
    case "$action" in
        "on")
            echo "Activation du Bluetooth..."
            sudo defaults write /Library/Preferences/com.apple.Bluetooth ControllerPowerState -int 1
            sudo launchctl stop "$daemon_name"
            sleep 2
            sudo launchctl start "$daemon_name"
            sleep 3
            echo "✅ Bluetooth activé avec launchctl"
            ;;
        "off")
            echo "Désactivation du Bluetooth..."
            sudo defaults write /Library/Preferences/com.apple.Bluetooth ControllerPowerState -int 0
            sudo launchctl stop "$daemon_name"
            sleep 2
            sudo launchctl start "$daemon_name"
            sleep 3
            echo "✅ Bluetooth désactivé avec launchctl"
            ;;
        "restart")
            echo "Redémarrage du service Bluetooth..."
            sudo launchctl stop "$daemon_name"
            sleep 2
            sudo launchctl start "$daemon_name"
            sleep 3
            echo "✅ Service Bluetooth redémarré"
            ;;
        "status")
            echo "Statut du daemon Bluetooth : $daemon_name"
            if launchctl list | grep -q "$daemon_name"; then
                echo "✅ Service Bluetooth en cours d'exécution"
                
                # Vérifier l'état d'alimentation
                local power_state
                power_state=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
                echo "État d'alimentation : $([[ "$power_state" == "1" ]] && echo "ACTIVÉ" || echo "DÉSACTIVÉ")"
            else
                echo "❌ Service Bluetooth non en cours d'exécution"
            fi
            ;;
        *)
            echo "Usage : bluetooth_service_control {on|off|restart|status}"
            return 1
            ;;
    esac
}

# Exemples
bluetooth_service_control "status"
# bluetooth_service_control "on"
# bluetooth_service_control "off"
# bluetooth_service_control "restart"

Système de Gestion Bluetooth d'Entreprise

#!/bin/bash

# Système de Gestion Bluetooth d'Entreprise Roundfleet
# Contrôle Bluetooth complet avec journalisation, surveillance, et gestion de flotte

# Configuration
LOG_FILE="/var/log/roundfleet_bluetooth.log"
CONFIG_FILE="/etc/roundfleet/bluetooth_config.conf"
BACKUP_DIR="/var/backups/roundfleet/bluetooth"
API_ENDPOINT="https://api.roundfleet.com/v1/bluetooth"

# Créer la structure des répertoires
setup_directories() {
    mkdir -p "$(dirname "$LOG_FILE")" "$BACKUP_DIR" "$(dirname "$CONFIG_FILE")"
    touch "$LOG_FILE"
}

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

# Obtenir des informations Bluetooth détaillées
get_bluetooth_info() {
    local info_json="{"
    
    # État d'alimentation
    local power_state
    power_state=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null || echo "unknown")
    info_json+="\"power_state\": \"$power_state\","
    
    # Statut du daemon
    local daemon_running=false
    if pgrep bluetoothd >/dev/null 2>&1 || pgrep blued >/dev/null 2>&1; then
        daemon_running=true
    fi
    info_json+="\"daemon_running\": $daemon_running,"
    
    # Version macOS
    local macos_version
    macos_version=$(sw_vers -productVersion)
    info_json+="\"macos_version\": \"$macos_version\","
    
    # Matériel système
    local hardware_model
    hardware_model=$(system_profiler SPHardwareDataType | grep "Model Name" | awk -F': ' '{print $2}' | xargs)
    info_json+="\"hardware_model\": \"$hardware_model\","
    
    # Informations du contrôleur Bluetooth
    local bt_controller=""
    if system_profiler SPBluetoothDataType >/dev/null 2>&1; then
        bt_controller=$(system_profiler SPBluetoothDataType | grep -A5 "Apple Bluetooth Software" | grep "Version" | awk -F': ' '{print $2}' | xargs)
    fi
    info_json+="\"bluetooth_version\": \"$bt_controller\","
    
    # Horodatage
    info_json+="\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\""
    info_json+="}"
    
    echo "$info_json"
}

# Sauvegarder la configuration Bluetooth actuelle
backup_bluetooth_config() {
    local backup_file="$BACKUP_DIR/bluetooth_backup_$(date +%Y%m%d_%H%M%S).plist"
    
    if [[ -f "/Library/Preferences/com.apple.Bluetooth.plist" ]]; then
        cp "/Library/Preferences/com.apple.Bluetooth.plist" "$backup_file"
        log_action "Configuration Bluetooth sauvegardée dans : $backup_file"
        echo "$backup_file"
    else
        log_action "ATTENTION : Fichier de préférences Bluetooth non trouvé pour la sauvegarde"
        return 1
    fi
}

# Définir l'état Bluetooth avec validation complète
set_bluetooth_state() {
    local desired_state="$1"  # "on" ou "off"
    local force_restart="${2:-false}"
    
    # Valider l'entrée
    if [[ "$desired_state" != "on" && "$desired_state" != "off" ]]; then
        log_action "ERREUR : État spécifié invalide. Utilisez 'on' ou 'off'"
        return 1
    fi
    
    # Obtenir l'état actuel
    local current_state
    current_state=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
    
    local target_value
    if [[ "$desired_state" == "on" ]]; then
        target_value=1
    else
        target_value=0
    fi
    
    # Vérifier si un changement est nécessaire
    if [[ "$current_state" == "$target_value" && "$force_restart" == "false" ]]; then
        log_action "Bluetooth déjà dans l'état désiré : $desired_state"
        return 0
    fi
    
    # Créer une sauvegarde avant les changements
    backup_bluetooth_config
    
    # Définir le nouvel état
    log_action "Définition de l'état Bluetooth à : $desired_state"
    sudo defaults write /Library/Preferences/com.apple.Bluetooth ControllerPowerState -int "$target_value"
    
    # Redémarrer le daemon Bluetooth
    restart_bluetooth_daemon
    
    # Attendre que les changements se propagent
    sleep 5
    
    # Vérifier le changement
    local new_state
    new_state=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
    
    if [[ "$new_state" == "$target_value" ]]; then
        log_action "✅ Bluetooth défini avec succès à : $desired_state"
        return 0
    else
        log_action "❌ Échec de la définition du Bluetooth à : $desired_state"
        return 1
    fi
}

# Redémarrer le daemon Bluetooth avec gestion d'erreurs
restart_bluetooth_daemon() {
    log_action "Redémarrage du daemon Bluetooth..."
    
    # Essayer d'abord bluetoothd moderne
    if pgrep bluetoothd >/dev/null 2>&1; then
        if sudo killall -HUP bluetoothd 2>/dev/null; then
            log_action "Daemon Bluetooth (bluetoothd) redémarré avec succès"
            return 0
        else
            log_action "ATTENTION : Échec du redémarrage de bluetoothd avec le signal HUP"
        fi
    fi
    
    # Essayer blued hérité
    if pgrep blued >/dev/null 2>&1; then
        if sudo killall -HUP blued 2>/dev/null; then
            log_action "Daemon Bluetooth (blued) redémarré avec succès"
            return 0
        else
            log_action "ATTENTION : Échec du redémarrage de blued avec le signal HUP"
        fi
    fi
    
    # Essayer l'approche launchctl
    local daemon_service=""
    if launchctl list | grep -q "com.apple.bluetoothd"; then
        daemon_service="com.apple.bluetoothd"
    elif launchctl list | grep -q "com.apple.blued"; then
        daemon_service="com.apple.blued"
    fi
    
    if [[ -n "$daemon_service" ]]; then
        log_action "Tentative de redémarrage launchctl pour : $daemon_service"
        sudo launchctl stop "$daemon_service" 2>/dev/null
        sleep 2
        sudo launchctl start "$daemon_service" 2>/dev/null
        log_action "Service Bluetooth redémarré via launchctl"
        return 0
    fi
    
    log_action "ERREUR : Impossible de redémarrer le daemon Bluetooth"
    return 1
}

# Surveiller la connectivité Bluetooth et les appareils appairés
monitor_bluetooth_devices() {
    log_action "Démarrage de la surveillance des appareils Bluetooth..."
    
    # Obtenir l'état d'alimentation actuel
    local power_state
    power_state=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
    
    if [[ "$power_state" != "1" ]]; then
        log_action "Bluetooth est DÉSACTIVÉ - pas d'appareils à surveiller"
        return 0
    fi
    
    # Vérifier la disponibilité de system_profiler
    if ! command -v system_profiler >/dev/null 2>&1; then
        log_action "ATTENTION : system_profiler non disponible pour la surveillance des appareils"
        return 1
    fi
    
    # Obtenir les informations des appareils Bluetooth
    local bt_devices
    bt_devices=$(system_profiler SPBluetoothDataType 2>/dev/null | grep -A10 "Devices:" || echo "Aucun appareil trouvé")
    
    log_action "Statut des appareils Bluetooth :"
    echo "$bt_devices" | while IFS= read -r line; do
        log_action "  $line"
    done
    
    # Compter les appareils connectés
    local connected_count
    connected_count=$(echo "$bt_devices" | grep -c "Connected: Yes" || echo "0")
    log_action "Total d'appareils Bluetooth connectés : $connected_count"
    
    return 0
}

# Générer un rapport Bluetooth complet
generate_bluetooth_report() {
    local report_file="/tmp/bluetooth_report_$(date +%Y%m%d_%H%M%S).json"
    
    log_action "Génération du rapport Bluetooth : $report_file"
    
    {
        echo "{"
        echo "  \"report_type\": \"bluetooth_status\","
        echo "  \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\","
        echo "  \"hostname\": \"$(hostname)\","
        echo "  \"device_info\": $(get_bluetooth_info),"
        
        # Informations système
        echo "  \"system_info\": {"
        echo "    \"macos_version\": \"$(sw_vers -productVersion)\","
        echo "    \"build_version\": \"$(sw_vers -buildVersion)\","
        echo "    \"hardware_uuid\": \"$(system_profiler SPHardwareDataType | grep 'Hardware UUID' | awk -F': ' '{print $2}' | xargs)\""
        echo "  },"
        
        # Préférences Bluetooth
        echo "  \"bluetooth_preferences\": {"
        if [[ -f "/Library/Preferences/com.apple.Bluetooth.plist" ]]; then
            echo "    \"preferences_file_exists\": true,"
            echo "    \"file_size\": $(stat -f%z "/Library/Preferences/com.apple.Bluetooth.plist"),"
            echo "    \"last_modified\": \"$(stat -f%Sm -t%Y-%m-%dT%H:%M:%SZ "/Library/Preferences/com.apple.Bluetooth.plist")\""
        else
            echo "    \"preferences_file_exists\": false"
        fi
        echo "  }"
        echo "}"
    } > "$report_file"
    
    log_action "Rapport Bluetooth généré avec succès"
    echo "$report_file"
}

# Vérification de conformité de sécurité
check_bluetooth_security() {
    log_action "Exécution de la vérification de conformité de sécurité Bluetooth..."
    
    local security_issues=()
    
    # Vérifier si Bluetooth est activé quand il devrait être désactivé
    if [[ -f "$CONFIG_FILE" ]] && grep -q "BLUETOOTH_POLICY=disabled" "$CONFIG_FILE"; then
        local current_state
        current_state=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
        
        if [[ "$current_state" == "1" ]]; then
            security_issues+=("Bluetooth activé malgré une politique exigeant l'état désactivé")
        fi
    fi
    
    # Vérifier la découvrabilité Bluetooth (si system_profiler disponible)
    if command -v system_profiler >/dev/null 2>&1; then
        local discoverable
        discoverable=$(system_profiler SPBluetoothDataType 2>/dev/null | grep -i "discoverable" || echo "unknown")
        
        if echo "$discoverable" | grep -qi "yes"; then
            security_issues+=("Bluetooth est découvrable - risque de sécurité potentiel")
        fi
    fi
    
    # Rapporter le statut de sécurité
    if [[ ${#security_issues[@]} -eq 0 ]]; then
        log_action "✅ Vérification de conformité de sécurité Bluetooth réussie"
        return 0
    else
        log_action "⚠️  Problèmes de sécurité Bluetooth trouvés :"
        for issue in "${security_issues[@]}"; do
            log_action "  - $issue"
        done
        return 1
    fi
}

# Fonction de gestion principale
main() {
    local action="${1:-status}"
    local parameter="$2"
    
    setup_directories
    log_action "Gestion Bluetooth Roundfleet démarrée avec l'action : $action"
    
    case "$action" in
        "on"|"enable")
            set_bluetooth_state "on"
            ;;
        "off"|"disable")
            set_bluetooth_state "off"
            ;;
        "restart"|"reload")
            restart_bluetooth_daemon
            ;;
        "status"|"info")
            get_bluetooth_info | python3 -m json.tool 2>/dev/null || get_bluetooth_info
            ;;
        "monitor")
            monitor_bluetooth_devices
            ;;
        "report")
            generate_bluetooth_report
            ;;
        "security")
            check_bluetooth_security
            ;;
        "backup")
            backup_bluetooth_config
            ;;
        "force-on")
            set_bluetooth_state "on" true
            ;;
        "force-off")
            set_bluetooth_state "off" true
            ;;
        *)
            echo "Usage : $0 {on|off|restart|status|monitor|report|security|backup|force-on|force-off}"
            echo ""
            echo "Commandes :"
            echo "  on/enable    - Activer le Bluetooth"
            echo "  off/disable  - Désactiver le Bluetooth"
            echo "  restart      - Redémarrer le daemon Bluetooth"
            echo "  status       - Afficher le statut Bluetooth détaillé"
            echo "  monitor      - Surveiller les appareils connectés"
            echo "  report       - Générer un rapport complet"
            echo "  security     - Exécuter la vérification de conformité de sécurité"
            echo "  backup       - Sauvegarder la configuration actuelle"
            echo "  force-on     - Forcer l'activation du Bluetooth (redémarrer daemon)"
            echo "  force-off    - Forcer la désactivation du Bluetooth (redémarrer daemon)"
            exit 1
            ;;
    esac
    
    log_action "Gestion Bluetooth Roundfleet terminée avec l'action : $action"
}

# Exécuter la fonction principale avec tous les arguments
main "$@"

Fonctions de Gestion Rapide

Vérification Rapide du Statut

#!/bin/bash

# Vérification rapide du statut Bluetooth avec sortie colorée
bluetooth_quick_status() {
    local status
    status=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
    
    case "$status" in
        "1")
            echo "🔵 Bluetooth : ACTIVÉ"
            ;;
        "0")
            echo "⚪ Bluetooth : DÉSACTIVÉ"
            ;;
        *)
            echo "❓ Bluetooth : INCONNU"
            ;;
    esac
}

bluetooth_quick_status

Basculer l'État Bluetooth

#!/bin/bash

# Basculer le Bluetooth entre les états ACTIVÉ et DÉSACTIVÉ
toggle_bluetooth() {
    local current_state
    current_state=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
    
    if [[ "$current_state" == "1" ]]; then
        echo "Bluetooth est ACTIVÉ, désactivation..."
        sudo defaults write /Library/Preferences/com.apple.Bluetooth ControllerPowerState -int 0
        sudo killall -HUP bluetoothd 2>/dev/null || sudo killall -HUP blued 2>/dev/null
        echo "✅ Bluetooth désactivé"
    else
        echo "Bluetooth est DÉSACTIVÉ, activation..."
        sudo defaults write /Library/Preferences/com.apple.Bluetooth ControllerPowerState -int 1
        sudo killall -HUP bluetoothd 2>/dev/null || sudo killall -HUP blued 2>/dev/null
        echo "✅ Bluetooth activé"
    fi
}

toggle_bluetooth

Modèles de Configuration

Configuration de Politique Bluetooth

# /etc/roundfleet/bluetooth_config.conf
# Configuration de Gestion Bluetooth Roundfleet

# Paramètres de politique
BLUETOOTH_POLICY="enabled"  # enabled, disabled, user_controlled
AUTO_DISABLE_ON_IDLE="false"
IDLE_TIMEOUT_MINUTES="30"

# Paramètres de sécurité
ALLOW_DISCOVERABLE="false"
REQUIRE_PAIRING_CONFIRMATION="true"
LOG_DEVICE_CONNECTIONS="true"

# Paramètres de surveillance
MONITOR_INTERVAL_SECONDS="300"
ALERT_ON_NEW_DEVICES="true"
COMPLIANCE_CHECK_ENABLED="true"

# Paramètres de sauvegarde
AUTO_BACKUP_ENABLED="true"
BACKUP_RETENTION_DAYS="30"

Dépannage des Problèmes Courants

Daemon Bluetooth qui ne Répond Pas

#!/bin/bash

# Dépanner et corriger les problèmes de daemon Bluetooth
fix_bluetooth_daemon() {
    echo "Diagnostic des problèmes de daemon Bluetooth..."
    
    # Vérifier si le daemon fonctionne
    if ! pgrep bluetoothd >/dev/null 2>&1 && ! pgrep blued >/dev/null 2>&1; then
        echo "❌ Aucun daemon Bluetooth trouvé en cours d'exécution"
        
        # Essayer de démarrer via launchctl
        if launchctl list | grep -q "com.apple.bluetoothd"; then
            echo "Démarrage de bluetoothd via launchctl..."
            sudo launchctl start com.apple.bluetoothd
        elif launchctl list | grep -q "com.apple.blued"; then
            echo "Démarrage de blued via launchctl..."
            sudo launchctl start com.apple.blued
        else
            echo "❌ Service Bluetooth non trouvé dans launchctl"
            return 1
        fi
    fi
    
    # Forcer le redémarrage en cas de problèmes persistants
    echo "Redémarrage forcé du système Bluetooth..."
    sudo pkill -f bluetooth
    sleep 3
    
    # Redémarrer via launchctl
    for service in "com.apple.bluetoothd" "com.apple.blued"; do
        if launchctl list | grep -q "$service"; then
            sudo launchctl stop "$service" 2>/dev/null
            sleep 2
            sudo launchctl start "$service" 2>/dev/null
            echo "Service redémarré : $service"
        fi
    done
    
    # Vérifier la récupération
    sleep 5
    if pgrep bluetoothd >/dev/null 2>&1 || pgrep blued >/dev/null 2>&1; then
        echo "✅ Récupération du daemon Bluetooth réussie"
        return 0
    else
        echo "❌ Récupération du daemon Bluetooth échouée"
        return 1
    fi
}

fix_bluetooth_daemon

Réinitialiser la Configuration Bluetooth

#!/bin/bash

# Réinitialiser le Bluetooth aux paramètres d'usine
reset_bluetooth_config() {
    echo "⚠️  Réinitialisation de la configuration Bluetooth aux paramètres par défaut..."
    
    # Créer une sauvegarde
    if [[ -f "/Library/Preferences/com.apple.Bluetooth.plist" ]]; then
        cp "/Library/Preferences/com.apple.Bluetooth.plist" "/tmp/bluetooth_backup_$(date +%s).plist"
        echo "Sauvegarde créée dans /tmp/"
    fi
    
    # Arrêter les services Bluetooth
    sudo launchctl stop com.apple.bluetoothd 2>/dev/null
    sudo launchctl stop com.apple.blued 2>/dev/null
    
    # Supprimer les fichiers de configuration
    sudo rm -f "/Library/Preferences/com.apple.Bluetooth.plist"
    sudo rm -f "/Library/Preferences/ByHost/com.apple.Bluetooth.*.plist"
    
    # Redémarrer les services
    sudo launchctl start com.apple.bluetoothd 2>/dev/null
    sudo launchctl start com.apple.blued 2>/dev/null
    
    # Définir l'état par défaut
    sleep 3
    sudo defaults write /Library/Preferences/com.apple.Bluetooth ControllerPowerState -int 1
    sudo killall -HUP bluetoothd 2>/dev/null || sudo killall -HUP blued 2>/dev/null
    
    echo "✅ Réinitialisation de la configuration Bluetooth terminée"
}

# Décommenter pour exécuter
# reset_bluetooth_config

Notes Techniques Importantes

Gestion des Daemons

  • bluetoothd : Daemon Bluetooth moderne (macOS 10.10+)
  • blued : Daemon Bluetooth hérité (anciennes versions macOS)
  • killall -HUP : Envoie un signal de raccrochage pour redémarrer le daemon gracieusement
  • launchctl : Méthode préférée pour la gestion des services

Détails de Configuration

  • Emplacement du fichier : /Library/Preferences/com.apple.Bluetooth
  • Clé d'état d'alimentation : ControllerPowerState
  • Valeurs : 1 (ACTIVÉ), 0 (DÉSACTIVÉ)
  • Persistance : Les changements persistent après redémarrage

Considérations de Sécurité

  1. Privilèges Administratifs : Toutes les modifications Bluetooth nécessitent sudo
  2. Redémarrage du Daemon : Les changements ne prennent effet qu'après redémarrage du daemon
  3. Impact Utilisateur : Désactiver le Bluetooth déconnecte tous les appareils appairés
  4. Conformité Politique : Surveiller les changements Bluetooth non autorisés

Meilleures Pratiques

  1. Toujours sauvegarder les configurations avant d'apporter des modifications
  2. Tester les méthodes de redémarrage du daemon pour votre version macOS
  3. Surveiller les délais - le redémarrage du daemon peut prendre plusieurs secondes
  4. Implémenter la journalisation pour les pistes d'audit et le dépannage
  5. Utiliser launchctl pour une gestion des services plus fiable
  6. Valider les changements après avoir appliqué les modifications
  7. Considérer l'impact utilisateur lors de la désactivation du Bluetooth à distance
  8. Tester les scripts minutieusement avant le déploiement de flotte

N'oubliez pas de valider tous les scripts sur des appareils de test avant de les déployer sur votre environnement Roundfleet, et soyez conscient que les changements Bluetooth peuvent prendre quelques minutes pour se propager complètement dans le système.

Gestion de Batterie d'Entreprise sur macOS

Surveillez et gérez la santé, les performances et le cycle de vie de la batterie sur vos appareils Roundfleet en utilisant des outils de surveillance de batterie avancés et des systèmes de gestion de batterie d'entreprise. Ce tutoriel couvre la surveillance de santé de batterie, l'optimisation d'alimentation, le suivi de conformité et la gestion du cycle de vie de batterie d'entreprise.

Comprendre la Gestion de Batterie macOS

macOS fournit plusieurs outils en ligne de commande pour la gestion et surveillance de batterie :

  • pmset - Paramètres de gestion d'alimentation et surveillance d'état de batterie
  • system_profiler - Informations système et batterie complètes
  • ioreg - Informations de registre I/O incluant détails de batterie
  • Gestion de Santé de Batterie - Optimisation de batterie intégrée macOS
  • Mode Économie d'Énergie - Conservation d'énergie au niveau système

Opérations de Batterie de Base

Voir l'État de la Batterie

#!/bin/bash

# État de batterie de base
pmset -g batt

echo "État de batterie récupéré avec succès"

Informations de Batterie Améliorées

#!/bin/bash

# Informations de batterie complètes avec analyse détaillée
get_comprehensive_battery_info() {
    echo "=== Analyse Complète de Batterie ==="
    
    # État de batterie de base
    echo "Aperçu de l'État de Batterie :"
    pmset -g batt
    
    echo -e "\nSanté Détaillée de Batterie :"
    system_profiler SPPowerDataType
    
    echo -e "\nParamètres de Gestion d'Alimentation :"
    pmset -g
    
    echo -e "\nInformations de Registre de Batterie :"
    ioreg -n AppleSmartBattery -r
    
    echo -e "\nÉtat Thermique :"
    pmset -g therm
    
    echo -e "\nDétails de Source d'Alimentation :"
    pmset -g ps
}

# Exécuter l'analyse complète
get_comprehensive_battery_info

Catégories de Gestion de Batterie

Classifications d'Appareils à Batterie

#!/bin/bash

# Catégories de gestion de batterie d'entreprise pour surveillance et application de politiques
declare -A BATTERY_DEVICE_CATEGORIES=(
    ["mobile_direction"]="MacBooks de direction nécessitant temps de fonctionnement et fiabilité maximums"
    ["poste_creatif"]="MacBooks haute performance pour professionnels créatifs avec usage intensif"
    ["ordinateur_developpeur"]="MacBooks de développement avec usage CPU élevé et sessions longues"
    ["service_terrain"]="MacBooks de service terrain nécessitant autonomie étendue et durabilité"
    ["flotte_education"]="MacBooks éducatifs avec modèles d'usage partagé et contraintes budgétaires"
    ["salle_conference"]="MacBooks de salle de réunion pour présentations et visioconférence"
    ["appareils_secours"]="Appareils de sauvegarde et prêt avec modèles d'usage peu fréquents"
    ["systemes_heritage"]="MacBooks anciens nécessitant gestion de batterie spécialisée"
    ["labo_test"]="Appareils de test et QA avec modèles d'usage variables"
    ["affichage_kiosque"]="MacBooks kiosque avec exigences d'opération continue"
)

# Seuils de santé de batterie
declare -A HEALTH_THRESHOLDS=(
    ["mobile_direction"]="excellent:90,bon:80,alerte:70,critique:60"
    ["poste_creatif"]="excellent:85,bon:75,alerte:65,critique:55"
    ["ordinateur_developpeur"]="excellent:85,bon:75,alerte:65,critique:55"
    ["service_terrain"]="excellent:95,bon:85,alerte:75,critique:65"
    ["flotte_education"]="excellent:80,bon:70,alerte:60,critique:50"
    ["salle_conference"]="excellent:85,bon:75,alerte:65,critique:55"
    ["appareils_secours"]="excellent:75,bon:65,alerte:55,critique:45"
    ["systemes_heritage"]="excellent:70,bon:60,alerte:50,critique:40"
    ["labo_test"]="excellent:80,bon:70,alerte:60,critique:50"
    ["affichage_kiosque"]="excellent:90,bon:80,alerte:70,critique:60"
)

print_battery_categories() {
    echo "=== Catégories de Gestion de Batterie ==="
    for category in "${!BATTERY_DEVICE_CATEGORIES[@]}"; do
        echo "Catégorie : $category"
        echo "  Description : ${BATTERY_DEVICE_CATEGORIES[$category]}"
        echo "  Seuils de Santé : ${HEALTH_THRESHOLDS[$category]}"
        echo ""
    done
}

# Afficher les catégories disponibles
print_battery_categories

Politiques de Gestion de Batterie

Moteur de Politique de Gestion d'Alimentation

#!/bin/bash

# Politiques de gestion de batterie pour différentes exigences organisationnelles
declare -A BATTERY_POLICIES=(
    ["performance_maximale"]="Politique priorité performance avec restrictions d'alimentation minimales"
    ["efficacite_equilibree"]="Optimisation équilibrée performance et autonomie batterie"
    ["autonomie_etendue"]="Autonomie maximale avec optimisation performance"
    ["entreprise_standard"]="Gestion batterie d'entreprise standard avec surveillance"
    ["mobile_haute_securite"]="Gestion batterie mobile axée sécurité"
    ["professionnel_creatif"]="Optimisé pour flux créatifs avec demandes d'alimentation élevées"
    ["operations_terrain"]="Opération étendue pour service terrain et travail distant"
    ["conformite_stricte"]="Conformité stricte avec suivi santé et cycle de vie batterie"
)

# Appliquer une politique de batterie
apply_battery_policy() {
    local policy="$1"
    local config_file="/tmp/politique_batterie.json"
    
    echo "Application de la politique de batterie : $policy"
    
    case "$policy" in
        "autonomie_etendue")
            cat > "$config_file" << EOF
{
    "mode_alimentation": "economie_etendue",
    "gestion_sante_batterie": true,
    "seuil_mode_economie": 20,
    "parametres_veille": {
        "veille_ecran": 5,
        "veille_systeme": 10,
        "veille_disque": 5
    },
    "performance_processeur": "efficace",
    "commutation_graphiques": "gpu_integre_seulement",
    "actualisation_apps_arriere_plan": false,
    "services_localisation": "essentiel_seulement",
    "optimisation_notification": true,
    "gestion_thermique": "priorite_batterie",
    "optimisation_charge": true,
    "charge_optimisee": true,
    "surveillance_batterie": "complete"
}
EOF
            ;;
        "entreprise_standard")
            cat > "$config_file" << EOF
{
    "mode_alimentation": "equilibre",
    "gestion_sante_batterie": true,
    "seuil_mode_economie": 15,
    "parametres_veille": {
        "veille_ecran": 15,
        "veille_systeme": 30,
        "veille_disque": 10
    },
    "performance_processeur": "equilibre",
    "commutation_graphiques": "automatique",
    "actualisation_apps_arriere_plan": true,
    "services_localisation": "standard",
    "optimisation_notification": true,
    "gestion_thermique": "equilibre",
    "optimisation_charge": true,
    "surveillance_batterie": "entreprise",
    "suivi_conformite": true,
    "gestion_cycle_vie": true,
    "analyse_usage": true,
    "alertes_sante": true,
    "rapports_flotte": true
}
EOF
            ;;
        *)
            echo "Politique de batterie inconnue : $policy"
            return 1
            ;;
    esac
    
    echo "✅ Politique de batterie appliquée avec succès"
    echo "Configuration : $config_file"
    
    return 0
}

Surveillance Avancée de Batterie

Analyse de Santé de Batterie

#!/bin/bash

# Surveillance et analyse complètes de santé de batterie
monitor_battery_health() {
    local device_category="$1"
    local monitoring_profile="${2:-standard}"
    local health_report="/tmp/sante_batterie_$(date +%Y%m%d_%H%M%S).json"
    
    echo "=== Surveillance de Santé de Batterie ==="
    echo "Catégorie d'Appareil : $device_category"
    echo "Profil de Surveillance : $monitoring_profile"
    
    # Extraire les informations de batterie
    echo "Collecte des données de batterie..."
    local battery_info
    battery_info=$(system_profiler SPPowerDataType)
    
    # Analyser les métriques clés de batterie
    local cycle_count
    cycle_count=$(echo "$battery_info" | grep "Cycle Count:" | awk -F': ' '{print $2}' | tr -d ' ')
    
    local condition
    condition=$(echo "$battery_info" | grep "Condition:" | awk -F': ' '{print $2}' | tr -d ' ')
    
    local max_capacity
    max_capacity=$(echo "$battery_info" | grep "Maximum Capacity:" | awk -F': ' '{print $2}' | tr -d ' %')
    
    local state_of_charge
    state_of_charge=$(echo "$battery_info" | grep "State of Charge" | awk -F': ' '{print $2}' | tr -d ' %')
    
    local charging_status
    charging_status=$(pmset -g batt | grep -o "charging\|discharging\|charged" | head -1)
    
    # Calculer le score de santé de batterie
    local health_score=100
    
    # Impact du nombre de cycles
    if [[ $cycle_count -gt 1000 ]]; then
        ((health_score -= 20))
    elif [[ $cycle_count -gt 500 ]]; then
        ((health_score -= 10))
    fi
    
    # Impact de la capacité
    if [[ $max_capacity -lt 80 ]]; then
        ((health_score -= 25))
    elif [[ $max_capacity -lt 90 ]]; then
        ((health_score -= 15))
    fi
    
    # Impact de la condition
    if [[ "$condition" == "Service Recommended" ]]; then
        ((health_score -= 30))
    elif [[ "$condition" == "Replace Soon" ]]; then
        ((health_score -= 50))
    elif [[ "$condition" == "Replace Now" ]]; then
        ((health_score -= 70))
    fi
    
    # Déterminer l'état de santé
    local health_status="excellent"
    if [[ $health_score -lt 60 ]]; then
        health_status="critique"
    elif [[ $health_score -lt 70 ]]; then
        health_status="mauvais"
    elif [[ $health_score -lt 80 ]]; then
        health_status="alerte"
    elif [[ $health_score -lt 90 ]]; then
        health_status="bon"
    fi
    
    # Générer les recommandations
    local recommendations=()
    
    if [[ $cycle_count -gt 800 ]]; then
        recommendations+=("Considérer la planification de remplacement de batterie")
    fi
    
    if [[ $max_capacity -lt 85 ]]; then
        recommendations+=("Surveiller la dégradation de capacité de batterie")
    fi
    
    if [[ "$condition" != "Normal" ]]; then
        recommendations+=("Programmer rendez-vous de service batterie")
    fi
    
    if [[ $state_of_charge -lt 20 && "$charging_status" == "discharging" ]]; then
        recommendations+=("Connecter à une source d'alimentation immédiatement")
    fi
    
    # Afficher les résultats
    echo ""
    echo "Résultats d'Analyse de Santé de Batterie :"
    echo "  Score de Santé Global : $health_score/100 ($health_status)"
    echo "  Nombre de Cycles : ${cycle_count:-0}"
    echo "  Condition : $condition"
    echo "  Capacité Maximale : ${max_capacity:-0}%"
    echo "  Charge Actuelle : ${state_of_charge:-0}%"
    echo "  État de Charge : $charging_status"
    
    if [[ ${#recommendations[@]} -gt 0 ]]; then
        echo "  Recommandations :"
        for rec in "${recommendations[@]}"; do
            echo "    - $rec"
        done
    else
        echo "  ✅ Aucune action immédiate requise"
    fi
    
    echo "  Rapport de Santé : $health_report"
    
    return 0
}

Système de Gestion de Batterie d'Entreprise

#!/bin/bash

# Système de Gestion de Batterie d'Entreprise Roundfleet
# Surveillance, optimisation et gestion de cycle de vie de batterie complètes

# Configuration
CONFIG_DIR="/etc/roundfleet/batterie"
LOG_FILE="/var/log/roundfleet_gestion_batterie.log"
DATA_DIR="/var/data/roundfleet/batterie"
REPORTS_DIR="/var/reports/roundfleet/batterie"
AUDIT_LOG="/var/log/roundfleet_audit_batterie.log"

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

log_error() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') [ERREUR] $1" | tee -a "$LOG_FILE" >&2
}

audit_log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') [AUDIT] $1" | tee -a "$AUDIT_LOG"
}

# Optimisation de performance de batterie
optimize_battery_performance() {
    local optimization_profile="$1"
    local target_usage="${2:-standard}"
    
    log_action "Début d'optimisation batterie (Profil : $optimization_profile)"
    
    echo "=== Optimisation de Performance de Batterie ==="
    echo "Profil d'Optimisation : $optimization_profile"
    echo "Usage Cible : $target_usage"
    
    local optimizations_applied=()
    
    case "$optimization_profile" in
        "autonomie_maximale")
            echo "Application des optimisations d'autonomie maximale..."
            
            # Activer le mode basse consommation
            sudo pmset -b lowpowermode 1 2>/dev/null && {
                optimizations_applied+=("mode_basse_consommation_active")
                echo "  ✅ Mode basse consommation activé"
            }
            
            # Optimiser les paramètres de veille pour batterie
            sudo pmset -b displaysleep 2 sleep 5 disksleep 3 && {
                optimizations_applied+=("parametres_veille_optimises")
                echo "  ✅ Paramètres de veille optimisés pour batterie"
            }
            
            # Désactiver les services non essentiels
            sudo pmset -b ttyskeepawake 0 && {
                optimizations_applied+=("veille_tty_optimisee")
                echo "  ✅ Optimisation de veille TTY activée"
            }
            ;;
            
        "performance_equilibree")
            echo "Application des optimisations de performance équilibrée..."
            
            # Optimiser les modèles de charge
            sudo pmset -b acwake 0 && {
                optimizations_applied+=("reveil_ac_optimise")
                echo "  ✅ Optimisation de réveil AC appliquée"
            }
            
            # Définir paramètres de veille équilibrés
            sudo pmset -b displaysleep 10 sleep 15 disksleep 10 && {
                optimizations_applied+=("parametres_veille_equilibres")
                echo "  ✅ Paramètres de veille équilibrés appliqués"
            }
            ;;
            
        "operations_terrain")
            echo "Application des optimisations d'opérations terrain..."
            
            # Paramètres d'alimentation ultra-conservateurs
            sudo pmset -b displaysleep 1 sleep 3 disksleep 1 && {
                optimizations_applied+=("veille_ultra_conservative")
                echo "  ✅ Paramètres de veille ultra-conservateurs pour usage terrain"
            }
            
            # Désactiver tous les événements de réveil
            sudo pmset -b wake 0 womp 0 && {
                optimizations_applied+=("evenements_reveil_desactives")
                echo "  ✅ Événements de réveil désactivés pour conservation maximale"
            }
            
            # Activer l'hibernation
            sudo pmset -b hibernatemode 25 && {
                optimizations_applied+=("hibernation_activee")
                echo "  ✅ Hibernation activée pour veille prolongée"
            }
            ;;
            
        *)
            echo "❌ Profil d'optimisation inconnu : $optimization_profile"
            return 1
            ;;
    esac
    
    echo ""
    echo "Résultats d'Optimisation :"
    echo "  Profil : $optimization_profile"
    echo "  Optimisations Appliquées : ${#optimizations_applied[@]}"
    
    audit_log "Optimisation batterie terminée : $optimization_profile (${#optimizations_applied[@]} optimisations)"
    
    return 0
}

# Surveillance de conformité de batterie
monitor_battery_compliance() {
    local compliance_framework="$1"
    
    log_action "Début surveillance conformité batterie : $compliance_framework"
    
    echo "=== Surveillance de Conformité de Batterie ==="
    echo "Cadre : $compliance_framework"
    
    local violations=()
    local compliance_score=100
    
    case "$compliance_framework" in
        "entreprise_standard")
            # Vérifier la gestion de santé de batterie
            if ! pmset -g | grep -q "Battery Health Management.*1"; then
                violations+=("gestion_sante_batterie_desactivee")
                ((compliance_score -= 20))
            fi
            
            # Vérifier la charge optimisée
            if ! pmset -g | grep -q "Optimized Battery Charging.*1"; then
                violations+=("charge_optimisee_desactivee")
                ((compliance_score -= 15))
            fi
            ;;
            
        "conformite_environnementale")
            # Vérifier la condition de batterie
            local condition
            condition=$(system_profiler SPPowerDataType | grep "Condition:" | awk -F': ' '{print $2}' | tr -d ' ')
            
            if [[ "$condition" != "Normal" ]]; then
                violations+=("condition_batterie_degradee")
                ((compliance_score -= 30))
            fi
            
            # Vérifier le nombre de cycles
            local cycle_count
            cycle_count=$(system_profiler SPPowerDataType | grep "Cycle Count:" | awk -F': ' '{print $2}' | tr -d ' ')
            
            if [[ $cycle_count -gt 1000 ]]; then
                violations+=("nombre_cycles_eleve_preoccupation_environnementale")
                ((compliance_score -= 25))
            fi
            ;;
            
        *)
            echo "❌ Cadre de conformité inconnu : $compliance_framework"
            return 1
            ;;
    esac
    
    echo ""
    echo "Résultats de Conformité :"
    echo "  Cadre : $compliance_framework"
    echo "  Score de Conformité : $compliance_score/100"
    echo "  Violations Trouvées : ${#violations[@]}"
    
    if [[ ${#violations[@]} -gt 0 ]]; then
        echo "  Violations :"
        for violation in "${violations[@]}"; do
            echo "    - $violation"
        done
    else
        echo "  ✅ Aucune violation trouvée"
    fi
    
    audit_log "Surveillance conformité batterie terminée : $compliance_framework (Score : $compliance_score/100)"
    
    return 0
}

# Fonction principale avec routage de commandes
main() {
    local command="$1"
    shift
    
    case "$command" in
        "statut")
            # État de batterie amélioré
            get_comprehensive_battery_info
            ;;
        "verification_sante")
            monitor_battery_health "$@"
            ;;
        "optimiser")
            optimize_battery_performance "$@"
            ;;
        "appliquer_politique")
            apply_battery_policy "$@"
            ;;
        "verification_conformite")
            monitor_battery_compliance "$@"
            ;;
        "afficher_categories")
            print_battery_categories
            ;;
        *)
            echo "Système de Gestion de Batterie d'Entreprise Roundfleet"
            echo "Utilisation : $0 <commande> [options]"
            echo ""
            echo "Commandes :"
            echo "  statut                                    - État et informations de batterie améliorés"
            echo "  verification_sante <categorie> [profil]  - Surveillance complète de santé de batterie"
            echo "  optimiser <profil> [usage]               - Optimisation de performance de batterie"
            echo "  appliquer_politique <politique>          - Appliquer politique de gestion de batterie"
            echo "  verification_conformite <cadre>          - Surveillance de conformité de batterie"
            echo "  afficher_categories                      - Afficher catégories d'appareils à batterie"
            echo ""
            echo "Exemples :"
            echo "  $0 statut"
            echo "  $0 verification_sante mobile_direction entreprise"
            echo "  $0 optimiser autonomie_maximale mobile"
            echo "  $0 appliquer_politique entreprise_standard"
            echo "  $0 verification_conformite entreprise_standard"
            ;;
    esac
}

# Exécuter la fonction principale avec tous les arguments
main "$@"

Considérations de Sécurité

Sécurité de Batterie

  • Intégrité de Gestion d'Alimentation - S'assurer que les paramètres de gestion d'alimentation ne peuvent pas être altérés
  • Surveillance de Santé de Batterie - Surveillance continue pour problèmes de batterie liés à la sécurité
  • Gestion Thermique - Surveiller la température de batterie pour sécurité et protection
  • Sécurité de Charge - Protocoles de charge sécurisés et protection contre logiciels malveillants
  • Sécurité Physique - Surveiller altération ou remplacement de batterie

Cadre de Conformité

  • Conformité Environnementale - Élimination et conformité de cycle de vie de batterie
  • Santé et Sécurité - Surveillance de sécurité de batterie et signalement d'incidents
  • Efficacité Énergétique - Suivi et optimisation de consommation d'énergie
  • Gestion d'Actifs - Suivi de remplacement et maintenance de batterie
  • Pistes d'Audit - Journalisation complète de toutes les opérations liées à la batterie

Guide de Dépannage

Problèmes Courants

Batterie Ne Se Charge Pas

  • Vérifier fonctionnalité du câble et adaptateur de charge
  • Vérifier port de charge pour débris ou dommages
  • Réinitialiser SMC (Contrôleur de Gestion Système)
  • Vérifier restrictions de charge basées sur logiciel

Décharge Rapide de Batterie

  • Identifier applications énergivores avec Moniteur d'Activité
  • Vérifier processus en fuite consommant CPU
  • Vérifier luminosité d'écran et autres paramètres d'alimentation
  • Surveiller actualisation d'apps en arrière-plan et services de localisation

Dégradation de Santé de Batterie

  • Surveiller tendances de nombre de cycles et capacité
  • Vérifier exposition à températures extrêmes
  • Vérifier modèles de charge et optimiser pour longévité
  • Considérer remplacement de batterie basé sur métriques de santé

Commandes de Diagnostic

# État complet de batterie
pmset -g batt

# Santé détaillée de batterie
system_profiler SPPowerDataType

# Paramètres de gestion d'alimentation
pmset -g

# Informations de registre de batterie
ioreg -n AppleSmartBattery -r

# Surveillance d'état thermique
pmset -g therm

Notes Importantes

  • Sécurité de Batterie - Surveiller température et modèles de charge pour sécurité
  • Gestion de Cycle de Vie - Surveillance proactive et planification de remplacement
  • Impact Environnemental - Procédures appropriées d'élimination et recyclage de batterie
  • Impact sur Performance - Équilibrer optimisation batterie avec besoins de performance système
  • Exigences de Conformité - Maintenir conformité avec standards environnementaux et de sécurité
  • Documentation - Conserver enregistrements détaillés de santé et activités de maintenance de batterie

Gestion des Limites de Charge de Batterie sur macOS

Gérez les limites de charge de batterie sur vos appareils Roundfleet pour optimiser la santé de la batterie et implémenter des politiques de gestion d'énergie d'entreprise. Ce tutoriel couvre l'installation d'outils de gestion de batterie, la configuration des limites de charge, les paramètres de persistance et la surveillance complète de la santé de la batterie à l'échelle de la flotte.

Comprendre les Limites de Charge de Batterie

Les limites de charge de batterie aident à préserver la santé de la batterie en empêchant la surcharge, particulièrement bénéfique pour les appareils qui restent branchés en permanence :

Pourquoi Définir des Limites de Charge ?

  • Longévité de la Batterie - Évite la dégradation due à une charge constante à 100%
  • Réduction de la Chaleur - Les niveaux de charge plus bas génèrent moins de chaleur pendant le fonctionnement
  • Économies d'Entreprise - Une durée de vie de batterie prolongée réduit les coûts de remplacement
  • Gestion d'Énergie - Charge optimisée pour les postes de travail stationnaires
  • Avantages Environnementaux - Une durée de vie de batterie plus longue réduit les déchets électroniques

Outils de Gestion de Batterie

  • bclm - Battery Charge Limit Max (outil principal)
  • SMC - System Management Controller (interface matérielle)
  • Homebrew - Gestionnaire de paquets pour les outils macOS
  • Gestion d'Énergie - Contrôles d'énergie intégrés de macOS

Prérequis et Installation

Installer le Gestionnaire de Paquets Homebrew

#!/bin/bash

# Installer le gestionnaire de paquets Homebrew
echo "🍺 Installation du gestionnaire de paquets Homebrew..."

# Vérifier si Homebrew est déjà installé
if command -v brew >/dev/null 2>&1; then
    echo "ℹ️ Homebrew est déjà installé"
    echo "Version Homebrew : $(brew --version | head -1)"
else
    echo "Installation de Homebrew..."
    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
    
    if [[ $? -eq 0 ]]; then
        echo "✅ Homebrew installé avec succès"
    else
        echo "❌ Échec de l'installation de Homebrew"
        exit 1
    fi
fi

# Vérifier l'installation et ajouter au PATH si nécessaire
if [[ -f /opt/homebrew/bin/brew ]]; then
    echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
    eval "$(/opt/homebrew/bin/brew shellenv)"
elif [[ -f /usr/local/bin/brew ]]; then
    echo 'eval "$(/usr/local/bin/brew shellenv)"' >> ~/.zprofile
    eval "$(/usr/local/bin/brew shellenv)"
fi

echo "Emplacement Homebrew : $(which brew)"

Installer BCLM (Battery Charge Limit Max)

#!/bin/bash

# Installer bclm (Battery Charge Limit Max)
echo "🔋 Installation de bclm (Battery Charge Limit Max)..."

# S'assurer que Homebrew est disponible
if ! command -v brew >/dev/null 2>&1; then
    echo "❌ Homebrew non trouvé. Veuillez installer Homebrew d'abord."
    exit 1
fi

# Installer bclm
echo "Ajout du tap zackelia/formulae..."
if brew tap zackelia/formulae; then
    echo "✅ Tap ajouté avec succès"
else
    echo "❌ Échec de l'ajout du tap"
    exit 1
fi

echo "Installation de bclm..."
if brew install bclm; then
    echo "✅ bclm installé avec succès"
else
    echo "❌ Échec de l'installation de bclm"
    exit 1
fi

# Vérifier l'installation
if command -v bclm >/dev/null 2>&1; then
    echo "Emplacement bclm : $(which bclm)"
    echo "Test d'accès à bclm..."
    
    # Tester l'accès en lecture (ne nécessite pas sudo)
    if bclm read >/dev/null 2>&1; then
        echo "✅ bclm fonctionne correctement"
        echo "Limite de charge actuelle : $(bclm read)%"
    else
        echo "⚠️ bclm installé mais peut nécessiter des permissions supplémentaires"
    fi
else
    echo "❌ bclm non trouvé dans PATH"
    exit 1
fi

Gestion de Base des Limites de Charge

Définir la Limite de Charge

#!/bin/bash

# Définir la limite de charge de batterie
LIMIT="$1"

if [[ -z "$LIMIT" ]]; then
    echo "❌ Utilisation : $0 <pourcentage>"
    echo "Exemple : $0 80"
    echo "Plage valide : 50-100"
    exit 1
fi

# Valider la plage de limite
if [[ "$LIMIT" -lt 50 || "$LIMIT" -gt 100 ]]; then
    echo "❌ Limite invalide : $LIMIT"
    echo "La limite de charge doit être entre 50% et 100%"
    exit 1
fi

echo "🔋 Définition de la limite de charge de batterie à $LIMIT%..."

# Obtenir la limite actuelle pour comparaison
CURRENT_LIMIT=$(bclm read 2>/dev/null)
if [[ -n "$CURRENT_LIMIT" ]]; then
    echo "Limite actuelle : $CURRENT_LIMIT%"
fi

# Définir la nouvelle limite de charge
if sudo bclm write "$LIMIT"; then
    echo "✅ Limite de charge définie à $LIMIT%"
    
    # Vérifier le paramètre
    NEW_LIMIT=$(bclm read 2>/dev/null)
    if [[ "$NEW_LIMIT" == "$LIMIT" ]]; then
        echo "✅ Paramètre vérifié : $NEW_LIMIT%"
    else
        echo "⚠️ Attention : La vérification montre $NEW_LIMIT% au lieu de $LIMIT%"
    fi
else
    echo "❌ Échec de définition de la limite de charge"
    exit 1
fi

echo "ℹ️ Note : Un redémarrage peut être nécessaire pour que les changements prennent effet"

Lire la Limite de Charge Actuelle

#!/bin/bash

# Lire la limite de charge de batterie actuelle
echo "🔍 Lecture de la limite de charge de batterie actuelle..."

if command -v bclm >/dev/null 2>&1; then
    CURRENT_LIMIT=$(bclm read 2>/dev/null)
    
    if [[ -n "$CURRENT_LIMIT" ]]; then
        echo "Limite de charge actuelle : $CURRENT_LIMIT%"
        
        # Fournir le contexte sur la limite
        if [[ "$CURRENT_LIMIT" -eq 100 ]]; then
            echo "ℹ️ La batterie se charge à la capacité maximale (aucune limite définie)"
        elif [[ "$CURRENT_LIMIT" -ge 80 ]]; then
            echo "ℹ️ Limite de charge conservatrice pour la longévité de la batterie"
        elif [[ "$CURRENT_LIMIT" -ge 60 ]]; then
            echo "ℹ️ Limite de charge modérée pour une durée de vie de batterie prolongée"
        else
            echo "ℹ️ Limite de charge agressive pour une préservation maximale de la batterie"
        fi
    else
        echo "❌ Impossible de lire la limite de charge"
        echo "Cela peut indiquer que bclm n'est pas correctement installé ou manque de permissions"
        exit 1
    fi
else
    echo "❌ bclm non trouvé. Veuillez installer bclm d'abord."
    exit 1
fi

Rendre la Limite de Charge Persistante

#!/bin/bash

# Rendre la limite de charge persistante entre les redémarrages
echo "💾 Rendre la limite de charge persistante..."

if ! command -v bclm >/dev/null 2>&1; then
    echo "❌ bclm non trouvé. Veuillez installer bclm d'abord."
    exit 1
fi

# Obtenir la limite actuelle
CURRENT_LIMIT=$(bclm read 2>/dev/null)
if [[ -z "$CURRENT_LIMIT" ]]; then
    echo "❌ Impossible de lire la limite de charge actuelle"
    exit 1
fi

echo "Limite de charge actuelle : $CURRENT_LIMIT%"

# Rendre persistant
if sudo bclm persist; then
    echo "✅ La limite de charge ($CURRENT_LIMIT%) est maintenant persistante"
    echo "La limite sera maintenue lors des redémarrages du système"
else
    echo "❌ Échec de rendre la limite de charge persistante"
    exit 1
fi

echo "ℹ️ Pour supprimer la persistance, exécutez : sudo bclm unpersist"

Supprimer la Persistance de la Limite de Charge

#!/bin/bash

# Supprimer la persistance de la limite de charge
echo "🔄 Suppression de la persistance de la limite de charge..."

if ! command -v bclm >/dev/null 2>&1; then
    echo "❌ bclm non trouvé. Veuillez installer bclm d'abord."
    exit 1
fi

# Obtenir la limite actuelle avant de supprimer la persistance
CURRENT_LIMIT=$(bclm read 2>/dev/null)
if [[ -n "$CURRENT_LIMIT" ]]; then
    echo "Limite de charge actuelle : $CURRENT_LIMIT%"
fi

# Supprimer la persistance
if sudo bclm unpersist; then
    echo "✅ Persistance de la limite de charge supprimée"
    echo "La limite peut se réinitialiser à 100% après le prochain redémarrage"
else
    echo "❌ Échec de suppression de la persistance de la limite de charge"
    exit 1
fi

echo "ℹ️ Pour restaurer la persistance, exécutez : sudo bclm persist"

Gestion Avancée de Batterie

Vérification de Santé de Batterie d'Entreprise

#!/bin/bash

# Évaluation complète de la santé de la batterie
echo "🔋 Exécution d'une vérification complète de la santé de la batterie..."

# Vérifier si une batterie est présente
if ! system_profiler SPPowerDataType | grep -q "Battery Information"; then
    echo "❌ Aucune batterie trouvée sur ce système"
    exit 1
fi

echo "=== Évaluation de la Santé de la Batterie ==="

# Obtenir les informations de batterie
BATTERY_INFO=$(system_profiler SPPowerDataType)

# Extraire les métriques clés
CYCLE_COUNT=$(echo "$BATTERY_INFO" | grep "Cycle Count" | awk '{print $3}')
CONDITION=$(echo "$BATTERY_INFO" | grep "Condition" | awk '{print $2}')
MAX_CAPACITY=$(echo "$BATTERY_INFO" | grep "Maximum Capacity" | awk '{print $3}' | sed 's/%//')

echo "État de la Batterie : $CONDITION"
echo "Nombre de Cycles : $CYCLE_COUNT"
echo "Capacité Maximale : $MAX_CAPACITY%"

# Vérifier la limite de charge si bclm est disponible
if command -v bclm >/dev/null 2>&1; then
    CHARGING_LIMIT=$(bclm read 2>/dev/null)
    if [[ -n "$CHARGING_LIMIT" ]]; then
        echo "Limite de Charge : $CHARGING_LIMIT%"
    else
        echo "Limite de Charge : Non définie (100%)"
    fi
fi

# Informations sur l'adaptateur secteur
POWER_ADAPTER=$(echo "$BATTERY_INFO" | grep -A5 "AC Charger Information" | grep "Wattage" | awk '{print $2}')
if [[ -n "$POWER_ADAPTER" ]]; then
    echo "Adaptateur Secteur : ${POWER_ADAPTER}W"
fi

# Évaluation de santé
echo -e "\n=== Évaluation de Santé ==="

if [[ "$CONDITION" == "Normal" ]]; then
    echo "✅ L'état de la batterie est normal"
elif [[ "$CONDITION" == "Replace Soon" ]]; then
    echo "⚠️ La batterie devrait être remplacée bientôt"
elif [[ "$CONDITION" == "Replace Now" ]]; then
    echo "❌ La batterie nécessite un remplacement immédiat"
else
    echo "ℹ️ État de la batterie : $CONDITION"
fi

if [[ -n "$CYCLE_COUNT" ]]; then
    if [[ "$CYCLE_COUNT" -lt 500 ]]; then
        echo "✅ Le nombre de cycles est sain ($CYCLE_COUNT cycles)"
    elif [[ "$CYCLE_COUNT" -lt 1000 ]]; then
        echo "⚠️ Nombre de cycles modéré ($CYCLE_COUNT cycles)"
    else
        echo "❌ Nombre de cycles élevé ($CYCLE_COUNT cycles)"
    fi
fi

if [[ -n "$MAX_CAPACITY" ]]; then
    if [[ "$MAX_CAPACITY" -ge 90 ]]; then
        echo "✅ La capacité maximale est excellente ($MAX_CAPACITY%)"
    elif [[ "$MAX_CAPACITY" -ge 80 ]]; then
        echo "✅ La capacité maximale est bonne ($MAX_CAPACITY%)"
    elif [[ "$MAX_CAPACITY" -ge 70 ]]; then
        echo "⚠️ La capacité maximale décline ($MAX_CAPACITY%)"
    else
        echo "❌ La capacité maximale est faible ($MAX_CAPACITY%)"
    fi
fi

# Recommandations
echo -e "\n=== Recommandations ==="

if [[ -z "$CHARGING_LIMIT" || "$CHARGING_LIMIT" -eq 100 ]]; then
    echo "💡 Considérez définir une limite de charge (80-90%) pour une meilleure santé de batterie"
fi

if [[ "$CYCLE_COUNT" -gt 1000 && "$MAX_CAPACITY" -lt 80 ]]; then
    echo "💡 Un remplacement de batterie peut être nécessaire bientôt"
fi

Script de Gestion de Batterie d'Entreprise

#!/bin/bash

# Outil de Gestion de Batterie Roundfleet
# Gestion complète des limites de charge de batterie pour les environnements d'entreprise

# Configuration
LOG_FILE="/var/log/roundfleet_battery.log"
BACKUP_DIR="/var/backups/roundfleet/battery"
REPORT_DIR="/var/reports/roundfleet/battery"
CONFIG_FILE="/etc/roundfleet/battery_policy.conf"

# Paramètres de politique de batterie
DEFAULT_CHARGING_LIMIT=80
MOBILE_DEVICE_LIMIT=90
STATIONARY_DEVICE_LIMIT=75
ENFORCE_BATTERY_POLICY=false
HEALTH_CHECK_ENABLED=true
ALERT_CAPACITY_THRESHOLD=80
ALERT_CYCLE_THRESHOLD=1000

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

# Vérifier si l'appareil a une batterie
check_battery_presence() {
    if system_profiler SPPowerDataType | grep -q "Battery Information"; then
        return 0
    else
        echo "❌ Aucune batterie trouvée sur ce système"
        log_action "Aucune batterie détectée sur l'appareil"
        return 1
    fi
}

# Installer les outils de gestion de batterie
install_battery_tools() {
    echo "🔧 Installation des outils de gestion de batterie..."
    log_action "Installation des outils de batterie démarrée"
    
    # Vérifier et installer Homebrew
    if ! command -v brew >/dev/null 2>&1; then
        echo "Installation de Homebrew..."
        /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
        
        # Ajouter au PATH
        if [[ -f /opt/homebrew/bin/brew ]]; then
            eval "$(/opt/homebrew/bin/brew shellenv)"
        elif [[ -f /usr/local/bin/brew ]]; then
            eval "$(/usr/local/bin/brew shellenv)"
        fi
    fi
    
    # Installer bclm
    if ! command -v bclm >/dev/null 2>&1; then
        echo "Installation de bclm..."
        brew tap zackelia/formulae && brew install bclm
        
        if command -v bclm >/dev/null 2>&1; then
            echo "✅ bclm installé avec succès"
            log_action "bclm installé avec succès"
        else
            echo "❌ Échec de l'installation de bclm"
            log_action "Échec de l'installation de bclm"
            return 1
        fi
    else
        echo "✅ bclm déjà installé"
    fi
    
    return 0
}

# Générer un rapport de batterie complet
generate_battery_report() {
    local report_file="$REPORT_DIR/rapport_batterie_$(date +%Y%m%d_%H%M%S).txt"
    
    echo "📊 Génération d'un rapport de batterie complet..."
    
    {
        echo "Rapport de Gestion de Batterie Roundfleet"
        echo "Généré : $(date)"
        echo "Appareil : $(hostname)"
        echo "================================="
        echo ""
        
        # Informations système
        echo "=== Informations Système ==="
        system_profiler SPHardwareDataType | grep -E "Model Name|Model Identifier|Processor|Memory"
        echo ""
        
        # Informations matérielles de batterie
        echo "=== Informations Matérielles de Batterie ==="
        local battery_info=$(system_profiler SPPowerDataType)
        echo "$battery_info" | grep -E "Condition|Cycle Count|Maximum Capacity|Manufacturer|Device Name|Pack Lot Code|PCB Lot Code|Firmware Version|Hardware Revision"
        echo ""
        
        # Limite de charge actuelle
        echo "=== Configuration de Limite de Charge ==="
        if command -v bclm >/dev/null 2>&1; then
            local current_limit=$(bclm read 2>/dev/null)
            if [[ -n "$current_limit" ]]; then
                echo "Limite de charge actuelle : $current_limit%"
                
                # Vérifier si persistant
                local smc_value=$(sudo bclm read 2>/dev/null)
                if [[ "$smc_value" == "$current_limit" ]]; then
                    echo "État de persistance : Activé"
                else
                    echo "État de persistance : Désactivé"
                fi
            else
                echo "Limite de charge actuelle : Non définie (100%)"
                echo "État de persistance : N/A"
            fi
        else
            echo "État bclm : Non installé"
        fi
        echo ""
        
        # Analyse d'utilisation d'énergie
        echo "=== Analyse d'Utilisation d'Énergie ==="
        echo "Adaptateur secteur connecté : $(system_profiler SPPowerDataType | grep -q "AC Charger Information" && echo "Oui" || echo "Non")"
        
        # Obtenir les infos de l'adaptateur si disponible
        local adapter_info=$(system_profiler SPPowerDataType | grep -A3 "AC Charger Information" | grep "Wattage")
        if [[ -n "$adapter_info" ]]; then
            echo "Adaptateur secteur : $(echo "$adapter_info" | awk '{print $2}')W"
        fi
        
        # Température et tension de batterie
        echo "Température batterie : $(ioreg -l | grep Temperature | awk '{print $3/100}' | head -1)°C"
        echo "Tension batterie : $(ioreg -l | grep Voltage | awk '{print $3/1000}' | head -1)V"
        echo ""
        
        # Évaluation de santé
        echo "=== Évaluation de Santé de Batterie ==="
        local condition=$(echo "$battery_info" | grep "Condition" | awk '{print $2}')
        local cycle_count=$(echo "$battery_info" | grep "Cycle Count" | awk '{print $3}')
        local max_capacity=$(echo "$battery_info" | grep "Maximum Capacity" | awk '{print $3}' | sed 's/%//')
        
        echo "État général : $condition"
        echo "Nombre de cycles : $cycle_count"
        echo "Capacité maximale : $max_capacity%"
        
        # Score de santé
        local health_score=100
        
        if [[ "$condition" != "Normal" ]]; then
            health_score=$((health_score - 20))
        fi
        
        if [[ -n "$cycle_count" ]]; then
            if [[ "$cycle_count" -gt 1000 ]]; then
                health_score=$((health_score - 20))
            elif [[ "$cycle_count" -gt 500 ]]; then
                health_score=$((health_score - 10))
            fi
        fi
        
        if [[ -n "$max_capacity" ]]; then
            if [[ "$max_capacity" -lt 70 ]]; then
                health_score=$((health_score - 30))
            elif [[ "$max_capacity" -lt 80 ]]; then
                health_score=$((health_score - 20))
            elif [[ "$max_capacity" -lt 90 ]]; then
                health_score=$((health_score - 10))
            fi
        fi
        
        echo "Score de santé : $health_score/100"
        
        # Recommandations
        echo ""
        echo "=== Recommandations ==="
        
        if [[ "$health_score" -ge 80 ]]; then
            echo "✅ La santé de la batterie est bonne"
            echo "💡 Considérez définir une limite de charge à 80% pour une longévité optimale"
        elif [[ "$health_score" -ge 60 ]]; then
            echo "⚠️ La santé de la batterie décline"
            echo "💡 Définissez une limite de charge à 75% et surveillez de près"
        else
            echo "❌ La santé de la batterie est faible"
            echo "💡 Considérez un remplacement de batterie et une limite immédiate à 70%"
        fi
        
    } > "$report_file"
    
    echo "📊 Rapport sauvegardé dans : $report_file"
    log_action "Rapport de batterie généré : $report_file"
}

# Fonction d'exécution principale
main() {
    local action="${1:-aide}"
    local target="${2:-}"
    
    log_action "=== Gestion de Batterie Roundfleet Démarrée ==="
    
    setup_directories
    
    case "$action" in
        "installer"|"configurer")
            install_battery_tools
            ;;
        "rapport"|"statut")
            generate_battery_report
            ;;
        "definir")
            if [[ -n "$target" ]]; then
                echo "🔋 Définition de la limite de charge à $target%..."
                if sudo bclm write "$target"; then
                    echo "✅ Limite de charge définie à $target%"
                    if sudo bclm persist; then
                        echo "✅ Limite de charge rendue persistante"
                    fi
                else
                    echo "❌ Échec de définition de la limite de charge"
                fi
            else
                echo "❌ Veuillez spécifier un pourcentage"
                echo "Usage: $0 definir <pourcentage>"
            fi
            ;;
        "lire"|"actuel")
            if command -v bclm >/dev/null 2>&1; then
                local current=$(bclm read 2>/dev/null)
                if [[ -n "$current" ]]; then
                    echo "Limite de charge actuelle : $current%"
                else
                    echo "Aucune limite de charge définie (100%)"
                fi
            else
                echo "❌ bclm non installé"
            fi
            ;;
        "persistant")
            if command -v bclm >/dev/null 2>&1; then
                if sudo bclm persist; then
                    echo "✅ Limite de charge rendue persistante"
                else
                    echo "❌ Échec de rendre la limite de charge persistante"
                fi
            else
                echo "❌ bclm non installé"
            fi
            ;;
        "non-persistant")
            if command -v bclm >/dev/null 2>&1; then
                if sudo bclm unpersist; then
                    echo "✅ Persistance de limite de charge supprimée"
                else
                    echo "❌ Échec de suppression de la persistance de limite de charge"
                fi
            else
                echo "❌ bclm non installé"
            fi
            ;;
        "sante")
            if check_battery_presence; then
                local battery_info=$(system_profiler SPPowerDataType)
                echo "=== Résumé de Santé de Batterie ==="
                echo "$battery_info" | grep -E "Condition|Cycle Count|Maximum Capacity"
                
                if command -v bclm >/dev/null 2>&1; then
                    local limit=$(bclm read 2>/dev/null)
                    echo "Limite de Charge : ${limit:-100}%"
                fi
            fi
            ;;
        "aide"|*)
            echo "Outil de Gestion de Batterie Roundfleet"
            echo "Utilisation : $0 [action] [options]"
            echo ""
            echo "Actions :"
            echo "  installer             - Installer les outils de gestion de batterie (Homebrew, bclm)"
            echo "  rapport               - Générer un rapport de batterie complet"
            echo "  definir [limite]      - Définir la limite de charge optimale (ou spécifier pourcentage)"
            echo "  lire                  - Lire la limite de charge actuelle"
            echo "  persistant            - Rendre la limite de charge persistante"
            echo "  non-persistant        - Supprimer la persistance de limite de charge"
            echo "  sante                 - Afficher le résumé de santé de batterie"
            echo "  aide                  - Afficher ce message d'aide"
            echo ""
            echo "Exemples :"
            echo "  $0 installer          # Installer les outils requis"
            echo "  $0 definir 80         # Définir limite de charge à 80%"
            echo "  $0 rapport            # Générer rapport détaillé"
            echo ""
            echo "Directives de Limite de Charge :"
            echo "  50-100%     - Plage valide"
            echo "  80%         - Recommandé pour la plupart des appareils"
            echo "  75%         - Pour les Macs stationnaires/de bureau"
            echo "  90%         - Pour les Macs mobiles/portables"
            ;;
    esac
    
    log_action "=== Gestion de Batterie Roundfleet Terminée ==="
}

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

Surveillance de la Santé de Batterie

Vérifications de Santé Automatisées

# Planifier la surveillance quotidienne de la santé de batterie
create_health_monitoring_schedule() {
    local plist_file="$HOME/Library/LaunchAgents/com.roundfleet.battery-monitor.plist"
    
    cat > "$plist_file" << 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.roundfleet.battery-monitor</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>$(realpath "$0")</string>
        <string>surveiller</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>9</integer>
        <key>Minute</key>
        <integer>0</integer>
    </dict>
    <key>RunAtLoad</key>
    <false/>
</dict>
</plist>
EOF
    
    launchctl load "$plist_file" 2>/dev/null
    echo "✅ Surveillance quotidienne de batterie planifiée"
}

Recommandations de Limite de Charge

Type d'AppareilLimite RecommandéeRaisonnement
Macs de Bureau75-80%Toujours branchés, prioriser la longévité
Macs Portables (Stationnaires)80%Utilisation principalement branchée
Macs Portables (Mobiles)90%Besoin de capacité plus élevée pour la portabilité
Batterie Dégradée70-75%Prolonger la durée de vie de batterie restante

Notes Importantes

  • Appareils Supportés - Fonctionne uniquement sur les appareils Mac avec batteries
  • Privilèges Administrateur - Nécessite un accès sudo pour les modifications SMC
  • Compatibilité Système - Compatible avec les versions macOS modernes
  • Exigences de Redémarrage - Certains changements peuvent nécessiter un redémarrage système
  • Déploiement de Flotte - Testez sur des appareils individuels avant le déploiement en bloc
  • Santé de Batterie - Surveillez régulièrement l'état de la batterie pour des résultats optimaux

Gérer les Notifications de Badge sur macOS

Contrôlez et gérez les notifications de badge sur vos appareils Roundfleet en utilisant des outils en ligne de commande. Ce tutoriel couvre la suppression des notifications de badge pour des applications spécifiques, la gestion des notifications à l'échelle du dock, et l'implémentation de politiques de notification d'entreprise.

Comprendre les Notifications de Badge

Les notifications de badge sont les icônes circulaires rouges qui apparaissent au-dessus des icônes d'application dans le dock macOS, affichant généralement des numéros pour indiquer des éléments non lus comme des emails, messages ou mises à jour. Bien qu'utiles pour les utilisateurs individuels, les environnements d'entreprise nécessitent souvent une gestion centralisée des notifications.

Fonctionnalités Clés

  • Contrôle spécifique aux applications - Supprimer les badges d'applications individuelles
  • Gestion à l'échelle du dock - Effacer toutes les notifications de badge en une fois
  • Restauration automatique - Les badges réapparaissent lorsque les applications sont ouvertes
  • Intégration des préférences système - Fonctionne avec les paramètres de notification macOS

Supprimer les Notifications de Badge pour des Applications Spécifiques

Suppression de Badge Basique

#!/bin/bash

# Supprimer la notification de badge pour une application spécifique
APP_NAME="Mail"

defaults write com.apple.systempreferences AttentionPrefBundleIDs 0 && killall "$APP_NAME"

echo "Notification de badge supprimée pour $APP_NAME"

Gestion Avancée des Badges d'Application

#!/bin/bash

# Suppression avancée de notification de badge avec validation
remove_app_badge() {
    local app_name="$1"
    
    if [[ -z "$app_name" ]]; then
        echo "❌ Erreur : Nom d'application requis"
        return 1
    fi
    
    # Vérifier si l'application est en cours d'exécution
    if pgrep -x "$app_name" > /dev/null; then
        echo "📱 Suppression de la notification de badge pour $app_name..."
        
        # Supprimer la notification de badge
        defaults write com.apple.systempreferences AttentionPrefBundleIDs 0
        
        # Terminer et redémarrer l'application pour appliquer les changements
        if killall "$app_name" 2>/dev/null; then
            echo "✅ Notification de badge supprimée pour $app_name"
            echo "ℹ️  Note : Le badge réapparaîtra lorsque $app_name sera rouvert"
        else
            echo "⚠️  Avertissement : Impossible de redémarrer $app_name (peut ne pas être en cours d'exécution)"
        fi
    else
        echo "ℹ️  $app_name n'est pas actuellement en cours d'exécution"
        defaults write com.apple.systempreferences AttentionPrefBundleIDs 0
        echo "✅ Paramètre de notification de badge mis à jour pour $app_name"
    fi
}

# Exemples d'utilisation
remove_app_badge "Mail"
remove_app_badge "Messages"
remove_app_badge "Calendar"

Suppression des Badges d'Applications Communes

#!/bin/bash

# Supprimer les notifications de badge pour les applications d'entreprise communes
COMMON_APPS=("Mail" "Messages" "Calendar" "Reminders" "FaceTime" "App Store" "System Preferences")

echo "🧹 Suppression des notifications de badge pour les applications communes..."

for app in "${COMMON_APPS[@]}"; do
    echo "Traitement de $app..."
    
    # Définir la préférence de notification
    defaults write com.apple.systempreferences AttentionPrefBundleIDs 0
    
    # Tuer l'application si elle est en cours d'exécution
    if pgrep -x "$app" > /dev/null; then
        killall "$app" 2>/dev/null
        echo "  ✅ Badge effacé pour $app"
    else
        echo "  ⏭️  $app n'est pas en cours d'exécution, préférence mise à jour"
    fi
done

echo "🎉 Nettoyage des notifications de badge terminé"

Supprimer Toutes les Notifications de Badge (À l'échelle du Dock)

Effacement Basique des Badges du Dock

#!/bin/bash

# Supprimer toutes les notifications de badge du dock
defaults write com.apple.systempreferences AttentionPrefBundleIDs 0 && killall Dock

echo "Toutes les notifications de badge effacées du dock"

Gestion Complète du Dock

#!/bin/bash

# Gestion avancée des notifications de badge du dock
clear_all_dock_badges() {
    echo "🏗️  Effacement de toutes les notifications de badge du dock..."
    
    # Sauvegarder les préférences actuelles du dock
    local backup_file="/tmp/dock_backup_$(date +%s).plist"
    defaults export com.apple.dock "$backup_file"
    echo "📋 Préférences du dock sauvegardées dans : $backup_file"
    
    # Effacer les notifications de badge
    defaults write com.apple.systempreferences AttentionPrefBundleIDs 0
    
    # Obtenir la liste des applications du dock en cours d'exécution
    local dock_apps
    dock_apps=$(osascript -e 'tell application "System Events" to get name of every application process whose background only is false')
    
    echo "🔄 Redémarrage du dock pour appliquer les changements..."
    killall Dock
    
    # Attendre que le dock redémarre
    sleep 3
    
    echo "✅ Toutes les notifications de badge effacées du dock"
    echo "ℹ️  Les badges réapparaîtront lorsque les applications recevront de nouvelles notifications"
    
    return 0
}

# Exécuter l'effacement des badges du dock
clear_all_dock_badges

Script de Gestion des Badges d'Entreprise

#!/bin/bash

# Outil de Gestion des Notifications de Badge Roundfleet
# Contrôle centralisé des notifications de badge sur les appareils d'entreprise

# Configuration
LOG_FILE="/var/log/roundfleet_badges.log"
CONFIG_FILE="/etc/roundfleet/badge_policy.conf"
BACKUP_DIR="/var/backups/roundfleet/badges"

# 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() {
    sudo mkdir -p "$(dirname "$LOG_FILE")" 2>/dev/null
    sudo mkdir -p "$BACKUP_DIR" 2>/dev/null
    sudo mkdir -p "$(dirname "$CONFIG_FILE")" 2>/dev/null
}

# Charger la configuration
load_config() {
    if [[ -f "$CONFIG_FILE" ]]; then
        source "$CONFIG_FILE"
        log_action "Configuration chargée depuis $CONFIG_FILE"
    else
        # Configuration par défaut
        BADGE_POLICY="disabled"  # Options : enabled, disabled, selective
        ALLOWED_BADGE_APPS=("Calendar" "Reminders")
        BLOCKED_BADGE_APPS=("App Store" "System Preferences")
        AUTO_CLEAR_INTERVAL=3600  # 1 heure
        
        log_action "Utilisation de la configuration par défaut"
    fi
}

# Sauvegarder les paramètres de badge actuels
backup_badge_settings() {
    local backup_file="$BACKUP_DIR/badge_settings_$(date +%Y%m%d_%H%M%S).plist"
    
    defaults export com.apple.systempreferences "$backup_file" 2>/dev/null
    
    if [[ -f "$backup_file" ]]; then
        log_action "Paramètres de badge sauvegardés dans : $backup_file"
        echo "$backup_file"
    else
        log_action "Avertissement : Impossible de sauvegarder les paramètres de badge"
        return 1
    fi
}

# Obtenir le statut actuel des badges
get_badge_status() {
    echo "📊 Statut Actuel des Notifications de Badge :"
    echo "============================================="
    
    # Vérifier le paramètre des préférences système
    local attention_pref
    attention_pref=$(defaults read com.apple.systempreferences AttentionPrefBundleIDs 2>/dev/null || echo "non défini")
    echo "AttentionPrefBundleIDs du système : $attention_pref"
    
    # Lister les applications avec des badges potentiels
    echo -e "\n📱 Applications pouvant afficher des badges :"
    local badge_apps=("Mail" "Messages" "Calendar" "Reminders" "FaceTime" "App Store" "System Preferences" "Contacts")
    
    for app in "${badge_apps[@]}"; do
        if pgrep -x "$app" > /dev/null; then
            echo "  🟢 $app (en cours d'exécution)"
        else
            echo "  ⚪ $app (non en cours d'exécution)"
        fi
    done
    
    # Vérifier les processus du dock
    echo -e "\n🏗️  Statut du dock :"
    if pgrep -x "Dock" > /dev/null; then
        echo "  🟢 Le dock est en cours d'exécution"
    else
        echo "  🔴 Le dock n'est pas en cours d'exécution"
    fi
}

# Appliquer la politique de badge
apply_badge_policy() {
    log_action "Application de la politique de badge : $BADGE_POLICY"
    
    case "$BADGE_POLICY" in
        "disabled")
            disable_all_badges
            ;;
        "enabled")
            enable_all_badges
            ;;
        "selective")
            apply_selective_policy
            ;;
        *)
            log_action "Erreur : Politique de badge inconnue : $BADGE_POLICY"
            return 1
            ;;
    esac
}

# Désactiver toutes les notifications de badge
disable_all_badges() {
    echo "🚫 Désactivation de toutes les notifications de badge..."
    
    backup_badge_settings
    
    # Définir la préférence système pour désactiver les badges
    defaults write com.apple.systempreferences AttentionPrefBundleIDs 0
    
    # Redémarrer le dock pour appliquer les changements
    killall Dock 2>/dev/null
    
    log_action "Toutes les notifications de badge désactivées"
    echo "✅ Toutes les notifications de badge ont été désactivées"
}

# Activer toutes les notifications de badge
enable_all_badges() {
    echo "✅ Activation de toutes les notifications de badge..."
    
    backup_badge_settings
    
    # Supprimer la restriction (supprimer la clé)
    defaults delete com.apple.systempreferences AttentionPrefBundleIDs 2>/dev/null
    
    # Redémarrer le dock pour appliquer les changements
    killall Dock 2>/dev/null
    
    log_action "Toutes les notifications de badge activées"
    echo "✅ Toutes les notifications de badge ont été activées"
}

# Appliquer une politique de badge sélective
apply_selective_policy() {
    echo "🎯 Application de la politique de badge sélective..."
    
    # Pour la politique sélective, nous devons gérer les notifications d'applications individuelles
    # Cela nécessite une gestion plus complexe des notifications
    
    for app in "${BLOCKED_BADGE_APPS[@]}"; do
        echo "  🚫 Blocage des badges pour $app"
        # Tuer l'application pour effacer le badge actuel
        killall "$app" 2>/dev/null
    done
    
    # Définir la préférence système
    defaults write com.apple.systempreferences AttentionPrefBundleIDs 0
    
    log_action "Politique de badge sélective appliquée"
    echo "✅ La politique de badge sélective a été appliquée"
}

# Surveiller et effacer automatiquement les badges
monitor_badges() {
    echo "👁️  Démarrage de la surveillance des badges (intervalle : ${AUTO_CLEAR_INTERVAL}s)..."
    
    while true; do
        if [[ "$BADGE_POLICY" == "disabled" ]]; then
            # Effacer les badges qui peuvent être apparus
            defaults write com.apple.systempreferences AttentionPrefBundleIDs 0
            log_action "Badges effacés automatiquement pendant la surveillance"
        fi
        
        sleep "$AUTO_CLEAR_INTERVAL"
    done
}

# Générer un rapport de gestion des badges
generate_report() {
    local report_file="/tmp/roundfleet_badge_report_$(date +%Y%m%d_%H%M%S).txt"
    
    {
        echo "Rapport de Notification de Badge Roundfleet"
        echo "========================================"
        echo "Généré : $(date)"
        echo "Nom d'hôte : $(hostname)"
        echo "Politique : $BADGE_POLICY"
        echo ""
        
        echo "Configuration Système :"
        echo "  AttentionPrefBundleIDs : $(defaults read com.apple.systempreferences AttentionPrefBundleIDs 2>/dev/null || echo 'non défini')"
        echo ""
        
        echo "Statut des Applications :"
        local apps=("Mail" "Messages" "Calendar" "Reminders" "App Store")
        for app in "${apps[@]}"; do
            if pgrep -x "$app" > /dev/null; then
                echo "  $app : En cours d'exécution"
            else
                echo "  $app : Non en cours d'exécution"
            fi
        done
        
        echo ""
        echo "Actions Récentes :"
        tail -10 "$LOG_FILE" 2>/dev/null || echo "Aucune entrée de journal récente"
        
    } > "$report_file"
    
    echo "📄 Rapport de gestion des badges généré : $report_file"
    log_action "Rapport généré : $report_file"
}

# Fonction principale
main() {
    case "${1:-status}" in
        "disable")
            setup_directories
            load_config
            log_action "=== Gestion des Badges : Désactiver Tout ==="
            disable_all_badges
            ;;
        "enable")
            setup_directories
            load_config
            log_action "=== Gestion des Badges : Activer Tout ==="
            enable_all_badges
            ;;
        "policy")
            setup_directories
            load_config
            log_action "=== Gestion des Badges : Appliquer la Politique ==="
            apply_badge_policy
            ;;
        "monitor")
            setup_directories
            load_config
            log_action "=== Gestion des Badges : Démarrer la Surveillance ==="
            monitor_badges
            ;;
        "report")
            setup_directories
            load_config
            generate_report
            ;;
        "status"|*)
            setup_directories
            load_config
            get_badge_status
            ;;
    esac
}

# Exécuter la fonction principale avec les paramètres
main "$@"

Commandes de Gestion des Notifications de Badge

Référence Rapide

TâcheCommande
Supprimer badge d'app spécifiquedefaults write com.apple.systempreferences AttentionPrefBundleIDs 0 && killall NomApp
Effacer tous les badges du dockdefaults write com.apple.systempreferences AttentionPrefBundleIDs 0 && killall Dock
Vérifier paramètre actueldefaults read com.apple.systempreferences AttentionPrefBundleIDs
Réinitialiser par défautdefaults delete com.apple.systempreferences AttentionPrefBundleIDs

Exemples Spécifiques aux Applications

# Applications communes
defaults write com.apple.systempreferences AttentionPrefBundleIDs 0 && killall Mail
defaults write com.apple.systempreferences AttentionPrefBundleIDs 0 && killall Messages
defaults write com.apple.systempreferences AttentionPrefBundleIDs 0 && killall "App Store"
defaults write com.apple.systempreferences AttentionPrefBundleIDs 0 && killall Calendar

Dépannage

Problèmes Courants

  1. Les badges réapparaissent après effacement

    • C'est un comportement normal ; les badges reviennent lorsque les applications reçoivent de nouvelles notifications
    • Utilisez des scripts de surveillance pour un effacement persistant
  2. L'application ne redémarre pas

    • Certaines applications peuvent nécessiter un redémarrage manuel
    • Vérifiez si l'application est configurée pour se lancer au démarrage
  3. Le dock ne se met pas à jour

    • Essayez de forcer le redémarrage du dock : killall Dock && sleep 2 && open /System/Library/CoreServices/Dock.app

Commandes de Vérification

# Vérifier si le paramètre a été appliqué
defaults read com.apple.systempreferences AttentionPrefBundleIDs

# Lister les applications en cours d'exécution
ps aux | grep -E "(Mail|Messages|Calendar)" | grep -v grep

# Surveiller le processus du dock
ps aux | grep Dock | grep -v grep

Notes Importantes

  • Effet temporaire : Les notifications de badge réapparaîtront lorsque les applications recevront de nouvelles notifications
  • Redémarrage système : Les paramètres persistent après les redémarrages système
  • Expérience utilisateur : Considérez les besoins de notification des utilisateurs avant le déploiement à l'échelle de l'entreprise
  • Comportement des applications : Certaines applications peuvent nécessiter un redémarrage manuel pour appliquer les changements
  • Tests recommandés : Testez sur des appareils individuels avant le déploiement à l'échelle de la flotte

Déploiement d'Entreprise

Pour le déploiement d'entreprise, considérez :

  1. Configuration de politique - Définir quelles applications doivent afficher des badges
  2. Communication utilisateur - Informer les utilisateurs des changements de notification
  3. Configuration de surveillance - Implémenter un effacement automatique des badges si requis
  4. Procédures de sauvegarde - Sauvegarder les paramètres originaux avant d'apporter des modifications
  5. Plan de retour en arrière - Capacité à restaurer le comportement de notification original

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 Roundfleet. 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.roundfleet.${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/roundfleet_${app_name}.error.log</string>
    <key>StandardOutPath</key>
    <string>/var/log/roundfleet_${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 Roundfleet
# Contrôle centralisé des lancements automatiques d'applications sur les appareils de flotte

# Configuration
LOG_FILE="/var/log/roundfleet_app_launch.log"
CONFIG_DIR="/etc/roundfleet/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.roundfleet.${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/roundfleet_$(echo "$app_name" | tr ' ' '_').error.log</string>
    <key>StandardOutPath</key>
    <string>/var/log/roundfleet_$(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>ROUNDFLEET_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.roundfleet.${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": "Roundfleet 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 Roundfleet ==="
    
    local agent_count=0
    for plist_file in "$LAUNCH_AGENTS_DIR"/com.roundfleet.*.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.roundfleet.*.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 Roundfleet" > "$cleanup_report"
    echo "Généré : $(date)" >> "$cleanup_report"
    echo "======================================" >> "$cleanup_report"
    
    for plist_file in "$LAUNCH_AGENTS_DIR"/com.roundfleet.*.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.roundfleet.${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 Roundfleet ==="
    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.roundfleet.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.roundfleet.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/roundfleet/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/roundfleet_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.roundfleet.*.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/roundfleet_app_maintenance.sh"
    local launchd_plist="/Library/LaunchDaemons/com.roundfleet.app.maintenance.plist"
    
    # Créer le script de maintenance
    cat > "$maintenance_script" << 'EOF'
#!/bin/bash
LOG_FILE="/var/log/roundfleet_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/roundfleet_app_manager.sh validate

# Surveiller le succès de lancement
monitor_launch_success

# Nettoyer les agents invalides
/usr/local/bin/roundfleet_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.roundfleet.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 Roundfleet 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 Roundfleet
# Suppression d'applications avancée et gestion du cycle de vie

# Configuration
CONFIG_FILE="/etc/roundfleet/app_uninstall_policy.conf"
LOG_FILE="/var/log/roundfleet_app_uninstall.log"
QUARANTINE_DIR="/Library/Roundfleet/QuarantinedApps"
AUDIT_LOG="/var/log/roundfleet_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 Roundfleet
# 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/Roundfleet/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 Roundfleet"
    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 Roundfleet 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 Roundfleet"
            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 Roundfleet 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 Roundfleet 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 Roundfleet
# Déploiement d'applications avancé et gestion du cycle de vie

# Configuration
CONFIG_FILE="/etc/roundfleet/app_launch_policy.conf"
LOG_FILE="/var/log/roundfleet_app_launch.log"
APP_REGISTRY="/Library/Roundfleet/AppRegistry"
DEPLOYMENT_LOG="/var/log/roundfleet_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 Roundfleet
# 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 Roundfleet
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/Roundfleet/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 Roundfleet 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 Roundfleet"
            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 Roundfleet 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 Roundfleet à 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 Roundfleet
# Gestion complète des langues pour les environnements d'entreprise

# Configuration
LOG_FILE="/var/log/roundfleet_language.log"
BACKUP_DIR="/var/backups/roundfleet/language"
REPORT_DIR="/var/reports/roundfleet/language"
CONFIG_FILE="/etc/roundfleet/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 Roundfleet ==="
        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 Roundfleet"
        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 Roundfleet 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 Roundfleet"
            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 Roundfleet 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 Roundfleet 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 Roundfleet
# Terminaison de processus avancée et contrôle d'applications

# Configuration
CONFIG_FILE="/etc/roundfleet/force_close_policy.conf"
LOG_FILE="/var/log/roundfleet_force_close.log"
QUARANTINE_DIR="/Library/Roundfleet/TerminatedApps"
AUDIT_LOG="/var/log/roundfleet_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 Roundfleet
# 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 Roundfleet"
    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 Roundfleet 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 Roundfleet"
            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 Roundfleet pour assurer la stabilité système et la productivité utilisateur.

Gestion Apple ID et Suivi d'Identité sur macOS

Gérez l'utilisation des Apple ID, surveillez les services iCloud et implémentez les politiques d'identité d'entreprise sur vos appareils Roundfleet. Ce tutoriel couvre la découverte d'Apple ID, la surveillance des services, la conformité de confidentialité et la gestion d'identité complète pour les environnements d'entreprise.

Comprendre l'Intégration Apple ID sur macOS

L'intégration Apple ID fournit l'accès à divers services Apple :

  • Services iCloud - Stockage, synchronisation et sauvegarde
  • Réseau Localiser - Localisation d'appareils et fonctionnalités de sécurité
  • App Store - Téléchargements et mises à jour d'applications
  • Apple Pay - Traitement de paiement sécurisé
  • Trousseau - Synchronisation de mots de passe et d'identifiants

Détection Apple ID Basique

Découverte Simple d'Apple ID

#!/bin/bash

# Détection Apple ID basique pour tous les utilisateurs
check_apple_ids() {
    echo "=== Détection Apple ID ==="
    
    for user in $(dscl . list /Users UniqueID | awk '$2 >= 500 {print $1}'); do
        local userHome
        userHome=$(dscl . read /Users/"$user" NFSHomeDirectory | sed 's/NFSHomeDirectory://' | grep "/" | sed 's/^[ \t]*//')
        
        local appleid
        appleid=$(dscl . readpl "${userHome}" dsAttrTypeNative:LinkedIdentity appleid.apple.com:linked\ identities:0:full\ name 2>/dev/null | awk -F'full name: ' '{print $2}')
        
        if [[ "${appleid}" == "" ]]; then
            echo "👤 Utilisateur : ${user} - Aucun Apple ID connecté"
        else
            echo "🍎 Utilisateur : ${user} - Apple ID : ${appleid}"
        fi
    done
}

# Utilisation
check_apple_ids

Informations Apple ID Améliorées

#!/bin/bash

# Détection Apple ID améliorée avec détails supplémentaires
get_detailed_apple_id_info() {
    local username="$1"
    
    echo "=== Informations Apple ID Détaillées pour : $username ==="
    
    # Obtenir le répertoire utilisateur
    local userHome
    userHome=$(dscl . read /Users/"$username" NFSHomeDirectory | sed 's/NFSHomeDirectory://' | grep "/" | sed 's/^[ \t]*//')
    
    if [[ -z "$userHome" ]]; then
        echo "❌ Utilisateur non trouvé : $username"
        return 1
    fi
    
    # Vérifier l'Apple ID
    local appleid
    appleid=$(dscl . readpl "${userHome}" dsAttrTypeNative:LinkedIdentity appleid.apple.com:linked\ identities:0:full\ name 2>/dev/null | awk -F'full name: ' '{print $2}')
    
    if [[ -n "$appleid" ]]; then
        echo "🍎 Apple ID : $appleid"
        
        # Vérifier le statut iCloud
        local icloud_status
        if defaults read "${userHome}/Library/Preferences/MobileMeAccounts" Accounts &>/dev/null; then
            echo "☁️  iCloud : Configuré"
        else
            echo "☁️  iCloud : Non configuré"
        fi
        
        # Vérifier le statut Localiser
        if defaults read "${userHome}/Library/Preferences/com.apple.preferences.account" &>/dev/null; then
            echo "📍 Localiser : Disponible"
        else
            echo "📍 Localiser : Non disponible"
        fi
        
        # Vérifier le statut Trousseau
        if [[ -d "${userHome}/Library/Keychains" ]]; then
            local keychain_count
            keychain_count=$(find "${userHome}/Library/Keychains" -name "*.keychain*" | wc -l)
            echo "🔑 Trousseaux : $keychain_count trouvés"
        fi
        
    else
        echo "❌ Aucun Apple ID connecté"
    fi
}

# Exemple d'utilisation
# get_detailed_apple_id_info "john.doe"

Système de Gestion Apple ID d'Entreprise

#!/bin/bash

# Système de Gestion Apple ID d'Entreprise Roundfleet
# Gestion d'identité complète, suivi de conformité et surveillance des services

# Configuration
LOG_FILE="/var/log/roundfleet_apple_id.log"
CONFIG_DIR="/etc/roundfleet/identity"
REPORTS_DIR="$CONFIG_DIR/reports"
POLICIES_DIR="$CONFIG_DIR/policies"
BACKUP_DIR="/var/backups/identity_configs"

# Configurations des services Apple
declare -A APPLE_SERVICES=(
    ["icloud"]="com.apple.bird,com.apple.iCloudHelper,MobileMeAccounts"
    ["localiser"]="com.apple.icloud.findmydeviced,com.apple.FindMyMacHelper"
    ["appstore"]="com.apple.appstore,com.apple.CommerceKit"
    ["trousseau"]="Security,Keychain"
    ["handoff"]="com.apple.coreservices.useractivityd"
)

# Modèles de politiques de confidentialité
declare -A PRIVACY_POLICIES=(
    ["strict"]="no_icloud,no_sync,audit_all"
    ["equilibre"]="limited_icloud,controlled_sync,audit_sensitive"
    ["permissif"]="full_icloud,full_sync,audit_minimal"
    ["education"]="edu_icloud,student_sync,audit_compliance"
    ["entreprise"]="corp_icloud,enterprise_sync,audit_comprehensive"
)

# Cadres de conformité
declare -A COMPLIANCE_FRAMEWORKS=(
    ["hipaa"]="healthcare,strict_audit,encrypted_only"
    ["sox"]="financial,comprehensive_audit,controlled_access"
    ["rgpd"]="privacy_first,consent_required,data_minimization"
    ["ferpa"]="education,student_privacy,limited_sharing"
    ["entreprise"]="business_use,managed_access,policy_enforcement"
)

# 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 "$CONFIG_DIR" "$REPORTS_DIR" "$POLICIES_DIR" "$BACKUP_DIR"; do
        if [[ ! -d "$dir" ]]; then
            mkdir -p "$dir"
            log_action "Répertoire créé : $dir"
        fi
    done
}

# Découverte complète d'Apple ID
discover_apple_ids() {
    log_action "Démarrage de la découverte complète d'Apple ID"
    
    local discovery_report="$REPORTS_DIR/apple_id_discovery_$(date '+%Y%m%d_%H%M%S').json"
    
    cat > "$discovery_report" << EOF
{
    "discovery_metadata": {
        "timestamp": "$(date -Iseconds)",
        "hostname": "$(hostname)",
        "os_version": "$(sw_vers -productVersion)",
        "generator": "Roundfleet Apple ID Manager"
    },
    "user_accounts": [
EOF

    local first=true
    local total_users=0
    local signed_in_users=0
    
    # Traiter chaque compte utilisateur
    for user in $(dscl . list /Users UniqueID | awk '$2 >= 500 {print $1}'); do
        total_users=$((total_users + 1))
        
        if [[ "$first" == true ]]; then
            first=false
        else
            echo "," >> "$discovery_report"
        fi
        
        local userHome uid gid realName
        userHome=$(dscl . read /Users/"$user" NFSHomeDirectory | sed 's/NFSHomeDirectory://' | grep "/" | sed 's/^[ \t]*//')
        uid=$(dscl . read /Users/"$user" UniqueID | awk '{print $2}')
        gid=$(dscl . read /Users/"$user" PrimaryGroupID | awk '{print $2}')
        realName=$(dscl . read /Users/"$user" RealName | sed 's/RealName://' | sed 's/^[ \t]*//' || echo "")
        
        # Obtenir les informations Apple ID
        local appleid=""
        if [[ -n "$userHome" ]]; then
            appleid=$(dscl . readpl "${userHome}" dsAttrTypeNative:LinkedIdentity appleid.apple.com:linked\ identities:0:full\ name 2>/dev/null | awk -F'full name: ' '{print $2}')
        fi
        
        local apple_id_status="not_signed_in"
        if [[ -n "$appleid" ]]; then
            apple_id_status="signed_in"
            signed_in_users=$((signed_in_users + 1))
        fi
        
        # Vérifier les services Apple
        local services_status
        services_status=$(check_apple_services "$user" "$userHome")
        
        # Générer l'enregistrement utilisateur
        cat >> "$discovery_report" << EOF
        {
            "username": "$user",
            "real_name": "$realName",
            "uid": $uid,
            "gid": $gid,
            "home_directory": "$userHome",
            "apple_id_status": "$apple_id_status",
            "apple_id": "${appleid:-null}",
            "services": $services_status,
            "last_login": "$(last -1 "$user" | head -1 | awk '{print $4, $5, $6, $7}' || echo 'Jamais')",
            "account_created": "$(stat -f %SB -t %Y-%m-%d "$userHome" 2>/dev/null || echo 'Inconnu')"
        }
EOF
        
        log_action "Utilisateur traité : $user (Apple ID : ${apple_id_status})"
    done
    
    cat >> "$discovery_report" << EOF
    ],
    "summary": {
        "total_users": $total_users,
        "users_with_apple_id": $signed_in_users,
        "users_without_apple_id": $((total_users - signed_in_users)),
        "apple_id_adoption_rate": $(awk "BEGIN {printf \"%.2f\", ($signed_in_users/$total_users)*100}")
    }
}
EOF

    log_action "✅ Découverte Apple ID terminée : $discovery_report"
    echo "$discovery_report"
}

# Vérifier les services Apple pour un utilisateur
check_apple_services() {
    local username="$1"
    local userHome="$2"
    
    cat << EOF
{
    "icloud": {
        "enabled": $(check_icloud_status "$userHome"),
        "drive": $(check_icloud_drive "$userHome"),
        "photos": $(check_icloud_photos "$userHome"),
        "mail": $(check_icloud_mail "$userHome")
    },
    "find_my": {
        "enabled": $(check_findmy_status "$userHome"),
        "device_registered": $(check_findmy_device "$userHome")
    },
    "app_store": {
        "signed_in": $(check_appstore_status "$userHome"),
        "automatic_downloads": $(check_auto_downloads "$userHome")
    },
    "keychain": {
        "sync_enabled": $(check_keychain_sync "$userHome"),
        "keychain_count": $(count_keychains "$userHome")
    },
    "handoff": {
        "enabled": $(check_handoff_status "$userHome")
    }
}
EOF
}

# Fonctions de vérification de service individuelles
check_icloud_status() {
    local userHome="$1"
    if defaults read "${userHome}/Library/Preferences/MobileMeAccounts" Accounts &>/dev/null; then
        echo "true"
    else
        echo "false"
    fi
}

check_icloud_drive() {
    local userHome="$1"
    if [[ -d "${userHome}/Library/Mobile Documents/com~apple~CloudDocs" ]]; then
        echo "true"
    else
        echo "false"
    fi
}

check_icloud_photos() {
    local userHome="$1"
    if defaults read "${userHome}/Library/Preferences/com.apple.photoanalysisd" &>/dev/null; then
        echo "true"
    else
        echo "false"
    fi
}

check_icloud_mail() {
    local userHome="$1"
    if defaults read "${userHome}/Library/Preferences/com.apple.mail" &>/dev/null | grep -q "icloud"; then
        echo "true"
    else
        echo "false"
    fi
}

check_findmy_status() {
    local userHome="$1"
    if defaults read "${userHome}/Library/Preferences/com.apple.preferences.account" &>/dev/null; then
        echo "true"
    else
        echo "false"
    fi
}

check_findmy_device() {
    local userHome="$1"
    if pgrep -f "FindMyMacHelper" >/dev/null; then
        echo "true"
    else
        echo "false"
    fi
}

check_appstore_status() {
    local userHome="$1"
    if defaults read "${userHome}/Library/Preferences/com.apple.appstore" &>/dev/null; then
        echo "true"
    else
        echo "false"
    fi
}

check_auto_downloads() {
    local userHome="$1"
    local auto_downloads
    auto_downloads=$(defaults read "${userHome}/Library/Preferences/com.apple.appstore" AutomaticallyDownloadApps 2>/dev/null || echo "0")
    if [[ "$auto_downloads" == "1" ]]; then
        echo "true"
    else
        echo "false"
    fi
}

check_keychain_sync() {
    local userHome="$1"
    if security list-keychains -d user | grep -q "iCloud"; then
        echo "true"
    else
        echo "false"
    fi
}

count_keychains() {
    local userHome="$1"
    if [[ -d "${userHome}/Library/Keychains" ]]; then
        find "${userHome}/Library/Keychains" -name "*.keychain*" | wc -l | tr -d ' '
    else
        echo "0"
    fi
}

check_handoff_status() {
    local userHome="$1"
    local handoff_enabled
    handoff_enabled=$(defaults read "${userHome}/Library/Preferences/com.apple.coreservices.useractivityd" ActivityAdvertisingAllowed 2>/dev/null || echo "0")
    if [[ "$handoff_enabled" == "1" ]]; then
        echo "true"
    else
        echo "false"
    fi
}

# Appliquer les politiques de confidentialité
apply_privacy_policy() {
    local policy_name="$1"
    local target_user="$2"
    
    log_action "Application de la politique de confidentialité : $policy_name pour l'utilisateur : ${target_user:-tous_utilisateurs}"
    
    local policy_config="${PRIVACY_POLICIES[$policy_name]}"
    if [[ -z "$policy_config" ]]; then
        log_action "❌ Politique de confidentialité inconnue : $policy_name"
        return 1
    fi
    
    # Analyser la configuration de politique
    IFS=',' read -ra POLICY_RULES <<< "$policy_config"
    
    for rule in "${POLICY_RULES[@]}"; do
        case "$rule" in
            "no_icloud")
                disable_icloud_services "$target_user"
                ;;
            "limited_icloud")
                configure_limited_icloud "$target_user"
                ;;
            "full_icloud")
                log_action "Accès iCloud complet autorisé"
                ;;
            "no_sync")
                disable_sync_services "$target_user"
                ;;
            "controlled_sync")
                configure_controlled_sync "$target_user"
                ;;
            "audit_all")
                enable_comprehensive_audit "$target_user"
                ;;
            "audit_sensitive")
                enable_sensitive_audit "$target_user"
                ;;
            "audit_minimal")
                enable_minimal_audit "$target_user"
                ;;
        esac
    done
    
    log_action "✅ Politique de confidentialité appliquée : $policy_name"
    return 0
}

# Désactiver les services iCloud
disable_icloud_services() {
    local target_user="$1"
    
    log_action "Désactivation des services iCloud pour l'utilisateur : ${target_user:-tous_utilisateurs}"
    
    if [[ -n "$target_user" ]]; then
        local userHome
        userHome=$(dscl . read /Users/"$target_user" NFSHomeDirectory | sed 's/NFSHomeDirectory://' | grep "/" | sed 's/^[ \t]*//')
        
        # Désactiver iCloud Drive
        defaults write "${userHome}/Library/Preferences/com.apple.bird" 'LastDisabledDate' -date "$(date)"
        
        # Désactiver iCloud Photos
        defaults write "${userHome}/Library/Preferences/com.apple.Photos" 'DisableiCloudPhotos' -bool true
        
        log_action "Services iCloud désactivés pour l'utilisateur : $target_user"
    else
        log_action "La désactivation iCloud en masse nécessite un traitement utilisateur individuel"
    fi
}

# Générer un rapport de conformité
generate_compliance_report() {
    local framework="$1"
    local report_type="${2:-summary}"
    
    log_action "Génération du rapport de conformité pour le cadre : $framework"
    
    local compliance_report="$REPORTS_DIR/compliance_${framework}_$(date '+%Y%m%d_%H%M%S').json"
    
    # Obtenir les données de découverte Apple ID actuelles
    local discovery_data
    discovery_data=$(discover_apple_ids)
    
    cat > "$compliance_report" << EOF
{
    "compliance_metadata": {
        "timestamp": "$(date -Iseconds)",
        "framework": "$framework",
        "report_type": "$report_type",
        "hostname": "$(hostname)",
        "generator": "Roundfleet Compliance Manager"
    },
    "compliance_assessment": {
        "framework_requirements": $(get_framework_requirements "$framework"),
        "current_status": $(assess_compliance_status "$framework"),
        "recommendations": $(generate_compliance_recommendations "$framework"),
        "risk_level": "$(calculate_risk_level "$framework")"
    },
    "user_data": $(echo "$discovery_data" | jq '.user_accounts')
}
EOF

    log_action "✅ Rapport de conformité généré : $compliance_report"
    echo "$compliance_report"
}

# Obtenir les exigences du cadre
get_framework_requirements() {
    local framework="$1"
    
    case "$framework" in
        "hipaa")
            echo '{"encryption_required": true, "audit_required": true, "data_minimization": true, "access_controls": true}'
            ;;
        "rgpd")
            echo '{"consent_required": true, "data_portability": true, "right_to_erasure": true, "privacy_by_design": true}'
            ;;
        "sox")
            echo '{"financial_controls": true, "audit_trails": true, "segregation_of_duties": true, "data_integrity": true}'
            ;;
        "ferpa")
            echo '{"student_privacy": true, "parental_consent": true, "educational_purpose": true, "limited_disclosure": true}'
            ;;
        *)
            echo '{"general_security": true, "basic_audit": true}'
            ;;
    esac
}

# Évaluer le statut de conformité
assess_compliance_status() {
    local framework="$1"
    
    # Évaluation de conformité simplifiée
    local compliance_score=0
    local total_checks=0
    
    # Vérifier le chiffrement
    total_checks=$((total_checks + 1))
    if system_profiler SPStorageDataType | grep -q "Encrypted: Yes"; then
        compliance_score=$((compliance_score + 1))
    fi
    
    # Vérifier la journalisation d'audit
    total_checks=$((total_checks + 1))
    if [[ -f "$LOG_FILE" ]]; then
        compliance_score=$((compliance_score + 1))
    fi
    
    local compliance_percentage
    compliance_percentage=$(awk "BEGIN {printf \"%.0f\", ($compliance_score/$total_checks)*100}")
    
    echo "{\"score\": $compliance_score, \"total_checks\": $total_checks, \"percentage\": $compliance_percentage, \"status\": \"$([ $compliance_percentage -ge 80 ] && echo 'compliant' || echo 'non_compliant')\"}"
}

# Générer des recommandations de conformité
generate_compliance_recommendations() {
    local framework="$1"
    
    local recommendations=()
    
    # Vérifier les problèmes de conformité courants
    if ! system_profiler SPStorageDataType | grep -q "Encrypted: Yes"; then
        recommendations+=("Activer le chiffrement FileVault")
    fi
    
    if [[ ! -f "$LOG_FILE" ]]; then
        recommendations+=("Configurer la journalisation d'audit complète")
    fi
    
    # Recommandations spécifiques au cadre
    case "$framework" in
        "hipaa")
            recommendations+=("Implémenter les contrôles d'accès aux données" "Configurer la gestion des données PHI")
            ;;
        "rgpd")
            recommendations+=("Implémenter la gestion du consentement" "Configurer les politiques de rétention des données")
            ;;
    esac
    
    # Convertir en tableau JSON
    local json_recommendations="["
    local first=true
    for rec in "${recommendations[@]}"; do
        if [[ "$first" == true ]]; then
            first=false
        else
            json_recommendations+=","
        fi
        json_recommendations+="\"$rec\""
    done
    json_recommendations+="]"
    
    echo "$json_recommendations"
}

# Calculer le niveau de risque
calculate_risk_level() {
    local framework="$1"
    
    local risk_factors=0
    
    # Vérifier les configurations à haut risque
    if discover_apple_ids | jq -r '.summary.users_with_apple_id' | awk '$1 > 0 {exit 1}'; then
        risk_factors=$((risk_factors + 1))
    fi
    
    if [[ $risk_factors -ge 3 ]]; then
        echo "high"
    elif [[ $risk_factors -ge 1 ]]; then
        echo "medium"
    else
        echo "low"
    fi
}

# Fonction d'exécution principale
main() {
    local action="${1:-discover}"
    local parameter="$2"
    local additional_param="$3"
    
    log_action "=== Démarrage de la Gestion Apple ID Roundfleet ==="
    log_action "Action : $action"
    log_action "Paramètre : ${parameter:-N/A}"
    
    # Configuration
    setup_directories
    
    case "$action" in
        "discover")
            discover_apple_ids
            ;;
        "policy")
            if [[ -z "$parameter" ]]; then
                echo "Politiques de confidentialité disponibles :"
                for policy in "${!PRIVACY_POLICIES[@]}"; do
                    echo "  - $policy : ${PRIVACY_POLICIES[$policy]}"
                done
                echo ""
                echo "Usage : $0 policy <nom_politique> [nom_utilisateur]"
                exit 1
            fi
            apply_privacy_policy "$parameter" "$additional_param"
            ;;
        "compliance")
            if [[ -z "$parameter" ]]; then
                echo "Cadres de conformité disponibles :"
                for framework in "${!COMPLIANCE_FRAMEWORKS[@]}"; do
                    echo "  - $framework : ${COMPLIANCE_FRAMEWORKS[$framework]}"
                done
                echo ""
                echo "Usage : $0 compliance <cadre> [type_rapport]"
                exit 1
            fi
            generate_compliance_report "$parameter" "$additional_param"
            ;;
        "user")
            if [[ -z "$parameter" ]]; then
                echo "Usage : $0 user <nom_utilisateur>"
                exit 1
            fi
            get_detailed_apple_id_info "$parameter"
            ;;
        *)
            echo "Usage : $0 {discover|policy|compliance|user}"
            echo "  discover    - Découvrir les Apple ID sur tous les utilisateurs"
            echo "  policy      - Appliquer les politiques de confidentialité"
            echo "  compliance  - Générer des rapports de conformité"
            echo "  user        - Obtenir des informations détaillées pour un utilisateur spécifique"
            exit 1
            ;;
    esac
    
    log_action "=== Gestion Apple ID terminée ==="
}

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

Analyse Apple ID Avancée

Analytique d'Utilisation des Services

#!/bin/bash

# Analyser les modèles d'utilisation des services Apple
analyze_service_usage() {
    echo "=== Analytique d'Utilisation des Services Apple ==="
    
    local analytics_report="$REPORTS_DIR/service_analytics_$(date '+%Y%m%d_%H%M%S').json"
    
    cat > "$analytics_report" << EOF
{
    "analytics_metadata": {
        "timestamp": "$(date -Iseconds)",
        "hostname": "$(hostname)"
    },
    "service_usage": {
EOF

    # Analyser l'utilisation iCloud
    local icloud_users=0
    local total_users=0
    
    for user in $(dscl . list /Users UniqueID | awk '$2 >= 500 {print $1}'); do
        total_users=$((total_users + 1))
        local userHome
        userHome=$(dscl . read /Users/"$user" NFSHomeDirectory | sed 's/NFSHomeDirectory://' | grep "/" | sed 's/^[ \t]*//')
        
        if [[ "$(check_icloud_status "$userHome")" == "true" ]]; then
            icloud_users=$((icloud_users + 1))
        fi
    done
    
    cat >> "$analytics_report" << EOF
        "icloud": {
            "users_enabled": $icloud_users,
            "total_users": $total_users,
            "adoption_rate": $(awk "BEGIN {printf \"%.2f\", ($icloud_users/$total_users)*100}")
        }
    }
}
EOF

    echo "📊 Analytique d'utilisation des services : $analytics_report"
}

Évaluation d'Impact sur la Confidentialité

#!/bin/bash

# Effectuer une évaluation d'impact sur la confidentialité
privacy_impact_assessment() {
    local assessment_type="${1:-standard}"
    
    echo "=== Évaluation d'Impact sur la Confidentialité ==="
    log_action "Démarrage de l'évaluation d'impact sur la confidentialité : $assessment_type"
    
    local pia_report="$REPORTS_DIR/privacy_assessment_$(date '+%Y%m%d_%H%M%S').json"
    
    cat > "$pia_report" << EOF
{
    "assessment_metadata": {
        "timestamp": "$(date -Iseconds)",
        "assessment_type": "$assessment_type",
        "hostname": "$(hostname)"
    },
    "privacy_risks": [
EOF

    local risks=()
    
    # Vérifier les risques de synchronisation de données
    local sync_users
    sync_users=$(discover_apple_ids | jq -r '.user_accounts[] | select(.services.icloud.enabled == true) | .username' | wc -l)
    
    if [[ $sync_users -gt 0 ]]; then
        risks+=("Synchronisation de données vers iCloud activée pour $sync_users utilisateurs")
    fi
    
    # Vérifier les services de localisation
    if pgrep -f "locationd" >/dev/null; then
        risks+=("Services de localisation actifs - préoccupation de confidentialité potentielle")
    fi
    
    # Ajouter les risques au rapport
    local first=true
    for risk in "${risks[@]}"; do
        if [[ "$first" == true ]]; then
            first=false
        else
            echo "," >> "$pia_report"
        fi
        echo "        \"$risk\"" >> "$pia_report"
    done
    
    cat >> "$pia_report" << EOF
    ],
    "recommendations": [
        "Implémenter les politiques de gouvernance des données",
        "Configurer les contrôles de confidentialité",
        "Audits de confidentialité réguliers"
    ]
}
EOF

    log_action "✅ Évaluation d'impact sur la confidentialité terminée : $pia_report"
    echo "$pia_report"
}

Meilleures Pratiques

🚀 Gestion d'Identité

  • Scans de découverte réguliers pour suivre l'utilisation des Apple ID dans la flotte
  • Application de politiques centralisées pour des contrôles de confidentialité cohérents
  • Surveillance automatisée pour les changements d'Apple ID non autorisés
  • Pistes d'audit complètes pour la conformité et la sécurité

🔐 Directives de Sécurité

  • Application de politiques de confidentialité basées sur les exigences organisationnelles
  • Principes de minimisation des données pour les environnements sensibles
  • Contrôles d'accès pour les configurations de services Apple
  • Évaluations de sécurité régulières des pratiques de gestion d'identité

📋 Gestion de Conformité

  • Politiques spécifiques au cadre (HIPAA, RGPD, SOX, FERPA)
  • Rapports de conformité réguliers avec évaluation automatisée
  • Approche basée sur les risques pour la gestion d'identité
  • Documentation et pistes d'audit pour les exigences réglementaires

🔍 Surveillance et Maintenance

  • Surveillance continue du statut Apple ID et de l'utilisation des services
  • Détection de changements avec alerte automatisée
  • Sauvegarde régulière des configurations d'identité
  • Optimisation des performances pour les déploiements à grande échelle

Notes Importantes

  • La conformité de confidentialité nécessite un équilibre soigneux entre fonctionnalité et protection des données
  • Le consentement de l'utilisateur peut être requis pour certaines activités de surveillance
  • La gestion Apple ID doit s'aligner avec les politiques de confidentialité organisationnelles
  • Mises à jour régulières nécessaires car Apple introduit de nouveaux services et fonctionnalités de confidentialité
  • Considérations légales varient selon la juridiction et le secteur industriel