Guide

Nouvelles mises à jour et améliorations de Roundfleet.

Gestion des Images Disque sur macOS

Créez et gérez des images disque sur vos appareils Roundfleet pour un empaquetage efficace des données, la distribution de logiciels et le déploiement système. Ce tutoriel couvre la création d'images disque à partir de dossiers, les options de formatage avancées, le chiffrement, la compression et les workflows de gestion d'images à l'échelle de l'entreprise.

Comprendre les Images Disque

Les images disque sont des conteneurs de fichiers qui répliquent la structure exacte et le contenu des volumes de stockage, offrant des capacités puissantes pour le déploiement d'entreprise :

Avantages des Images Disque

  • Distribution de Logiciels - Empaqueter applications et configurations pour le déploiement
  • Sauvegarde de Données - Créer des instantanés portables du contenu des dossiers
  • Déploiement Système - Standardiser les configurations sur plusieurs appareils
  • Transport Sécurisé - Images chiffrées pour le transfert de données sensibles
  • Contrôle de Version - Maintenir différentes versions de paquets logiciels
  • Efficacité Réseau - Images compressées réduisent le temps de transfert

Types d'Images Disque

  • .dmg - Apple Disk Image (format principal)
  • .iso - Format ISO 9660 pour compatibilité multi-plateforme
  • .sparseimage - Images sparse qui grandissent selon les besoins
  • .sparsebundle - Format bundle pour stockage réseau

Création Basique d'Images Disque

Dossier Simple vers Image Disque

#!/bin/bash

# Créer une image disque basique à partir d'un dossier
SOURCE_FOLDER="$1"
IMAGE_NAME="$2"
OUTPUT_PATH="$3"

if [[ -z "$SOURCE_FOLDER" || -z "$IMAGE_NAME" || -z "$OUTPUT_PATH" ]]; then
    echo "❌ Utilisation : $0 <dossier_source> <nom_image> <chemin_sortie>"
    echo "Exemple : $0 /Users/Shared MonImage /Users/username/Documents"
    exit 1
fi

# Valider que le dossier source existe
if [[ ! -d "$SOURCE_FOLDER" ]]; then
    echo "❌ Le dossier source n'existe pas : $SOURCE_FOLDER"
    exit 1
fi

# Créer le répertoire de sortie s'il n'existe pas
mkdir -p "$OUTPUT_PATH"

echo "💿 Création d'une image disque à partir du dossier..."
echo "Source : $SOURCE_FOLDER"
echo "Nom de l'image : $IMAGE_NAME"
echo "Sortie : $OUTPUT_PATH/$IMAGE_NAME.dmg"

# Créer l'image disque
if hdiutil create -fs HFS+ -srcfolder "$SOURCE_FOLDER" -volname "$IMAGE_NAME" "$OUTPUT_PATH/$IMAGE_NAME.dmg"; then
    echo "✅ Image disque créée avec succès"
    
    # Obtenir la taille de l'image
    IMAGE_SIZE=$(du -h "$OUTPUT_PATH/$IMAGE_NAME.dmg" | cut -f1)
    echo "Taille de l'image : $IMAGE_SIZE"
    
    # Obtenir la taille du dossier source pour comparaison
    SOURCE_SIZE=$(du -sh "$SOURCE_FOLDER" | cut -f1)
    echo "Taille du dossier source : $SOURCE_SIZE"
else
    echo "❌ Échec de création de l'image disque"
    exit 1
fi

Création d'Image Disque Avancée

#!/bin/bash

# Création d'image disque avancée avec options
create_advanced_disk_image() {
    local source_folder="$1"
    local image_name="$2"
    local output_path="$3"
    local filesystem="${4:-HFS+}"
    local compression="${5:-UDBZ}"
    local encryption="${6:-false}"
    
    if [[ ! -d "$source_folder" ]]; then
        echo "❌ Dossier source non trouvé : $source_folder"
        return 1
    fi
    
    mkdir -p "$output_path"
    
    echo "💿 Création d'une image disque avancée..."
    echo "Source : $source_folder"
    echo "Système de fichiers : $filesystem"
    echo "Compression : $compression"
    echo "Chiffrement : $encryption"
    
    # Construire la commande hdiutil
    local cmd="hdiutil create"
    cmd="$cmd -fs $filesystem"
    cmd="$cmd -srcfolder '$source_folder'"
    cmd="$cmd -volname '$image_name'"
    
    # Ajouter la compression si spécifiée
    if [[ "$compression" != "none" ]]; then
        cmd="$cmd -format $compression"
    fi
    
    # Ajouter le chiffrement si demandé
    if [[ "$encryption" == "true" ]]; then
        cmd="$cmd -encryption AES-256"
        cmd="$cmd -stdinpass"
        echo "🔐 Chiffrement activé - vous serez invité à saisir un mot de passe"
    fi
    
    cmd="$cmd '$output_path/$image_name.dmg'"
    
    # Exécuter la commande
    if [[ "$encryption" == "true" ]]; then
        echo "Entrez le mot de passe pour le chiffrement de l'image disque :"
        read -s password
        echo "$password" | eval "$cmd"
    else
        eval "$cmd"
    fi
    
    if [[ $? -eq 0 ]]; then
        echo "✅ Image disque avancée créée avec succès"
        
        # Afficher les informations de l'image
        echo ""
        echo "=== Informations de l'Image ==="
        hdiutil imageinfo "$output_path/$image_name.dmg" | grep -E "Format|Compressed|Encrypted|Checksum"
        
        return 0
    else
        echo "❌ Échec de création de l'image disque"
        return 1
    fi
}

# Exemples d'utilisation
# create_advanced_disk_image "/Users/Shared" "DonneesPartagees" "/tmp" "HFS+" "UDBZ" "false"
# create_advanced_disk_image "/Applications/MonApp.app" "MonApp" "/tmp" "HFS+" "UDZO" "true"

Options de Système de Fichiers et Format

Systèmes de Fichiers Supportés

#!/bin/bash

# Afficher les options de système de fichiers et format disponibles
show_disk_image_options() {
    echo "=== Systèmes de Fichiers Disponibles ==="
    echo "HFS+     - Mac OS Extended (par défaut)"
    echo "HFS+J    - Mac OS Extended (Journalisé)"
    echo "HFSX     - Mac OS Extended (Sensible à la casse)"
    echo "JHFS+X   - Mac OS Extended (Sensible à la casse, Journalisé)"
    echo "MS-DOS   - FAT32 (compatible avec Windows)"
    echo "UDF      - Universal Disk Format"
    echo "APFS     - Apple File System (macOS 10.13+)"
    echo ""
    
    echo "=== Options de Format d'Image ==="
    echo "UDRO     - Lecture seule, taille plus petite"
    echo "UDCO     - Compression ADC (obsolète)"
    echo "UDZO     - Compression zlib (plus commun)"
    echo "UDBZ     - Compression bzip2 (meilleure compression)"
    echo "ULFO     - Compression lzfse (macOS 10.11+)"
    echo "ULMO     - Compression lzma"
    echo "UFBI     - Appareil entier"
    echo "SPARSE   - Image sparse (grandit selon les besoins)"
    echo "SPARSEBUNDLE - Bundle sparse (convivial réseau)"
    echo ""
    
    echo "=== Options de Chiffrement ==="
    echo "AES-128  - Chiffrement AES 128 bits"
    echo "AES-256  - Chiffrement AES 256 bits (recommandé)"
}

# Créer une image disque avec des options spécifiques
create_custom_disk_image() {
    local source="$1"
    local name="$2"
    local output="$3"
    local fs="$4"
    local format="$5"
    local encrypt="$6"
    
    echo "Création d'une image disque personnalisée avec :"
    echo "  Système de fichiers : $fs"
    echo "  Format : $format"
    echo "  Chiffrement : $encrypt"
    
    local cmd="hdiutil create -fs '$fs' -format '$format'"
    
    if [[ "$encrypt" != "none" ]]; then
        cmd="$cmd -encryption '$encrypt' -stdinpass"
    fi
    
    cmd="$cmd -srcfolder '$source' -volname '$name' '$output/$name.dmg'"
    
    if [[ "$encrypt" != "none" ]]; then
        echo "Entrez le mot de passe de chiffrement :"
        read -s password
        echo "$password" | eval "$cmd"
    else
        eval "$cmd"
    fi
}

# Exemple d'utilisation
# show_disk_image_options
# create_custom_disk_image "/chemin/vers/dossier" "MonImage" "/chemin/sortie" "HFS+" "UDBZ" "AES-256"

Opérations par Lots sur les Images Disque

Création par Lots

#!/bin/bash

# Créer des images disque par lots à partir de plusieurs dossiers
batch_create_disk_images() {
    local base_path="$1"
    local output_dir="$2"
    local filesystem="${3:-HFS+}"
    local format="${4:-UDBZ}"
    
    if [[ ! -d "$base_path" ]]; then
        echo "❌ Chemin de base non trouvé : $base_path"
        return 1
    fi
    
    mkdir -p "$output_dir"
    
    echo "🔄 Démarrage de la création d'images disque par lots..."
    echo "Chemin de base : $base_path"
    echo "Répertoire de sortie : $output_dir"
    echo "Système de fichiers : $filesystem"
    echo "Format : $format"
    echo ""
    
    local success_count=0
    local failed_count=0
    local total_size=0
    
    # Traiter chaque sous-répertoire
    find "$base_path" -type d -mindepth 1 -maxdepth 1 | while read -r folder; do
        local folder_name=$(basename "$folder")
        local image_path="$output_dir/${folder_name}.dmg"
        
        echo "Traitement : $folder_name"
        
        # Ignorer si l'image existe déjà
        if [[ -f "$image_path" ]]; then
            echo "  ⏭️  L'image existe déjà, ignorer"
            continue
        fi
        
        # Créer l'image disque
        if hdiutil create -fs "$filesystem" -format "$format" \
           -srcfolder "$folder" -volname "$folder_name" \
           "$image_path" >/dev/null 2>&1; then
            
            local size=$(du -h "$image_path" | cut -f1)
            echo "  ✅ Créée avec succès ($size)"
            ((success_count++))
            
            # Ajouter au calcul de taille totale
            local size_bytes=$(du -b "$image_path" | cut -f1)
            total_size=$((total_size + size_bytes))
        else
            echo "  ❌ Échec de création de l'image"
            ((failed_count++))
        fi
    done
    
    echo ""
    echo "=== Résumé de l'Opération par Lots ==="
    echo "Réussies : $success_count"
    echo "Échouées : $failed_count"
    echo "Taille totale : $(numfmt --to=iec $total_size)"
}

# Exemple d'utilisation
# batch_create_disk_images "/Users/Shared/Projets" "/tmp/images_projets" "HFS+" "UDBZ"

Vérification et Analyse d'Images

#!/bin/bash

# Vérifier et analyser les images disque
verify_disk_image() {
    local image_path="$1"
    
    if [[ ! -f "$image_path" ]]; then
        echo "❌ Fichier image non trouvé : $image_path"
        return 1
    fi
    
    echo "🔍 Vérification de l'image disque : $(basename "$image_path")"
    
    # Informations basiques de l'image
    echo ""
    echo "=== Informations Basiques ==="
    echo "Taille du fichier : $(du -h "$image_path" | cut -f1)"
    echo "Type de fichier : $(file "$image_path" | cut -d: -f2- | xargs)"
    
    # Informations détaillées de l'image
    echo ""
    echo "=== Informations Détaillées de l'Image ==="
    hdiutil imageinfo "$image_path" | grep -E "Format|Partition|Compressed|Encrypted|Checksum|Total"
    
    # Vérifier l'intégrité de l'image
    echo ""
    echo "=== Vérification d'Intégrité ==="
    if hdiutil verify "$image_path" >/dev/null 2>&1; then
        echo "✅ Intégrité de l'image vérifiée avec succès"
    else
        echo "❌ Échec de vérification d'intégrité de l'image"
        return 1
    fi
    
    # Test de capacité de montage
    echo ""
    echo "=== Test de Montage ==="
    local mount_point=$(mktemp -d)
    
    if hdiutil attach "$image_path" -mountpoint "$mount_point" -nobrowse -quiet; then
        echo "✅ L'image se monte avec succès"
        
        # Obtenir les informations du volume
        echo "Info du volume : $(df -h "$mount_point" | tail -1 | awk '{print $2 " total, " $3 " utilisé, " $4 " disponible"}')"
        
        # Compter le contenu
        local file_count=$(find "$mount_point" -type f | wc -l | xargs)
        local dir_count=$(find "$mount_point" -type d | wc -l | xargs)
        echo "Contenu : $file_count fichiers, $dir_count répertoires"
        
        # Démonter
        hdiutil detach "$mount_point" -quiet
        rmdir "$mount_point"
    else
        echo "❌ Échec de montage de l'image"
        rmdir "$mount_point"
        return 1
    fi
    
    echo ""
    echo "✅ Vérification de l'image terminée avec succès"
    return 0
}

# Vérification par lots
verify_multiple_images() {
    local image_dir="$1"
    
    if [[ ! -d "$image_dir" ]]; then
        echo "❌ Répertoire d'images non trouvé : $image_dir"
        return 1
    fi
    
    echo "🔍 Vérification de toutes les images disque dans : $image_dir"
    echo ""
    
    local verified=0
    local failed=0
    
    find "$image_dir" -name "*.dmg" -type f | while read -r image; do
        echo "Vérification : $(basename "$image")"
        if verify_disk_image "$image" >/dev/null 2>&1; then
            echo "  ✅ Vérifiée"
            ((verified++))
        else
            echo "  ❌ Échouée"
            ((failed++))
        fi
    done
    
    echo ""
    echo "Vérification terminée : $verified réussies, $failed échouées"
}

Gestion d'Images Disque d'Entreprise

#!/bin/bash

# Outil de Gestion d'Images Disque Roundfleet
# Création et gestion complètes d'images disque pour environnements d'entreprise

# Configuration
LOG_FILE="/var/log/roundfleet_diskimage.log"
BACKUP_DIR="/var/backups/roundfleet/images"
REPORT_DIR="/var/reports/roundfleet/images"
CONFIG_FILE="/etc/roundfleet/image_policy.conf"
TEMP_DIR="/tmp/roundfleet_images"

# Paramètres par défaut
DEFAULT_FILESYSTEM="HFS+"
DEFAULT_FORMAT="UDBZ"
DEFAULT_ENCRYPTION="AES-256"
ENABLE_COMPRESSION=true
ENABLE_VERIFICATION=true
MAX_IMAGE_SIZE="10G"
CLEANUP_TEMP_FILES=true

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

# Vérifier l'espace disque disponible
check_disk_space() {
    local target_dir="$1"
    local required_space="$2"  # en octets
    
    local available_space=$(df "$target_dir" | tail -1 | awk '{print $4}')
    available_space=$((available_space * 1024))  # Convertir en octets
    
    if [[ "$available_space" -lt "$required_space" ]]; then
        echo "❌ Espace disque insuffisant"
        echo "Requis : $(numfmt --to=iec $required_space)"
        echo "Disponible : $(numfmt --to=iec $available_space)"
        return 1
    fi
    
    return 0
}

# Créer une image disque d'entreprise avec journalisation et validation complètes
create_enterprise_disk_image() {
    local source_path="$1"
    local image_name="$2"
    local output_dir="$3"
    local filesystem="${4:-$DEFAULT_FILESYSTEM}"
    local format="${5:-$DEFAULT_FORMAT}"
    local encrypt="${6:-false}"
    local description="$7"
    
    log_action "Démarrage de la création d'image disque d'entreprise : $image_name"
    
    # Validation
    if [[ ! -d "$source_path" ]]; then
        echo "❌ Chemin source non trouvé : $source_path"
        log_action "ERREUR : Chemin source non trouvé : $source_path"
        return 1
    fi
    
    # Calculer la taille source
    local source_size_bytes=$(du -sb "$source_path" | cut -f1)
    local source_size_human=$(du -sh "$source_path" | cut -f1)
    
    echo "📁 Analyse source :"
    echo "  Chemin : $source_path"
    echo "  Taille : $source_size_human ($source_size_bytes octets)"
    
    # Vérifier l'espace disque (estimer 2x la taille source pour sécurité)
    local estimated_space=$((source_size_bytes * 2))
    if ! check_disk_space "$output_dir" "$estimated_space"; then
        log_action "ERREUR : Espace disque insuffisant pour la création d'image"
        return 1
    fi
    
    # Créer le répertoire de sortie
    mkdir -p "$output_dir"
    
    local output_path="$output_dir/$image_name.dmg"
    local start_time=$(date +%s)
    
    echo ""
    echo "💿 Création de l'image disque..."
    echo "  Nom : $image_name"
    echo "  Système de fichiers : $filesystem"
    echo "  Format : $format"
    echo "  Sortie : $output_path"
    
    # Construire la commande
    local cmd="hdiutil create -fs '$filesystem' -format '$format'"
    cmd="$cmd -srcfolder '$source_path' -volname '$image_name'"
    
    # Ajouter le chiffrement si demandé
    if [[ "$encrypt" == "true" ]]; then
        cmd="$cmd -encryption '$DEFAULT_ENCRYPTION' -stdinpass"
        echo "  Chiffrement : Activé ($DEFAULT_ENCRYPTION)"
    fi
    
    cmd="$cmd '$output_path'"
    
    # Exécuter la création d'image
    local creation_success=false
    
    if [[ "$encrypt" == "true" ]]; then
        echo ""
        echo "🔐 Entrez le mot de passe pour le chiffrement de l'image disque :"
        read -s password
        if echo "$password" | eval "$cmd" >/dev/null 2>&1; then
            creation_success=true
        fi
    else
        if eval "$cmd" >/dev/null 2>&1; then
            creation_success=true
        fi
    fi
    
    local end_time=$(date +%s)
    local duration=$((end_time - start_time))
    
    if [[ "$creation_success" == "true" ]]; then
        local image_size_bytes=$(du -b "$output_path" | cut -f1)
        local image_size_human=$(du -h "$output_path" | cut -f1)
        local compression_ratio=$(echo "scale=2; $image_size_bytes * 100 / $source_size_bytes" | bc)
        
        echo ""
        echo "✅ Image disque créée avec succès"
        echo "  Temps de création : ${duration}s"
        echo "  Taille de l'image : $image_size_human"
        echo "  Ratio de compression : ${compression_ratio}%"
        
        # Vérifier l'image si activé
        if [[ "$ENABLE_VERIFICATION" == "true" ]]; then
            echo ""
            echo "🔍 Vérification de l'intégrité de l'image..."
            if hdiutil verify "$output_path" >/dev/null 2>&1; then
                echo "✅ Vérification d'image réussie"
                log_action "Image créée et vérifiée : $image_name ($image_size_human)"
            else
                echo "❌ Échec de vérification d'image"
                log_action "ERREUR : Échec de vérification d'image : $image_name"
                return 1
            fi
        fi
        
        # Générer les métadonnées de l'image
        create_image_metadata "$output_path" "$source_path" "$description"
        
        return 0
    else
        echo "❌ Échec de création de l'image disque"
        log_action "ERREUR : Échec de création de l'image disque : $image_name"
        return 1
    fi
}

# Créer un fichier de métadonnées pour l'image
create_image_metadata() {
    local image_path="$1"
    local source_path="$2"
    local description="$3"
    
    local metadata_file="${image_path%.dmg}.json"
    
    cat > "$metadata_file" << EOF
{
    "nom_image": "$(basename "$image_path")",
    "date_creation": "$(date -Iseconds)",
    "chemin_source": "$source_path",
    "chemin_image": "$image_path",
    "description": "$description",
    "taille_fichier": $(du -b "$image_path" | cut -f1),
    "taille_fichier_lisible": "$(du -h "$image_path" | cut -f1)",
    "somme_controle_md5": "$(md5 -q "$image_path")",
    "somme_controle_sha256": "$(shasum -a 256 "$image_path" | cut -d' ' -f1)",
    "info_format": $(hdiutil imageinfo "$image_path" -plist | plutil -convert json -o - -)
}
EOF
    
    echo "📄 Métadonnées sauvegardées : $metadata_file"
}

# Générer un rapport d'images complet
generate_image_report() {
    local image_dir="${1:-$BACKUP_DIR}"
    local report_file="$REPORT_DIR/rapport_images_$(date +%Y%m%d_%H%M%S).txt"
    
    echo "📊 Génération du rapport d'images disque..."
    
    {
        echo "Rapport de Gestion d'Images Disque Roundfleet"
        echo "Généré : $(date)"
        echo "Répertoire d'Images : $image_dir"
        echo "======================================"
        echo ""
        
        if [[ ! -d "$image_dir" ]]; then
            echo "❌ Répertoire d'images non trouvé : $image_dir"
            return 1
        fi
        
        # Compter et analyser les images
        local total_images=$(find "$image_dir" -name "*.dmg" -type f | wc -l)
        local total_size_bytes=0
        local verified_count=0
        local failed_count=0
        
        echo "=== Résumé ==="
        echo "Total d'images disque : $total_images"
        
        if [[ "$total_images" -eq 0 ]]; then
            echo "Aucune image disque trouvée dans le répertoire"
            return 0
        fi
        
        echo ""
        echo "=== Détails des Images ==="
        printf "%-30s %-10s %-15s %-10s\n" "Nom" "Taille" "Format" "Statut"
        printf "%-30s %-10s %-15s %-10s\n" "----" "------" "------" "------"
        
        find "$image_dir" -name "*.dmg" -type f | sort | while read -r image_path; do
            local image_name=$(basename "$image_path")
            local image_size=$(du -h "$image_path" | cut -f1)
            local image_size_bytes=$(du -b "$image_path" | cut -f1)
            
            # Obtenir les infos de format
            local format_info=$(hdiutil imageinfo "$image_path" 2>/dev/null | grep "Format Description" | cut -d: -f2- | xargs)
            if [[ -z "$format_info" ]]; then
                format_info="Inconnu"
            fi
            
            # Vérifier l'intégrité
            local status="Inconnu"
            if hdiutil verify "$image_path" >/dev/null 2>&1; then
                status="✅ Valide"
                ((verified_count++))
            else
                status="❌ Échouée"
                ((failed_count++))
            fi
            
            printf "%-30s %-10s %-15s %-10s\n" "$image_name" "$image_size" "$format_info" "$status"
            
            total_size_bytes=$((total_size_bytes + image_size_bytes))
        done
        
        echo ""
        echo "=== Statistiques ==="
        echo "Taille totale : $(numfmt --to=iec $total_size_bytes)"
        echo "Images vérifiées : $verified_count"
        echo "Vérification échouée : $failed_count"
        echo "Taille moyenne : $(numfmt --to=iec $((total_size_bytes / total_images)))"
        
        # Recommandations de stockage
        echo ""
        echo "=== Recommandations ==="
        if [[ "$failed_count" -gt 0 ]]; then
            echo "⚠️  $failed_count images ont échoué la vérification - enquêter et recréer"
        fi
        
        if [[ "$total_size_bytes" -gt $((50 * 1024 * 1024 * 1024)) ]]; then  # 50GB
            echo "💡 Considérez archiver les anciennes images pour libérer de l'espace"
        fi
        
        echo "💡 Vérification régulière recommandée pour les images critiques"
        
    } > "$report_file"
    
    echo "📊 Rapport sauvegardé dans : $report_file"
    log_action "Rapport d'images généré : $report_file"
}

# Fonction d'exécution principale
main() {
    local action="${1:-aide}"
    local source="${2:-}"
    local name="${3:-}"
    local output="${4:-$BACKUP_DIR}"
    
    log_action "=== Gestion d'Images Disque Roundfleet Démarrée ==="
    
    setup_directories
    
    case "$action" in
        "creer")
            if [[ -n "$source" && -n "$name" ]]; then
                create_enterprise_disk_image "$source" "$name" "$output"
            else
                echo "❌ Utilisation : $0 creer <dossier_source> <nom_image> [repertoire_sortie]"
                echo "Exemple : $0 creer /Users/Shared MonImage /tmp"
            fi
            ;;
        "lot")
            if [[ -n "$source" ]]; then
                batch_create_disk_images "$source" "$output"
            else
                echo "❌ Utilisation : $0 lot <dossier_base> [repertoire_sortie]"
                echo "Exemple : $0 lot /Users/Shared/Projets /tmp/images"
            fi
            ;;
        "verifier")
            if [[ -n "$source" ]]; then
                if [[ -f "$source" ]]; then
                    verify_disk_image "$source"
                elif [[ -d "$source" ]]; then
                    verify_multiple_images "$source"
                else
                    echo "❌ Chemin non trouvé : $source"
                fi
            else
                echo "❌ Utilisation : $0 verifier <fichier_image_ou_repertoire>"
            fi
            ;;
        "rapport")
            generate_image_report "$source"
            ;;
        "info")
            if [[ -n "$source" && -f "$source" ]]; then
                echo "=== Informations de l'Image ==="
                hdiutil imageinfo "$source"
            else
                echo "❌ Utilisation : $0 info <fichier_image>"
            fi
            ;;
        "options")
            show_disk_image_options
            ;;
        "aide"|*)
            echo "Outil de Gestion d'Images Disque Roundfleet"
            echo "Utilisation : $0 [action] [options]"
            echo ""
            echo "Actions :"
            echo "  creer <source> <nom> [sortie]    - Créer image disque à partir du dossier"
            echo "  lot <repertoire_base> [sortie]   - Créer images à partir de tous les sous-répertoires"
            echo "  verifier <image_ou_rep>          - Vérifier l'intégrité de l'image"
            echo "  rapport [repertoire_images]      - Générer rapport complet"
            echo "  info <fichier_image>             - Afficher informations détaillées de l'image"
            echo "  options                          - Afficher options de système de fichiers et format disponibles"
            echo "  aide                             - Afficher ce message d'aide"
            echo ""
            echo "Exemples :"
            echo "  $0 creer /Users/Shared MesDonnees  # Créer MesDonnees.dmg à partir de /Users/Shared"
            echo "  $0 lot /Applications /tmp          # Créer images pour toutes les apps"
            echo "  $0 verifier /tmp/MesDonnees.dmg    # Vérifier une seule image"
            echo "  $0 rapport /tmp                    # Générer rapport pour toutes les images dans /tmp"
            echo ""
            echo "Configuration :"
            echo "  Système de fichiers par défaut : $DEFAULT_FILESYSTEM"
            echo "  Format par défaut : $DEFAULT_FORMAT"
            echo "  Chiffrement par défaut : $DEFAULT_ENCRYPTION"
            echo "  Vérification activée : $ENABLE_VERIFICATION"
            ;;
    esac
    
    log_action "=== Gestion d'Images Disque Roundfleet Terminée ==="
}

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

