Tutorial

Neue Updates und Verbesserungen zu Macfleet.

Wichtiger Hinweis

Die in diesen Tutorials bereitgestellten Codebeispiele und Skripte dienen nur zu Bildungszwecken. Macfleet ist nicht verantwortlich für Probleme, Schäden oder Sicherheitslücken, die durch die Verwendung, Änderung oder Implementierung dieser Beispiele entstehen können. Überprüfen und testen Sie Code immer in einer sicheren Umgebung, bevor Sie ihn in Produktionssystemen verwenden.

App Store Inventory Management on macOS

Efficiently manage and track App Store applications across your MacFleet devices with comprehensive inventory tools. This tutorial covers app discovery, license compliance, usage analytics, and enterprise-grade reporting for Mac App Store applications.

Understanding App Store App Management

App Store applications on macOS contain unique identifiers and receipts that enable enterprise tracking:

Core Components

  • MAS Receipts - Digital receipts stored in app bundles for verification
  • Bundle Identifiers - Unique app identification across the App Store ecosystem
  • Version Tracking - App version management and update compliance
  • License Management - Enterprise license allocation and compliance monitoring
  • Usage Analytics - Application usage patterns and adoption metrics

Enterprise Benefits

  • Software Asset Management - Complete inventory of App Store applications
  • License Compliance - Track enterprise app licenses and allocations
  • Security Monitoring - Identify unauthorized or non-compliant applications
  • Cost Optimization - Optimize App Store spending and license utilization
  • Policy Enforcement - Ensure compliance with enterprise app policies

Basic App Store App Discovery

Simple App Store App List

#!/bin/bash

# Basic App Store app discovery
echo "📱 Discovering App Store Applications"
echo "===================================="
echo ""

app_store_apps=$(find /Applications -path '*Contents/_MASReceipt/receipt' -maxdepth 4 -print | \
    sed 's#.app/Contents/_MASReceipt/receipt#.app#g; s#/Applications/##')

if [[ -n "$app_store_apps" ]]; then
    echo "App Store Applications Found:"
    echo "$app_store_apps" | sort
    
    local app_count=$(echo "$app_store_apps" | wc -l | xargs)
    echo ""
    echo "Total App Store apps: $app_count"
else
    echo "No App Store applications found in /Applications"
fi

Enhanced App Discovery with Details

#!/bin/bash

# Comprehensive App Store app discovery with metadata
discover_app_store_apps() {
    echo "🔍 Comprehensive App Store App Discovery"
    echo "======================================="
    echo ""
    
    local apps_found=0
    local total_size=0
    
    # Find all App Store apps with receipts
    while IFS= read -r -d '' app_path; do
        if [[ -n "$app_path" ]]; then
            local app_name=$(basename "$app_path" .app)
            local app_bundle_id=""
            local app_version=""
            local app_size=""
            local install_date=""
            
            # Get bundle identifier
            if [[ -f "$app_path/Contents/Info.plist" ]]; then
                app_bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2>/dev/null || echo "Unknown")
                app_version=$(defaults read "$app_path/Contents/Info.plist" CFBundleShortVersionString 2>/dev/null || echo "Unknown")
            fi
            
            # Get app size
            if [[ -d "$app_path" ]]; then
                app_size=$(du -sh "$app_path" 2>/dev/null | cut -f1 || echo "Unknown")
                # Convert size to bytes for totaling
                local size_bytes=$(du -s "$app_path" 2>/dev/null | cut -f1 || echo "0")
                total_size=$((total_size + size_bytes))
            fi
            
            # Get installation/modification date
            if [[ -f "$app_path/Contents/_MASReceipt/receipt" ]]; then
                install_date=$(stat -f "%Sm" -t "%Y-%m-%d %H:%M:%S" "$app_path/Contents/_MASReceipt/receipt" 2>/dev/null || echo "Unknown")
            fi
            
            echo "App: $app_name"
            echo "  Bundle ID: $app_bundle_id"
            echo "  Version: $app_version"
            echo "  Size: $app_size"
            echo "  Install Date: $install_date"
            echo "  Path: $app_path"
            echo ""
            
            ((apps_found++))
        fi
    done < <(find /Applications -name "*.app" -path '*Contents/_MASReceipt/receipt' -exec dirname {} \; 2>/dev/null | \
             sed 's|/Contents/_MASReceipt||' | sort -u | tr '\n' '\0')
    
    # Summary statistics
    echo "=== Discovery Summary ==="
    echo "Total App Store apps found: $apps_found"
    echo "Total disk space used: $(echo "scale=2; $total_size / 1024 / 1024" | bc 2>/dev/null || echo "Unknown") MB"
    echo "Average app size: $(echo "scale=2; $total_size / $apps_found / 1024 / 1024" | bc 2>/dev/null || echo "Unknown") MB"
    echo "Scan completed: $(date)"
    
    return $apps_found
}

# Execute discovery
discover_app_store_apps

Enterprise App Inventory Management

Comprehensive App Inventory Script

#!/bin/bash

# Enterprise App Store inventory management
generate_app_store_inventory() {
    local output_format="${1:-json}"
    local include_metadata="${2:-true}"
    local export_path="${3:-/tmp}"
    
    echo "📊 Generating Enterprise App Store Inventory"
    echo "==========================================="
    echo "Output format: $output_format"
    echo "Include metadata: $include_metadata"
    echo ""
    
    local timestamp=$(date +"%Y%m%d_%H%M%S")
    local hostname=$(hostname)
    local report_file="$export_path/app_store_inventory_${hostname}_${timestamp}"
    
    # System information
    local device_info=$(system_profiler SPHardwareDataType | grep -E "(Model Name|Serial Number)" | \
                        awk -F': ' '{print $2}' | xargs | tr ' ' '_')
    local macos_version=$(sw_vers -productVersion)
    local current_user=$(stat -f%Su /dev/console 2>/dev/null || echo "$USER")
    
    # Initialize inventory data structure
    local inventory_data=""
    local app_count=0
    local total_size_bytes=0
    
    echo "Scanning applications..."
    
    # Discover all App Store applications
    while IFS= read -r -d '' app_path; do
        if [[ -d "$app_path" && -f "$app_path/Contents/_MASReceipt/receipt" ]]; then
            local app_name=$(basename "$app_path" .app)
            local bundle_id=""
            local version=""
            local build_version=""
            local size_bytes=0
            local size_human=""
            local install_date=""
            local last_modified=""
            local executable_path=""
            local app_category=""
            local developer_name=""
            local app_store_url=""
            
            # Extract app metadata
            if [[ -f "$app_path/Contents/Info.plist" ]]; then
                bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2>/dev/null || echo "unknown")
                version=$(defaults read "$app_path/Contents/Info.plist" CFBundleShortVersionString 2>/dev/null || echo "unknown")
                build_version=$(defaults read "$app_path/Contents/Info.plist" CFBundleVersion 2>/dev/null || echo "unknown")
                executable_path=$(defaults read "$app_path/Contents/Info.plist" CFBundleExecutable 2>/dev/null || echo "unknown")
                app_category=$(defaults read "$app_path/Contents/Info.plist" LSApplicationCategoryType 2>/dev/null || echo "unknown")
            fi
            
            # Get app size
            size_bytes=$(du -sk "$app_path" 2>/dev/null | cut -f1 || echo "0")
            size_bytes=$((size_bytes * 1024))  # Convert KB to bytes
            size_human=$(du -sh "$app_path" 2>/dev/null | cut -f1 || echo "unknown")
            total_size_bytes=$((total_size_bytes + size_bytes))
            
            # Get timestamps
            install_date=$(stat -f "%Sm" -t "%Y-%m-%d %H:%M:%S" "$app_path/Contents/_MASReceipt/receipt" 2>/dev/null || echo "unknown")
            last_modified=$(stat -f "%Sm" -t "%Y-%m-%d %H:%M:%S" "$app_path" 2>/dev/null || echo "unknown")
            
            # Try to extract additional metadata if requested
            if [[ "$include_metadata" == "true" ]]; then
                # Look for team identifier
                local team_id=""
                if command -v codesign >/dev/null 2>&1; then
                    team_id=$(codesign -dv "$app_path" 2>&1 | grep "TeamIdentifier" | awk -F'=' '{print $2}' || echo "unknown")
                fi
                
                # Check if app is running
                local is_running="false"
                if pgrep -f "$app_name" >/dev/null 2>&1; then
                    is_running="true"
                fi
                
                # Get app permissions (simplified check)
                local has_camera_permission="unknown"
                local has_microphone_permission="unknown"
                local has_location_permission="unknown"
                
                if [[ -f "$app_path/Contents/Info.plist" ]]; then
                    if defaults read "$app_path/Contents/Info.plist" NSCameraUsageDescription >/dev/null 2>&1; then
                        has_camera_permission="requested"
                    else
                        has_camera_permission="not_requested"
                    fi
                    
                    if defaults read "$app_path/Contents/Info.plist" NSMicrophoneUsageDescription >/dev/null 2>&1; then
                        has_microphone_permission="requested"
                    else
                        has_microphone_permission="not_requested"
                    fi
                    
                    if defaults read "$app_path/Contents/Info.plist" NSLocationUsageDescription >/dev/null 2>&1; then
                        has_location_permission="requested"
                    else
                        has_location_permission="not_requested"
                    fi
                fi
            fi
            
            # Format output based on requested format
            case "$output_format" in
                "json")
                    local app_json="{
                        \"app_name\": \"$app_name\",
                        \"bundle_id\": \"$bundle_id\",
                        \"version\": \"$version\",
                        \"build_version\": \"$build_version\",
                        \"size_bytes\": $size_bytes,
                        \"size_human\": \"$size_human\",
                        \"install_date\": \"$install_date\",
                        \"last_modified\": \"$last_modified\",
                        \"app_path\": \"$app_path\",
                        \"executable_path\": \"$executable_path\",
                        \"category\": \"$app_category\""
                    
                    if [[ "$include_metadata" == "true" ]]; then
                        app_json="$app_json,
                        \"team_id\": \"$team_id\",
                        \"is_running\": $is_running,
                        \"camera_permission\": \"$has_camera_permission\",
                        \"microphone_permission\": \"$has_microphone_permission\",
                        \"location_permission\": \"$has_location_permission\""
                    fi
                    
                    app_json="$app_json}"
                    
                    if [[ $app_count -gt 0 ]]; then
                        inventory_data="$inventory_data,$app_json"
                    else
                        inventory_data="$app_json"
                    fi
                    ;;
                    
                "csv")
                    if [[ $app_count -eq 0 ]]; then
                        # CSV header
                        if [[ "$include_metadata" == "true" ]]; then
                            inventory_data="App Name,Bundle ID,Version,Build Version,Size (Bytes),Size (Human),Install Date,Last Modified,App Path,Category,Team ID,Is Running,Camera Permission,Microphone Permission,Location Permission"
                        else
                            inventory_data="App Name,Bundle ID,Version,Build Version,Size (Bytes),Size (Human),Install Date,Last Modified,App Path,Category"
                        fi
                    fi
                    
                    local csv_line="\"$app_name\",\"$bundle_id\",\"$version\",\"$build_version\",$size_bytes,\"$size_human\",\"$install_date\",\"$last_modified\",\"$app_path\",\"$app_category\""
                    
                    if [[ "$include_metadata" == "true" ]]; then
                        csv_line="$csv_line,\"$team_id\",$is_running,\"$has_camera_permission\",\"$has_microphone_permission\",\"$has_location_permission\""
                    fi
                    
                    inventory_data="$inventory_data\n$csv_line"
                    ;;
                    
                "text"|*)
                    inventory_data="$inventory_data