Comparaison des Formats d'Image

FormatCompressionTailleCompatibilitéCas d'Usage
UDROAucuneGrandeUniverselleTests, images temporaires
UDZOzlib (bonne)MoyennemacOS 10.2+Usage général, bon équilibre
UDBZbzip2 (meilleure)PetitemacOS 10.4+Stockage archive, accès plus lent
ULFOlzfse (rapide)PetitemacOS 10.11+Systèmes modernes, compression rapide
SPARSEAucuneVariablemacOSImages grandissantes, développement

Sécurité et Chiffrement

Création d'Image Chiffrée

# Créer une image disque chiffrée avec politiques d'entreprise
create_secure_image() {
    local source="$1"
    local name="$2"
    local output="$3"
    local encryption_level="${4:-AES-256}"
    
    echo "🔐 Création d'une image disque chiffrée..."
    echo "Chiffrement : $encryption_level"
    
    # Générer un mot de passe sécurisé si non fourni
    if [[ -z "$IMAGE_PASSWORD" ]]; then
        echo "Entrez le mot de passe de chiffrement (ou définissez la variable IMAGE_PASSWORD) :"
        read -s IMAGE_PASSWORD
    fi
    
    echo "$IMAGE_PASSWORD" | hdiutil create \
        -fs HFS+ \
        -format UDBZ \
        -encryption "$encryption_level" \
        -stdinpass \
        -srcfolder "$source" \
        -volname "$name" \
        "$output/$name.dmg"
    
    if [[ $? -eq 0 ]]; then
        echo "✅ Image chiffrée créée avec succès"
        echo "🔒 L'image est protégée avec un chiffrement $encryption_level"
    else
        echo "❌ Échec de création de l'image chiffrée"
        return 1
    fi
}

Notes Importantes

  • Espace Disque - Assurez-vous d'avoir suffisamment d'espace pour la création d'image (2x la taille source recommandée)
  • Performance - La compression échange la taille contre la vitesse de création/accès
  • Sécurité - Utilisez le chiffrement AES-256 pour les données sensibles
  • Compatibilité - Choisissez le système de fichiers approprié pour les systèmes cibles
  • Vérification - Vérifiez toujours les images critiques après création
  • Déploiement d'Entreprise - Testez le montage d'image sur les systèmes cibles avant le déploiement en lot

Désactiver les Mises à Jour Automatiques de macOS

Dans les environnements d'entreprise, contrôler quand et comment les mises à jour macOS sont installées est crucial pour maintenir la stabilité du système et assurer la compatibilité avec les applications métier. Ce tutoriel montre comment désactiver les mises à jour automatiques de macOS avec des scripts shell.

Comprendre les Paramètres de Mise à Jour macOS

macOS a deux comportements principaux de mise à jour automatique :

  • Téléchargement Automatique : Télécharge les mises à jour en arrière-plan
  • Vérification Automatique : Vérifie périodiquement les serveurs d'Apple pour de nouvelles mises à jour

Les deux peuvent être contrôlés via le domaine de préférences com.apple.SoftwareUpdate.

Scripts de Base

Désactiver le Téléchargement Automatique

#!/bin/bash
defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticDownload -boolean false

Désactiver la Vérification Automatique

#!/bin/bash
defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticCheckEnabled -boolean false

Script de Déploiement Entreprise

Pour un déploiement à grande échelle dans votre Roundfleet :

#!/bin/bash

# Désactiver les Mises à Jour Automatiques macOS - Script Entreprise
# Compatible avec macOS 10.14+

# Fonction pour enregistrer les messages
log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a /var/log/macos_update_config.log
}

# Fonction pour obtenir l'utilisateur actuel
get_current_user() {
    stat -f "%Su" /dev/console
}

# Fonction principale de configuration
configure_updates() {
    local current_user
    current_user=$(get_current_user)
    
    log_message "Début de la configuration des mises à jour macOS pour l'utilisateur : $current_user"
    
    # Désactiver le téléchargement automatique
    if defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticDownload -boolean false; then
        log_message "✓ Téléchargement automatique désactivé avec succès"
    else
        log_message "✗ Échec de la désactivation du téléchargement automatique"
        return 1
    fi
    
    # Désactiver la vérification automatique
    if defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticCheckEnabled -boolean false; then
        log_message "✓ Vérification automatique désactivée avec succès"
    else
        log_message "✗ Échec de la désactivation de la vérification automatique"
        return 1
    fi
    
    # Désactiver l'installation automatique (sécurité supplémentaire)
    if defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticallyInstallMacOSUpdates -boolean false; then
        log_message "✓ Installation automatique désactivée avec succès"
    else
        log_message "✗ Échec de la désactivation de l'installation automatique"
    fi
    
    # Redémarrer les préférences Software Update pour forcer le rechargement
    killall -HUP cfprefsd 2>/dev/null || true
    
    log_message "Configuration des mises à jour macOS terminée"
    return 0
}

# Fonction de vérification
verify_configuration() {
    local download_status check_status install_status
    
    download_status=$(defaults read /Library/Preferences/com.apple.SoftwareUpdate AutomaticDownload 2>/dev/null || echo "non défini")
    check_status=$(defaults read /Library/Preferences/com.apple.SoftwareUpdate AutomaticCheckEnabled 2>/dev/null || echo "non défini")
    install_status=$(defaults read /Library/Preferences/com.apple.SoftwareUpdate AutomaticallyInstallMacOSUpdates 2>/dev/null || echo "non défini")
    
    log_message "Vérification de la configuration :"
    log_message "  Téléchargement Automatique : $download_status"
    log_message "  Vérification Automatique : $check_status"
    log_message "  Installation Automatique : $install_status"
}

# Exécuter la fonction principale
if configure_updates; then
    verify_configuration
    log_message "Script exécuté avec succès"
    exit 0
else
    log_message "Échec de l'exécution du script"
    exit 1
fi

Script de Vérification

Vérifier les paramètres de mise à jour actuels :

#!/bin/bash

echo "=== État des Paramètres de Mise à Jour macOS ==="
echo "Téléchargement Automatique : $(defaults read /Library/Preferences/com.apple.SoftwareUpdate AutomaticDownload 2>/dev/null || echo 'Non configuré')"
echo "Vérification Automatique : $(defaults read /Library/Preferences/com.apple.SoftwareUpdate AutomaticCheckEnabled 2>/dev/null || echo 'Non configuré')"
echo "Installation Automatique : $(defaults read /Library/Preferences/com.apple.SoftwareUpdate AutomaticallyInstallMacOSUpdates 2>/dev/null || echo 'Non configuré')"

Réactiver les Mises à Jour

Pour restaurer les mises à jour automatiques :

#!/bin/bash

# Réactiver le téléchargement automatique
defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticDownload -boolean true

# Réactiver la vérification automatique
defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticCheckEnabled -boolean true

# Réactiver l'installation automatique
defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticallyInstallMacOSUpdates -boolean true

echo "Mises à jour automatiques réactivées"

Dépannage

ProblèmeSolution
Les paramètres reviennent après redémarrageS'assurer que les scripts s'exécutent avec des privilèges admin
Préférences Système affiche les anciennes valeursRedémarrer Préférences Système ou exécuter killall -HUP cfprefsd
Les changements ne s'appliquent pasVérifier /var/log/macos_update_config.log pour les erreurs
Les mises à jour se téléchargent encoreVérifier les permissions du fichier de préférences

Notes Importantes

  • Les utilisateurs avec privilèges admin peuvent changer manuellement ces paramètres
  • L'app Préférences Système/Réglages peut nécessiter un redémarrage pour refléter les changements
  • Considérez l'utilisation de Profils de Configuration pour un contrôle plus strict
  • Testez sur un petit groupe avant le déploiement à l'échelle de l'entreprise

Alternative avec Profil de Configuration

Pour un contrôle plus strict, considérez le déploiement d'un Profil de Configuration :

<key>AutomaticCheckEnabled</key>
<false/>
<key>AutomaticDownload</key>
<false/>
<key>AutomaticallyInstallMacOSUpdates</key>
<false/>

Gestion de Listage de Répertoires et Fichiers sur macOS

Maîtrisez les opérations de listage de fichiers et répertoires sur vos appareils Roundfleet en utilisant des outils puissants en ligne de commande. Ce tutoriel couvre le listage de base des fichiers, la découverte de fichiers cachés, l'affichage d'informations détaillées et les techniques de tri avancées pour une gestion efficace du système de fichiers.

Comprendre les Opérations de Répertoires macOS

macOS fournit des outils robustes en ligne de commande pour la gestion des répertoires :

  • ls - Lister le contenu des répertoires avec diverses options
  • find - Rechercher des fichiers et répertoires avec des critères avancés
  • tree - Afficher la structure des répertoires en format arbre
  • stat - Afficher des informations détaillées sur les fichiers/répertoires

Opérations de Base pour Lister les Fichiers

Lister Tous les Fichiers d'un Répertoire

#!/bin/bash

# Listage de répertoire de base
FOLDER_PATH="/Users/QA/Desktop/Wallpapers"

echo "Fichiers dans $FOLDER_PATH :"
ls "$FOLDER_PATH"

echo "Listage de répertoire terminé avec succès"

Listage Récursif de Répertoire

#!/bin/bash

# Lister tous les fichiers récursivement (y compris les sous-répertoires)
FOLDER_PATH="/Users/QA/Desktop/Wallpapers"

echo "Listage récursif de $FOLDER_PATH :"
ls -R "$FOLDER_PATH"

echo "Listage récursif de répertoire terminé"

Fichiers et Répertoires Cachés

Afficher le Contenu Caché

#!/bin/bash

# Afficher tous les fichiers y compris les cachés
FOLDER_PATH="/Users/QA/Desktop/Wallpapers"

echo "Tous les fichiers (y compris cachés) dans $FOLDER_PATH :"
ls -a "$FOLDER_PATH"

echo "Fichiers cachés affichés avec succès"

Analyse des Fichiers Cachés

#!/bin/bash

# Analyse complète des fichiers cachés
analyze_hidden_files() {
    local folder_path="$1"
    
    echo "=== Analyse des Fichiers Cachés pour : $folder_path ==="
    
    # Compter les fichiers cachés
    local hidden_count
    hidden_count=$(ls -la "$folder_path" | grep "^\." | wc -l)
    echo "Fichiers cachés trouvés : $((hidden_count - 2))" # Soustraire . et ..
    
    # Lister les fichiers cachés avec détails
    echo -e "\nFichiers cachés :"
    ls -la "$folder_path" | grep "^\." | grep -v "^total"
    
    # Vérifier les types de fichiers cachés courants
    echo -e "\nTypes de fichiers cachés courants :"
    ls -a "$folder_path" | grep "^\." | grep -E "\.(DS_Store|Spotlight-V100|Trashes|fseventsd)" || echo "Aucun fichier caché système courant trouvé"
}

# Utilisation
analyze_hidden_files "/Users/QA/Desktop/Wallpapers"

Informations Détaillées sur les Fichiers

Listage au Format Long

#!/bin/bash

# Afficher des informations détaillées sur les fichiers
FOLDER_PATH="/Users/QA/Desktop/Wallpapers"

echo "Informations détaillées sur les fichiers pour $FOLDER_PATH :"
ls -l "$FOLDER_PATH"

echo "Listage détaillé terminé"

Tailles de Fichiers Lisibles par l'Homme

#!/bin/bash

# Afficher les tailles de fichiers dans un format lisible par l'homme
FOLDER_PATH="/Users/QA/Desktop/Wallpapers"

echo "Fichiers avec tailles lisibles dans $FOLDER_PATH :"
ls -lh "$FOLDER_PATH"

echo "Listage lisible terminé"

Détails Complets des Fichiers

#!/bin/bash

# Affichage avancé d'informations sur les fichiers
show_file_details() {
    local folder_path="$1"
    
    echo "=== Détails Complets des Fichiers : $folder_path ==="
    
    # Listage de base avec permissions, tailles, dates
    echo "Listage détaillé des fichiers :"
    ls -lah "$folder_path"
    
    echo -e "\nRésumé du répertoire :"
    echo "Total d'éléments : $(ls -1 "$folder_path" | wc -l)"
    echo "Taille totale : $(du -sh "$folder_path" | cut -f1)"
    echo "Utilisation disque : $(du -sk "$folder_path" | cut -f1) KB"
    
    # Répartition par type de fichier
    echo -e "\nAnalyse des types de fichiers :"
    find "$folder_path" -maxdepth 1 -type f -exec file {} \; | cut -d: -f2 | sort | uniq -c | sort -nr
}

# Utilisation
show_file_details "/Users/QA/Desktop/Wallpapers"

Options de Tri des Fichiers

Tri par Heure de Modification

#!/bin/bash

# Trier les fichiers par heure de dernière modification (le plus récent en premier)
FOLDER_PATH="/Users/QA/Desktop/Wallpapers"

echo "Fichiers triés par heure de modification (le plus récent en premier) :"
ls -t "$FOLDER_PATH"

echo "Tri basé sur le temps terminé"

Tri par Taille de Fichier

#!/bin/bash

# Trier les fichiers par taille (le plus grand en premier)
FOLDER_PATH="/Users/QA/Desktop/Wallpapers"

echo "Fichiers triés par taille (le plus grand en premier) :"
ls -lhS "$FOLDER_PATH"

echo "Tri basé sur la taille terminé"

Options de Tri Avancées

#!/bin/bash

# Critères de tri multiples
advanced_sorting() {
    local folder_path="$1"
    
    echo "=== Tri Avancé des Fichiers : $folder_path ==="
    
    echo "1. Par heure de modification (le plus récent en premier) :"
    ls -lt "$folder_path" | head -10
    
    echo -e "\n2. Par heure de modification (le plus ancien en premier) :"
    ls -ltr "$folder_path" | head -10
    
    echo -e "\n3. Par taille (le plus grand en premier) :"
    ls -lhS "$folder_path" | head -10
    
    echo -e "\n4. Par taille (le plus petit en premier) :"
    ls -lhSr "$folder_path" | head -10
    
    echo -e "\n5. Par nom (alphabétique) :"
    ls -l "$folder_path" | sort -k9
    
    echo -e "\n6. Par extension :"
    ls -1 "$folder_path" | grep '\.' | sort -t. -k2
}

# Utilisation
advanced_sorting "/Users/QA/Desktop/Wallpapers"

Système de Gestion de Répertoires d'Entreprise

#!/bin/bash

# Outil de Gestion de Répertoires Roundfleet
# Analyse complète des fichiers et répertoires pour les appareils de flotte

# Configuration
SCRIPT_VERSION="1.0.0"
LOG_FILE="/var/log/roundfleet_directory.log"
REPORT_DIR="/etc/roundfleet/reports/directory"
CONFIG_DIR="/etc/roundfleet/directory"

# Créer les répertoires s'ils n'existent pas
mkdir -p "$REPORT_DIR" "$CONFIG_DIR"

# Catégories de répertoires pour l'analyse
declare -A DIRECTORY_CATEGORIES=(
    ["system"]="/System,/Library,/usr,/bin,/sbin"
    ["user"]="/Users,/home"
    ["applications"]="/Applications,/Applications/Utilities"
    ["documents"]="/Documents,/Desktop,/Downloads"
    ["media"]="/Movies,/Music,/Pictures"
    ["temporary"]="/tmp,/var/tmp,/var/cache"
    ["logs"]="/var/log,/Library/Logs"
    ["configuration"]="/etc,/private/etc,/Library/Preferences"
    ["development"]="/usr/local,/opt,/Developer"
    ["network"]="/Network,/Volumes"
)

# Politiques de répertoires pour différents niveaux de scan
declare -A DIRECTORY_POLICIES=(
    ["quick_scan"]="basic_listing,file_count,size_summary"
    ["standard_scan"]="detailed_listing,hidden_files,type_analysis,permission_check"
    ["comprehensive_scan"]="full_recursive,security_scan,duplicate_detection,metadata_extraction"
    ["security_audit"]="permission_audit,ownership_check,sensitive_file_scan,access_log"
    ["performance_scan"]="large_file_detection,fragmentation_check,cache_analysis"
    ["compliance_scan"]="policy_validation,retention_check,classification_audit"
)

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

# Moteur d'analyse de répertoires
analyze_directory() {
    local target_path="$1"
    local scan_level="${2:-standard_scan}"
    local category="${3:-general}"
    
    log_action "Début de l'analyse de répertoire : $target_path (Niveau : $scan_level, Catégorie : $category)"
    
    if [[ ! -d "$target_path" ]]; then
        log_action "ERREUR : Répertoire non trouvé : $target_path"
        return 1
    fi
    
    local report_file="$REPORT_DIR/directory_analysis_$(echo "$target_path" | tr '/' '_')_$(date +%Y%m%d_%H%M%S).json"
    
    # Initialiser le rapport
    cat > "$report_file" << EOF
{
    "analysis_info": {
        "target_path": "$target_path",
        "scan_level": "$scan_level",
        "category": "$category",
        "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
        "hostname": "$(hostname)",
        "script_version": "$SCRIPT_VERSION"
    },
    "directory_metrics": {},
    "file_analysis": {},
    "security_findings": {},
    "recommendations": []
}
EOF
    
    # Métriques de base du répertoire
    local total_items=$(find "$target_path" -maxdepth 1 2>/dev/null | wc -l)
    local total_files=$(find "$target_path" -maxdepth 1 -type f 2>/dev/null | wc -l)
    local total_dirs=$(find "$target_path" -maxdepth 1 -type d 2>/dev/null | wc -l)
    local total_size=$(du -sk "$target_path" 2>/dev/null | cut -f1)
    
    # Mettre à jour le rapport avec les métriques
    jq --argjson total_items "$((total_items - 1))" \
       --argjson total_files "$total_files" \
       --argjson total_dirs "$((total_dirs - 1))" \
       --argjson total_size_kb "$total_size" \
       '.directory_metrics = {
           "total_items": $total_items,
           "total_files": $total_files,
           "total_directories": $total_dirs,
           "total_size_kb": $total_size_kb,
           "average_file_size": (if $total_files > 0 then ($total_size_kb / $total_files) else 0 end)
       }' "$report_file" > "${report_file}.tmp" && mv "${report_file}.tmp" "$report_file"
    
    # Effectuer l'analyse selon le niveau de scan
    case "$scan_level" in
        "quick_scan")
            perform_quick_scan "$target_path" "$report_file"
            ;;
        "standard_scan")
            perform_standard_scan "$target_path" "$report_file"
            ;;
        "comprehensive_scan")
            perform_comprehensive_scan "$target_path" "$report_file"
            ;;
        "security_audit")
            perform_security_audit "$target_path" "$report_file"
            ;;
        "performance_scan")
            perform_performance_scan "$target_path" "$report_file"
            ;;
        "compliance_scan")
            perform_compliance_scan "$target_path" "$report_file"
            ;;
    esac
    
    log_action "Analyse de répertoire terminée : $report_file"
    echo "$report_file"
}

# Implémentation de scan rapide
perform_quick_scan() {
    local target_path="$1"
    local report_file="$2"
    
    echo "Exécution d'un scan rapide de $target_path..."
    
    # Listage de fichiers de base
    local file_listing=$(ls -la "$target_path" 2>/dev/null | tail -n +2)
    
    # Distribution des types de fichiers
    local file_types=$(find "$target_path" -maxdepth 1 -type f -exec file {} \; 2>/dev/null | cut -d: -f2 | sort | uniq -c | sort -nr)
    
    # Mettre à jour le rapport
    jq --arg file_listing "$file_listing" \
       --arg file_types "$file_types" \
       '.file_analysis.quick_scan = {
           "file_listing": $file_listing,
           "file_type_distribution": $file_types
       }' "$report_file" > "${report_file}.tmp" && mv "${report_file}.tmp" "$report_file"
}

# Implémentation de scan standard
perform_standard_scan() {
    local target_path="$1"
    local report_file="$2"
    
    echo "Exécution d'un scan standard de $target_path..."
    
    # Analyse détaillée des fichiers
    local detailed_listing=$(ls -lah "$target_path" 2>/dev/null)
    local hidden_files=$(ls -la "$target_path" 2>/dev/null | grep "^\." | wc -l)
    local executable_files=$(find "$target_path" -maxdepth 1 -type f -executable 2>/dev/null | wc -l)
    
    # Analyse de taille des fichiers
    local large_files=$(find "$target_path" -maxdepth 1 -type f -size +10M 2>/dev/null)
    local small_files=$(find "$target_path" -maxdepth 1 -type f -size -1k 2>/dev/null | wc -l)
    
    # Analyse de l'âge des fichiers
    local recent_files=$(find "$target_path" -maxdepth 1 -type f -mtime -7 2>/dev/null | wc -l)
    local old_files=$(find "$target_path" -maxdepth 1 -type f -mtime +365 2>/dev/null | wc -l)
    
    # Mettre à jour le rapport
    jq --arg detailed_listing "$detailed_listing" \
       --argjson hidden_files "$((hidden_files - 2))" \
       --argjson executable_files "$executable_files" \
       --arg large_files "$large_files" \
       --argjson small_files "$small_files" \
       --argjson recent_files "$recent_files" \
       --argjson old_files "$old_files" \
       '.file_analysis.standard_scan = {
           "detailed_listing": $detailed_listing,
           "hidden_files_count": $hidden_files,
           "executable_files_count": $executable_files,
           "large_files": $large_files,
           "small_files_count": $small_files,
           "recent_files_count": $recent_files,
           "old_files_count": $old_files
       }' "$report_file" > "${report_file}.tmp" && mv "${report_file}.tmp" "$report_file"
}

# Implémentation de scan complet
perform_comprehensive_scan() {
    local target_path="$1"
    local report_file="$2"
    
    echo "Exécution d'un scan complet de $target_path..."
    
    # Analyse récursive
    local total_recursive_items=$(find "$target_path" 2>/dev/null | wc -l)
    local max_depth=$(find "$target_path" -type d 2>/dev/null | awk -F/ '{print NF}' | sort -n | tail -1)
    
    # Analyse des extensions de fichiers
    local extensions=$(find "$target_path" -type f 2>/dev/null | grep '\.' | rev | cut -d. -f1 | rev | sort | uniq -c | sort -nr)
    
    # Détection de fichiers dupliqués
    local duplicates=$(find "$target_path" -type f 2>/dev/null -exec md5 {} \; | sort | uniq -d -w 32)
    
    # Analyse des liens symboliques
    local symlinks=$(find "$target_path" -type l 2>/dev/null)
    
    # Mettre à jour le rapport
    jq --argjson total_recursive "$((total_recursive_items - 1))" \
       --argjson max_depth "$((max_depth - $(echo "$target_path" | tr -cd '/' | wc -c) - 1))" \
       --arg extensions "$extensions" \
       --arg duplicates "$duplicates" \
       --arg symlinks "$symlinks" \
       '.file_analysis.comprehensive_scan = {
           "total_recursive_items": $total_recursive,
           "maximum_depth": $max_depth,
           "file_extensions": $extensions,
           "duplicate_files": $duplicates,
           "symbolic_links": $symlinks
       }' "$report_file" > "${report_file}.tmp" && mv "${report_file}.tmp" "$report_file"
}

# Fonction d'exécution principale
main() {
    local action="${1:-analyze}"
    local target="${2:-/Users}"
    local scan_level="${3:-standard_scan}"
    
    log_action "=== Gestion de Répertoires Roundfleet Démarrée ==="
    log_action "Action : $action, Cible : $target, Niveau de Scan : $scan_level"
    
    case "$action" in
        "analyze")
            analyze_directory "$target" "$scan_level"
            ;;
        "fleet-scan")
            manage_fleet_directories "scan_all" "" "$scan_level"
            ;;
        "category-scan")
            manage_fleet_directories "scan_category" "$target" "$scan_level"
            ;;
        "fleet-report")
            manage_fleet_directories "generate_report"
            ;;
        "help")
            echo "Usage : $0 [action] [cible] [niveau_scan]"
            echo "Actions : analyze, fleet-scan, category-scan, fleet-report, help"
            echo "Niveaux de Scan : quick_scan, standard_scan, comprehensive_scan, security_audit, performance_scan, compliance_scan"
            echo "Catégories : ${!DIRECTORY_CATEGORIES[*]}"
            ;;
        *)
            log_action "ERREUR : Action inconnue : $action"
            exit 1
            ;;
    esac
    
    log_action "=== Gestion de répertoires terminée ==="
}

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

Meilleures Pratiques de Listage de Répertoires

Gérer les Espaces dans les Noms de Fichiers

# Gestion sécurisée des noms de fichiers avec espaces
while IFS= read -r -d '' file; do
    echo "Traitement : $file"
    # Votre logique de traitement ici
done < <(find "/chemin/avec espaces" -type f -print0)

Filtrer par Types de Fichiers

# Lister seulement les fichiers images
ls -la /Users/QA/Desktop/Wallpapers/*.{jpg,jpeg,png,gif,bmp} 2>/dev/null

# Lister seulement les fichiers texte
find /chemin/vers/répertoire -name "*.txt" -o -name "*.md" -o -name "*.doc"

Considérations de Performance

# Pour les gros répertoires, utiliser find avec profondeur limitée
find /gros/répertoire -maxdepth 2 -type f | head -100

# Utiliser xargs pour traiter beaucoup de fichiers
find /répertoire -name "*.log" | xargs wc -l

Opérations Courantes de Répertoires

Analyse de Taille de Répertoires

#!/bin/bash

# Analyser les tailles de répertoires
analyze_directory_sizes() {
    local base_path="$1"
    
    echo "Analyse de taille de répertoire pour : $base_path"
    echo "=============================================="
    
    # Top 10 des plus gros sous-répertoires
    du -h "$base_path"/* 2>/dev/null | sort -hr | head -10
    
    echo -e "\nTaille totale du répertoire :"
    du -sh "$base_path"
}

analyze_directory_sizes "/Users"

Comptage de Fichiers par Type

#!/bin/bash

# Compter les fichiers par extension
count_files_by_type() {
    local directory="$1"
    
    echo "Distribution des types de fichiers dans : $directory"
    find "$directory" -type f | grep '\.' | rev | cut -d. -f1 | rev | sort | uniq -c | sort -nr
}

count_files_by_type "/Users/QA/Desktop/Wallpapers"

Notes Importantes

  • Utilisez des guillemets autour des chemins contenant des espaces : ls "/chemin avec espaces"
  • Échappez les caractères spéciaux avec une barre oblique inverse quand vous n'utilisez pas de guillemets
  • Vérifiez les permissions avant d'accéder aux répertoires système
  • Testez les scripts sur des répertoires d'exemple avant utilisation en production
  • Surveillez les performances lors de l'analyse de grandes structures de répertoires
  • Considérez la sécurité lors du listage de répertoires sensibles dans les journaux

Gestion d'Identité et Nomenclature d'Appareils Entreprise sur macOS

Implémentez une gestion d'identité et de nomenclature d'appareils de niveau entreprise sur votre déploiement Roundfleet avec des conventions de nomenclature standardisées, un provisioning automatisé et une validation d'identité complète. Ce tutoriel fournit des solutions pour maintenir une identification cohérente des appareils et une présence réseau.

Comprendre l'Identité des Appareils macOS

Les appareils macOS ont trois composants de nomenclature distincts qui affectent l'identification et le comportement réseau :

  • Nom d'Ordinateur - Nom convivial affiché dans les préférences Partage et Finder
  • Nom d'Hôte Local - Identifiant réseau avec suffixe ".local" pour Bonjour/mDNS
  • Nom d'Hôte - Nom d'hôte réseau principal utilisé dans terminal et services réseau
  • Nom NetBIOS - Identifiant de compatibilité réseau Windows

Opérations de Nomenclature d'Appareils de Base

Changer le Nom d'Ordinateur

#!/bin/bash

# Définir le nom d'ordinateur
scutil --set ComputerName "$ComputerName"

echo "Nom d'ordinateur défini à : $ComputerName"

Changer le Nom d'Hôte Local

#!/bin/bash

# Définir le nom d'hôte local pour la découverte Bonjour/mDNS
scutil --set LocalHostName "$LocalHostName"

echo "Nom d'hôte local défini à : $LocalHostName"

Changer le Nom d'Hôte

#!/bin/bash

# Définir le nom d'hôte principal pour les services réseau
# Exemple : Votre-iMac.domaine.com
scutil --set HostName "$HostName"

echo "Nom d'hôte défini à : $HostName"

Vider le Cache DNS

#!/bin/bash

# Vider le cache DNS après les changements de nom
sudo dscacheutil --flushcache

echo "Cache DNS vidé avec succès"

Normes de Nomenclature d'Entreprise

Script de Convention de Nomenclature Standardisée

#!/bin/bash

# Outil de Nomenclature d'Appareils Entreprise Roundfleet
# Implémente des conventions de nomenclature standardisées dans la flotte

# Configuration
CONFIG_FILE="/etc/roundfleet/naming_policy.conf"
LOG_FILE="/var/log/roundfleet_naming.log"
REPORT_DIR="/var/log/roundfleet_reports"

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

# Politique de nomenclature par défaut
cat > "$CONFIG_FILE" 2>/dev/null << 'EOF' || true
# Politique de Nomenclature d'Appareils Roundfleet
# Version : 2.0

# Format de Convention de Nomenclature
# Modèle : {PREFIXE}-{LOCALISATION}-{TYPE}-{SEQUENCE}
# Exemple : MF-PAR-MBP-001

# Paramètres d'Organisation
ORG_PREFIX="MF"
DOMAIN_SUFFIX="roundfleet.local"
ENABLE_AUTO_NAMING=true
VALIDATE_NAMES=true

# Codes de Localisation
LOCATION_CODES="PAR,LYO,MRS,LIL,STR,NAN,BOR,TOU"
DEFAULT_LOCATION="PAR"

# Mapping des Types d'Appareils
# Format : IDENTIFIANT_MODELE:CODE_COURT
DEVICE_TYPES=(
    "MacBookPro:MBP"
    "MacBookAir:MBA"
    "iMac:iMac"
    "iMacPro:iMP"
    "MacPro:MP"
    "Macmini:MM"
    "MacStudio:MS"
)

# Paramètres Réseau
ENABLE_NETBIOS=true
SYNC_ALL_NAMES=true
FLUSH_DNS_AFTER_CHANGE=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"
}

# Obtenir les informations du modèle d'appareil
get_device_model() {
    local model_identifier
    model_identifier=$(system_profiler SPHardwareDataType | grep "Model Identifier" | awk -F: '{print $2}' | xargs)
    
    # Mapper le modèle au type d'appareil
    for mapping in "${DEVICE_TYPES[@]}"; do
        local model_pattern="${mapping%:*}"
        local type_code="${mapping#*:}"
        
        if [[ "$model_identifier" == *"$model_pattern"* ]]; then
            echo "$type_code"
            return 0
        fi
    done
    
    # Fallback par défaut
    echo "MAC"
}

# Obtenir le code de localisation depuis la saisie utilisateur ou configuration
get_location_code() {
    local location="$1"
    
    if [[ -z "$location" ]]; then
        location="$DEFAULT_LOCATION"
    fi
    
    # Valider le code de localisation
    if [[ ",$LOCATION_CODES," == *",$location,"* ]]; then
        echo "$location"
    else
        echo "$DEFAULT_LOCATION"
        log_action "AVERTISSEMENT : Localisation invalide '$location', utilisation par défaut '$DEFAULT_LOCATION'"
    fi
}

# Générer un numéro de séquence
generate_sequence() {
    local prefix="$1"
    local sequence_file="/var/lib/roundfleet/sequences/${prefix}.seq"
    
    mkdir -p "$(dirname "$sequence_file")"
    
    if [[ -f "$sequence_file" ]]; then
        local current_seq
        current_seq=$(cat "$sequence_file")
        ((current_seq++))
    else
        current_seq=1
    fi
    
    printf "%03d" "$current_seq" > "$sequence_file"
    printf "%03d" "$current_seq"
}

# Générer un nom d'appareil standardisé
generate_device_name() {
    local location="$1"
    local custom_suffix="$2"
    
    local device_type
    device_type=$(get_device_model)
    
    local location_code
    location_code=$(get_location_code "$location")
    
    local base_prefix="${ORG_PREFIX}-${location_code}-${device_type}"
    
    if [[ -n "$custom_suffix" ]]; then
        echo "${base_prefix}-${custom_suffix}"
    else
        local sequence
        sequence=$(generate_sequence "$base_prefix")
        echo "${base_prefix}-${sequence}"
    fi
}

# Valider la conformité de nomenclature
validate_device_name() {
    local name="$1"
    local errors=()
    
    # Validation de longueur
    if [[ ${#name} -gt 15 ]]; then
        errors+=("Nom trop long (max 15 caractères pour compatibilité NetBIOS)")
    fi
    
    # Validation de caractères
    if [[ "$name" =~ [^A-Za-z0-9\-] ]]; then
        errors+=("Caractères invalides (seuls alphanumériques et tirets autorisés)")
    fi
    
    # Validation de format
    if [[ ! "$name" =~ ^[A-Z]{2,4}-[A-Z]{2,4}-[A-Z]{2,4}-[0-9]{3}$ ]] && [[ "$VALIDATE_NAMES" == "true" ]]; then
        errors+=("Le nom ne correspond pas au format standard de l'organisation")
    fi
    
    if [[ ${#errors[@]} -eq 0 ]]; then
        return 0
    else
        printf '%s\n' "${errors[@]}"
        return 1
    fi
}

# Appliquer les noms d'appareils avec validation
apply_device_names() {
    local computer_name="$1"
    local hostname="$2"
    local local_hostname="$3"
    
    echo "=== Application des Noms d'Appareils ==="
    
    # Valider tous les noms
    local validation_errors=0
    
    if ! validate_device_name "$computer_name"; then
        echo "❌ Validation du nom d'ordinateur échouée"
        ((validation_errors++))
    fi
    
    if [[ -n "$hostname" ]] && ! validate_device_name "${hostname%%.*}"; then
        echo "❌ Validation du nom d'hôte échouée"
        ((validation_errors++))
    fi
    
    if [[ -n "$local_hostname" ]] && ! validate_device_name "$local_hostname"; then
        echo "❌ Validation du nom d'hôte local échouée"
        ((validation_errors++))
    fi
    
    if [[ $validation_errors -gt 0 ]] && [[ "$VALIDATE_NAMES" == "true" ]]; then
        echo "⚠️  Validation des noms échouée, abandon des changements"
        return 1
    fi
    
    # Appliquer le nom d'ordinateur
    if [[ -n "$computer_name" ]]; then
        scutil --set ComputerName "$computer_name"
        echo "✅ Nom d'ordinateur : $computer_name"
        log_action "Nom d'ordinateur changé en : $computer_name"
    fi
    
    # Appliquer le nom d'hôte
    if [[ -n "$hostname" ]]; then
        scutil --set HostName "$hostname"
        echo "✅ Nom d'hôte : $hostname"
        log_action "Nom d'hôte changé en : $hostname"
    fi
    
    # Appliquer le nom d'hôte local
    if [[ -n "$local_hostname" ]]; then
        scutil --set LocalHostName "$local_hostname"
        echo "✅ Nom d'hôte local : $local_hostname"
        log_action "Nom d'hôte local changé en : $local_hostname"
    fi
    
    # Appliquer le nom NetBIOS si activé
    if [[ "$ENABLE_NETBIOS" == "true" ]] && [[ -n "$computer_name" ]]; then
        defaults write /Library/Preferences/SystemConfiguration/com.apple.smb.server NetBIOSName "$computer_name"
        echo "✅ Nom NetBIOS : $computer_name"
        log_action "Nom NetBIOS changé en : $computer_name"
    fi
    
    # Vider le cache DNS
    if [[ "$FLUSH_DNS_AFTER_CHANGE" == "true" ]]; then
        sudo dscacheutil --flushcache
        echo "✅ Cache DNS vidé"
        log_action "Cache DNS vidé après changements de noms"
    fi
    
    return 0
}

# Auto-génération et application des noms
auto_provision_device() {
    local location="$1"
    local custom_suffix="$2"
    
    echo "=== Provisioning Automatique des Noms d'Appareils ==="
    
    if [[ "$ENABLE_AUTO_NAMING" != "true" ]]; then
        echo "❌ Nomenclature automatique désactivée dans la politique"
        return 1
    fi
    
    # Générer le nom de base
    local base_name
    base_name=$(generate_device_name "$location" "$custom_suffix")
    
    # Créer toutes les variantes de nomenclature
    local computer_name="$base_name"
    local local_hostname="$base_name"
    local hostname="${base_name}.${DOMAIN_SUFFIX}"
    
    echo "Noms générés :"
    echo "  Nom d'Ordinateur : $computer_name"
    echo "  Nom d'Hôte Local : $local_hostname"
    echo "  Nom d'Hôte : $hostname"
    echo ""
    
    # Appliquer les noms
    apply_device_names "$computer_name" "$hostname" "$local_hostname"
}

# Obtenir l'identité actuelle de l'appareil
get_current_identity() {
    echo "=== Identité Actuelle de l'Appareil ==="
    
    local computer_name
    computer_name=$(scutil --get ComputerName 2>/dev/null || echo "Non Défini")
    
    local hostname
    hostname=$(scutil --get HostName 2>/dev/null || echo "Non Défini")
    
    local local_hostname
    local_hostname=$(scutil --get LocalHostName 2>/dev/null || echo "Non Défini")
    
    local netbios_name
    netbios_name=$(defaults read /Library/Preferences/SystemConfiguration/com.apple.smb.server NetBIOSName 2>/dev/null || echo "Non Défini")
    
    echo "Nom d'Ordinateur : $computer_name"
    echo "Nom d'Hôte : $hostname"
    echo "Nom d'Hôte Local : $local_hostname"
    echo "Nom NetBIOS : $netbios_name"
    echo "Numéro de Série : $(system_profiler SPHardwareDataType | grep "Serial Number" | awk -F: '{print $2}' | xargs)"
    echo "Modèle : $(system_profiler SPHardwareDataType | grep "Model Identifier" | awk -F: '{print $2}' | xargs)"
}

# Générer un rapport de conformité
generate_naming_report() {
    local report_file="$REPORT_DIR/device_naming_$(date +%Y%m%d_%H%M%S).json"
    
    echo "=== Génération du Rapport de Conformité de Nomenclature ==="
    
    local computer_name hostname local_hostname netbios_name
    computer_name=$(scutil --get ComputerName 2>/dev/null || echo "")
    hostname=$(scutil --get HostName 2>/dev/null || echo "")
    local_hostname=$(scutil --get LocalHostName 2>/dev/null || echo "")
    netbios_name=$(defaults read /Library/Preferences/SystemConfiguration/com.apple.smb.server NetBIOSName 2>/dev/null || echo "")
    
    # Vérifier la conformité
    local computer_compliant="false"
    local hostname_compliant="false"
    local naming_compliant="false"
    
    if validate_device_name "$computer_name" >/dev/null 2>&1; then
        computer_compliant="true"
    fi
    
    if [[ -n "$hostname" ]] && validate_device_name "${hostname%%.*}" >/dev/null 2>&1; then
        hostname_compliant="true"
    fi
    
    if [[ "$computer_compliant" == "true" ]] && [[ "$hostname_compliant" == "true" ]]; then
        naming_compliant="true"
    fi
    
    # Créer le rapport JSON
    cat > "$report_file" << EOF
{
  "type_rapport": "conformite_nomenclature_appareil",
  "horodatage": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
  "infos_appareil": {
    "nom_hote": "$(hostname)",
    "numero_serie": "$(system_profiler SPHardwareDataType | grep "Serial Number" | awk -F: '{print $2}' | xargs)",
    "identifiant_modele": "$(system_profiler SPHardwareDataType | grep "Model Identifier" | awk -F: '{print $2}' | xargs)",
    "version_macos": "$(sw_vers -productVersion)"
  },
  "noms_actuels": {
    "nom_ordinateur": "$computer_name",
    "nom_hote": "$hostname",
    "nom_hote_local": "$local_hostname",
    "nom_netbios": "$netbios_name"
  },
  "conformite": {
    "nom_ordinateur_conforme": $computer_compliant,
    "nom_hote_conforme": $hostname_compliant,
    "conformite_globale": $naming_compliant,
    "version_politique": "2.0"
  },
  "parametres_politique": {
    "prefixe_org": "$ORG_PREFIX",
    "suffixe_domaine": "$DOMAIN_SUFFIX",
    "nomenclature_auto_activee": $ENABLE_AUTO_NAMING,
    "validation_activee": $VALIDATE_NAMES
  }
}
EOF
    
    echo "Rapport de nomenclature sauvegardé dans : $report_file"
    log_action "Rapport de nomenclature d'appareil généré : $report_file"
}

# Fonction principale avec gestion d'arguments
main() {
    log_action "=== Outil de Nomenclature d'Appareils Roundfleet Démarré ==="
    
    case "${1:-status}" in
        "auto-provision")
            auto_provision_device "$2" "$3"
            ;;
        "set-names")
            apply_device_names "$2" "$3" "$4"
            ;;
        "validate")
            get_current_identity
            echo ""
            if validate_device_name "$2" 2>/dev/null; then
                echo "✅ Le nom '$2' est conforme"
            else
                echo "❌ Validation du nom '$2' échouée :"
                validate_device_name "$2"
            fi
            ;;
        "report")
            generate_naming_report
            ;;
        "status"|*)
            get_current_identity
            ;;
    esac
    
    log_action "=== Opération de nomenclature d'appareil terminée ==="
}

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

Gestion d'Identité Avancée

Synchronisation d'Identité Réseau

#!/bin/bash

# Synchroniser tous les composants de nomenclature pour la cohérence
sync_network_identity() {
    echo "=== Synchronisation d'Identité Réseau ==="
    
    # Obtenir le nom d'ordinateur actuel comme source
    local source_name
    source_name=$(scutil --get ComputerName 2>/dev/null)
    
    if [[ -z "$source_name" ]]; then
        echo "❌ Aucun nom d'ordinateur défini, impossible de synchroniser"
        return 1
    fi
    
    # Générer des noms cohérents
    local clean_name
    clean_name=$(echo "$source_name" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
    
    local local_hostname="$clean_name"
    local hostname="${clean_name}.local"
    
    echo "Synchronisation de l'identité depuis : $source_name"
    echo "Nom d'Hôte Local : $local_hostname"
    echo "Nom d'Hôte : $hostname"
    
    # Appliquer les noms synchronisés
    scutil --set LocalHostName "$local_hostname"
    scutil --set HostName "$hostname"
    
    # Mettre à jour NetBIOS
    defaults write /Library/Preferences/SystemConfiguration/com.apple.smb.server NetBIOSName "$source_name"
    
    # Vider le cache DNS
    sudo dscacheutil --flushcache
    
    echo "✅ Identité réseau synchronisée"
}

sync_network_identity

Provisioning d'Appareils en Masse

#!/bin/bash

# Nomenclature d'appareils en masse pour déploiement de flotte
bulk_provision_devices() {
    local csv_file="$1"
    local report_file="/tmp/bulk_naming_$(date +%Y%m%d_%H%M%S).csv"
    
    echo "=== Provisioning d'Appareils en Masse ==="
    echo "Serie,Nom_Ordinateur,Nom_Hote,Statut,Horodatage" > "$report_file"
    
    # Lire le fichier CSV avec les affectations d'appareils
    # Format : Serie,Localisation,SuffixePersonnalise
    while IFS=',' read -r serial location suffix; do
        # Ignorer l'en-tête
        [[ "$serial" == "Serie" ]] && continue
        
        local current_serial
        current_serial=$(system_profiler SPHardwareDataType | grep "Serial Number" | awk -F: '{print $2}' | xargs)
        
        if [[ "$current_serial" == "$serial" ]]; then
            echo "Traitement de l'appareil : $serial"
            
            # Générer les noms pour cet appareil
            local device_name
            device_name=$(generate_device_name "$location" "$suffix")
            
            local computer_name="$device_name"
            local hostname="${device_name}.roundfleet.local"
            
            # Appliquer les noms
            if apply_device_names "$computer_name" "$hostname" "$device_name"; then
                echo "$serial,$computer_name,$hostname,SUCCES,$(date)" >> "$report_file"
                echo "✅ Provisionné avec succès : $computer_name"
            else
                echo "$serial,$computer_name,$hostname,ECHEC,$(date)" >> "$report_file"
                echo "❌ Échec du provisioning : $computer_name"
            fi
            
            break
        fi
    done < "$csv_file"
    
    echo "Rapport de provisioning en masse : $report_file"
}

# Exemple d'utilisation :
# bulk_provision_devices "/chemin/vers/affectations_appareils.csv"

Validation d'Identité et Conformité

Script de Validation Complète

#!/bin/bash

# Validation complète de l'identité d'appareil
validate_device_identity() {
    echo "=== Validation de l'Identité d'Appareil ==="
    
    local issues=0
    local warnings=0
    
    # Obtenir tous les noms actuels
    local computer_name hostname local_hostname
    computer_name=$(scutil --get ComputerName 2>/dev/null)
    hostname=$(scutil --get HostName 2>/dev/null)
    local_hostname=$(scutil --get LocalHostName 2>/dev/null)
    
    # Validation du nom d'ordinateur
    if [[ -z "$computer_name" ]]; then
        echo "❌ Nom d'ordinateur non défini"
        ((issues++))
    elif ! validate_device_name "$computer_name" >/dev/null 2>&1; then
        echo "⚠️  Format de nom d'ordinateur non conforme : $computer_name"
        ((warnings++))
    else
        echo "✅ Nom d'ordinateur conforme : $computer_name"
    fi
    
    # Validation du nom d'hôte
    if [[ -z "$hostname" ]]; then
        echo "⚠️  Nom d'hôte non défini"
        ((warnings++))
    elif ! validate_device_name "${hostname%%.*}" >/dev/null 2>&1; then
        echo "⚠️  Format de nom d'hôte non conforme : $hostname"
        ((warnings++))
    else
        echo "✅ Nom d'hôte conforme : $hostname"
    fi
    
    # Validation du nom d'hôte local
    if [[ -z "$local_hostname" ]]; then
        echo "⚠️  Nom d'hôte local non défini"
        ((warnings++))
    elif ! validate_device_name "$local_hostname" >/dev/null 2>&1; then
        echo "⚠️  Format de nom d'hôte local non conforme : $local_hostname"
        ((warnings++))
    else
        echo "✅ Nom d'hôte local conforme : $local_hostname"
    fi
    
    # Vérification de cohérence
    if [[ -n "$computer_name" && -n "$local_hostname" ]]; then
        local expected_local
        expected_local=$(echo "$computer_name" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
        
        if [[ "$local_hostname" != "$expected_local" ]]; then
            echo "⚠️  Noms non synchronisés (Ordinateur : $computer_name, Local : $local_hostname)"
            ((warnings++))
        fi
    fi
    
    # Validation de connectivité réseau
    echo ""
    echo "=== Test d'Identité Réseau ==="
    
    # Tester la résolution Bonjour
    if ping -c 1 "${local_hostname}.local" >/dev/null 2>&1; then
        echo "✅ Résolution Bonjour fonctionnelle"
    else
        echo "⚠️  Résolution Bonjour échouée"
        ((warnings++))
    fi
    
    # Tester la résolution du nom d'hôte
    if [[ -n "$hostname" ]] && ping -c 1 "$hostname" >/dev/null 2>&1; then
        echo "✅ Résolution de nom d'hôte fonctionnelle"
    elif [[ -n "$hostname" ]]; then
        echo "⚠️  Résolution de nom d'hôte échouée"
        ((warnings++))
    fi
    
    # Résumé
    echo ""
    echo "=== Résumé de Validation ==="
    echo "Problèmes : $issues"
    echo "Avertissements : $warnings"
    
    if [[ $issues -eq 0 && $warnings -eq 0 ]]; then
        echo "✅ Identité d'appareil entièrement conforme"
        return 0
    elif [[ $issues -eq 0 ]]; then
        echo "⚠️  Identité d'appareil a des avertissements mais est fonctionnelle"
        return 1
    else
        echo "❌ Identité d'appareil a des problèmes critiques"
        return 2
    fi
}

validate_device_identity

Intégration des Services Réseau

DNS et Services d'Annuaire

#!/bin/bash

# Mettre à jour les entrées DNS et services d'annuaire
update_network_services() {
    echo "=== Mise à Jour des Services Réseau ==="
    
    # Obtenir les noms actuels
    local computer_name hostname
    computer_name=$(scutil --get ComputerName 2>/dev/null)
    hostname=$(scutil --get HostName 2>/dev/null)
    
    # Mettre à jour mDNSResponder
    echo "Redémarrage de mDNSResponder..."
    sudo launchctl kickstart -k system/com.apple.mDNSResponder
    
    # Mettre à jour l'enregistrement NetBIOS
    if [[ -n "$computer_name" ]]; then
        echo "Mise à jour de l'enregistrement NetBIOS..."
        defaults write /Library/Preferences/SystemConfiguration/com.apple.smb.server NetBIOSName "$computer_name"
        sudo launchctl kickstart -k system/com.apple.smbd
    fi
    
    # Vider tous les caches
    echo "Vidage des caches système..."
    sudo dscacheutil --flushcache
    sudo killall -HUP mDNSResponder
    
    # Mettre à jour Open Directory si lié
    if dsconfigldap -v >/dev/null 2>&1; then
        echo "Mise à jour de l'enregistrement Open Directory..."
        sudo dsconfigldap -f
    fi
    
    echo "✅ Services réseau mis à jour"
}

update_network_services

Notes de Configuration Importantes

Utilitaire de Configuration Système (scutil)

La commande scutil gère la configuration système macOS :

  • ComputerName - Nom d'appareil visible par l'utilisateur
  • HostName - Nom d'hôte réseau principal
  • LocalHostName - Identifiant Bonjour/mDNS
  • NetBIOSName - Compatibilité réseau Windows

Bonnes Pratiques pour l'Entreprise

  1. Convention de Nomenclature Standardisée

    • Utiliser un format cohérent : ORG-LOCALISATION-TYPE-SEQUENCE
    • Garder les noms sous 15 caractères pour compatibilité NetBIOS
    • Éviter les caractères spéciaux et espaces
  2. Considérations Réseau

    • Assurer des noms uniques dans les segments réseau
    • Tester la résolution DNS après changements
    • Coordonner avec les administrateurs réseau
  3. Gestion de Flotte

    • Implémenter un provisioning automatisé
    • Maintenir des bases de données de nomenclature
    • Audit de conformité régulier
    • Sauvegarder la configuration avant changements
  4. Implications de Sécurité

    • Les noms d'appareils peuvent révéler la structure organisationnelle
    • Considérer les exigences de confidentialité
    • Implémenter des contrôles d'accès pour les changements de nomenclature

Dépannage des Problèmes Courants

  • Changements de noms non effectifs - Redémarrer les services réseau ou redémarrer
  • Conflits Bonjour - Assurer des valeurs LocalHostName uniques
  • Problèmes de résolution DNS - Vider le cache et redémarrer mDNSResponder
  • Problèmes réseau Windows - Vérifier la configuration du nom NetBIOS

N'oubliez pas de tester les changements de nomenclature de manière approfondie et de coordonner avec les administrateurs réseau avant l'implémentation dans votre déploiement Roundfleet.

Suivi de Localisation des Appareils sur macOS

Suivez et surveillez la localisation géographique de vos appareils Roundfleet en utilisant les services de géolocalisation par adresse IP. Ce tutoriel couvre la découverte de localisation, la surveillance de sécurité, les politiques de géofencing, et les rapports de conformité pour la gestion d'appareils d'entreprise.

Comprendre le Suivi de Localisation Basé sur IP

La géolocalisation IP fournit des informations précieuses sur la localisation des appareils et l'environnement réseau, essentielles pour la sécurité et la conformité d'entreprise. Cette méthode suit les appareils basée sur leur connexion internet plutôt que le GPS, la rendant adaptée à la gestion de flotte et la surveillance de sécurité.

Informations Clés Récupérées

  • Adresse IP - Adresse IP publique de l'appareil
  • Localisation Géographique - Ville, région et pays
  • Coordonnées - Latitude et longitude pour la cartographie
  • Fournisseur Réseau - Détails du FAI ou de l'organisation
  • Fuseau Horaire - Informations de fuseau horaire local
  • Contexte de Sécurité - Évaluation de l'environnement réseau

Découverte de Localisation Basique

Géolocalisation IP Simple

#!/bin/bash

# Recherche de localisation basique utilisant l'adresse IP
curl -s https://ipinfo.io

echo "Informations de localisation récupérées avec succès"

Requête de Localisation Améliorée

#!/bin/bash

# Découverte de localisation améliorée avec sortie formatée
get_device_location() {
    echo "📍 Récupération des informations de localisation de l'appareil..."
    
    # Interroger les données de localisation
    local location_data
    location_data=$(curl -s https://ipinfo.io)
    
    if [[ $? -eq 0 && -n "$location_data" ]]; then
        echo "✅ Données de localisation récupérées avec succès :"
        echo "$location_data" | jq '.' 2>/dev/null || echo "$location_data"
    else
        echo "❌ Échec de la récupération des informations de localisation"
        return 1
    fi
}

# Exécuter la découverte de localisation
get_device_location

Informations de Localisation Structurées

#!/bin/bash

# Analyser et afficher les informations de localisation structurées
parse_location_data() {
    echo "🌍 Analyse de Localisation de l'Appareil"
    echo "======================================="
    
    # Obtenir les données de localisation
    local location_json
    location_json=$(curl -s https://ipinfo.io)
    
    if [[ $? -ne 0 ]]; then
        echo "❌ Échec de la récupération des données de localisation"
        return 1
    fi
    
    # Analyser les champs individuels
    local ip city region country loc org timezone
    ip=$(echo "$location_json" | jq -r '.ip // "Inconnu"')
    city=$(echo "$location_json" | jq -r '.city // "Inconnu"')
    region=$(echo "$location_json" | jq -r '.region // "Inconnu"')
    country=$(echo "$location_json" | jq -r '.country // "Inconnu"')
    loc=$(echo "$location_json" | jq -r '.loc // "Inconnu"')
    org=$(echo "$location_json" | jq -r '.org // "Inconnu"')
    timezone=$(echo "$location_json" | jq -r '.timezone // "Inconnu"')
    
    # Afficher les informations formatées
    echo "🔗 Adresse IP : $ip"
    echo "🏙️  Ville : $city"
    echo "🗺️  Région : $region"
    echo "🌎 Pays : $country"
    echo "📍 Coordonnées : $loc"
    echo "🏢 Organisation : $org"
    echo "🕐 Fuseau horaire : $timezone"
    
    # Informations de sécurité supplémentaires
    echo ""
    echo "🔒 Évaluation de Sécurité :"
    if [[ "$org" == *"VPN"* ]] || [[ "$org" == *"Proxy"* ]]; then
        echo "  ⚠️  VPN/Proxy potentiel détecté"
    else
        echo "  ✅ Connexion internet directe"
    fi
}

# Exécuter l'analyse de localisation
parse_location_data

Suivi de Localisation Avancé

Vérification de Localisation Multi-Sources

#!/bin/bash

# Vérifier la localisation en utilisant plusieurs services de géolocalisation
verify_location_multi_source() {
    echo "🔍 Vérification de localisation multi-sources..."
    
    # Service 1 : ipinfo.io
    echo "Vérification ipinfo.io..."
    local ipinfo_data
    ipinfo_data=$(curl -s https://ipinfo.io)
    
    # Service 2 : ipapi.co (alternative)
    echo "Vérification ipapi.co..."
    local ipapi_data
    ipapi_data=$(curl -s https://ipapi.co/json/)
    
    # Service 3 : ip-api.com (sauvegarde)
    echo "Vérification ip-api.com..."
    local ipapi_alt_data
    ipapi_alt_data=$(curl -s http://ip-api.com/json/)
    
    # Comparer les résultats
    echo ""
    echo "📊 Résultats de Vérification de Localisation :"
    echo "=============================================="
    
    if [[ -n "$ipinfo_data" ]]; then
        echo "Résultats ipinfo.io :"
        echo "$ipinfo_data" | jq '.city, .region, .country' 2>/dev/null || echo "$ipinfo_data"
        echo ""
    fi
    
    if [[ -n "$ipapi_data" ]]; then
        echo "Résultats ipapi.co :"
        echo "$ipapi_data" | jq '.city, .region, .country_name' 2>/dev/null || echo "$ipapi_data"
        echo ""
    fi
    
    if [[ -n "$ipapi_alt_data" ]]; then
        echo "Résultats ip-api.com :"
        echo "$ipapi_alt_data" | jq '.city, .regionName, .country' 2>/dev/null || echo "$ipapi_alt_data"
    fi
}

# Exécuter la vérification multi-sources
verify_location_multi_source

Détection de Changement de Localisation

#!/bin/bash

# Surveiller et détecter les changements de localisation
monitor_location_changes() {
    local previous_location_file="/tmp/last_known_location.json"
    local current_location
    
    echo "📱 Surveillance des changements de localisation de l'appareil..."
    
    # Obtenir la localisation actuelle
    current_location=$(curl -s https://ipinfo.io)
    
    if [[ $? -ne 0 ]]; then
        echo "❌ Échec de la récupération de la localisation actuelle"
        return 1
    fi
    
    # Vérifier si la localisation précédente existe
    if [[ -f "$previous_location_file" ]]; then
        local previous_location
        previous_location=$(cat "$previous_location_file")
        
        # Comparer les localisations
        local current_city previous_city
        current_city=$(echo "$current_location" | jq -r '.city // "Inconnu"')
        previous_city=$(echo "$previous_location" | jq -r '.city // "Inconnu"')
        
        if [[ "$current_city" != "$previous_city" ]]; then
            echo "🚨 Changement de localisation détecté !"
            echo "Précédente : $previous_city"
            echo "Actuelle : $current_city"
            
            # Enregistrer le changement
            echo "$(date) : Localisation changée de $previous_city à $current_city" >> /var/log/location_changes.log
        else
            echo "✅ Aucun changement de localisation détecté"
        fi
    else
        echo "📝 Première vérification de localisation - sauvegarde de la référence"
    fi
    
    # Sauvegarder la localisation actuelle comme précédente
    echo "$current_location" > "$previous_location_file"
}

# Exécuter la surveillance de localisation
monitor_location_changes

Script de Gestion de Localisation d'Entreprise

#!/bin/bash

# Système de Gestion de Localisation d'Appareil Roundfleet
# Suivi complet de localisation et surveillance de sécurité pour appareils d'entreprise

# Configuration
LOG_FILE="/var/log/roundfleet_location.log"
CONFIG_FILE="/etc/roundfleet/location_policy.conf"
LOCATION_DB="/var/lib/roundfleet/location_history.db"
ALERT_THRESHOLD_MILES=100

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

# Configurer les répertoires et fichiers
setup_environment() {
    sudo mkdir -p "$(dirname "$LOG_FILE")" 2>/dev/null
    sudo mkdir -p "$(dirname "$CONFIG_FILE")" 2>/dev/null
    sudo mkdir -p "$(dirname "$LOCATION_DB")" 2>/dev/null
    
    # Créer la base de données de localisation si elle n'existe pas
    if [[ ! -f "$LOCATION_DB" ]]; then
        echo "timestamp,ip,city,region,country,lat,lon,org,timezone" > "$LOCATION_DB"
    fi
}

# 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
        LOCATION_TRACKING_ENABLED=true
        GEOFENCE_ENABLED=false
        ALLOWED_COUNTRIES=("US" "CA" "GB" "FR")
        BLOCKED_NETWORKS=("VPN" "TOR" "PROXY")
        MONITORING_INTERVAL=3600  # 1 heure
        REPORT_EMAIL="[email protected]"
        
        log_action "Utilisation de la configuration de localisation par défaut"
    fi
}

# Obtenir des informations de localisation complètes
get_location_info() {
    local provider="${1:-ipinfo.io}"
    
    case "$provider" in
        "ipinfo.io")
            curl -s https://ipinfo.io
            ;;
        "ipapi.co")
            curl -s https://ipapi.co/json/
            ;;
        "ip-api.com")
            curl -s http://ip-api.com/json/
            ;;
        *)
            curl -s https://ipinfo.io
            ;;
    esac
}

# Valider les données de localisation
validate_location() {
    local location_data="$1"
    
    # Vérifier si les données sont du JSON valide
    if ! echo "$location_data" | jq empty 2>/dev/null; then
        log_action "Format de données de localisation invalide"
        return 1
    fi
    
    # Vérifier les champs requis
    local ip city country
    ip=$(echo "$location_data" | jq -r '.ip // empty')
    city=$(echo "$location_data" | jq -r '.city // empty')
    country=$(echo "$location_data" | jq -r '.country // empty')
    
    if [[ -z "$ip" ]] || [[ -z "$city" ]] || [[ -z "$country" ]]; then
        log_action "Champs de localisation requis manquants"
        return 1
    fi
    
    return 0
}

# Stocker la localisation dans la base de données
store_location() {
    local location_data="$1"
    
    if ! validate_location "$location_data"; then
        return 1
    fi
    
    # Analyser les données de localisation
    local timestamp ip city region country lat lon org timezone
    timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    ip=$(echo "$location_data" | jq -r '.ip')
    city=$(echo "$location_data" | jq -r '.city')
    region=$(echo "$location_data" | jq -r '.region // ""')
    country=$(echo "$location_data" | jq -r '.country')
    lat=$(echo "$location_data" | jq -r '.loc | split(",")[0] // ""')
    lon=$(echo "$location_data" | jq -r '.loc | split(",")[1] // ""')
    org=$(echo "$location_data" | jq -r '.org // ""')
    timezone=$(echo "$location_data" | jq -r '.timezone // ""')
    
    # Ajouter à la base de données
    echo "$timestamp,$ip,$city,$region,$country,$lat,$lon,$org,$timezone" >> "$LOCATION_DB"
    
    log_action "Localisation stockée : $city, $region, $country"
}

# Vérifier la conformité géofencing
check_geofence() {
    local location_data="$1"
    local country
    country=$(echo "$location_data" | jq -r '.country')
    
    if [[ "$GEOFENCE_ENABLED" == "true" ]]; then
        # Vérifier si le pays est dans la liste autorisée
        local allowed=false
        for allowed_country in "${ALLOWED_COUNTRIES[@]}"; do
            if [[ "$country" == "$allowed_country" ]]; then
                allowed=true
                break
            fi
        done
        
        if [[ "$allowed" == "false" ]]; then
            log_action "ALERTE : Appareil en dehors du géofencing autorisé - Pays : $country"
            send_security_alert "Violation de géofencing" "Appareil détecté dans un pays non autorisé : $country"
            return 1
        fi
    fi
    
    return 0
}

# Vérifier les réseaux suspects
check_network_security() {
    local location_data="$1"
    local org
    org=$(echo "$location_data" | jq -r '.org')
    
    # Vérifier contre les réseaux bloqués
    for blocked_network in "${BLOCKED_NETWORKS[@]}"; do
        if [[ "$org" == *"$blocked_network"* ]]; then
            log_action "ALERTE SÉCURITÉ : Réseau suspect détecté - $org"
            send_security_alert "Réseau Suspect" "Appareil connecté via un réseau potentiellement risqué : $org"
            return 1
        fi
    done
    
    return 0
}

# Envoyer des alertes de sécurité
send_security_alert() {
    local alert_type="$1"
    local message="$2"
    local hostname
    hostname=$(hostname)
    
    # Enregistrer l'alerte
    log_action "ALERTE SÉCURITÉ [$alert_type] : $message"
    
    # Envoyer une alerte email (si configurée)
    if command -v mail >/dev/null 2>&1 && [[ -n "$REPORT_EMAIL" ]]; then
        echo "Alerte de Sécurité de l'Appareil Roundfleet : $hostname
        
Type d'Alerte : $alert_type
Message : $message
Horodatage : $(date)
Appareil : $hostname

Ceci est une alerte de sécurité automatisée de la Surveillance de Localisation Roundfleet." | \
        mail -s "Alerte de Sécurité Roundfleet : $alert_type" "$REPORT_EMAIL"
    fi
}

# Calculer la distance entre coordonnées
calculate_distance() {
    local lat1="$1" lon1="$2" lat2="$3" lon2="$4"
    
    # Utiliser python pour un calcul plus précis si disponible
    if command -v python3 >/dev/null 2>&1; then
        python3 -c "
import math
def haversine(lat1, lon1, lat2, lon2):
    R = 6371  # Rayon de la Terre en kilomètres
    dlat = math.radians(float(lat2) - float(lat1))
    dlon = math.radians(float(lon2) - float(lon1))
    a = math.sin(dlat/2)**2 + math.cos(math.radians(float(lat1))) * math.cos(math.radians(float(lat2))) * math.sin(dlon/2)**2
    c = 2 * math.asin(math.sqrt(a))
    return R * c
print(f'{haversine($lat1, $lon1, $lat2, $lon2):.2f}')
"
    else
        echo "0"
    fi
}

# Surveiller les changements de localisation
monitor_location() {
    log_action "Démarrage de la surveillance de localisation..."
    
    while true; do
        # Obtenir la localisation actuelle
        local current_location
        current_location=$(get_location_info)
        
        if validate_location "$current_location"; then
            # Stocker la localisation
            store_location "$current_location"
            
            # Vérifications de sécurité
            check_geofence "$current_location"
            check_network_security "$current_location"
            
            # Vérifier les mouvements significatifs
            check_location_change "$current_location"
        else
            log_action "Échec de la récupération de données de localisation valides"
        fi
        
        sleep "$MONITORING_INTERVAL"
    done
}

# Vérifier les changements de localisation significatifs
check_location_change() {
    local current_location="$1"
    local last_location_file="/tmp/last_location.json"
    
    if [[ -f "$last_location_file" ]]; then
        local previous_location
        previous_location=$(cat "$last_location_file")
        
        # Obtenir les coordonnées
        local curr_lat curr_lon prev_lat prev_lon
        curr_lat=$(echo "$current_location" | jq -r '.loc | split(",")[0] // ""')
        curr_lon=$(echo "$current_location" | jq -r '.loc | split(",")[1] // ""')
        prev_lat=$(echo "$previous_location" | jq -r '.loc | split(",")[0] // ""')
        prev_lon=$(echo "$previous_location" | jq -r '.loc | split(",")[1] // ""')
        
        if [[ -n "$curr_lat" && -n "$curr_lon" && -n "$prev_lat" && -n "$prev_lon" ]]; then
            local distance
            distance=$(calculate_distance "$prev_lat" "$prev_lon" "$curr_lat" "$curr_lon")
            
            local threshold_km
            threshold_km=$(echo "$ALERT_THRESHOLD_MILES * 1.60934" | bc -l)
            
            if (( $(echo "$distance > $threshold_km" | bc -l) )); then
                log_action "ALERTE : Changement de localisation significatif détecté - Distance : ${distance} km"
                send_security_alert "Changement de Localisation" "L'appareil s'est déplacé de ${distance} km de la localisation précédente"
            fi
        fi
    fi
    
    # Sauvegarder la localisation actuelle
    echo "$current_location" > "$last_location_file"
}

# Générer un rapport de localisation
generate_location_report() {
    local report_file="/tmp/roundfleet_location_report_$(date +%Y%m%d_%H%M%S).txt"
    local hostname
    hostname=$(hostname)
    
    {
        echo "Rapport de Localisation d'Appareil Roundfleet"
        echo "==========================================="
        echo "Généré : $(date)"
        echo "Appareil : $hostname"
        echo ""
        
        echo "Localisation Actuelle :"
        local current_location
        current_location=$(get_location_info)
        if validate_location "$current_location"; then
            echo "$current_location" | jq '.'
        else
            echo "Impossible de récupérer la localisation actuelle"
        fi
        echo ""
        
        echo "Historique de Localisation (10 dernières entrées) :"
        if [[ -f "$LOCATION_DB" ]]; then
            tail -10 "$LOCATION_DB" | column -t -s ','
        else
            echo "Aucun historique de localisation disponible"
        fi
        echo ""
        
        echo "Événements de Sécurité (10 derniers) :"
        if [[ -f "$LOG_FILE" ]]; then
            grep -i "alerte\|sécurité" "$LOG_FILE" | tail -10
        else
            echo "Aucun événement de sécurité enregistré"
        fi
        
    } > "$report_file"
    
    echo "📄 Rapport de localisation généré : $report_file"
    log_action "Rapport de localisation généré : $report_file"
}

# Fonction principale
main() {
    case "${1:-status}" in
        "track")
            setup_environment
            load_config
            log_action "=== Suivi de Localisation : Vérification Unique ==="
            current_location=$(get_location_info)
            if validate_location "$current_location"; then
                store_location "$current_location"
                check_geofence "$current_location"
                check_network_security "$current_location"
                echo "$current_location" | jq '.'
            fi
            ;;
        "monitor")
            setup_environment
            load_config
            log_action "=== Surveillance de Localisation : Continue ==="
            monitor_location
            ;;
        "report")
            setup_environment
            load_config
            generate_location_report
            ;;
        "history")
            if [[ -f "$LOCATION_DB" ]]; then
                echo "📍 Historique de Localisation :"
                column -t -s ',' "$LOCATION_DB"
            else
                echo "Aucun historique de localisation disponible"
            fi
            ;;
        "status"|*)
            setup_environment
            load_config
            echo "📊 Statut de Localisation Roundfleet :"
            echo "===================================="
            current_location=$(get_location_info)
            if validate_location "$current_location"; then
                echo "$current_location" | jq '.'
            else
                echo "Impossible de récupérer les informations de localisation"
            fi
            ;;
    esac
}

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

Commandes de Suivi de Localisation

Référence Rapide

TâcheCommande
Obtenir localisation actuellecurl -s https://ipinfo.io
Infos de localisation formatéescurl -s https://ipinfo.io | jq '.'
Sauvegarder localisation dans fichiercurl -s https://ipinfo.io > location.json
Obtenir champ spécifiquecurl -s https://ipinfo.io | jq -r '.city'

Champs de Données de Localisation

# Extraire des informations spécifiques
IP=$(curl -s https://ipinfo.io | jq -r '.ip')
CITY=$(curl -s https://ipinfo.io | jq -r '.city')
REGION=$(curl -s https://ipinfo.io | jq -r '.region')
COUNTRY=$(curl -s https://ipinfo.io | jq -r '.country')
COORDINATES=$(curl -s https://ipinfo.io | jq -r '.loc')
ORGANIZATION=$(curl -s https://ipinfo.io | jq -r '.org')
TIMEZONE=$(curl -s https://ipinfo.io | jq -r '.timezone')

Sécurité et Conformité

Considérations de Confidentialité

  1. Collecte de Données - Ne collecter que les données de localisation nécessaires
  2. Sécurité de Stockage - Chiffrer les bases de données de localisation
  3. Contrôle d'Accès - Limiter l'accès aux informations de localisation
  4. Politique de Rétention - Définir les périodes de rétention des données
  5. Consentement Utilisateur - Assurer une autorisation appropriée

Implémentation de Géofencing

#!/bin/bash

# Vérification simple de géofencing
check_location_compliance() {
    local location_data
    location_data=$(curl -s https://ipinfo.io)
    
    local country
    country=$(echo "$location_data" | jq -r '.country')
    
    case "$country" in
        "US"|"CA"|"GB"|"FR")
            echo "✅ Localisation approuvée : $country"
            ;;
        *)
            echo "⚠️  Localisation en dehors des régions approuvées : $country"
            # Enregistrer l'événement de sécurité
            logger "Roundfleet : Appareil dans une localisation non autorisée : $country"
            ;;
    esac
}

Dépannage

Problèmes Courants

  1. Problèmes de connectivité réseau

    • Vérifier la connexion internet
    • Vérifier les paramètres du pare-feu
    • Tester des services de géolocalisation alternatifs
  2. Données de localisation inexactes

    • Les connexions VPN/proxy affectent la précision
    • La localisation du FAI peut ne pas correspondre à la localisation physique
    • Utiliser plusieurs services pour la vérification
  3. Limites de taux d'API

    • Implémenter des délais appropriés entre les requêtes
    • Utiliser des clés API authentifiées quand disponibles
    • Mettre en cache les données de localisation de manière appropriée

Commandes de Vérification

# Tester la connectivité réseau
ping -c 3 ipinfo.io

# Vérifier si jq est installé
which jq || echo "jq non installé"

# Vérifier la disponibilité du service de localisation
curl -I https://ipinfo.io

Notes Importantes

  • Limitations de précision : La géolocalisation IP fournit une localisation approximative, pas des coordonnées précises
  • Dépendances réseau : Nécessite une connectivité internet pour fonctionner
  • Conformité à la confidentialité : Assurer la conformité aux lois locales de confidentialité et réglementations
  • Détection VPN : Les résultats peuvent être affectés par les services VPN ou proxy
  • Limitation de taux : Implémenter des délais appropriés pour éviter les limites de taux de service
  • Stockage de données : Stocker et gérer de manière sécurisée les données de localisation collectées

Déploiement d'Entreprise

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

  1. Définition de politique - Établir des politiques claires de suivi de localisation
  2. Conformité légale - Assurer la conformité aux réglementations de confidentialité
  3. Surveillance de sécurité - Implémenter des alertes de localisation en temps réel
  4. Gouvernance des données - Définir les politiques de rétention et d'accès aux données
  5. Communication utilisateur - Informer les utilisateurs des pratiques de suivi de localisation

Gestion des Fonds d'Écran Entreprise sur macOS

Implémentez une gestion de fonds d'écran de niveau entreprise sur votre déploiement Roundfleet avec des politiques de marque standardisées, un déploiement d'images automatisé et une surveillance de conformité complète. Ce tutoriel fournit des solutions pour maintenir une identité visuelle corporative cohérente et une présence de marque.

Comprendre la Gestion des Fonds d'Écran macOS

macOS fournit plusieurs méthodes pour définir les fonds d'écran de manière programmatique :

  • AppleScript via osascript - Script inter-applications pour l'intégration Finder
  • Préférences Système - Configuration native via la base de données système
  • Base de données Desktop Pictures - Manipulation directe de la base de données SQLite
  • Profils de Configuration - Déploiement de fonds d'écran basé MDM
  • User Defaults - Gestion des préférences par utilisateur

Opérations de Base des Fonds d'Écran

Définir un Fond d'Écran

#!/bin/bash

# Définition de fond d'écran basique
path="$1"
osascript -e 'tell application "Finder" to set desktop picture to POSIX file "'"$path"'"'
ret=$?

if [ $ret == "0" ]; then
    echo "Fond d'écran défini avec succès"
else
    echo "Opération échouée."
fi

Définition de Fond d'Écran Améliorée avec Validation

#!/bin/bash

# Définition de fond d'écran améliorée avec validation et gestion d'erreurs
set_wallpaper() {
    local image_path="$1"
    local user_name="$2"
    
    # Valider que le fichier image existe
    if [[ ! -f "$image_path" ]]; then
        echo "❌ Erreur : Fichier image non trouvé : $image_path"
        return 1
    fi
    
    # Valider le format d'image
    local file_type
    file_type=$(file --mime-type "$image_path" | awk '{print $2}')
    
    if [[ ! "$file_type" =~ ^image/ ]]; then
        echo "❌ Erreur : Format d'image invalide : $file_type"
        return 1
    fi
    
    # Définir le fond d'écran en utilisant AppleScript
    echo "Définition du fond d'écran : $image_path"
    
    if [[ -n "$user_name" ]]; then
        # Définir pour un utilisateur spécifique
        sudo -u "$user_name" osascript -e "tell application \"Finder\" to set desktop picture to POSIX file \"$image_path\""
    else
        # Définir pour l'utilisateur actuel
        osascript -e "tell application \"Finder\" to set desktop picture to POSIX file \"$image_path\""
    fi
    
    local result=$?
    
    if [[ $result -eq 0 ]]; then
        echo "✅ Fond d'écran défini avec succès pour ${user_name:-utilisateur actuel}"
        return 0
    else
        echo "❌ Échec de définition du fond d'écran (code de sortie : $result)"
        return 1
    fi
}

# Exemple d'utilisation
# set_wallpaper "/Users/Admin/Pictures/wallpaper_entreprise.png" "admin"

Système de Gestion de Fonds d'Écran Entreprise

Outil de Gestion de Fonds d'Écran Complet

#!/bin/bash

# Outil de Gestion des Fonds d'Écran Bureau Entreprise Roundfleet
# Application de politiques de marque centralisées et de fonds d'écran

# Configuration
CONFIG_FILE="/etc/roundfleet/wallpaper_policy.conf"
LOG_FILE="/var/log/roundfleet_wallpaper.log"
WALLPAPER_DIR="/Library/Roundfleet/Wallpapers"
REPORT_DIR="/var/log/roundfleet_reports"

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

# Politique de fonds d'écran par défaut
cat > "$CONFIG_FILE" 2>/dev/null << 'EOF' || true
# Politique de Gestion des Fonds d'Écran Roundfleet
# Version : 2.0

# Configuration de Marque
ENFORCE_CORPORATE_BRANDING=true
ALLOW_USER_CUSTOMIZATION=false
DEFAULT_WALLPAPER="corporate_standard.jpg"
FALLBACK_WALLPAPER="roundfleet_default.png"

# Dépôt de Fonds d'Écran
WALLPAPER_REPOSITORY_URL="https://assets.roundfleet.com/wallpapers"
LOCAL_WALLPAPER_DIR="/Library/Roundfleet/Wallpapers"
SYNC_WALLPAPERS=true

# Paramètres de Politique
SCHEDULE_WALLPAPER_CHANGES=false
WALLPAPER_ROTATION_INTERVAL=86400  # 24 heures
VALIDATE_IMAGE_INTEGRITY=true
LOG_WALLPAPER_CHANGES=true

# Restrictions Utilisateur
BLOCK_SYSTEM_PREFERENCES_DESKTOP=false
HIDE_DESKTOP_PREFERENCES=false
LOCK_WALLPAPER_SETTINGS=false

# Formats Supportés
SUPPORTED_FORMATS="jpg,jpeg,png,heic,bmp,tiff,gif"
MAX_FILE_SIZE_MB=10
MIN_RESOLUTION="1024x768"
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"
}

# Valider le fichier image
validate_image() {
    local image_path="$1"
    local errors=()
    
    # Vérifier l'existence du fichier
    if [[ ! -f "$image_path" ]]; then
        errors+=("Le fichier n'existe pas")
        printf '%s\n' "${errors[@]}"
        return 1
    fi
    
    # Vérifier le format de fichier
    local file_type
    file_type=$(file --mime-type "$image_path" 2>/dev/null | awk '{print $2}')
    
    if [[ ! "$file_type" =~ ^image/ ]]; then
        errors+=("Format d'image invalide : $file_type")
    fi
    
    # Vérifier l'extension de fichier
    local extension
    extension=$(echo "${image_path##*.}" | tr '[:upper:]' '[:lower:]')
    
    if [[ ",$SUPPORTED_FORMATS," != *",$extension,"* ]]; then
        errors+=("Format de fichier non supporté : $extension")
    fi
    
    # Vérifier la taille de fichier
    if command -v stat >/dev/null; then
        local file_size_mb
        file_size_mb=$(( $(stat -f%z "$image_path" 2>/dev/null || echo 0) / 1024 / 1024 ))
        
        if [[ $file_size_mb -gt $MAX_FILE_SIZE_MB ]]; then
            errors+=("Fichier trop volumineux : ${file_size_mb}MB (max : ${MAX_FILE_SIZE_MB}MB)")
        fi
    fi
    
    # Vérifier les dimensions d'image
    if command -v sips >/dev/null; then
        local dimensions
        dimensions=$(sips -g pixelWidth -g pixelHeight "$image_path" 2>/dev/null | grep -E "pixelWidth|pixelHeight" | awk '{print $2}' | tr '\n' 'x' | sed 's/x$//')
        
        if [[ -n "$dimensions" ]]; then
            local width height
            width=$(echo "$dimensions" | cut -d'x' -f1)
            height=$(echo "$dimensions" | cut -d'x' -f2)
            local min_width min_height
            min_width=$(echo "$MIN_RESOLUTION" | cut -d'x' -f1)
            min_height=$(echo "$MIN_RESOLUTION" | cut -d'x' -f2)
            
            if [[ $width -lt $min_width || $height -lt $min_height ]]; then
                errors+=("Résolution trop faible : ${dimensions} (min : ${MIN_RESOLUTION})")
            fi
        fi
    fi
    
    if [[ ${#errors[@]} -eq 0 ]]; then
        return 0
    else
        printf '%s\n' "${errors[@]}"
        return 1
    fi
}

# Télécharger un fond d'écran depuis le dépôt
download_wallpaper() {
    local wallpaper_name="$1"
    local local_path="$WALLPAPER_DIR/$wallpaper_name"
    
    if [[ "$SYNC_WALLPAPERS" != "true" ]]; then
        echo "Synchronisation de fonds d'écran désactivée"
        return 1
    fi
    
    echo "Téléchargement du fond d'écran : $wallpaper_name"
    
    # Télécharger en utilisant curl
    if curl -fsSL "$WALLPAPER_REPOSITORY_URL/$wallpaper_name" -o "$local_path.tmp"; then
        # Valider l'image téléchargée
        if validate_image "$local_path.tmp" >/dev/null 2>&1; then
            mv "$local_path.tmp" "$local_path"
            echo "✅ Téléchargé et validé : $wallpaper_name"
            log_action "Fond d'écran téléchargé : $wallpaper_name"
            return 0
        else
            rm -f "$local_path.tmp"
            echo "❌ Le fichier téléchargé a échoué la validation"
            return 1
        fi
    else
        echo "❌ Échec du téléchargement du fond d'écran"
        return 1
    fi
}

# Définir le fond d'écran pour l'utilisateur
set_user_wallpaper() {
    local image_path="$1"
    local target_user="$2"
    
    echo "=== Définition du Fond d'Écran ==="
    echo "Image : $image_path"
    echo "Utilisateur : ${target_user:-utilisateur actuel}"
    
    # Valider l'image
    if ! validate_image "$image_path"; then
        echo "❌ Validation d'image échouée"
        return 1
    fi
    
    # Définir le fond d'écran
    if [[ -n "$target_user" ]]; then
        # Définir pour un utilisateur spécifique (nécessite les permissions appropriées)
        sudo -u "$target_user" osascript -e "tell application \"Finder\" to set desktop picture to POSIX file \"$image_path\"" 2>/dev/null
    else
        # Définir pour l'utilisateur actuel
        osascript -e "tell application \"Finder\" to set desktop picture to POSIX file \"$image_path\""
    fi
    
    local result=$?
    
    if [[ $result -eq 0 ]]; then
        echo "✅ Fond d'écran défini avec succès"
        log_action "Fond d'écran défini : $image_path pour utilisateur : ${target_user:-actuel}"
        return 0
    else
        echo "❌ Échec de définition du fond d'écran (code de sortie : $result)"
        log_action "ÉCHEC de définition du fond d'écran : $image_path pour utilisateur : ${target_user:-actuel}"
        return 1
    fi
}

# Appliquer le fond d'écran corporatif
apply_corporate_wallpaper() {
    local wallpaper_name="${1:-$DEFAULT_WALLPAPER}"
    local target_user="$2"
    
    echo "=== Application du Fond d'Écran Corporatif ==="
    
    if [[ "$ENFORCE_CORPORATE_BRANDING" != "true" ]]; then
        echo "Application de marque corporative désactivée"
        return 0
    fi
    
    # Vérifier si le fond d'écran existe localement
    local wallpaper_path="$WALLPAPER_DIR/$wallpaper_name"
    
    if [[ ! -f "$wallpaper_path" ]]; then
        echo "Fond d'écran non trouvé localement, tentative de téléchargement..."
        if ! download_wallpaper "$wallpaper_name"; then
            echo "Utilisation du fond d'écran de secours..."
            wallpaper_path="$WALLPAPER_DIR/$FALLBACK_WALLPAPER"
            
            if [[ ! -f "$wallpaper_path" ]]; then
                echo "❌ Aucun fond d'écran disponible (y compris de secours)"
                return 1
            fi
        else
            wallpaper_path="$WALLPAPER_DIR/$wallpaper_name"
        fi
    fi
    
    # Appliquer le fond d'écran
    set_user_wallpaper "$wallpaper_path" "$target_user"
}

# Obtenir les informations du fond d'écran actuel
get_current_wallpaper() {
    echo "=== Informations du Fond d'Écran Actuel ==="
    
    # Obtenir le chemin du fond d'écran actuel en utilisant AppleScript
    local current_wallpaper
    current_wallpaper=$(osascript -e 'tell application "Finder" to get desktop picture as string' 2>/dev/null)
    
    if [[ -n "$current_wallpaper" ]]; then
        # Convertir du chemin HFS+ vers le chemin POSIX
        local posix_path
        posix_path=$(osascript -e "get POSIX path of \"$current_wallpaper\"" 2>/dev/null)
        
        echo "Fond d'écran actuel : $posix_path"
        
        if [[ -f "$posix_path" ]]; then
            echo "Taille de fichier : $(du -h "$posix_path" | cut -f1)"
            
            # Obtenir les dimensions d'image si sips est disponible
            if command -v sips >/dev/null; then
                local dimensions
                dimensions=$(sips -g pixelWidth -g pixelHeight "$posix_path" 2>/dev/null | grep -E "pixelWidth|pixelHeight" | awk '{print $2}' | tr '\n' 'x' | sed 's/x$//')
                echo "Dimensions : $dimensions"
            fi
            
            # Vérifier s'il s'agit d'un fond d'écran corporatif
            if [[ "$posix_path" == "$WALLPAPER_DIR"* ]]; then
                echo "✅ Fond d'écran corporatif détecté"
            else
                echo "⚠️  Fond d'écran non corporatif détecté"
            fi
        else
            echo "⚠️  Fichier de fond d'écran non trouvé"
        fi
    else
        echo "❌ Impossible de récupérer le fond d'écran actuel"
    fi
}

# Synchroniser le dépôt de fonds d'écran
sync_wallpaper_repository() {
    echo "=== Synchronisation du Dépôt de Fonds d'Écran ==="
    
    if [[ "$SYNC_WALLPAPERS" != "true" ]]; then
        echo "Synchronisation de fonds d'écran désactivée"
        return 0
    fi
    
    # Créer le répertoire de fonds d'écran
    mkdir -p "$WALLPAPER_DIR"
    
    # Télécharger le fichier manifeste
    local manifest_file="$WALLPAPER_DIR/.manifest"
    
    if curl -fsSL "$WALLPAPER_REPOSITORY_URL/manifest.txt" -o "$manifest_file.tmp" 2>/dev/null; then
        echo "Manifeste de fonds d'écran téléchargé"
        
        # Traiter le manifeste et télécharger les fonds d'écran
        while IFS= read -r wallpaper_name; do
            [[ -z "$wallpaper_name" || "$wallpaper_name" =~ ^# ]] && continue
            
            local local_path="$WALLPAPER_DIR/$wallpaper_name"
            
            if [[ ! -f "$local_path" ]]; then
                echo "Téléchargement : $wallpaper_name"
                download_wallpaper "$wallpaper_name"
            else
                echo "Existe déjà : $wallpaper_name"
            fi
        done < "$manifest_file.tmp"
        
        mv "$manifest_file.tmp" "$manifest_file"
        echo "✅ Dépôt de fonds d'écran synchronisé"
        log_action "Dépôt de fonds d'écran synchronisé"
    else
        echo "❌ Échec du téléchargement du manifeste"
        return 1
    fi
}

# Générer un rapport de conformité
generate_wallpaper_report() {
    local report_file="$REPORT_DIR/wallpaper_compliance_$(date +%Y%m%d_%H%M%S).json"
    
    echo "=== Génération du Rapport de Conformité de Fonds d'Écran ==="
    
    # Obtenir le fond d'écran actuel
    local current_wallpaper
    current_wallpaper=$(osascript -e 'tell application "Finder" to get desktop picture as string' 2>/dev/null)
    local posix_path=""
    local is_corporate="false"
    local compliance_status="non_conforme"
    
    if [[ -n "$current_wallpaper" ]]; then
        posix_path=$(osascript -e "get POSIX path of \"$current_wallpaper\"" 2>/dev/null)
        
        if [[ "$posix_path" == "$WALLPAPER_DIR"* ]]; then
            is_corporate="true"
            compliance_status="conforme"
        fi
    fi
    
    # Créer le rapport JSON
    cat > "$report_file" << EOF
{
  "type_rapport": "conformite_fond_ecran",
  "horodatage": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
  "infos_appareil": {
    "nom_hote": "$(hostname)",
    "numero_serie": "$(system_profiler SPHardwareDataType | grep "Serial Number" | awk -F: '{print $2}' | xargs)",
    "version_macos": "$(sw_vers -productVersion)",
    "utilisateur_actuel": "$(whoami)"
  },
  "statut_fond_ecran": {
    "fond_ecran_actuel": "$posix_path",
    "est_fond_ecran_corporatif": $is_corporate,
    "statut_conformite": "$compliance_status",
    "marque_appliquee": $ENFORCE_CORPORATE_BRANDING
  },
  "parametres_politique": {
    "marque_corporative_appliquee": $ENFORCE_CORPORATE_BRANDING,
    "personnalisation_utilisateur_autorisee": $ALLOW_USER_CUSTOMIZATION,
    "fond_ecran_par_defaut": "$DEFAULT_WALLPAPER",
    "synchronisation_fond_ecran_activee": $SYNC_WALLPAPERS
  },
  "infos_depot": {
    "nombre_fonds_ecran_locaux": $(find "$WALLPAPER_DIR" -name "*.jpg" -o -name "*.png" -o -name "*.jpeg" 2>/dev/null | wc -l | xargs),
    "derniere_synchronisation": "$(stat -f %Sm "$WALLPAPER_DIR/.manifest" 2>/dev/null || echo "jamais")"
  }
}
EOF
    
    echo "Rapport de conformité de fonds d'écran sauvegardé dans : $report_file"
    log_action "Rapport de conformité de fonds d'écran généré : $report_file"
}

# Fonction principale avec gestion d'arguments
main() {
    log_action "=== Outil de Gestion de Fonds d'Écran Roundfleet Démarré ==="
    
    case "${1:-status}" in
        "set")
            set_user_wallpaper "$2" "$3"
            ;;
        "corporate")
            apply_corporate_wallpaper "$2" "$3"
            ;;
        "sync")
            sync_wallpaper_repository
            ;;
        "validate")
            if validate_image "$2"; then
                echo "✅ Validation d'image réussie : $2"
            else
                echo "❌ Validation d'image échouée : $2"
            fi
            ;;
        "download")
            download_wallpaper "$2"
            ;;
        "report")
            generate_wallpaper_report
            ;;
        "status"|*)
            get_current_wallpaper
            ;;
    esac
    
    log_action "=== Opération de gestion de fonds d'écran terminée ==="
}

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

Gestion Avancée de Fonds d'Écran

Rotation Automatique de Fonds d'Écran

#!/bin/bash

# Système de rotation automatique de fonds d'écran
setup_wallpaper_rotation() {
    echo "=== Configuration de la Rotation de Fonds d'Écran ==="
    
    if [[ "$SCHEDULE_WALLPAPER_CHANGES" != "true" ]]; then
        echo "Rotation de fonds d'écran désactivée dans la politique"
        return 0
    fi
    
    # Créer le script de rotation
    local rotation_script="/usr/local/bin/roundfleet-wallpaper-rotate"
    
    cat > "$rotation_script" << 'EOF'
#!/bin/bash
# Script de Rotation de Fonds d'Écran Roundfleet

WALLPAPER_DIR="/Library/Roundfleet/Wallpapers"
LOG_FILE="/var/log/roundfleet_wallpaper.log"

# Obtenir la liste des fonds d'écran disponibles
mapfile -t wallpapers < <(find "$WALLPAPER_DIR" -name "*.jpg" -o -name "*.png" -o -name "*.jpeg" 2>/dev/null)

if [[ ${#wallpapers[@]} -eq 0 ]]; then
    echo "$(date) - Aucun fond d'écran trouvé pour la rotation" >> "$LOG_FILE"
    exit 1
fi

# Sélectionner un fond d'écran aléatoire
random_wallpaper="${wallpapers[$RANDOM % ${#wallpapers[@]}]}"

# Appliquer le fond d'écran
/usr/local/bin/roundfleet-wallpaper set "$random_wallpaper"

echo "$(date) - Rotation vers le fond d'écran : $random_wallpaper" >> "$LOG_FILE"
EOF
    
    chmod +x "$rotation_script"
    
    # Créer LaunchDaemon pour la planification
    local plist_file="/Library/LaunchDaemons/com.roundfleet.wallpaper-rotation.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.wallpaper-rotation</string>
    <key>ProgramArguments</key>
    <array>
        <string>$rotation_script</string>
    </array>
    <key>StartInterval</key>
    <integer>$WALLPAPER_ROTATION_INTERVAL</integer>
    <key>RunAtLoad</key>
    <false/>
</dict>
</plist>
EOF
    
    # Charger le démon
    launchctl load "$plist_file"
    
    echo "✅ Rotation de fonds d'écran configurée (intervalle : ${WALLPAPER_ROTATION_INTERVAL}s)"
}

setup_wallpaper_rotation

Gestion de Fonds d'Écran Multi-Écrans

#!/bin/bash

# Gestion de fonds d'écran multi-écrans
set_multi_display_wallpapers() {
    echo "=== Gestion de Fonds d'Écran Multi-Écrans ==="
    
    # Obtenir le nombre d'écrans
    local display_count
    display_count=$(system_profiler SPDisplaysDataType | grep -c "Resolution:")
    
    echo "Écrans détectés : $display_count"
    
    if [[ $display_count -gt 1 ]]; then
        echo "Définition de fonds d'écran pour plusieurs écrans..."
        
        # Définir différents fonds d'écran pour chaque écran
        for ((i=1; i<=display_count; i++)); do
            local wallpaper_file="display_${i}_wallpaper.jpg"
            local wallpaper_path="$WALLPAPER_DIR/$wallpaper_file"
            
            if [[ -f "$wallpaper_path" ]]; then
                echo "Définition du fond d'écran pour l'écran $i : $wallpaper_file"
                
                # Utiliser AppleScript pour définir le fond d'écran pour un écran spécifique
                osascript << EOF
                tell application "System Events"
                    tell desktop $i
                        set picture to "$wallpaper_path"
                    end tell
                end tell
EOF
            else
                echo "⚠️  Fond d'écran non trouvé pour l'écran $i : $wallpaper_file"
            fi
        done
    else
        echo "Écran unique détecté, utilisation du fond d'écran standard"
        apply_corporate_wallpaper
    fi
}

set_multi_display_wallpapers

Application de Politiques de Fonds d'Écran

#!/bin/bash

# Surveiller et appliquer les politiques de fonds d'écran
monitor_wallpaper_compliance() {
    echo "=== Surveillance de la Conformité des Fonds d'Écran ==="
    
    # Vérifier la conformité du fond d'écran actuel
    local current_wallpaper
    current_wallpaper=$(osascript -e 'tell application "Finder" to get desktop picture as string' 2>/dev/null)
    
    if [[ -n "$current_wallpaper" ]]; then
        local posix_path
        posix_path=$(osascript -e "get POSIX path of \"$current_wallpaper\"" 2>/dev/null)
        
        # Vérifier si le fond d'écran provient du dépôt corporatif
        if [[ "$posix_path" != "$WALLPAPER_DIR"* ]] && [[ "$ENFORCE_CORPORATE_BRANDING" == "true" ]]; then
            echo "⚠️  Fond d'écran non corporatif détecté : $posix_path"
            log_action "VIOLATION DE POLITIQUE : Fond d'écran non corporatif détecté : $posix_path"
            
            # Auto-remédiation si activée
            if [[ "$ALLOW_USER_CUSTOMIZATION" != "true" ]]; then
                echo "Auto-remédiation avec fond d'écran corporatif..."
                apply_corporate_wallpaper
                log_action "Auto-remédiation de violation de politique de fond d'écran"
            fi
        else
            echo "✅ Conformité des fonds d'écran : OK"
        fi
    else
        echo "❌ Impossible de vérifier la conformité des fonds d'écran"
    fi
}

monitor_wallpaper_compliance

Intégration et Déploiement

Script d'Intégration MDM

#!/bin/bash

# Intégration MDM pour le déploiement de fonds d'écran
deploy_wallpaper_via_mdm() {
    local wallpaper_url="$1"
    local deployment_mode="${2:-immediate}"
    
    echo "=== Déploiement de Fond d'Écran MDM ==="
    echo "Source : $wallpaper_url"
    echo "Mode : $deployment_mode"
    
    # Télécharger le fond d'écran
    local temp_file="/tmp/mdm_wallpaper_$(date +%s).jpg"
    
    if curl -fsSL "$wallpaper_url" -o "$temp_file"; then
        echo "✅ Fond d'écran téléchargé depuis la MDM"
        
        # Valider et déployer
        if validate_image "$temp_file"; then
            # Copier dans le répertoire de fonds d'écran corporatifs
            local dest_file="$WALLPAPER_DIR/mdm_deployed_$(basename "$temp_file")"
            cp "$temp_file" "$dest_file"
            
            # Appliquer immédiatement ou planifier
            case "$deployment_mode" in
                "immediate")
                    set_user_wallpaper "$dest_file"
                    ;;
                "next_login")
                    # Définir comme défaut pour la prochaine connexion
                    defaults write /Library/Preferences/com.apple.desktop Background "{default = {ImageFilePath = '$dest_file'; }; }"
                    ;;
                "scheduled")
                    # Ajouter au pool de rotation
                    echo "Ajouté au pool de rotation : $dest_file"
                    ;;
            esac
            
            # Nettoyage
            rm -f "$temp_file"
            echo "✅ Déploiement de fond d'écran MDM terminé"
        else
            echo "❌ Validation de fond d'écran MDM échouée"
            rm -f "$temp_file"
            return 1
        fi
    else
        echo "❌ Échec du téléchargement du fond d'écran depuis la MDM"
        return 1
    fi
}

# Exemple d'utilisation :
# deploy_wallpaper_via_mdm "https://mdm.entreprise.com/wallpapers/marque_trimestrielle.jpg" "immediate"

Notes de Configuration Importantes

AppleScript et Sécurité

  • Permissions de confidentialité requises pour l'exécution de scripts
  • Consentement utilisateur nécessaire pour l'accès Finder lors de la première exécution
  • Permissions d'automatisation pour l'accès System Events
  • Accès disque complet peut être requis pour certaines opérations

Formats d'Images Supportés

  • JPEG/JPG - Le plus courant, bonne compression
  • PNG - Supporte la transparence, fichiers plus volumineux
  • HEIC - Format moderne d'Apple, efficace
  • TIFF/BMP - Formats legacy, fichiers volumineux
  • GIF - Support animé (image unique utilisée)

Bonnes Pratiques pour l'Entreprise

  1. Optimisation d'Images

    • Utiliser la résolution appropriée pour les écrans cibles
    • Optimiser les tailles de fichiers pour le déploiement réseau
    • Tester les images sur différentes configurations d'écran
  2. Cohérence de Marque

    • Maintenir les schémas de couleurs corporatifs
    • Inclure logos et messages d'entreprise
    • Considérer les variations saisonnières ou de campagne
  3. Stratégie de Déploiement

    • Utiliser des dépôts d'images centralisés
    • Implémenter des déploiements échelonnés pour nouveaux fonds d'écran
    • Fournir des options de secours pour problèmes de connectivité
  4. Expérience Utilisateur

    • Considérer les préférences utilisateur là où la politique le permet
    • Fournir une communication claire sur les politiques de marque
    • Offrir des mécanismes d'opt-out pour la marque non critique

Dépannage des Problèmes Courants

  • Erreurs de permission refusée - Vérifier les permissions d'exécution de script et contexte utilisateur
  • Image ne s'affiche pas - Vérifier la compatibilité de format de fichier et l'accessibilité du chemin
  • Conflits de politiques - Examiner les paramètres de profil de configuration et les user defaults
  • Problèmes de performance - Optimiser les tailles de fichiers d'images et le timing de déploiement

N'oubliez pas de tester les changements de fonds d'écran sur différentes configurations d'écran et scénarios utilisateur avant le déploiement dans votre environnement Roundfleet complet.

Déployer des Fichiers ZIP sur Mac via URLs

Déployer des applications et fichiers dans votre Roundfleet peut être rationalisé en hébergeant des fichiers ZIP sur un serveur et en les téléchargeant automatiquement sur les appareils cibles. Ce tutoriel montre comment créer des scripts robustes pour le déploiement de fichiers ZIP via URLs.

Comprendre le Processus

Le processus de déploiement implique :

  1. Téléchargement - Récupérer le fichier ZIP depuis une URL avec curl
  2. Extraction - Décompresser le contenu avec unzip
  3. Installation - Optionnellement installer ou déplacer les fichiers vers leur emplacement final
  4. Nettoyage - Supprimer les fichiers temporaires

Script de Déploiement ZIP de Base

Téléchargement et Extraction Simple

#!/bin/bash

# Script de déploiement ZIP basique
cd /tmp

# Télécharger le fichier ZIP
curl -k https://example.com/myapp.zip -o "myapp.zip"

# Extraire le contenu
unzip "myapp.zip"

Script Entreprise Amélioré

Pour les environnements de production avec gestion d'erreurs et journalisation :

#!/bin/bash

# Script de Déploiement de Fichiers ZIP pour Roundfleet
# Compatible avec macOS 10.14+

# Variables de configuration
DOWNLOAD_URL="$1"  # Premier argument : URL vers le fichier ZIP
ZIP_NAME="$2"      # Deuxième argument : nom du fichier ZIP
DESTINATION="${3:-/tmp}"  # Troisième argument : destination (défaut : /tmp)
LOG_FILE="/var/log/zip_deployment.log"

# Fonction pour enregistrer les messages
log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}

# Fonction pour valider l'URL
validate_url() {
    local url="$1"
    if [[ ! "$url" =~ ^https?:// ]]; then
        log_message "✗ Format d'URL invalide : $url"
        return 1
    fi
    return 0
}

# Fonction pour vérifier la connectivité internet
check_connectivity() {
    if ! ping -c 1 google.com &> /dev/null; then
        log_message "✗ Pas de connectivité internet"
        return 1
    fi
    return 0
}

# Fonction pour télécharger le fichier ZIP
download_zip() {
    local url="$1"
    local filename="$2"
    local destination="$3"
    
    log_message "Début du téléchargement depuis : $url"
    
    # Créer le répertoire de destination s'il n'existe pas
    mkdir -p "$destination"
    cd "$destination" || return 1
    
    # Télécharger avec progression et gestion d'erreurs
    if curl -L --fail --connect-timeout 30 --max-time 300 \
            --progress-bar "$url" -o "$filename"; then
        log_message "✓ Téléchargement terminé : $filename"
        return 0
    else
        log_message "✗ Échec du téléchargement depuis : $url"
        return 1
    fi
}

# Fonction pour extraire le fichier ZIP
extract_zip() {
    local filename="$1"
    local destination="$2"
    
    cd "$destination" || return 1
    
    # Vérifier si le fichier ZIP existe
    if [[ ! -f "$filename" ]]; then
        log_message "✗ Fichier ZIP non trouvé : $filename"
        return 1
    fi
    
    # Tester l'intégrité du ZIP
    if ! unzip -t "$filename" &> /dev/null; then
        log_message "✗ Fichier ZIP corrompu : $filename"
        return 1
    fi
    
    # Extraire les fichiers
    if unzip -o "$filename"; then
        log_message "✓ Extraction terminée : $filename"
        return 0
    else
        log_message "✗ Échec de l'extraction : $filename"
        return 1
    fi
}

# Fonction pour installer l'application (si bundle .app trouvé)
install_application() {
    local source_dir="$1"
    
    # Chercher les bundles .app
    local app_bundle
    app_bundle=$(find "$source_dir" -name "*.app" -type d | head -1)
    
    if [[ -n "$app_bundle" ]]; then
        local app_name
        app_name=$(basename "$app_bundle")
        
        log_message "Bundle d'application trouvé : $app_name"
        
        # Copier dans le dossier Applications
        if cp -R "$app_bundle" "/Applications/"; then
            log_message "✓ Application installée : $app_name"
            return 0
        else
            log_message "✗ Échec de l'installation de l'application : $app_name"
            return 1
        fi
    else
        log_message "! Aucun bundle d'application trouvé pour l'installation"
        return 0
    fi
}

# Fonction pour nettoyer les fichiers temporaires
cleanup() {
    local destination="$1"
    local filename="$2"
    
    cd "$destination" || return 1
    
    if [[ -f "$filename" ]]; then
        rm -f "$filename"
        log_message "✓ Nettoyage terminé : $filename"
    fi
}

# Fonction principale de déploiement
deploy_zip() {
    log_message "=== Début du déploiement ZIP ==="
    
    # Valider les entrées
    if [[ -z "$DOWNLOAD_URL" ]] || [[ -z "$ZIP_NAME" ]]; then
        log_message "✗ Usage : $0 <download_url> <zip_filename> [destination]"
        return 1
    fi
    
    # Valider le format de l'URL
    if ! validate_url "$DOWNLOAD_URL"; then
        return 1
    fi
    
    # Vérifier la connectivité
    if ! check_connectivity; then
        return 1
    fi
    
    # Télécharger le fichier ZIP
    if ! download_zip "$DOWNLOAD_URL" "$ZIP_NAME" "$DESTINATION"; then
        return 1
    fi
    
    # Extraire le fichier ZIP
    if ! extract_zip "$ZIP_NAME" "$DESTINATION"; then
        return 1
    fi
    
    # Tentative d'installation d'application
    install_application "$DESTINATION"
    
    # Nettoyer le fichier ZIP temporaire
    cleanup "$DESTINATION" "$ZIP_NAME"
    
    log_message "=== Déploiement ZIP terminé avec succès ==="
    return 0
}

# Exécuter la fonction principale
if deploy_zip; then
    exit 0
else
    log_message "=== Échec du déploiement ZIP ==="
    exit 1
fi

Scripts de Déploiement Rapide

Téléchargement vers Applications

#!/bin/bash

# Télécharger et installer l'app directement dans Applications
URL="https://example.com/MyApp.zip"
ZIP_NAME="MyApp.zip"

cd /tmp
curl -L "$URL" -o "$ZIP_NAME"
unzip "$ZIP_NAME"

# Trouver et déplacer le bundle .app
APP_BUNDLE=$(find . -name "*.app" -type d | head -1)
if [[ -n "$APP_BUNDLE" ]]; then
    cp -R "$APP_BUNDLE" /Applications/
    echo "Application installée avec succès"
fi

# Nettoyage
rm -f "$ZIP_NAME"
rm -rf "${ZIP_NAME%.zip}"

Téléchargement avec Vérification de Somme de Contrôle

#!/bin/bash

URL="https://example.com/myapp.zip"
ZIP_NAME="myapp.zip"
EXPECTED_SHA256="abc123..."  # Remplacer par la vraie somme de contrôle

cd /tmp
curl -L "$URL" -o "$ZIP_NAME"

# Vérifier la somme de contrôle
if echo "$EXPECTED_SHA256  $ZIP_NAME" | shasum -a 256 -c; then
    echo "Somme de contrôle vérifiée"
    unzip "$ZIP_NAME"
else
    echo "Échec de la vérification de la somme de contrôle"
    exit 1
fi

Exemples d'Usage

Usage en Ligne de Commande

# Usage basique
./deploy_zip.sh "https://example.com/app.zip" "app.zip"

# Destination personnalisée
./deploy_zip.sh "https://example.com/app.zip" "app.zip" "/Applications"

# Avec journalisation
./deploy_zip.sh "https://example.com/app.zip" "app.zip" 2>&1 | tee deployment.log

Déploiement Roundfleet

#!/bin/bash

# Déployer plusieurs applications
APPS=(
    "https://cdn.example.com/productivity.zip:productivity.zip"
    "https://cdn.example.com/security.zip:security.zip"
    "https://cdn.example.com/utilities.zip:utilities.zip"
)

for app in "${APPS[@]}"; do
    IFS=':' read -r url filename <<< "$app"
    echo "Déploiement : $filename"
    ./deploy_zip.sh "$url" "$filename"
done

Dépannage

ProblèmeSolution
Échec du téléchargementVérifier l'accessibilité de l'URL et la connexion internet
Erreurs d'extraction ZIPVérifier l'intégrité du fichier ZIP avec unzip -t
Permission refuséeExécuter le script avec des privilèges admin
App ne s'installe pasVérifier la présence d'un bundle .app dans le contenu extrait
Téléchargements lentsAugmenter les valeurs de timeout de curl

Considérations de Sécurité

  • URLs HTTPS - Toujours utiliser des connexions sécurisées quand possible
  • Vérification de somme de contrôle - Vérifier l'intégrité des fichiers avant extraction
  • Signature de code - Vérifier les signatures d'application avant installation
  • Téléchargements isolés - Utiliser /tmp pour le stockage temporaire

Référence d'Emplacements de Fichiers

  • Fichiers temporaires : /tmp/
  • Applications : /Applications/
  • Applications utilisateur : ~/Applications/
  • Utilitaires système : /usr/local/bin/
  • Journaux : /var/log/zip_deployment.log

Fonctionnalités Avancées

Surveillance de Progression

# Afficher la progression du téléchargement
curl --progress-bar -L "$URL" -o "$ZIP_NAME"

# Ou avec progression détaillée
curl -# -L "$URL" -o "$ZIP_NAME"

Logique de Nouvelle Tentative

# Réessayer le téléchargement jusqu'à 3 fois
for i in {1..3}; do
    if curl -L "$URL" -o "$ZIP_NAME"; then
        break
    elif [[ $i -eq 3 ]]; then
        echo "Échec du téléchargement après 3 tentatives"
        exit 1
    fi
    sleep 5
done

Notes Importantes

  • Les fichiers ZIP sont temporairement stockés dans /tmp par défaut
  • Utiliser des URLs HTTPS sécurisées quand possible
  • Tester les scripts sur un petit groupe avant le déploiement d'entreprise
  • Surveiller l'espace disque lors du déploiement de gros fichiers
  • Considérer les limitations de bande passante de votre réseau

Supprimer les comptes mobiles sur les appareils macOS

Ce guide complet démontre comment supprimer les comptes mobiles sur les appareils macOS pour une gestion efficace des utilisateurs, la réaffectation d'appareils et les scénarios de nettoyage organisationnel.

Aperçu

Les comptes mobiles sur macOS sont des profils utilisateur conçus pour un accès transparent aux ressources sur différents appareils au sein des organisations. Contrairement aux comptes locaux, les comptes mobiles sont gérés de manière centralisée par des services d'annuaire comme Open Directory ou Active Directory.

Scénarios courants pour la suppression de comptes mobiles

  • Départs d'employés : Nettoyer les comptes lorsque les employés quittent l'organisation
  • Changements de rôle : Supprimer les anciens comptes mobiles lors de changements de poste
  • Réaffectation d'appareils : Préparer les appareils pour de nouveaux utilisateurs
  • Conformité de sécurité : Supprimer les comptes inactifs ou compromis
  • Optimisation du stockage : Libérer l'espace disque en supprimant les comptes non utilisés

Suppression de base des comptes mobiles

Script simple de suppression des comptes mobiles

#!/bin/bash

# Script de suppression basique des comptes mobiles
# Utilisation : ./supprimer_comptes_mobiles.sh

supprimer_comptes_mobiles() {
    echo "Démarrage du processus de suppression des comptes mobiles..."
    
    # Vérifier si le script est exécuté en tant que root
    if [ "$EUID" -ne 0 ]; then
        echo "Erreur : Ce script doit être exécuté en tant que root"
        exit 1
    fi
    
    # Compteur pour les comptes supprimés
    local compteur_suppression=0
    
    # Parcourir tous les utilisateurs
    for nom_utilisateur in $(dscl . -list /Users | grep -v '^_'); do
        # Vérifier si l'utilisateur a une authentification de compte mobile
        autorite_auth=$(dscl . -read "/Users/$nom_utilisateur" AuthenticationAuthority 2>/dev/null | grep "LocalCachedUser")
        
        if [ -n "$autorite_auth" ]; then
            echo "Suppression du compte mobile : $nom_utilisateur"
            
            # Supprimer l'utilisateur des services d'annuaire
            dscl . -delete "/Users/$nom_utilisateur"
            
            # Supprimer le répertoire home de l'utilisateur
            rm -rf "/Users/$nom_utilisateur"
            
            ((compteur_suppression++))
        fi
    done
    
    echo "Suppression des comptes mobiles terminée. $compteur_suppression comptes supprimés."
    echo "Note : Veuillez redémarrer l'appareil pour que les changements prennent pleinement effet."
}

# Exécuter la suppression
supprimer_comptes_mobiles

Gestionnaire de comptes mobiles avancé

#!/bin/bash

# Gestionnaire avancé de comptes mobiles avec vérifications de sécurité
# Utilisation : ./gestionnaire_comptes_mobiles_avance.sh

gestionnaire_comptes_mobiles_avance() {
    local fichier_log="/var/log/roundfleet_comptes_mobiles.log"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    local repertoire_sauvegarde="/var/backups/roundfleet/comptes_mobiles"
    
    # Vérifier si le script est exécuté en tant que root
    if [ "$EUID" -ne 0 ]; then
        echo "Erreur : Ce script doit être exécuté en tant que root"
        exit 1
    fi
    
    # Créer le répertoire de log
    mkdir -p /var/log
    mkdir -p "$repertoire_sauvegarde"
    
    echo "[$timestamp] Gestionnaire avancé de comptes mobiles démarré" >> "$fichier_log"
    
    # Afficher les comptes mobiles actuels
    afficher_comptes_mobiles
    
    # Confirmer la suppression
    echo ""
    read -p "Voulez-vous procéder à la suppression des comptes mobiles ? (o/N) : " confirmer
    
    if [ "$confirmer" = "o" ] || [ "$confirmer" = "O" ]; then
        creer_sauvegarde_comptes
        supprimer_comptes_mobiles_avance
    else
        echo "Opération annulée."
        echo "[$timestamp] Suppression des comptes mobiles annulée par l'utilisateur" >> "$fichier_log"
    fi
}

afficher_comptes_mobiles() {
    echo "======================================="
    echo "Comptes mobiles actuels sur l'appareil"
    echo "======================================="
    
    local comptes_mobiles_trouves=false
    
    for nom_utilisateur in $(dscl . -list /Users | grep -v '^_'); do
        autorite_auth=$(dscl . -read "/Users/$nom_utilisateur" AuthenticationAuthority 2>/dev/null | grep "LocalCachedUser")
        
        if [ -n "$autorite_auth" ]; then
            comptes_mobiles_trouves=true
            
            # Obtenir des informations supplémentaires sur l'utilisateur
            local nom_reel=$(dscl . -read "/Users/$nom_utilisateur" RealName 2>/dev/null | cut -d: -f2 | xargs)
            local uid=$(dscl . -read "/Users/$nom_utilisateur" UniqueID 2>/dev/null | cut -d: -f2 | xargs)
            local repertoire_home=$(dscl . -read "/Users/$nom_utilisateur" NFSHomeDirectory 2>/dev/null | cut -d: -f2 | xargs)
            local derniere_connexion=$(last -1 "$nom_utilisateur" 2>/dev/null | head -1 | awk '{print $3, $4, $5, $6}')
            
            echo "Nom d'utilisateur : $nom_utilisateur"
            echo "  Nom réel : $nom_reel"
            echo "  UID : $uid"
            echo "  Répertoire home : $repertoire_home"
            echo "  Dernière connexion : $derniere_connexion"
            
            # Vérifier la taille du répertoire home
            if [ -d "$repertoire_home" ]; then
                local taille_repertoire=$(du -sh "$repertoire_home" 2>/dev/null | cut -f1)
                echo "  Taille du répertoire home : $taille_repertoire"
            fi
            
            echo ""
        fi
    done
    
    if [ "$comptes_mobiles_trouves" = false ]; then
        echo "Aucun compte mobile trouvé sur cet appareil."
        echo "[$timestamp] Aucun compte mobile trouvé sur l'appareil" >> "$fichier_log"
        exit 0
    fi
}

creer_sauvegarde_comptes() {
    echo "Création d'une sauvegarde des informations des comptes mobiles..."
    
    local fichier_sauvegarde="$repertoire_sauvegarde/sauvegarde_comptes_mobiles_$(date +%Y%m%d_%H%M%S).txt"
    
    cat > "$fichier_sauvegarde" << EOF
Sauvegarde des comptes mobiles Roundfleet
Généré : $(date)
Appareil : $(scutil --get ComputerName)
Version macOS : $(sw_vers -productVersion)

Comptes mobiles trouvés :
EOF
    
    for nom_utilisateur in $(dscl . -list /Users | grep -v '^_'); do
        autorite_auth=$(dscl . -read "/Users/$nom_utilisateur" AuthenticationAuthority 2>/dev/null | grep "LocalCachedUser")
        
        if [ -n "$autorite_auth" ]; then
            echo "Nom d'utilisateur : $nom_utilisateur" >> "$fichier_sauvegarde"
            dscl . -read "/Users/$nom_utilisateur" >> "$fichier_sauvegarde"
            echo "---" >> "$fichier_sauvegarde"
        fi
    done
    
    echo "Sauvegarde créée : $fichier_sauvegarde"
    echo "[$timestamp] Sauvegarde des comptes mobiles créée : $fichier_sauvegarde" >> "$fichier_log"
}

supprimer_comptes_mobiles_avance() {
    echo "======================================="
    echo "Suppression des comptes mobiles"
    echo "======================================="
    
    local compteur_suppression=0
    local compteur_echec=0
    
    for nom_utilisateur in $(dscl . -list /Users | grep -v '^_'); do
        autorite_auth=$(dscl . -read "/Users/$nom_utilisateur" AuthenticationAuthority 2>/dev/null | grep "LocalCachedUser")
        
        if [ -n "$autorite_auth" ]; then
            echo "Traitement du compte mobile : $nom_utilisateur"
            
            # Vérifier si l'utilisateur est actuellement connecté
            if who | grep -q "^$nom_utilisateur "; then
                echo "  Attention : L'utilisateur $nom_utilisateur est actuellement connecté. Ignoré..."
                echo "[$timestamp] Suppression ignorée pour $nom_utilisateur - utilisateur connecté" >> "$fichier_log"
                ((compteur_echec++))
                continue
            fi
            
            # Supprimer l'utilisateur des services d'annuaire
            if dscl . -delete "/Users/$nom_utilisateur" 2>/dev/null; then
                echo "  Enregistrement utilisateur supprimé des services d'annuaire"
            else
                echo "  Attention : Échec de la suppression de l'enregistrement utilisateur"
                echo "[$timestamp] Échec de la suppression de l'enregistrement utilisateur pour $nom_utilisateur" >> "$fichier_log"
                ((compteur_echec++))
                continue
            fi
            
            # Supprimer le répertoire home de l'utilisateur
            local repertoire_home=$(dscl . -read "/Users/$nom_utilisateur" NFSHomeDirectory 2>/dev/null | cut -d: -f2 | xargs)
            if [ -d "$repertoire_home" ]; then
                if rm -rf "$repertoire_home" 2>/dev/null; then
                    echo "  Répertoire home supprimé : $repertoire_home"
                else
                    echo "  Attention : Échec de la suppression du répertoire home : $repertoire_home"
                    echo "[$timestamp] Échec de la suppression du répertoire home pour $nom_utilisateur : $repertoire_home" >> "$fichier_log"
                fi
            fi
            
            echo "  Compte mobile supprimé avec succès : $nom_utilisateur"
            echo "[$timestamp] Compte mobile supprimé avec succès : $nom_utilisateur" >> "$fichier_log"
            ((compteur_suppression++))
        fi
    done
    
    echo ""
    echo "======================================="
    echo "Résumé de la suppression des comptes mobiles"
    echo "======================================="
    echo "Supprimés avec succès : $compteur_suppression comptes"
    echo "Échec de suppression : $compteur_echec comptes"
    echo "Total traité : $((compteur_suppression + compteur_echec)) comptes"
    echo ""
    echo "Note : Veuillez redémarrer l'appareil pour que les changements prennent pleinement effet."
    
    echo "[$timestamp] Suppression des comptes mobiles terminée - Succès : $compteur_suppression, Échec : $compteur_echec" >> "$fichier_log"
}

# Exécuter la gestion avancée
gestionnaire_comptes_mobiles_avance

Gestion sélective des comptes mobiles

Suppression d'un compte mobile spécifique

#!/bin/bash

# Suppression sélective d'un compte mobile spécifique
# Utilisation : ./supprimer_compte_mobile_specifique.sh

supprimer_compte_mobile_specifique() {
    local nom_utilisateur="$1"
    local fichier_log="/var/log/roundfleet_comptes_mobiles.log"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    
    # Vérifier si le script est exécuté en tant que root
    if [ "$EUID" -ne 0 ]; then
        echo "Erreur : Ce script doit être exécuté en tant que root"
        exit 1
    fi
    
    # Vérifier si le nom d'utilisateur est fourni
    if [ -z "$nom_utilisateur" ]; then
        echo "Utilisation : $0 <nom_utilisateur>"
        echo "Exemple : $0 john.doe"
        exit 1
    fi
    
    # Vérifier si l'utilisateur existe et est un compte mobile
    local autorite_auth=$(dscl . -read "/Users/$nom_utilisateur" AuthenticationAuthority 2>/dev/null | grep "LocalCachedUser")
    
    if [ -z "$autorite_auth" ]; then
        echo "Erreur : L'utilisateur '$nom_utilisateur' n'existe pas ou n'est pas un compte mobile."
        exit 1
    fi
    
    echo "======================================="
    echo "Suppression du compte mobile : $nom_utilisateur"
    echo "======================================="
    
    # Afficher les détails du compte
    local nom_reel=$(dscl . -read "/Users/$nom_utilisateur" RealName 2>/dev/null | cut -d: -f2 | xargs)
    local repertoire_home=$(dscl . -read "/Users/$nom_utilisateur" NFSHomeDirectory 2>/dev/null | cut -d: -f2 | xargs)
    
    echo "Nom d'utilisateur : $nom_utilisateur"
    echo "Nom réel : $nom_reel"
    echo "Répertoire home : $repertoire_home"
    
    # Vérifier si l'utilisateur est connecté
    if who | grep -q "^$nom_utilisateur "; then
        echo "Attention : L'utilisateur est actuellement connecté !"
        read -p "Voulez-vous continuer quand même ? (o/N) : " forcer_suppression
        if [ "$forcer_suppression" != "o" ] && [ "$forcer_suppression" != "O" ]; then
            echo "Suppression annulée."
            return
        fi
    fi
    
    echo ""
    read -p "Êtes-vous sûr de vouloir supprimer ce compte ? (o/N) : " confirmer
    
    if [ "$confirmer" = "o" ] || [ "$confirmer" = "O" ]; then
        # Créer une sauvegarde des données utilisateur
        local repertoire_sauvegarde="/var/backups/roundfleet/comptes_mobiles"
        mkdir -p "$repertoire_sauvegarde"
        
        local fichier_sauvegarde="$repertoire_sauvegarde/${nom_utilisateur}_sauvegarde_$(date +%Y%m%d_%H%M%S).txt"
        
        echo "Création d'une sauvegarde des données utilisateur..."
        dscl . -read "/Users/$nom_utilisateur" > "$fichier_sauvegarde"
        
        # Supprimer l'utilisateur des services d'annuaire
        if dscl . -delete "/Users/$nom_utilisateur" 2>/dev/null; then
            echo "Enregistrement utilisateur supprimé des services d'annuaire"
        else
            echo "Échec de la suppression de l'enregistrement utilisateur"
            return 1
        fi
        
        # Supprimer le répertoire home de l'utilisateur
        if [ -d "$repertoire_home" ]; then
            read -p "Voulez-vous supprimer le répertoire home ? (o/N) : " supprimer_home
            if [ "$supprimer_home" = "o" ] || [ "$supprimer_home" = "O" ]; then
                if rm -rf "$repertoire_home"; then
                    echo "Répertoire home supprimé : $repertoire_home"
                else
                    echo "Échec de la suppression du répertoire home : $repertoire_home"
                fi
            else
                echo "Répertoire home préservé : $repertoire_home"
            fi
        fi
        
        echo "Compte mobile supprimé avec succès : $nom_utilisateur"
        echo "Sauvegarde créée : $fichier_sauvegarde"
        echo "[$timestamp] Compte mobile supprimé avec succès : $nom_utilisateur" >> "$fichier_log"
    else
        echo "Suppression annulée."
    fi
}

# Exécuter la suppression si un nom d'utilisateur est fourni
if [ $# -eq 1 ]; then
    supprimer_compte_mobile_specifique "$1"
else
    echo "Utilisation : $0 <nom_utilisateur>"
    echo "Exemple : $0 john.doe"
    exit 1
fi

Nettoyage automatisé des comptes mobiles

Script de nettoyage automatisé pour l'entreprise

#!/bin/bash

# Nettoyage automatisé des comptes mobiles pour les environnements d'entreprise
# Utilisation : ./nettoyage_comptes_mobiles_automatise.sh

nettoyage_comptes_mobiles_automatise() {
    local fichier_config="/etc/roundfleet/config_nettoyage_mobile.conf"
    local fichier_log="/var/log/roundfleet_nettoyage_automatise.log"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    
    # Vérifier si le script est exécuté en tant que root
    if [ "$EUID" -ne 0 ]; then
        echo "Erreur : Ce script doit être exécuté en tant que root"
        exit 1
    fi
    
    # Créer la configuration si elle n'existe pas
    if [ ! -f "$fichier_config" ]; then
        creer_config_nettoyage
    fi
    
    source "$fichier_config"
    
    echo "[$timestamp] Nettoyage automatisé des comptes mobiles démarré" >> "$fichier_log"
    
    # Exécuter le nettoyage basé sur la configuration
    executer_nettoyage_automatise
}

creer_config_nettoyage() {
    mkdir -p /etc/roundfleet
    
    cat > "/etc/roundfleet/config_nettoyage_mobile.conf" << 'EOF'
# Configuration du nettoyage automatisé des comptes mobiles Roundfleet

# Paramètres de l'organisation
NOM_ORGANISATION="Organisation Roundfleet"
CONTACT_IT="[email protected]"

# Critères de nettoyage
JOURS_INACTIVITE_NETTOYAGE="30"
NETTOYAGE_ACTIVE="true"
PRESERVER_COMPTES_ADMIN="true"

# Paramètres de sécurité
NECESSITER_CONFIRMATION="true"
CREER_SAUVEGARDE="true"
PRESERVER_DONNEES_HOME="false"
IGNORER_UTILISATEURS_CONNECTES="true"

# Paramètres de notification
ENVOYER_NOTIFICATIONS="true"
EMAIL_NOTIFICATION="[email protected]"

# Planification
ACTIVER_NETTOYAGE_PLANIFIE="false"
PLANIFICATION_NETTOYAGE="hebdomadaire"
HEURE_NETTOYAGE="02:00"

# Journalisation
ACTIVER_JOURNALISATION_DETAILLEE="true"
RETENTION_LOGS_JOURS="90"
EOF
    
    echo "Configuration de nettoyage créée à /etc/roundfleet/config_nettoyage_mobile.conf"
}

executer_nettoyage_automatise() {
    echo "[$timestamp] Exécution du nettoyage automatisé des comptes mobiles" >> "$fichier_log"
    
    # Vérifier si le nettoyage est activé
    if [ "$NETTOYAGE_ACTIVE" != "true" ]; then
        echo "[$timestamp] Le nettoyage automatisé est désactivé" >> "$fichier_log"
        echo "Le nettoyage automatisé est désactivé dans la configuration."
        return
    fi
    
    # Afficher la configuration de nettoyage
    echo "======================================="
    echo "Nettoyage automatisé des comptes mobiles Roundfleet"
    echo "======================================="
    echo "Organisation : $NOM_ORGANISATION"
    echo "Nettoyer les comptes inactifs depuis : $JOURS_INACTIVITE_NETTOYAGE jours"
    echo "Préserver les comptes admin : $PRESERVER_COMPTES_ADMIN"
    echo "Créer une sauvegarde : $CREER_SAUVEGARDE"
    echo "Ignorer les utilisateurs connectés : $IGNORER_UTILISATEURS_CONNECTES"
    echo ""
    
    # Nécessiter une confirmation si activé
    if [ "$NECESSITER_CONFIRMATION" = "true" ]; then
        read -p "Voulez-vous procéder au nettoyage automatisé ? (o/N) : " confirmer
        if [ "$confirmer" != "o" ] && [ "$confirmer" != "O" ]; then
            echo "[$timestamp] Nettoyage automatisé annulé par l'utilisateur" >> "$fichier_log"
            echo "Nettoyage automatisé annulé."
            return
        fi
    fi
    
    # Trouver les comptes à nettoyer
    local comptes_a_nettoyer=()
    local timestamp_actuel=$(date +%s)
    local timestamp_limite=$((timestamp_actuel - (JOURS_INACTIVITE_NETTOYAGE * 24 * 60 * 60)))
    
    echo "Recherche des comptes mobiles à nettoyer..."
    
    for nom_utilisateur in $(dscl . -list /Users | grep -v '^_'); do
        autorite_auth=$(dscl . -read "/Users/$nom_utilisateur" AuthenticationAuthority 2>/dev/null | grep "LocalCachedUser")
        
        if [ -n "$autorite_auth" ]; then
            local doit_nettoyer=true
            
            # Vérifier si l'utilisateur est admin (si la préservation est activée)
            if [ "$PRESERVER_COMPTES_ADMIN" = "true" ]; then
                if dscl . -read "/Groups/admin" GroupMembership 2>/dev/null | grep -q "$nom_utilisateur"; then
                    echo "Ignorer le compte admin : $nom_utilisateur"
                    echo "[$timestamp] Compte admin ignoré : $nom_utilisateur" >> "$fichier_log"
                    doit_nettoyer=false
                fi
            fi
            
            # Vérifier si l'utilisateur est connecté (si l'option d'ignorer est activée)
            if [ "$IGNORER_UTILISATEURS_CONNECTES" = "true" ] && who | grep -q "^$nom_utilisateur "; then
                echo "Ignorer l'utilisateur connecté : $nom_utilisateur"
                echo "[$timestamp] Utilisateur connecté ignoré : $nom_utilisateur" >> "$fichier_log"
                doit_nettoyer=false
            fi
            
            # Vérifier la date de dernière connexion
            if [ "$doit_nettoyer" = true ]; then
                local derniere_connexion=$(last -1 "$nom_utilisateur" 2>/dev/null | head -1 | awk '{print $3, $4, $5, $6}')
                
                if [ -n "$derniere_connexion" ]; then
                    local timestamp_derniere_connexion=$(date -j -f "%a %b %d %H:%M:%S %Y" "$derniere_connexion" +%s 2>/dev/null)
                    
                    if [ -n "$timestamp_derniere_connexion" ] && [ "$timestamp_derniere_connexion" -lt "$timestamp_limite" ]; then
                        comptes_a_nettoyer+=("$nom_utilisateur")
                        echo "Compte marqué pour nettoyage : $nom_utilisateur (dernière connexion : $derniere_connexion)"
                    fi
                else
                    # Aucun enregistrement de connexion - considérer pour nettoyage
                    comptes_a_nettoyer+=("$nom_utilisateur")
                    echo "Compte marqué pour nettoyage : $nom_utilisateur (aucun enregistrement de connexion)"
                fi
            fi
        fi
    done
    
    if [ ${#comptes_a_nettoyer[@]} -eq 0 ]; then
        echo "Aucun compte mobile trouvé répondant aux critères de nettoyage."
        echo "[$timestamp] Aucun compte mobile trouvé pour le nettoyage" >> "$fichier_log"
        return
    fi
    
    echo ""
    echo "Trouvé ${#comptes_a_nettoyer[@]} comptes mobiles pour le nettoyage :"
    for compte in "${comptes_a_nettoyer[@]}"; do
        echo "  - $compte"
    done
    
    # Créer une sauvegarde si activé
    if [ "$CREER_SAUVEGARDE" = "true" ]; then
        creer_sauvegarde_automatisee "${comptes_a_nettoyer[@]}"
    fi
    
    # Nettoyer les comptes
    local compteur_succes=0
    local compteur_echec=0
    
    echo ""
    echo "Démarrage du nettoyage automatisé..."
    
    for nom_utilisateur in "${comptes_a_nettoyer[@]}"; do
        echo "Traitement : $nom_utilisateur"
        
        # Supprimer l'utilisateur des services d'annuaire
        if dscl . -delete "/Users/$nom_utilisateur" 2>/dev/null; then
            echo "  Enregistrement utilisateur supprimé des services d'annuaire"
            
            # Gérer le répertoire home basé sur la configuration
            local repertoire_home="/Users/$nom_utilisateur"
            
            if [ -d "$repertoire_home" ]; then
                if [ "$PRESERVER_DONNEES_HOME" = "true" ]; then
                    echo "  Répertoire home préservé : $repertoire_home"
                else
                    if rm -rf "$repertoire_home" 2>/dev/null; then
                        echo "  Répertoire home supprimé : $repertoire_home"
                    else
                        echo "  Attention : Échec de la suppression du répertoire home : $repertoire_home"
                    fi
                fi
            fi
            
            echo "  Compte nettoyé avec succès : $nom_utilisateur"
            echo "[$timestamp] Compte nettoyé avec succès : $nom_utilisateur" >> "$fichier_log"
            ((compteur_succes++))
        else
            echo "  Échec du nettoyage du compte : $nom_utilisateur"
            echo "[$timestamp] Échec du nettoyage du compte : $nom_utilisateur" >> "$fichier_log"
            ((compteur_echec++))
        fi
    done
    
    # Générer le résumé du nettoyage
    echo ""
    echo "======================================="
    echo "Résumé du nettoyage automatisé"
    echo "======================================="
    echo "Total des comptes traités : ${#comptes_a_nettoyer[@]}"
    echo "Nettoyés avec succès : $compteur_succes"
    echo "Échec de nettoyage : $compteur_echec"
    echo "Nettoyage terminé à : $(date)"
    
    echo "[$timestamp] Nettoyage automatisé terminé - Succès : $compteur_succes, Échec : $compteur_echec" >> "$fichier_log"
    
    # Envoyer une notification si activé
    if [ "$ENVOYER_NOTIFICATIONS" = "true" ]; then
        envoyer_notification_nettoyage "$compteur_succes" "$compteur_echec"
    fi
    
    echo ""
    echo "Note : Veuillez redémarrer l'appareil pour que les changements prennent pleinement effet."
}

creer_sauvegarde_automatisee() {
    local comptes=("$@")
    local repertoire_sauvegarde="/var/backups/roundfleet/nettoyage_automatise"
    local fichier_sauvegarde="$repertoire_sauvegarde/sauvegarde_nettoyage_$(date +%Y%m%d_%H%M%S).txt"
    
    mkdir -p "$repertoire_sauvegarde"
    
    echo "Création d'une sauvegarde des comptes à nettoyer..."
    
    cat > "$fichier_sauvegarde" << EOF
Sauvegarde de nettoyage automatisé Roundfleet
Généré : $(date)
Appareil : $(scutil --get ComputerName)
Organisation : $NOM_ORGANISATION
Critères de nettoyage : Inactif depuis $JOURS_INACTIVITE_NETTOYAGE jours

Comptes à nettoyer :
EOF
    
    for nom_utilisateur in "${comptes[@]}"; do
        echo "" >> "$fichier_sauvegarde"
        echo "Nom d'utilisateur : $nom_utilisateur" >> "$fichier_sauvegarde"
        dscl . -read "/Users/$nom_utilisateur" >> "$fichier_sauvegarde" 2>/dev/null
        echo "---" >> "$fichier_sauvegarde"
    done
    
    echo "Sauvegarde créée : $fichier_sauvegarde"
    echo "[$timestamp] Sauvegarde de nettoyage automatisé créée : $fichier_sauvegarde" >> "$fichier_log"
}

envoyer_notification_nettoyage() {
    local compteur_succes=$1
    local compteur_echec=$2
    
    # Utiliser osascript pour afficher une notification (pour les notifications locales)
    osascript -e "display notification \"Nettoyage terminé : $compteur_succes succès, $compteur_echec échec\" with title \"Nettoyage des comptes mobiles Roundfleet\""
    
    echo "[$timestamp] Notification de nettoyage envoyée" >> "$fichier_log"
}

# Exécuter le nettoyage automatisé
nettoyage_comptes_mobiles_automatise

Dépannage et bonnes pratiques

Problèmes courants et solutions

  1. Erreurs de permissions : Toujours exécuter les scripts en tant que root avec sudo
  2. Utilisateurs connectés : Vérifier les sessions actives avant suppression
  3. Services d'annuaire : Vérifier l'identification des comptes mobiles avec l'attribut LocalCachedUser
  4. Protection des répertoires home : Créer des sauvegardes avant de supprimer les répertoires home
  5. Comptes réseau : S'assurer de la connectivité réseau pour les opérations de services d'annuaire

Bonnes pratiques

  • Tester d'abord : Toujours tester les scripts dans des environnements contrôlés
  • Créer des sauvegardes : Maintenir des sauvegardes des données utilisateur avant suppression
  • Journaliser les opérations : Garder des logs détaillés de toutes les opérations de comptes
  • Communication utilisateur : Notifier les utilisateurs avant le nettoyage des comptes
  • Planifier la maintenance : Effectuer le nettoyage pendant les fenêtres de maintenance

Conclusion

La gestion des comptes mobiles est cruciale pour maintenir des environnements macOS propres et sécurisés. Ces scripts fournissent des solutions complètes pour supprimer les comptes mobiles, du nettoyage simple à l'automatisation de niveau entreprise. Assurez-vous toujours de procédures de test et de sauvegarde appropriées avant l'implémentation en environnement de production.

Supprimer Fichiers par Taille sur macOS

Apprenez à trouver et supprimer les gros fichiers sur Mac basés sur des critères de taille. Cela aide à libérer de l'espace de stockage et améliorer les performances de l'appareil quand le stockage devient faible.

Lister les Gros Fichiers

Trouvez et listez les fichiers plus grands qu'une taille spécifiée :

#!/bin/bash

# Obtenir le contexte utilisateur actuel
CURRENT_USER=$(stat -f "%Su" /dev/console)
CURRENT_USER_UID=$(id -u "$CURRENT_USER")

# Configuration
SEARCH_PATH="/Users/$CURRENT_USER/Desktop"
SIZE_LIMIT="100"  # Taille en kilooctets

echo "Recherche de fichiers plus grands que ${SIZE_LIMIT}KB dans : $SEARCH_PATH"

# Lister les gros fichiers
launchctl asuser $CURRENT_USER_UID sudo -iu "$CURRENT_USER" \
    find "$SEARCH_PATH" -type f -size +${SIZE_LIMIT}k -print

Supprimer les Gros Fichiers

Trouvez et supprimez les fichiers plus grands qu'une taille spécifiée :

#!/bin/bash

# Obtenir le contexte utilisateur actuel
CURRENT_USER=$(stat -f "%Su" /dev/console)
CURRENT_USER_UID=$(id -u "$CURRENT_USER")

# Configuration
SEARCH_PATH="/Users/$CURRENT_USER/Downloads"
SIZE_LIMIT="50"  # Taille en kilooctets

echo "⚠️  ATTENTION : Ceci supprimera définitivement les fichiers plus grands que ${SIZE_LIMIT}KB"
echo "Recherche dans : $SEARCH_PATH"

# Lister et supprimer les gros fichiers
launchctl asuser $CURRENT_USER_UID sudo -iu "$CURRENT_USER" \
    find "$SEARCH_PATH" -type f -size +${SIZE_LIMIT}k -print -delete

echo "✅ Gros fichiers supprimés avec succès"

Script de Nettoyage Amélioré

Script avec vérifications de sécurité et rapport détaillé :

#!/bin/bash

# Fonction pour nettoyer les gros fichiers en toute sécurité
cleanup_large_files() {
    local search_path="$1"
    local size_limit="$2"
    local dry_run="${3:-false}"
    
    # Obtenir le contexte utilisateur actuel
    local current_user=$(stat -f "%Su" /dev/console)
    local current_user_uid=$(id -u "$current_user")
    
    if [[ ! -d "$search_path" ]]; then
        echo "❌ Erreur : Répertoire non trouvé : $search_path"
        return 1
    fi
    
    echo "=== Nettoyage Gros Fichiers ==="
    echo "📁 Chemin : $search_path"
    echo "📏 Limite taille : ${size_limit}KB"
    echo "👤 Utilisateur : $current_user"
    echo "🔍 Test à sec : $dry_run"
    echo "==============================="
    
    # Trouver les gros fichiers
    local files_command="find '$search_path' -type f -size +${size_limit}k"
    
    if [[ "$dry_run" == "true" ]]; then
        echo "📋 Fichiers qui seraient supprimés :"
        launchctl asuser $current_user_uid sudo -iu "$current_user" \
            bash -c "$files_command -ls"
        
        local count=$(launchctl asuser $current_user_uid sudo -iu "$current_user" \
            bash -c "$files_command | wc -l")
        echo "📊 Total fichiers trouvés : $count"
    else
        echo "🗑️  Suppression des gros fichiers..."
        launchctl asuser $current_user_uid sudo -iu "$current_user" \
            bash -c "$files_command -print -delete"
        echo "✅ Nettoyage terminé"
    fi
}

# Configuration
SEARCH_PATH="/Users/$(stat -f "%Su" /dev/console)/Downloads"
SIZE_LIMIT="100"
DRY_RUN="true"  # Définir à "false" pour supprimer réellement les fichiers

# Exécuter le nettoyage
cleanup_large_files "$SEARCH_PATH" "$SIZE_LIMIT" "$DRY_RUN"

Gestion de Stockage Entreprise

Nettoyage de stockage complet pour environnements d'entreprise :

#!/bin/bash

# Configuration nettoyage stockage entreprise
CLEANUP_LOCATIONS=(
    "/Users/*/Downloads:50"
    "/Users/*/Desktop:100"
    "/Users/*/Documents:200"
    "/tmp:10"
)