App: $app_name
  Bundle ID: $bundle_id
  Version: $version ($build_version)
  Size: $size_human ($size_bytes bytes)
  Install Date: $install_date
  Last Modified: $last_modified
  Path: $app_path
  Category: $app_category"
                    
                    if [[ "$include_metadata" == "true" ]]; then
                        inventory_data="$inventory_data
  Team ID: $team_id
  Currently Running: $is_running
  Camera Permission: $has_camera_permission
  Microphone Permission: $has_microphone_permission
  Location Permission: $has_location_permission"
                    fi
                    
                    inventory_data="$inventory_data
"
                    ;;
            esac
            
            ((app_count++))
        fi
    done < <(find /Applications -name "*.app" -type d -print0 2>/dev/null)
    
    # Generate final report
    local total_size_human=$(echo "scale=2; $total_size_bytes / 1024 / 1024 / 1024" | bc 2>/dev/null || echo "unknown")
    
    case "$output_format" in
        "json")
            local final_report="{
                \"report_metadata\": {
                    \"timestamp\": \"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\",
                    \"hostname\": \"$hostname\",
                    \"device_info\": \"$device_info\",
                    \"macos_version\": \"$macos_version\",
                    \"current_user\": \"$current_user\",
                    \"total_apps\": $app_count,
                    \"total_size_bytes\": $total_size_bytes,
                    \"total_size_gb\": \"$total_size_human GB\"
                },
                \"applications\": [$inventory_data]
            }"
            
            echo "$final_report" > "${report_file}.json"
            echo "✅ JSON report saved to: ${report_file}.json"
            ;;
            
        "csv")
            echo -e "$inventory_data" > "${report_file}.csv"
            echo "✅ CSV report saved to: ${report_file}.csv"
            ;;
            
        "text"|*)
            {
                echo "MacFleet App Store Inventory Report"
                echo "=================================="
                echo "Generated: $(date)"
                echo "Hostname: $hostname"
                echo "Device: $device_info"
                echo "macOS Version: $macos_version"
                echo "Current User: $current_user"
                echo "Total Apps: $app_count"
                echo "Total Size: $total_size_human GB"
                echo ""
                echo "Applications:"
                echo "============"
                echo "$inventory_data"
            } > "${report_file}.txt"
            echo "✅ Text report saved to: ${report_file}.txt"
            ;;
    esac
    
    # Summary output
    echo ""
    echo "=== Inventory Summary ==="
    echo "Apps discovered: $app_count"
    echo "Total size: $total_size_human GB"
    echo "Report format: $output_format"
    echo "Report location: $report_file"
    echo ""
    
    return $app_count
}

# Usage examples
echo "📊 Enterprise App Store Inventory Management"
echo ""
echo "1. Generate JSON inventory with metadata"
generate_app_store_inventory "json" "true" "/tmp"
echo ""

echo "2. Generate CSV inventory (basic)"
generate_app_store_inventory "csv" "false" "/tmp"
echo ""

echo "3. Generate text inventory with metadata"
generate_app_store_inventory "text" "true" "/tmp"

App Compliance and Security Analysis

#!/bin/bash