# Fonction pour formater la taille des fichiers
format_size() {
    local size=$1
    if [[ $size -gt 1048576 ]]; then
        echo "$(( size / 1048576 ))GB"
    elif [[ $size -gt 1024 ]]; then
        echo "$(( size / 1024 ))MB"
    else
        echo "${size}KB"
    fi
}

# Fonction pour obtenir la taille du répertoire
get_directory_size() {
    local dir="$1"
    if [[ -d "$dir" ]]; then
        du -sk "$dir" 2>/dev/null | cut -f1
    else
        echo "0"
    fi
}

# Exécution nettoyage entreprise
echo "🏢 Nettoyage Stockage Entreprise Roundfleet"
echo "=========================================="
echo "Généré : $(date)"
echo "Appareil : $(hostname)"

total_freed=0
current_user=$(stat -f "%Su" /dev/console)
current_user_uid=$(id -u "$current_user")

for location_config in "${CLEANUP_LOCATIONS[@]}"; do
    IFS=':' read -r location_pattern size_limit <<< "$location_config"
    
    echo ""
    echo "📁 Traitement : $location_pattern (>${size_limit}KB)"
    
    # Expansion du motif pour répertoires utilisateur
    for location in $location_pattern; do
        if [[ -d "$location" ]]; then
            echo "  🔍 Scan : $location"
            
            # Obtenir taille avant
            before_size=$(get_directory_size "$location")
            
            # Trouver et supprimer gros fichiers
            deleted_files=$(launchctl asuser $current_user_uid sudo -iu "$current_user" \
                find "$location" -type f -size +${size_limit}k -print -delete 2>/dev/null | wc -l)
            
            # Obtenir taille après
            after_size=$(get_directory_size "$location")
            freed=$((before_size - after_size))
            total_freed=$((total_freed + freed))
            
            if [[ $deleted_files -gt 0 ]]; then
                echo "  ✅ Supprimé : $deleted_files fichiers"
                echo "  💾 Libéré : $(format_size $freed)"
            else
                echo "  ℹ️  Aucun gros fichier trouvé"
            fi
        fi
    done
done

echo ""
echo "=========================================="
echo "📊 Résumé :"
echo "  Espace total libéré : $(format_size $total_freed)"
echo "  Nettoyage terminé : $(date)"

if [[ $total_freed -gt 0 ]]; then
    echo "✅ Nettoyage stockage réussi"
else
    echo "ℹ️  Aucun nettoyage nécessaire - utilisation stockage optimale"
fi

Nettoyage Sécurisé avec Option Sauvegarde

Script avec sauvegarde optionnelle avant suppression :

#!/bin/bash

# Fonction pour sauvegarder gros fichiers avant suppression
backup_and_delete() {
    local search_path="$1"
    local size_limit="$2"
    local backup_enabled="${3:-false}"
    
    local current_user=$(stat -f "%Su" /dev/console)
    local current_user_uid=$(id -u "$current_user")
    local backup_dir="/tmp/roundfleet_backup_$(date +%Y%m%d_%H%M%S)"
    
    echo "🔄 Traitement : $search_path"
    
    if [[ "$backup_enabled" == "true" ]]; then
        echo "📦 Création répertoire sauvegarde : $backup_dir"
        mkdir -p "$backup_dir"
        
        # Trouver et sauvegarder gros fichiers
        launchctl asuser $current_user_uid sudo -iu "$current_user" \
            find "$search_path" -type f -size +${size_limit}k -exec cp {} "$backup_dir/" \;
        
        echo "✅ Fichiers sauvegardés dans : $backup_dir"
    fi
    
    # Supprimer gros fichiers
    local deleted_count=$(launchctl asuser $current_user_uid sudo -iu "$current_user" \
        find "$search_path" -type f -size +${size_limit}k -print -delete | wc -l)
    
    echo "🗑️  Supprimé $deleted_count gros fichiers"
    
    if [[ "$backup_enabled" == "true" && $deleted_count -gt 0 ]]; then
        echo "💡 Emplacement sauvegarde : $backup_dir"
        echo "⏰ Sauvegarde sera auto-supprimée dans 7 jours"
        
        # Programmer nettoyage sauvegarde (nécessite launchd ou cron)
        echo "find '$backup_dir' -type f -mtime +7 -delete" > /tmp/cleanup_backup.sh
        chmod +x /tmp/cleanup_backup.sh
    fi
}