# App Store app compliance and security analysis
analyze_app_compliance() {
    echo "🔒 App Store Application Compliance Analysis"
    echo "==========================================="
    echo ""
    
    local compliance_issues=0
    local security_concerns=0
    local policy_violations=0
    
    # Define enterprise policy rules
    local blocked_apps=("Games" "Social Media" "Dating")  # Example categories
    local required_apps=("Microsoft Word" "Microsoft Excel" "Slack")  # Example required apps
    local max_app_age_days=365  # Apps older than 1 year flagged
    local min_security_permissions=("Camera" "Microphone" "Location")
    
    echo "Analyzing App Store applications for compliance..."
    echo ""
    
    # Track required apps
    local found_required_apps=()
    local missing_required_apps=()
    
    # Analyze each App Store application
    while IFS= read -r -d '' app_path; do
        if [[ -d "$app_path" && -f "$app_path/Contents/_MASReceipt/receipt" ]]; then
            local app_name=$(basename "$app_path" .app)
            local bundle_id=""
            local version=""
            local category=""
            local install_date=""
            local days_since_install=""
            
            echo "Analyzing: $app_name"
            
            # Get app metadata
            if [[ -f "$app_path/Contents/Info.plist" ]]; then
                bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2>/dev/null || echo "unknown")
                version=$(defaults read "$app_path/Contents/Info.plist" CFBundleShortVersionString 2>/dev/null || echo "unknown")
                category=$(defaults read "$app_path/Contents/Info.plist" LSApplicationCategoryType 2>/dev/null || echo "unknown")
            fi
            
            # Get install date and calculate age
            if [[ -f "$app_path/Contents/_MASReceipt/receipt" ]]; then
                install_date=$(stat -f "%Sm" -t "%Y-%m-%d" "$app_path/Contents/_MASReceipt/receipt" 2>/dev/null || echo "unknown")
                if [[ "$install_date" != "unknown" ]]; then
                    local install_epoch=$(date -j -f "%Y-%m-%d" "$install_date" "+%s" 2>/dev/null || echo "0")
                    local current_epoch=$(date "+%s")
                    days_since_install=$(( (current_epoch - install_epoch) / 86400 ))
                fi
            fi
            
            # Check against blocked apps/categories
            local is_blocked=false
            for blocked_category in "${blocked_apps[@]}"; do
                if [[ "$category" =~ "$blocked_category" ]] || [[ "$app_name" =~ "$blocked_category" ]]; then
                    echo "  ❌ POLICY VIOLATION: App category '$category' is blocked"
                    is_blocked=true
                    ((policy_violations++))
                    break
                fi
            done
            
            # Check app age
            if [[ -n "$days_since_install" && "$days_since_install" -gt "$max_app_age_days" ]]; then
                echo "  ⚠️ COMPLIANCE: App is older than $max_app_age_days days ($days_since_install days)"
                ((compliance_issues++))
            fi
            
            # Check for security permissions
            local permission_count=0
            if [[ -f "$app_path/Contents/Info.plist" ]]; then
                if defaults read "$app_path/Contents/Info.plist" NSCameraUsageDescription >/dev/null 2>&1; then
                    echo "  🔒 SECURITY: App requests camera access"
                    ((permission_count++))
                fi
                
                if defaults read "$app_path/Contents/Info.plist" NSMicrophoneUsageDescription >/dev/null 2>&1; then
                    echo "  🔒 SECURITY: App requests microphone access"
                    ((permission_count++))
                fi
                
                if defaults read "$app_path/Contents/Info.plist" NSLocationUsageDescription >/dev/null 2>&1; then
                    echo "  🔒 SECURITY: App requests location access"
                    ((permission_count++))
                fi
                
                if [[ "$permission_count" -gt 2 ]]; then
                    echo "  ⚠️ SECURITY CONCERN: App requests multiple sensitive permissions"
                    ((security_concerns++))
                fi
            fi
            
            # Check against required apps
            for required_app in "${required_apps[@]}"; do
                if [[ "$app_name" =~ "$required_app" ]]; then
                    found_required_apps+=("$required_app")
                fi
            done
            
            # Code signing verification
            if command -v codesign >/dev/null 2>&1; then
                if ! codesign -v "$app_path" >/dev/null 2>&1; then
                    echo "  ❌ SECURITY: Invalid code signature"
                    ((security_concerns++))
                fi
            fi
            
            echo "  ✅ Analysis complete"
            echo ""
        fi
    done < <(find /Applications -name "*.app" -type d -print0 2>/dev/null)
    
    # Check for missing required apps
    for required_app in "${required_apps[@]}"; do
        local found=false
        for found_app in "${found_required_apps[@]}"; do
            if [[ "$found_app" == "$required_app" ]]; then
                found=true
                break
            fi
        done
        
        if [[ "$found" == "false" ]]; then
            missing_required_apps+=("$required_app")
        fi
    done
    
    # Generate compliance report
    echo "=== Compliance Analysis Report ==="
    echo "Policy violations: $policy_violations"
    echo "Compliance issues: $compliance_issues"
    echo "Security concerns: $security_concerns"
    echo ""
    
    if [[ ${#missing_required_apps[@]} -gt 0 ]]; then
        echo "Missing required applications:"
        for missing_app in "${missing_required_apps[@]}"; do
            echo "  - $missing_app"
        done
        echo ""
    fi
    
    if [[ ${#found_required_apps[@]} -gt 0 ]]; then
        echo "Found required applications:"
        for found_app in "${found_required_apps[@]}"; do
            echo "  ✅ $found_app"
        done
        echo ""
    fi
    
    # Overall compliance score
    local total_issues=$((policy_violations + compliance_issues + security_concerns + ${#missing_required_apps[@]}))
    if [[ "$total_issues" -eq 0 ]]; then
        echo "🎉 COMPLIANCE STATUS: FULLY COMPLIANT"
    elif [[ "$total_issues" -le 3 ]]; then
        echo "⚠️ COMPLIANCE STATUS: MINOR ISSUES ($total_issues issues)"
    else
        echo "❌ COMPLIANCE STATUS: MAJOR ISSUES ($total_issues issues)"
    fi
    
    return $total_issues
}

# Execute compliance analysis
analyze_app_compliance

Fleet Management and Reporting

Enterprise Fleet App Management

#!/bin/bash

# Enterprise fleet-wide App Store management
manage_fleet_app_inventory() {
    local operation="${1:-discover}"  # discover, report, compliance, update
    local fleet_config_file="${2:-/etc/macfleet/app_config.json}"
    local output_dir="${3:-/var/log/macfleet}"
    
    echo "🚀 MacFleet Enterprise App Store Management"
    echo "=========================================="
    echo "Operation: $operation"
    echo "Config file: $fleet_config_file"
    echo "Output directory: $output_dir"
    echo ""
    
    # Ensure output directory exists
    mkdir -p "$output_dir"
    
    # Device identification
    local device_id=$(system_profiler SPHardwareDataType | grep "Serial Number" | awk -F': ' '{print $2}' | xargs)
    local device_name=$(hostname)
    local timestamp=$(date +"%Y%m%d_%H%M%S")
    local report_file="$output_dir/fleet_app_report_${device_id}_${timestamp}.json"
    
    case "$operation" in
        "discover")
            echo "🔍 Fleet App Discovery Mode"
            echo "==========================="
            
            # Generate comprehensive app inventory
            local app_data=$(generate_comprehensive_app_data)
            
            # Create fleet report
            local fleet_report="{
                \"device_info\": {
                    \"device_id\": \"$device_id\",
                    \"device_name\": \"$device_name\",
                    \"timestamp\": \"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\",
                    \"macos_version\": \"$(sw_vers -productVersion)\",
                    \"current_user\": \"$(stat -f%Su /dev/console 2>/dev/null || echo "$USER")\"
                },
                \"app_inventory\": $app_data,
                \"operation\": \"fleet_discovery\"
            }"
            
            echo "$fleet_report" > "$report_file"
            echo "✅ Fleet discovery report saved: $report_file"
            ;;
            
        "compliance")
            echo "📋 Fleet Compliance Check"
            echo "========================="
            
            # Run compliance analysis
            local compliance_result=$(analyze_fleet_compliance)
            
            # Create compliance report
            local compliance_report="{
                \"device_info\": {
                    \"device_id\": \"$device_id\",
                    \"device_name\": \"$device_name\",
                    \"timestamp\": \"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\"
                },
                \"compliance_results\": $compliance_result,
                \"operation\": \"compliance_check\"
            }"
            
            echo "$compliance_report" > "$report_file"
            echo "✅ Compliance report saved: $report_file"
            ;;
            
        "report")
            echo "📊 Fleet Reporting Mode"
            echo "======================"
            
            # Generate all report formats
            echo "Generating comprehensive fleet reports..."
            
            generate_app_store_inventory "json" "true" "$output_dir"
            generate_app_store_inventory "csv" "true" "$output_dir"
            generate_app_store_inventory "text" "true" "$output_dir"
            
            echo "✅ All fleet reports generated in: $output_dir"
            ;;
            
        "update")
            echo "🔄 Fleet Update Check"
            echo "===================="
            
            # Check for app updates
            check_app_store_updates
            ;;
            
        *)
            echo "❌ Unknown operation: $operation"
            echo "Available operations: discover, report, compliance, update"
            return 1
            ;;
    esac
    
    return 0
}