# Configuration
SEARCH_PATH="/Users/$(stat -f "%Su" /dev/console)/Downloads"
SIZE_LIMIT="100"
BACKUP_ENABLED="true"

# Exécuter nettoyage sécurisé
backup_and_delete "$SEARCH_PATH" "$SIZE_LIMIT" "$BACKUP_ENABLED"

Utilisation avec Roundfleet

  1. Configurez le chemin de recherche et la limite de taille dans le script
  2. Choisissez entre mode test à sec et suppression réelle
  3. Considérez activer l'option sauvegarde pour sécurité
  4. Déployez via l'exécution de script distant Roundfleet
  5. Surveillez les résultats et l'espace libéré dans l'historique des actions

Unités de Taille et Exemples

UnitéDescriptionCas d'Usage Exemple
+50kFichiers plus grands que 50KBNettoyage petits documents
+10MFichiers plus grands que 10MBNettoyage fichiers média
+100MFichiers plus grands que 100MBNettoyage gros fichiers
+1GFichiers plus grands que 1GBNettoyage archives

Cibles de Nettoyage Communes

RépertoireLimite Taille TypiqueObjectif
Téléchargements50MBSupprimer gros téléchargements
Bureau100MBNettoyer fichiers bureau
Documents200MBArchiver anciens documents
Dossiers temp10MBNettoyage système

Considérations de Sécurité

⚠️ ATTENTION CRITIQUE : Les fichiers supprimés avec ces scripts sont définitivement supprimés et ne peuvent pas être récupérés de la Corbeille.

  • Tester d'abord : Toujours exécuter en mode test à sec initialement
  • Sauvegarder données importantes : Considérez l'option sauvegarde pour fichiers précieux
  • Vérifier chemins : Vérifiez deux fois les répertoires de recherche avant exécution
  • Contexte utilisateur : Les scripts s'exécutent dans le contexte utilisateur actuel pour sécurité

Dépannage

Permission refusée : Assurez-vous du bon contexte utilisateur avec launchctl asuser Répertoire non trouvé : Vérifiez que les chemins de recherche existent avant exécution Aucun fichier trouvé : Vérifiez les limites de taille et contenu des répertoires


Note : Validez toujours les scripts sur des systèmes de test avant déploiement en masse. La suppression permanente de fichiers nécessite une considération attentive et planification de sauvegarde.

Gestion Entreprise de la Date et Heure sur macOS

Gérez les paramètres de date et heure dans votre Roundfleet avec automation de niveau entreprise, contrôles de validation et capacités de surveillance complètes.

Comprendre la Gestion Entreprise de Date et Heure

La gestion de date et heure en entreprise nécessite plus qu'un simple réglage de date, exigeant :

  • Validation et formatage automatisés avec gestion d'erreurs de niveau entreprise
  • Opérations date/heure planifiées avec intégration de logique métier
  • Suivi de conformité pour exigences d'audit et standards réglementaires
  • Surveillance en temps réel des changements date/heure et intégrité système
  • Capacités d'intégration avec systèmes de planification et workflow d'entreprise
  • Contrôles de sécurité pour empêcher manipulation temporelle non autorisée

Processus de Base de Gestion Date et Heure

Commandes de Base

  1. Définir Date/Heure - date MMDDhhmm[[CC]YY][.ss]
  2. Définition Paramétrée - date $1 (avec passage d'argument)

Format Date de Base

Le format suit : MMDDhhmm[[CC]YY][.ss]

  • MM - Mois (01-12)
  • DD - Jour (01-31)
  • hh - Heure (00-23)
  • mm - Minutes (00-59)
  • CC - Siècle (optionnel)
  • YY - Année (optionnel)
  • ss - Secondes (optionnel)

Exemples de Configuration de Base

# Réglage de date de base
date 120614302023.00  # 6 décembre 2023 14h30

# Version paramétrée
date $1  # Passer la date comme argument

Système de Gestion Date et Heure Entreprise

#!/bin/bash

# Système de Gestion Date et Heure Entreprise Roundfleet
# Gestion complète date/heure avec contrôles entreprise et surveillance

# Configuration
SCRIPT_NAME="Gestionnaire Date/Heure Roundfleet"
VERSION="1.0.0"
LOG_FILE="/var/log/roundfleet_datetime.log"
AUDIT_LOG="/var/log/roundfleet_datetime_audit.log"
CONFIG_DIR="/etc/roundfleet/datetime"
POLICIES_DIR="/etc/roundfleet/datetime/policies"
BACKUP_DIR="/var/backups/datetime"
TEMP_DIR="/tmp/roundfleet_datetime"
ORGANIZATION_NAME="Roundfleet Entreprise"
DEPLOYMENT_MODE="enterprise"
ENABLE_VALIDATION=true
ENABLE_AUDIT_LOGGING=true
ENABLE_BACKUP_RESTORE=true
MAX_TIME_DRIFT_HOURS=24
BUSINESS_HOURS_START="08:00"
BUSINESS_HOURS_END="18:00"

# Modèles de Validation Date/Heure
declare -A DATE_FORMATS=(
    ["roundfleet"]="MMDDhhmm[[CC]YY][.ss]"
    ["iso8601"]="YYYY-MM-DDTHH:MM:SS"
    ["unix"]="secondes_depuis_epoch"
    ["readable"]="Mois DD, YYYY HH:MM:SS"
)

# Politiques de Planification Entreprise
declare -A SCHEDULING_POLICIES=(
    ["immediate"]="executer_maintenant"
    ["business_hours"]="heures_ouverture_seulement"
    ["maintenance_window"]="maintenance_planifiee_seulement"
    ["emergency"]="outrepasser_toutes_restrictions"
)

# Standards de Conformité
declare -A COMPLIANCE_REQUIREMENTS=(
    ["SOX"]="piste_audit_financiere"
    ["HIPAA"]="integrite_temps_sante"
    ["PCI_DSS"]="precision_transaction_paiement"
    ["ISO27001"]="horodatage_securite_information"
)

# Créer les répertoires nécessaires
mkdir -p "$CONFIG_DIR"
mkdir -p "$POLICIES_DIR"
mkdir -p "$BACKUP_DIR"
mkdir -p "$TEMP_DIR"
mkdir -p "$(dirname "$LOG_FILE")"
mkdir -p "$(dirname "$AUDIT_LOG")"

# Définir les permissions sécurisées
chmod 755 "$CONFIG_DIR"
chmod 750 "$POLICIES_DIR"
chmod 750 "$BACKUP_DIR"
chmod 700 "$TEMP_DIR"

# Fonctions de journalisation
log_operation() {
    local level="$1"
    local message="$2"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    local admin_user=$(whoami)
    echo "[$timestamp] [$level] [$admin_user] $message" | tee -a "$LOG_FILE"
}

log_security_event() {
    local event_type="$1"
    local details="$2"
    local severity="$3"
    local admin_user=$(whoami)
    
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    local source_ip=$(who am i | awk '{print $5}' | tr -d '()')
    echo "SECURITY|$timestamp|$event_type|$severity|$admin_user|$source_ip|$details" >> "$AUDIT_LOG"
}

# Obtenir les informations actuelles de date et heure
get_current_datetime() {
    local format="${1:-all}"
    
    echo "=== Informations Date et Heure Actuelles ==="
    echo "Heure Locale : $(date '+%Y-%m-%d %H:%M:%S %Z')"
    echo "Heure UTC : $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
    echo "Timestamp Unix : $(date +%s)"
    echo "Uptime : $(uptime | awk -F'up ' '{print $2}' | awk -F',' '{print $1}')"
    
    case "$format" in
        "roundfleet")
            echo "Format Roundfleet : $(date '+%m%d%H%M%Y.%S')"
            ;;
        "iso8601")
            echo "Format ISO 8601 : $(date '+%Y-%m-%dT%H:%M:%S')"
            ;;
        "detailed")
            echo ""
            echo "Informations Détaillées :"
            echo "  Année : $(date '+%Y')"
            echo "  Mois : $(date '+%m') ($(date '+%B'))"
            echo "  Jour : $(date '+%d')"
            echo "  Heure : $(date '+%H')"
            echo "  Minute : $(date '+%M')"
            echo "  Seconde : $(date '+%S')"
            echo "  Jour de la Semaine : $(date '+%A')"
            echo "  Jour de l'Année : $(date '+%j')"
            echo "  Semaine de l'Année : $(date '+%U')"
            ;;
    esac
    echo ""
}

# Valider le format date/heure
validate_datetime_format() {
    local datetime_string="$1"
    local format_type="${2:-roundfleet}"
    
    echo "=== Validation Format Date/Heure ==="
    echo "Entrée : $datetime_string"
    echo "Format Attendu : $format_type"
    echo ""
    
    case "$format_type" in
        "roundfleet")
            # Valider format Roundfleet : MMDDhhmm[[CC]YY][.ss]
            if [[ "$datetime_string" =~ ^[0-9]{8}([0-9]{4})?(\.[0-9]{2})?$ ]]; then
                local month="${datetime_string:0:2}"
                local day="${datetime_string:2:2}"
                local hour="${datetime_string:4:2}"
                local minute="${datetime_string:6:2}"
                
                # Valider les plages
                if [[ $month -ge 1 && $month -le 12 ]] && \
                   [[ $day -ge 1 && $day -le 31 ]] && \
                   [[ $hour -ge 0 && $hour -le 23 ]] && \
                   [[ $minute -ge 0 && $minute -le 59 ]]; then
                    echo "✅ Format Roundfleet valide"
                    return 0
                else
                    echo "❌ Valeurs date/heure invalides dans format Roundfleet"
                    return 1
                fi
            else
                echo "❌ Structure format Roundfleet invalide"
                echo "Attendu : MMDDhhmm[[CC]YY][.ss]"
                return 1
            fi
            ;;
        "iso8601")
            # Valider format ISO 8601
            if date -j -f "%Y-%m-%dT%H:%M:%S" "$datetime_string" &>/dev/null; then
                echo "✅ Format ISO 8601 valide"
                return 0
            else
                echo "❌ Format ISO 8601 invalide"
                return 1
            fi
            ;;
        "unix")
            # Valider timestamp Unix
            if [[ "$datetime_string" =~ ^[0-9]+$ ]] && [[ $datetime_string -gt 0 ]]; then
                echo "✅ Timestamp Unix valide"
                return 0
            else
                echo "❌ Timestamp Unix invalide"
                return 1
            fi
            ;;
        *)
            echo "❌ Type de format inconnu : $format_type"
            return 1
            ;;
    esac
}