# Helper function to generate comprehensive app data
generate_comprehensive_app_data() {
    local apps_json="["
    local first_app=true
    
    while IFS= read -r -d '' app_path; do
        if [[ -d "$app_path" && -f "$app_path/Contents/_MASReceipt/receipt" ]]; then
            local app_name=$(basename "$app_path" .app)
            local bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2>/dev/null || echo "unknown")
            local version=$(defaults read "$app_path/Contents/Info.plist" CFBundleShortVersionString 2>/dev/null || echo "unknown")
            local size_bytes=$(du -sk "$app_path" 2>/dev/null | cut -f1 || echo "0")
            size_bytes=$((size_bytes * 1024))
            
            if [[ "$first_app" == "false" ]]; then
                apps_json="$apps_json,"
            fi
            
            apps_json="$apps_json{
                \"name\": \"$app_name\",
                \"bundle_id\": \"$bundle_id\",
                \"version\": \"$version\",
                \"size_bytes\": $size_bytes,
                \"path\": \"$app_path\"
            }"
            
            first_app=false
        fi
    done < <(find /Applications -name "*.app" -type d -print0 2>/dev/null)
    
    apps_json="$apps_json]"
    echo "$apps_json"
}

# Check for App Store updates
check_app_store_updates() {
    echo "Checking for available App Store updates..."
    
    # Use softwareupdate to check for available updates
    local updates_available=$(softwareupdate -l 2>/dev/null | grep -i "app store" || echo "")
    
    if [[ -n "$updates_available" ]]; then
        echo "📦 App Store updates available:"
        echo "$updates_available"
    else
        echo "✅ All App Store apps are up to date"
    fi
    
    # Also check Mac App Store directly if available
    if command -v mas >/dev/null 2>&1; then
        echo ""
        echo "Checking with mas-cli..."
        mas outdated 2>/dev/null || echo "mas-cli not available or no updates found"
    fi
}