# Convertir entre formats de date
convert_datetime_format() {
    local input_datetime="$1"
    local from_format="$2"
    local to_format="$3"
    
    echo "=== Conversion Format Date/Heure ==="
    echo "Entrée : $input_datetime"
    echo "De : $from_format"
    echo "Vers : $to_format"
    echo ""
    
    local converted_datetime
    
    case "$from_format" in
        "roundfleet")
            # Convertir depuis format Roundfleet
            local month="${input_datetime:0:2}"
            local day="${input_datetime:2:2}"
            local hour="${input_datetime:4:2}"
            local minute="${input_datetime:6:2}"
            local year_part="${input_datetime:8:4}"
            local seconds_part="${input_datetime:13:2}"
            
            # Année par défaut si non fournie
            if [[ -z "$year_part" ]]; then
                year_part=$(date '+%Y')
            fi
            
            # Secondes par défaut si non fournies
            if [[ -z "$seconds_part" ]]; then
                seconds_part="00"
            fi
            
            case "$to_format" in
                "iso8601")
                    converted_datetime="${year_part}-${month}-${day}T${hour}:${minute}:${seconds_part}"
                    ;;
                "readable")
                    converted_datetime=$(date -j "${month}${day}${hour}${minute}${year_part}.${seconds_part}" '+%B %d, %Y %H:%M:%S' 2>/dev/null)
                    ;;
                "unix")
                    converted_datetime=$(date -j "${month}${day}${hour}${minute}${year_part}.${seconds_part}" '+%s' 2>/dev/null)
                    ;;
            esac
            ;;
        "iso8601")
            # Convertir depuis format ISO 8601
            case "$to_format" in
                "roundfleet")
                    converted_datetime=$(date -j -f "%Y-%m-%dT%H:%M:%S" "$input_datetime" '+%m%d%H%M%Y.%S' 2>/dev/null)
                    ;;
                "readable")
                    converted_datetime=$(date -j -f "%Y-%m-%dT%H:%M:%S" "$input_datetime" '+%B %d, %Y %H:%M:%S' 2>/dev/null)
                    ;;
                "unix")
                    converted_datetime=$(date -j -f "%Y-%m-%dT%H:%M:%S" "$input_datetime" '+%s' 2>/dev/null)
                    ;;
            esac
            ;;
        "unix")
            # Convertir depuis timestamp Unix
            case "$to_format" in
                "roundfleet")
                    converted_datetime=$(date -r "$input_datetime" '+%m%d%H%M%Y.%S' 2>/dev/null)
                    ;;
                "iso8601")
                    converted_datetime=$(date -r "$input_datetime" '+%Y-%m-%dT%H:%M:%S' 2>/dev/null)
                    ;;
                "readable")
                    converted_datetime=$(date -r "$input_datetime" '+%B %d, %Y %H:%M:%S' 2>/dev/null)
                    ;;
            esac
            ;;
    esac
    
    if [[ -n "$converted_datetime" ]]; then
        echo "✅ Conversion réussie"
        echo "Résultat : $converted_datetime"
        echo "$converted_datetime"
        return 0
    else
        echo "❌ Conversion échouée"
        return 1
    fi
}

# Définir date et heure avec validation entreprise
set_enterprise_datetime() {
    local datetime_input="$1"
    local format_type="${2:-roundfleet}"
    local policy="${3:-business_hours}"
    local force="${4:-false}"
    local admin_user=$(whoami)
    
    log_security_event "DATETIME_CHANGE_ATTEMPT" "datetime=$datetime_input,format=$format_type,policy=$policy" "INFO"
    
    echo "=== Configuration Date/Heure Entreprise ==="
    echo "Entrée : $datetime_input"
    echo "Format : $format_type"
    echo "Politique : $policy"
    echo "Administrateur : $admin_user"
    echo "Mode Force : $force"
    echo ""
    
    # Valider le format d'entrée
    if ! validate_datetime_format "$datetime_input" "$format_type"; then
        log_operation "ERROR" "Format date/heure invalide : $datetime_input ($format_type)"
        return 1
    fi
    
    # Vérifier la politique de planification
    if [[ "$policy" == "business_hours" && "$force" != "true" ]]; then
        local current_hour=$(date '+%H')
        local business_start_hour="${BUSINESS_HOURS_START:0:2}"
        local business_end_hour="${BUSINESS_HOURS_END:0:2}"
        
        if [[ $current_hour -lt $business_start_hour || $current_hour -gt $business_end_hour ]]; then
            echo "⚠️  Opération demandée en dehors des heures d'ouverture ($BUSINESS_HOURS_START-$BUSINESS_HOURS_END)"
            echo "Utilisez force=true pour outrepasser ou attendez les heures d'ouverture"
            log_operation "WARNING" "Changement date/heure bloqué par politique heures d'ouverture"
            return 1
        fi
    fi
    
    # Sauvegarder date/heure actuelle
    local backup_file="$BACKUP_DIR/datetime_$(date +%Y%m%d_%H%M%S).backup"
    {
        echo "# Sauvegarde Date/Heure Roundfleet"
        echo "PREVIOUS_DATETIME=$(date '+%Y-%m-%d %H:%M:%S %Z')"
        echo "PREVIOUS_UTC=$(date -u '+%Y-%m-%d %H:%M:%S UTC')"
        echo "PREVIOUS_UNIX=$(date +%s)"
        echo "BACKUP_TIMESTAMP=$(date)"
        echo "CHANGED_BY=$admin_user"
        echo "NEW_DATETIME=$datetime_input"
        echo "FORMAT_TYPE=$format_type"
        echo "POLICY=$policy"
    } > "$backup_file"
    
    log_operation "INFO" "Sauvegarde date/heure créée : $backup_file"
    
    # Convertir au format approprié si nécessaire
    local roundfleet_format="$datetime_input"
    
    if [[ "$format_type" != "roundfleet" ]]; then
        echo "Conversion $format_type vers format Roundfleet..."
        roundfleet_format=$(convert_datetime_format "$datetime_input" "$format_type" "roundfleet")
        
        if [[ $? -ne 0 ]]; then
            log_operation "ERROR" "Échec conversion format date/heure"
            return 1
        fi
        
        echo "Format converti : $roundfleet_format"
    fi
    
    # Valider la dérive temporelle
    local current_unix=$(date +%s)
    local target_unix=$(convert_datetime_format "$roundfleet_format" "roundfleet" "unix")
    local time_diff=$((target_unix - current_unix))
    local time_diff_hours=$((time_diff / 3600))
    local time_diff_abs=${time_diff_hours#-}
    
    if [[ $time_diff_abs -gt $MAX_TIME_DRIFT_HOURS && "$force" != "true" ]]; then
        echo "⚠️  Grand changement de temps détecté : ${time_diff_hours} heures"
        echo "Ceci dépasse le seuil de dérive maximum de $MAX_TIME_DRIFT_HOURS heures"
        echo "Utilisez force=true pour outrepasser cette vérification de sécurité"
        log_operation "WARNING" "Grande dérive temporelle bloquée : ${time_diff_hours} heures"
        return 1
    fi
    
    # Appliquer le changement date/heure
    echo "Définition date/heure sur : $roundfleet_format"
    
    if sudo date "$roundfleet_format" &>/dev/null; then
        echo "✅ Date/heure définie avec succès"
        log_operation "INFO" "Date/heure changée vers : $roundfleet_format (format: $format_type)"
        log_security_event "DATETIME_CHANGED" "new_datetime=$roundfleet_format,drift_hours=$time_diff_hours,policy=$policy" "INFO"
        
        # Vérifier le changement
        sleep 1
        echo ""
        echo "Vérification :"
        get_current_datetime
        
        return 0
    else
        echo "❌ Échec de définition date/heure"
        log_operation "ERROR" "Échec définition date/heure : $roundfleet_format"
        log_security_event "DATETIME_CHANGE_FAILED" "datetime=$roundfleet_format,error=date_command_failed" "ERROR"
        return 1
    fi
}

# Planifier les opérations date/heure
schedule_datetime_operation() {
    local operation="$1"
    local target_datetime="$2"
    local schedule_time="$3"
    local policy="${4:-business_hours}"
    
    echo "=== Opération Date/Heure Planifiée ==="
    echo "Opération : $operation"
    echo "Date/Heure Cible : $target_datetime"
    echo "Heure Planifiée : $schedule_time"
    echo "Politique : $policy"
    echo ""
    
    # Créer fichier d'opération planifiée
    local schedule_file="$POLICIES_DIR/scheduled_$(date +%Y%m%d_%H%M%S).sh"
    
    cat > "$schedule_file" << EOF
#!/bin/bash
# Opération Date/Heure Planifiée Roundfleet
# Généré : $(date)
# Opération : $operation
# Cible : $target_datetime
# Politique : $policy

# Exécuter l'opération planifiée
case "$operation" in
    "set_datetime")
        $0 set "$target_datetime" roundfleet "$policy" true
        ;;
    "sync_time")
        sudo systemsetup -setusingnetworktime on
        ;;
    "backup_datetime")
        $0 backup
        ;;
    *)
        echo "Opération inconnue : $operation"
        exit 1
        ;;
esac
EOF
    
    chmod +x "$schedule_file"
    
    # Planifier avec commande at (si disponible)
    if command -v at &>/dev/null; then
        echo "$schedule_file" | at "$schedule_time" 2>/dev/null
        
        if [[ $? -eq 0 ]]; then
            echo "✅ Opération planifiée avec succès pour $schedule_time"
            log_operation "INFO" "Opération planifiée : $operation à $schedule_time"
            return 0
        else
            echo "❌ Échec de planification de l'opération"
            log_operation "ERROR" "Échec planification opération : $operation"
            return 1
        fi
    else
        echo "⚠️  Commande 'at' non disponible pour planification"
        echo "Fichier de planification créé : $schedule_file"
        echo "Exécutez manuellement ou intégrez avec votre système de planification"
        return 1
    fi
}

# Surveiller l'intégrité date/heure
monitor_datetime_integrity() {
    local check_type="${1:-basic}"
    local admin_user=$(whoami)
    
    echo "=== Surveillance Intégrité Date/Heure ==="
    echo "Type de Vérification : $check_type"
    echo "Surveillant : $admin_user"
    echo ""
    
    local integrity_status="HEALTHY"
    local issues=()
    
    # Vérifier horloge système vs heure réseau
    if command -v ntpdate &>/dev/null; then
        echo "Vérification synchronisation horaire..."
        
        local ntp_time=$(ntpdate -q pool.ntp.org 2>/dev/null | tail -1 | grep -o "offset [+-][0-9.]*" | awk '{print $2}')
        
        if [[ -n "$ntp_time" ]]; then
            local offset_abs=${ntp_time#-}
            
            echo "Décalage NTP : ${ntp_time}s"
            
            if (( $(echo "$offset_abs > 30" | bc -l) )); then
                integrity_status="WARNING"
                issues+=("Grand décalage NTP : ${ntp_time}s")
            else
                echo "✅ Synchronisation horaire dans plage acceptable"
            fi
        else
            integrity_status="WARNING"
            issues+=("Impossible de vérifier synchronisation NTP")
        fi
    fi
    
    # Vérifier les changements de temps récents importants
    if [[ -f "$AUDIT_LOG" ]]; then
        echo ""
        echo "Vérification changements de temps récents..."
        
        local recent_changes=$(grep "DATETIME_CHANGED" "$AUDIT_LOG" | tail -5)
        
        if [[ -n "$recent_changes" ]]; then
            echo "Changements date/heure récents trouvés :"
            echo "$recent_changes" | while read -r line; do
                echo "  $(echo "$line" | cut -d'|' -f2,6)"
            done
        else
            echo "✅ Aucun changement date/heure récent détecté"
        fi
    fi
    
    # Vérifications avancées
    if [[ "$check_type" == "comprehensive" ]]; then
        echo ""
        echo "Exécution vérifications intégrité complètes..."
        
        # Vérifier logs système pour erreurs liées au temps
        local time_errors=$(log show --predicate 'subsystem == "com.apple.time"' --last 1h 2>/dev/null | grep -i error | wc -l)
        
        if [[ $time_errors -gt 0 ]]; then
            integrity_status="WARNING"
            issues+=("Erreurs temps système détectées : $time_errors")
        fi
        
        # Vérifier les incompatibilités de fuseau horaire
        local system_tz=$(systemsetup -gettimezone 2>/dev/null | awk -F': ' '{print $2}')
        local env_tz="$TZ"
        
        if [[ -n "$env_tz" && "$env_tz" != "$system_tz" ]]; then
            integrity_status="WARNING"
            issues+=("Incompatibilité fuseau horaire : système=$system_tz, env=$env_tz")
        fi
    fi
    
    # Générer rapport d'intégrité
    echo ""
    echo "=== Rapport Intégrité Date/Heure ==="
    echo "État Global : $integrity_status"
    echo "Horodatage : $(date)"
    
    if [[ ${#issues[@]} -gt 0 ]]; then
        echo "Problèmes Trouvés :"
        printf '  - %s\n' "${issues[@]}"
    else
        echo "✅ Tous les systèmes date/heure fonctionnent normalement"
    fi
    
    # Enregistrer les résultats de surveillance
    log_operation "INFO" "Vérification intégrité date/heure terminée : $integrity_status (${#issues[@]} problèmes)"
    log_security_event "DATETIME_INTEGRITY_CHECK" "status=$integrity_status,issues=${#issues[@]}" "INFO"
    
    # Retourner le code de sortie approprié
    case "$integrity_status" in
        "HEALTHY") return 0 ;;
        "WARNING") return 1 ;;
        "CRITICAL") return 2 ;;
        *) return 3 ;;
    esac
}

# Générer rapport de gestion date/heure
generate_datetime_report() {
    local report_type="${1:-summary}"
    local admin_user=$(whoami)
    local report_file="/var/reports/datetime_report_$(date +%Y%m%d_%H%M%S).txt"
    
    mkdir -p "$(dirname "$report_file")"
    
    log_security_event "REPORT_GENERATION" "type=$report_type" "INFO"
    
    {
        echo "Rapport Gestion Date/Heure Roundfleet"
        echo "=================================="
        echo "Type de Rapport : $report_type"
        echo "Généré : $(date)"
        echo "Généré Par : $admin_user"
        echo "Nom d'hôte : $(hostname)"
        echo ""
        
        case "$report_type" in
            "summary")
                echo "== Résumé Date/Heure =="
                get_current_datetime "detailed"
                
                echo "Formats Disponibles :"
                for format in "${!DATE_FORMATS[@]}"; do
                    echo "  $format: ${DATE_FORMATS[$format]}"
                done
                ;;
            "compliance")
                echo "== Évaluation Conformité =="
                
                echo "Standards de Conformité :"
                for standard in "${!COMPLIANCE_REQUIREMENTS[@]}"; do
                    echo "  $standard: ${COMPLIANCE_REQUIREMENTS[$standard]}"
                done
                
                echo ""
                echo "Événements d'Audit Récents :"
                if [[ -f "$AUDIT_LOG" ]]; then
                    tail -10 "$AUDIT_LOG"
                else
                    echo "Aucun log d'audit disponible"
                fi
                ;;
            "audit")
                echo "== Information d'Audit =="
                if [[ -f "$AUDIT_LOG" ]]; then
                    echo "Piste d'audit complète :"
                    cat "$AUDIT_LOG"
                else
                    echo "Aucun log d'audit disponible"
                fi
                ;;
        esac
        
        echo ""
        echo "== Informations Système =="
        echo "Uptime : $(uptime)"
        echo "Charge Moyenne : $(uptime | awk -F'load average:' '{print $2}')"
        
    } > "$report_file"
    
    echo "Rapport date/heure généré : $report_file"
    log_operation "INFO" "Rapport date/heure généré : $report_file"
}

# Sauvegarder et restaurer paramètres date/heure
backup_datetime_settings() {
    local backup_type="${1:-full}"
    
    echo "=== Sauvegarde Paramètres Date/Heure ==="
    echo "Type de Sauvegarde : $backup_type"
    echo ""
    
    local backup_file="$BACKUP_DIR/datetime_settings_$(date +%Y%m%d_%H%M%S).backup"
    
    {
        echo "# Sauvegarde Paramètres Date/Heure Roundfleet"
        echo "BACKUP_DATE=$(date)"
        echo "BACKUP_TYPE=$backup_type"
        echo ""
        echo "# Paramètres Actuels"
        echo "CURRENT_DATETIME=$(date '+%Y-%m-%d %H:%M:%S %Z')"
        echo "CURRENT_UTC=$(date -u '+%Y-%m-%d %H:%M:%S UTC')"
        echo "CURRENT_UNIX=$(date +%s)"
        echo "CURRENT_TIMEZONE=$(systemsetup -gettimezone 2>/dev/null | awk -F': ' '{print $2}')"
        echo "NETWORK_TIME=$(systemsetup -getusingnetworktime 2>/dev/null | awk -F': ' '{print $2}')"
        echo "TIME_SERVER=$(systemsetup -getnetworktimeserver 2>/dev/null | awk -F': ' '{print $2}')"
        echo ""
        echo "# Informations Système"
        echo "HOSTNAME=$(hostname)"
        echo "UPTIME=$(uptime)"
        echo "BACKUP_BY=$(whoami)"
    } > "$backup_file"
    
    echo "✅ Sauvegarde créée : $backup_file"
    log_operation "INFO" "Paramètres date/heure sauvegardés : $backup_file"
}

# Fonction principale de gestion date/heure
main() {
    local action="${1:-help}"
    
    case "$action" in
        "status"|"current")
            local format="${2:-all}"
            get_current_datetime "$format"
            ;;
        "set")
            local datetime_input="$2"
            local format_type="${3:-roundfleet}"
            local policy="${4:-business_hours}"
            local force="$5"
            
            if [[ -z "$datetime_input" ]]; then
                echo "Usage : $0 set <datetime> [format] [policy] [force]"
                echo "Exemple : $0 set 120614302023.00 roundfleet business_hours"
                return 1
            fi
            
            set_enterprise_datetime "$datetime_input" "$format_type" "$policy" "$force"
            ;;
        "validate")
            local datetime_input="$2"
            local format_type="${3:-roundfleet}"
            
            if [[ -z "$datetime_input" ]]; then
                echo "Usage : $0 validate <datetime> [format]"
                return 1
            fi
            
            validate_datetime_format "$datetime_input" "$format_type"
            ;;
        "convert")
            local datetime_input="$2"
            local from_format="$3"
            local to_format="$4"
            
            if [[ -z "$datetime_input" || -z "$from_format" || -z "$to_format" ]]; then
                echo "Usage : $0 convert <datetime> <from_format> <to_format>"
                echo "Formats disponibles : roundfleet, iso8601, unix, readable"
                return 1
            fi
            
            convert_datetime_format "$datetime_input" "$from_format" "$to_format"
            ;;
        "schedule")
            local operation="$2"
            local target_datetime="$3"
            local schedule_time="$4"
            local policy="${5:-business_hours}"
            
            if [[ -z "$operation" || -z "$target_datetime" || -z "$schedule_time" ]]; then
                echo "Usage : $0 schedule <operation> <target_datetime> <schedule_time> [policy]"
                echo "Opérations : set_datetime, sync_time, backup_datetime"
                return 1
            fi
            
            schedule_datetime_operation "$operation" "$target_datetime" "$schedule_time" "$policy"
            ;;
        "monitor")
            local check_type="${2:-basic}"
            
            monitor_datetime_integrity "$check_type"
            ;;
        "backup")
            local backup_type="${2:-full}"
            
            backup_datetime_settings "$backup_type"
            ;;
        "report")
            local report_type="${2:-summary}"
            
            generate_datetime_report "$report_type"
            ;;
        "help"|*)
            echo "$SCRIPT_NAME v$VERSION"
            echo "Gestion Entreprise Date et Heure"
            echo ""
            echo "Usage : $0 <action> [options]"
            echo ""
            echo "Actions :"
            echo "  status [format]                         - Afficher informations date/heure actuelles"
            echo "  set <datetime> [format] [policy] [force] - Définir date/heure avec validation"
            echo "  validate <datetime> [format]            - Valider format date/heure"
            echo "  convert <datetime> <from> <to>          - Convertir entre formats"
            echo "  schedule <op> <datetime> <time> [policy] - Planifier opérations date/heure"
            echo "  monitor [type]                          - Surveiller intégrité date/heure"
            echo "  backup [type]                           - Sauvegarder paramètres date/heure"
            echo "  report [type]                           - Générer rapports date/heure"
            echo "  help                                    - Afficher ce message d'aide"
            echo ""
            echo "Formats Date/Heure :"
            for format in "${!DATE_FORMATS[@]}"; do
                echo "  $format: ${DATE_FORMATS[$format]}"
            done
            echo ""
            echo "Politiques de Planification :"
            for policy in "${!SCHEDULING_POLICIES[@]}"; do
                echo "  $policy: ${SCHEDULING_POLICIES[$policy]}"
            done
            echo ""
            echo "Types de Surveillance :"
            echo "  basic       - Vérifications intégrité de base"
            echo "  comprehensive - Surveillance étendue et validation"
            echo ""
            echo "Types de Rapport :"
            echo "  summary     - Aperçu date/heure (défaut)"
            echo "  compliance  - Évaluation conformité"
            echo "  audit       - Piste d'audit complète"
            echo ""
            echo "Exemples :"
            echo "  $0 set 120614302023.00                 - Définir date/heure en format Roundfleet"
            echo "  $0 set '2023-12-06T14:30:00' iso8601   - Définir avec format ISO 8601"
            echo "  $0 convert 120614302023.00 roundfleet iso8601 - Convertir formats"
            echo "  $0 schedule set_datetime 010108002024.00 '09:00 tomorrow' - Planifier opération"
            echo "  $0 monitor comprehensive               - Vérification intégrité complète"
            echo ""
            echo "Fonctionnalités :"
            echo "  • Gestion date/heure niveau entreprise"
            echo "  • Support et conversion formats multiples"
            echo "  • Application heures d'ouverture et politiques"
            echo "  • Validation complète et vérifications sécurité"
            echo "  • Journalisation audit et suivi conformité"
            echo "  • Opérations planifiées et automation"
            echo "  • Surveillance temps réel et vérifications intégrité"
            ;;
    esac
}

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

Commandes de Référence Rapide

Opérations Date/Heure de Base

# Vérifier statut date/heure actuel
./datetime_manager.sh status

# Définir date/heure avec format Roundfleet (6 déc 2023 14h30)
./datetime_manager.sh set 120614302023.00

# Définir avec format ISO 8601
./datetime_manager.sh set "2023-12-06T14:30:00" iso8601

# Valider format date/heure avant définition
./datetime_manager.sh validate 120614302023.00 roundfleet

Conversion de Format

# Convertir Roundfleet vers ISO 8601
./datetime_manager.sh convert 120614302023.00 roundfleet iso8601

# Convertir ISO 8601 vers format lisible
./datetime_manager.sh convert "2023-12-06T14:30:00" iso8601 readable

# Convertir vers timestamp Unix
./datetime_manager.sh convert 120614302023.00 roundfleet unix

Opérations Entreprise

# Définir avec application politique heures d'ouverture
./datetime_manager.sh set 120614302023.00 roundfleet business_hours

# Forcer définition en dehors heures d'ouverture
./datetime_manager.sh set 120614302023.00 roundfleet business_hours true

# Planifier opération date/heure future
./datetime_manager.sh schedule set_datetime 010108002024.00 "09:00 tomorrow"

Surveillance et Conformité

# Surveillance intégrité de base
./datetime_manager.sh monitor basic

# Surveillance complète
./datetime_manager.sh monitor comprehensive

# Générer rapport conformité
./datetime_manager.sh report compliance

# Sauvegarder paramètres actuels
./datetime_manager.sh backup full

Exemples d'Intégration

Intégration JAMF Pro

#!/bin/bash

# Script JAMF Pro pour gestion date/heure
# Paramètres : $4 = target_datetime, $5 = format, $6 = policy, $7 = force

TARGET_DATETIME="$4"
FORMAT="${5:-roundfleet}"
POLICY="${6:-business_hours}"
FORCE="$7"

# Télécharger gestionnaire datetime s'il n'est pas présent
if [[ ! -f "/usr/local/bin/roundfleet_datetime_manager.sh" ]]; then
    curl -o "/usr/local/bin/roundfleet_datetime_manager.sh" \
         "https://scripts.roundfleet.com/datetime_manager.sh"
    chmod +x "/usr/local/bin/roundfleet_datetime_manager.sh"
fi

# Définir date/heure avec contrôles entreprise
if [[ -n "$TARGET_DATETIME" ]]; then
    /usr/local/bin/roundfleet_datetime_manager.sh set \
        "$TARGET_DATETIME" "$FORMAT" "$POLICY" "$FORCE"
    
    # Rapporter statut à JAMF
    if [[ $? -eq 0 ]]; then
        echo "Date/heure définie avec succès : $TARGET_DATETIME"
        exit 0
    else
        echo "Définition date/heure échouée : $TARGET_DATETIME"
        exit 1
    fi
else
    echo "Aucune date/heure cible fournie"
    exit 1
fi

Meilleures Pratiques

  1. Valider toutes entrées date/heure avant application des changements
  2. Utiliser politiques heures d'ouverture pour empêcher changements non autorisés
  3. Implémenter sauvegarde et restauration pour changements temps critiques
  4. Surveiller dérive temporelle en continu avec alertes automatisées
  5. Maintenir logs d'audit complets pour conformité
  6. Tester conversions formats avant déploiement production
  7. Coordonner avec équipes réseau pour synchronisation temporelle
  8. Documenter toutes opérations planifiées pour conscience opérationnelle

Ce système de gestion date et heure entreprise fournit un contrôle date/heure complet avec validation, planification, suivi de conformité et capacités d'automation de niveau entreprise pour une gestion temporelle Roundfleet efficace.