# Usage examples
echo "Fleet Management Examples:"
echo "========================="
echo ""

echo "1. Discovery mode:"
manage_fleet_app_inventory "discover"
echo ""

echo "2. Compliance check:"
manage_fleet_app_inventory "compliance"
echo ""

echo "3. Generate all reports:"
manage_fleet_app_inventory "report"

App Store Connect Integration

Enterprise App Management with App Store Connect

#!/bin/bash

# Enterprise App Store Connect integration for license management
manage_app_store_licenses() {
    echo "📄 App Store Connect License Management"
    echo "======================================"
    echo ""
    
    # Note: This requires enterprise App Store Connect access
    # and proper API credentials configuration
    
    local license_report_file="/tmp/app_store_licenses_$(date +%Y%m%d_%H%M%S).json"
    
    echo "Analyzing App Store app licensing..."
    echo ""
    
    # Track apps that may require business licenses
    local business_apps=()
    local consumer_apps=()
    local unknown_apps=()
    
    while IFS= read -r -d '' app_path; do
        if [[ -d "$app_path" && -f "$app_path/Contents/_MASReceipt/receipt" ]]; then
            local app_name=$(basename "$app_path" .app)
            local bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2>/dev/null || echo "unknown")
            
            # Categories that typically require business licenses
            local business_categories=("Developer Tools" "Business" "Productivity" "Graphics & Design")
            local is_business_app=false
            
            # Check if app is business-oriented
            if [[ -f "$app_path/Contents/Info.plist" ]]; then
                local category=$(defaults read "$app_path/Contents/Info.plist" LSApplicationCategoryType 2>/dev/null || echo "unknown")
                
                for biz_cat in "${business_categories[@]}"; do
                    if [[ "$category" =~ "$biz_cat" ]]; then
                        is_business_app=true
                        break
                    fi
                done
                
                # Also check common business app patterns
                if [[ "$app_name" =~ (Microsoft|Adobe|Slack|Zoom|Teams|Office) ]]; then
                    is_business_app=true
                fi
            fi
            
            if [[ "$is_business_app" == "true" ]]; then
                business_apps+=("$app_name ($bundle_id)")
                echo "💼 Business app detected: $app_name"
            else
                # Consumer apps that might be acceptable
                if [[ "$app_name" =~ (Calculator|TextEdit|Safari|Mail|Photos) ]]; then
                    consumer_apps+=("$app_name ($bundle_id)")
                    echo "👤 Consumer app: $app_name"
                else
                    unknown_apps+=("$app_name ($bundle_id)")
                    echo "❓ Unknown category: $app_name"
                fi
            fi
        fi
    done < <(find /Applications -name "*.app" -type d -print0 2>/dev/null)
    
    # Generate license compliance report
    {
        echo "{"
        echo "  \"license_analysis\": {"
        echo "    \"timestamp\": \"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\","
        echo "    \"device_id\": \"$(system_profiler SPHardwareDataType | grep "Serial Number" | awk -F': ' '{print $2}' | xargs)\","
        echo "    \"business_apps\": ["
        
        local first=true
        for app in "${business_apps[@]}"; do
            if [[ "$first" == "false" ]]; then echo ","; fi
            echo "      \"$app\""
            first=false
        done
        
        echo "    ],"
        echo "    \"consumer_apps\": ["
        
        first=true
        for app in "${consumer_apps[@]}"; do
            if [[ "$first" == "false" ]]; then echo ","; fi
            echo "      \"$app\""
            first=false
        done
        
        echo "    ],"
        echo "    \"unknown_apps\": ["
        
        first=true
        for app in "${unknown_apps[@]}"; do
            if [[ "$first" == "false" ]]; then echo ","; fi
            echo "      \"$app\""
            first=false
        done
        
        echo "    ],"
        echo "    \"summary\": {"
        echo "      \"total_business_apps\": ${#business_apps[@]},"
        echo "      \"total_consumer_apps\": ${#consumer_apps[@]},"
        echo "      \"total_unknown_apps\": ${#unknown_apps[@]},"
        echo "      \"requires_license_review\": $([[ ${#business_apps[@]} -gt 0 || ${#unknown_apps[@]} -gt 0 ]] && echo "true" || echo "false")"
        echo "    }"
        echo "  }"
        echo "}"
    } > "$license_report_file"
    
    echo ""
    echo "=== License Analysis Summary ==="
    echo "Business apps found: ${#business_apps[@]}"
    echo "Consumer apps found: ${#consumer_apps[@]}"
    echo "Unknown apps found: ${#unknown_apps[@]}"
    echo ""
    
    if [[ ${#business_apps[@]} -gt 0 || ${#unknown_apps[@]} -gt 0 ]]; then
        echo "⚠️ License review required for business/unknown applications"
        echo "📄 License report saved: $license_report_file"
    else
        echo "✅ No business licensing concerns detected"
    fi
    
    return 0
}

# Execute license management
manage_app_store_licenses

Important Notes

Enterprise Management Features

  • Comprehensive Inventory - Complete App Store application discovery and tracking
  • Compliance Monitoring - Policy enforcement and security analysis
  • License Management - Enterprise App Store Connect integration
  • Fleet Reporting - Multi-format reporting (JSON, CSV, text) with metadata
  • Security Analysis - Permission auditing and code signature verification
  • Update Management - App Store update detection and management

App Store Receipt System

  • MAS Receipts - Digital proof of App Store purchase located in Contents/_MASReceipt/receipt
  • Bundle Identification - Unique app tracking via CFBundleIdentifier
  • Version Tracking - App version and build number management
  • Installation Metadata - Install date and modification tracking
  • License Verification - Enterprise license compliance and allocation

Security and Compliance

  • Permission Auditing - Camera, microphone, location access monitoring
  • Code Signature Verification - Validate app integrity and authenticity
  • Policy Enforcement - Blocked app categories and required app validation
  • Age Analysis - Identify outdated applications requiring updates
  • Team Identifier Tracking - Developer team identification for enterprise policies

Usage Examples

# Basic App Store app discovery
find /Applications -path '*Contents/_MASReceipt/receipt' -maxdepth 4 -print | \
    sed 's#.app/Contents/_MASReceipt/receipt#.app#g; s#/Applications/##'

# Enterprise inventory with JSON output
generate_app_store_inventory "json" "true" "/tmp"

# Compliance analysis
analyze_app_compliance

# Fleet management - discovery mode
manage_fleet_app_inventory "discover"

# License management
manage_app_store_licenses

Tutorial

Neue Updates und Verbesserungen zu Macfleet.

Konfiguration eines GitHub Actions Runners auf einem Mac Mini (Apple Silicon)

GitHub Actions Runner

GitHub Actions ist eine leistungsstarke CI/CD-Plattform, die es Ihnen ermöglicht, Ihre Software-Entwicklungsworkflows zu automatisieren. Während GitHub gehostete Runner anbietet, bieten selbst-gehostete Runner erhöhte Kontrolle und Anpassung für Ihr CI/CD-Setup. Dieses Tutorial führt Sie durch die Einrichtung, Konfiguration und Verbindung eines selbst-gehosteten Runners auf einem Mac mini zur Ausführung von macOS-Pipelines.

Voraussetzungen

Bevor Sie beginnen, stellen Sie sicher, dass Sie haben:

  • Einen Mac mini (registrieren Sie sich bei Macfleet)
  • Ein GitHub-Repository mit Administratorrechten
  • Einen installierten Paketmanager (vorzugsweise Homebrew)
  • Git auf Ihrem System installiert

Schritt 1: Ein dediziertes Benutzerkonto erstellen

Erstellen Sie zunächst ein dediziertes Benutzerkonto für den GitHub Actions Runner:

# Das 'gh-runner' Benutzerkonto erstellen
sudo dscl . -create /Users/gh-runner
sudo dscl . -create /Users/gh-runner UserShell /bin/bash
sudo dscl . -create /Users/gh-runner RealName "GitHub runner"
sudo dscl . -create /Users/gh-runner UniqueID "1001"
sudo dscl . -create /Users/gh-runner PrimaryGroupID 20
sudo dscl . -create /Users/gh-runner NFSHomeDirectory /Users/gh-runner

# Das Passwort für den Benutzer setzen
sudo dscl . -passwd /Users/gh-runner ihr_passwort

# 'gh-runner' zur 'admin'-Gruppe hinzufügen
sudo dscl . -append /Groups/admin GroupMembership gh-runner

Wechseln Sie zum neuen Benutzerkonto:

su gh-runner

Schritt 2: Erforderliche Software installieren

Installieren Sie Git und Rosetta 2 (wenn Sie Apple Silicon verwenden):

# Git installieren, falls noch nicht installiert
brew install git

# Rosetta 2 für Apple Silicon Macs installieren
softwareupdate --install-rosetta

Schritt 3: Den GitHub Actions Runner konfigurieren

  1. Gehen Sie zu Ihrem GitHub-Repository
  2. Navigieren Sie zu Einstellungen > Actions > Runners

GitHub Actions Runner

  1. Klicken Sie auf "New self-hosted runner" (https://github.com/<username>/<repository>/settings/actions/runners/new)
  2. Wählen Sie macOS als Runner-Image und ARM64 als Architektur
  3. Folgen Sie den bereitgestellten Befehlen, um den Runner herunterzuladen und zu konfigurieren

GitHub Actions Runner

Erstellen Sie eine .env-Datei im _work-Verzeichnis des Runners:

# _work/.env Datei
ImageOS=macos15
XCODE_15_DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
  1. Führen Sie das run.sh-Skript in Ihrem Runner-Verzeichnis aus, um die Einrichtung abzuschließen.
  2. Überprüfen Sie, dass der Runner aktiv ist und auf Jobs im Terminal wartet, und überprüfen Sie die GitHub-Repository-Einstellungen für die Runner-Zuordnung und den Idle-Status.

GitHub Actions Runner

Schritt 4: Sudoers konfigurieren (Optional)

Wenn Ihre Actions Root-Privilegien benötigen, konfigurieren Sie die sudoers-Datei:

sudo visudo

Fügen Sie die folgende Zeile hinzu:

gh-runner ALL=(ALL) NOPASSWD: ALL

Schritt 5: Den Runner in Workflows verwenden

Konfigurieren Sie Ihren GitHub Actions Workflow, um den selbst-gehosteten Runner zu verwenden:

name: Beispiel-Workflow

on:
  workflow_dispatch:

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

Der Runner ist bei Ihrem Repository authentifiziert und mit self-hosted, macOS und ARM64 markiert. Verwenden Sie ihn in Ihren Workflows, indem Sie diese Labels im runs-on-Feld angeben:

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

Best Practices

  • Halten Sie Ihre Runner-Software auf dem neuesten Stand
  • Überwachen Sie regelmäßig Runner-Logs auf Probleme
  • Verwenden Sie spezifische Labels für verschiedene Runner-Typen
  • Implementieren Sie angemessene Sicherheitsmaßnahmen
  • Erwägen Sie die Verwendung mehrerer Runner für Lastverteilung

Fehlerbehebung

Häufige Probleme und Lösungen:

  1. Runner verbindet sich nicht:

    • Überprüfen Sie die Netzwerkverbindung
    • Überprüfen Sie die Gültigkeit des GitHub-Tokens
    • Stellen Sie angemessene Berechtigungen sicher
  2. Build-Fehler:

    • Überprüfen Sie die Xcode-Installation
    • Überprüfen Sie erforderliche Abhängigkeiten
    • Überprüfen Sie Workflow-Logs
  3. Berechtigungsprobleme:

    • Überprüfen Sie Benutzerberechtigungen
    • Überprüfen Sie sudoers-Konfiguration
    • Überprüfen Sie Dateisystem-Berechtigungen

Fazit

Sie haben jetzt einen selbst-gehosteten GitHub Actions Runner auf Ihrem Mac mini konfiguriert. Diese Einrichtung bietet Ihnen mehr Kontrolle über Ihre CI/CD-Umgebung und ermöglicht es Ihnen, macOS-spezifische Workflows effizient auszuführen.

Denken Sie daran, Ihren Runner regelmäßig zu warten und ihn mit den neuesten Sicherheitspatches und Software-Versionen auf dem neuesten Stand zu halten.

Native App

Macfleet native App

Macfleet Installationsanleitung

Macfleet ist eine leistungsstarke Flottenmanagement-Lösung, die speziell für Cloud-gehostete Mac Mini-Umgebungen entwickelt wurde. Als Mac Mini Cloud-Hosting-Anbieter können Sie Macfleet verwenden, um Ihre gesamte Flotte virtualisierter Mac-Instanzen zu überwachen, zu verwalten und zu optimieren.

Diese Installationsanleitung führt Sie durch die Einrichtung der Macfleet-Überwachung auf macOS-, Windows- und Linux-Systemen, um eine umfassende Übersicht über Ihre Cloud-Infrastruktur zu gewährleisten.

🍎 macOS

  • Laden Sie die .dmg-Datei für Mac hier herunter
  • Doppelklicken Sie auf die heruntergeladene .dmg-Datei
  • Ziehen Sie die Macfleet-App in den Anwendungsordner
  • Werfen Sie die .dmg-Datei aus
  • Öffnen Sie Systemeinstellungen > Sicherheit & Datenschutz
    • Datenschutz-Tab > Bedienungshilfen
    • Aktivieren Sie Macfleet, um Überwachung zu erlauben
  • Starten Sie Macfleet aus den Anwendungen
  • Die Verfolgung startet automatisch

🪟 Windows

  • Laden Sie die .exe-Datei für Windows hier herunter
  • Rechtsklick auf die .exe-Datei > "Als Administrator ausführen"
  • Folgen Sie dem Installationsassistenten
  • Akzeptieren Sie die Allgemeinen Geschäftsbedingungen
  • Erlauben Sie in Windows Defender, wenn aufgefordert
  • Gewähren Sie Anwendungsüberwachungsberechtigungen
  • Starten Sie Macfleet aus dem Startmenü
  • Die Anwendung beginnt automatisch mit der Verfolgung

🐧 Linux

  • Laden Sie das .deb-Paket (Ubuntu/Debian) oder .rpm (CentOS/RHEL) hier herunter
  • Installieren Sie mit Ihrem Paketmanager
    • Ubuntu/Debian: sudo dpkg -i Macfleet-linux.deb
    • CentOS/RHEL: sudo rpm -ivh Macfleet-linux.rpm
  • Erlauben Sie X11-Zugriffsberechtigungen, wenn aufgefordert
  • Fügen Sie den Benutzer zu entsprechenden Gruppen hinzu, falls erforderlich
  • Starten Sie Macfleet aus dem Anwendungsmenü
  • Die Anwendung beginnt automatisch mit der Verfolgung

Hinweis: Nach der Installation auf allen Systemen melden Sie sich mit Ihren Macfleet-Anmeldedaten an, um Daten mit Ihrem Dashboard zu synchronisieren.