Menu Bar Customization and Icon Management on macOS
Customize menu bar appearance and manage system icons across your MacFleet devices. This tutorial covers icon visibility control, user interface optimization, and enterprise-grade menu bar management for improved user experience and organizational consistency.
Understanding Menu Bar Management
The macOS menu bar contains various system indicators and quick access icons:
- Network indicators - VPN, Wi-Fi, Bluetooth status
- System monitors - Battery, volume, date/time
- Application icons - Third-party app status indicators
- User controls - Spotlight, Control Center, Siri
- Corporate tools - MDM agents, security software
Basic VPN Icon Management
Hide VPN Icon from Menu Bar
#!/bin/bash
# Hide VPN icon from menu bar
hide_vpn_icon() {
echo "=== Hiding VPN Icon from Menu Bar ==="
# Get current user
local currentUser
currentUser=$(echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ { print $3 }')
if [[ "$currentUser" == "loginwindow" || -z "$currentUser" ]]; then
echo "❌ No active user session found"
return 1
fi
echo "👤 Current user: $currentUser"
# Function to run commands as current user
runAsUser() {
local currentUserID
currentUserID=$(id -u "$currentUser")
if [[ "$currentUser" != "loginwindow" ]]; then
/bin/launchctl asuser "$currentUserID" sudo -u "$currentUser" "$@"
fi
}
# Remove VPN menu extra
if runAsUser /usr/libexec/PlistBuddy -c 'delete menuExtras: "/System/Library/CoreServices/Menu Extras/VPN.menu"' "/Users/$currentUser/Library/Preferences/com.apple.systemuiserver.plist" 2>/dev/null; then
echo "✅ VPN menu entry removed"
else
echo "⚠️ VPN menu entry not found or already removed"
fi
# Restart system services to apply changes
runAsUser /usr/bin/killall cfprefsd 2>/dev/null
runAsUser /usr/bin/killall SystemUIServer 2>/dev/null
echo "🔄 System UI refreshed"
echo "✅ VPN icon hidden from menu bar"
}
# Execute function
hide_vpn_icon
Show VPN Icon in Menu Bar
#!/bin/bash
# Show VPN icon in menu bar
show_vpn_icon() {
echo "=== Adding VPN Icon to Menu Bar ==="
local currentUser
currentUser=$(echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ { print $3 }')
if [[ "$currentUser" == "loginwindow" || -z "$currentUser" ]]; then
echo "❌ No active user session found"
return 1
fi
echo "👤 Current user: $currentUser"
runAsUser() {
local currentUserID
currentUserID=$(id -u "$currentUser")
if [[ "$currentUser" != "loginwindow" ]]; then
/bin/launchctl asuser "$currentUserID" sudo -u "$currentUser" "$@"
fi
}
# Add VPN menu extra
local vpn_path="/System/Library/CoreServices/Menu Extras/VPN.menu"
if runAsUser /usr/libexec/PlistBuddy -c "add menuExtras: string '$vpn_path'" "/Users/$currentUser/Library/Preferences/com.apple.systemuiserver.plist" 2>/dev/null; then
echo "✅ VPN menu entry added"
else
echo "⚠️ VPN menu entry already exists"
fi
# Restart system services
runAsUser /usr/bin/killall cfprefsd 2>/dev/null
runAsUser /usr/bin/killall SystemUIServer 2>/dev/null
echo "✅ VPN icon shown in menu bar"
}
# Execute function
show_vpn_icon
Enterprise Menu Bar Management System
#!/bin/bash
# MacFleet Enterprise Menu Bar Management System
# Comprehensive interface customization, user experience optimization, and fleet deployment
# Configuration
LOG_FILE="/var/log/macfleet_menubar.log"
CONFIG_DIR="/etc/macfleet/interface"
PROFILES_DIR="$CONFIG_DIR/profiles"
BACKUP_DIR="/var/backups/menubar_configs"
REPORTS_DIR="$CONFIG_DIR/reports"
# Available menu extras
declare -A MENU_EXTRAS=(
["vpn"]="/System/Library/CoreServices/Menu Extras/VPN.menu"
["wifi"]="/System/Library/CoreServices/Menu Extras/AirPort.menu"
["bluetooth"]="/System/Library/CoreServices/Menu Extras/Bluetooth.menu"
["volume"]="/System/Library/CoreServices/Menu Extras/Volume.menu"
["battery"]="/System/Library/CoreServices/Menu Extras/Battery.menu"
["clock"]="/System/Library/CoreServices/Menu Extras/Clock.menu"
["displays"]="/System/Library/CoreServices/Menu Extras/Displays.menu"
["timemachine"]="/System/Library/CoreServices/Menu Extras/TimeMachine.menu"
["textinput"]="/System/Library/CoreServices/Menu Extras/TextInput.menu"
["script"]="/System/Library/CoreServices/Menu Extras/Script Menu.menu"
)
# Profile configurations
declare -A MENU_PROFILES=(
["minimal"]="clock,battery,wifi"
["standard"]="clock,battery,wifi,volume,bluetooth"
["full"]="clock,battery,wifi,volume,bluetooth,displays,vpn"
["corporate"]="clock,battery,wifi,vpn,timemachine"
["education"]="clock,battery,wifi,volume,textinput"
["kiosk"]="clock,battery"
["developer"]="clock,battery,wifi,volume,bluetooth,displays,script"
["secure"]="clock,battery,wifi"
)
# Logging function
log_action() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
# Setup directories
setup_directories() {
for dir in "$CONFIG_DIR" "$PROFILES_DIR" "$BACKUP_DIR" "$REPORTS_DIR"; do
if [[ ! -d "$dir" ]]; then
mkdir -p "$dir"
log_action "Created directory: $dir"
fi
done
}
# Get current user
get_current_user() {
local user
user=$(echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ { print $3 }')
if [[ "$user" == "loginwindow" || -z "$user" ]]; then
return 1
fi
echo "$user"
return 0
}
# Execute command as current user
run_as_user() {
local user="$1"
shift
local userID
userID=$(id -u "$user" 2>/dev/null) || return 1
/bin/launchctl asuser "$userID" sudo -u "$user" "$@"
}
# Get current menu bar configuration
get_current_menubar_config() {
local user="$1"
local plist_path="/Users/$user/Library/Preferences/com.apple.systemuiserver.plist"
if [[ ! -f "$plist_path" ]]; then
echo "[]"
return 1
fi
# Extract current menu extras
local menu_extras
menu_extras=$(run_as_user "$user" /usr/libexec/PlistBuddy -c "print menuExtras:" "$plist_path" 2>/dev/null | grep -E "\.menu$" | sed 's/^[ ]*//' || echo "")
if [[ -z "$menu_extras" ]]; then
echo "[]"
else
echo "$menu_extras" | jq -R -s 'split("\n") | map(select(length > 0))'
fi
}
# Apply menu bar profile
apply_menubar_profile() {
local profile_name="$1"
local target_user="$2"
log_action "Applying menu bar profile: $profile_name"
# Get profile configuration
local profile_config="${MENU_PROFILES[$profile_name]}"
if [[ -z "$profile_config" ]]; then
log_action "❌ Unknown profile: $profile_name"
return 1
fi
# Get target users
local users=()
if [[ -n "$target_user" ]]; then
users=("$target_user")
else
# Get all users with UID >= 500
while IFS= read -r user; do
users+=("$user")
done < <(dscl . list /Users UniqueID | awk '$2 >= 500 {print $1}')
fi
# Apply profile to each user
for user in "${users[@]}"; do
log_action "Configuring menu bar for user: $user"
local plist_path="/Users/$user/Library/Preferences/com.apple.systemuiserver.plist"
# Backup current configuration
backup_user_menubar_config "$user"
# Clear existing menu extras
run_as_user "$user" /usr/libexec/PlistBuddy -c "delete menuExtras:" "$plist_path" 2>/dev/null || true
run_as_user "$user" /usr/libexec/PlistBuddy -c "add menuExtras: array" "$plist_path" 2>/dev/null
# Add profile menu items
IFS=',' read -ra ITEMS <<< "$profile_config"
local index=0
for item in "${ITEMS[@]}"; do
local menu_path="${MENU_EXTRAS[$item]}"
if [[ -n "$menu_path" && -f "$menu_path" ]]; then
run_as_user "$user" /usr/libexec/PlistBuddy -c "add menuExtras:$index string '$menu_path'" "$plist_path" 2>/dev/null
log_action "Added menu item: $item for user: $user"
((index++))
else
log_action "⚠️ Menu item not found: $item"
fi
done
# Restart UI services
run_as_user "$user" /usr/bin/killall cfprefsd 2>/dev/null
run_as_user "$user" /usr/bin/killall SystemUIServer 2>/dev/null
log_action "✅ Profile '$profile_name' applied to user: $user"
done
return 0
}
# Backup user menu bar configuration
backup_user_menubar_config() {
local user="$1"
local timestamp
timestamp=$(date '+%Y%m%d_%H%M%S')
local plist_path="/Users/$user/Library/Preferences/com.apple.systemuiserver.plist"
local backup_file="$BACKUP_DIR/menubar_${user}_${timestamp}.plist"
if [[ -f "$plist_path" ]]; then
cp "$plist_path" "$backup_file" 2>/dev/null
log_action "Backed up menu bar config for $user: $backup_file"
echo "$backup_file"
fi
}
# Custom menu bar configuration
configure_custom_menubar() {
local items_string="$1"
local target_user="$2"
log_action "Applying custom menu bar configuration: $items_string"
local users=()
if [[ -n "$target_user" ]]; then
users=("$target_user")
else
while IFS= read -r user; do
users+=("$user")
done < <(dscl . list /Users UniqueID | awk '$2 >= 500 {print $1}')
fi
# Parse items
IFS=',' read -ra ITEMS <<< "$items_string"
for user in "${users[@]}"; do
local plist_path="/Users/$user/Library/Preferences/com.apple.systemuiserver.plist"
# Backup and clear
backup_user_menubar_config "$user"
run_as_user "$user" /usr/libexec/PlistBuddy -c "delete menuExtras:" "$plist_path" 2>/dev/null || true
run_as_user "$user" /usr/libexec/PlistBuddy -c "add menuExtras: array" "$plist_path" 2>/dev/null
# Add custom items
local index=0
for item in "${ITEMS[@]}"; do
# Remove whitespace
item=$(echo "$item" | sed 's/^[ \t]*//;s/[ \t]*$//')
local menu_path="${MENU_EXTRAS[$item]}"
if [[ -n "$menu_path" && -f "$menu_path" ]]; then
run_as_user "$user" /usr/libexec/PlistBuddy -c "add menuExtras:$index string '$menu_path'" "$plist_path" 2>/dev/null
((index++))
fi
done
# Apply changes
run_as_user "$user" /usr/bin/killall cfprefsd 2>/dev/null
run_as_user "$user" /usr/bin/killall SystemUIServer 2>/dev/null
log_action "✅ Custom configuration applied to user: $user"
done
}
# Generate menu bar audit report
generate_menubar_audit() {
log_action "Generating menu bar audit report"
local report_file="$REPORTS_DIR/menubar_audit_$(date '+%Y%m%d_%H%M%S').json"
cat > "$report_file" << EOF
{
"audit_metadata": {
"timestamp": "$(date -Iseconds)",
"hostname": "$(hostname)",
"os_version": "$(sw_vers -productVersion)",
"generator": "MacFleet Menu Bar Manager"
},
"user_configurations": [
EOF
local first=true
local total_users=0
# Audit each user
for user in $(dscl . list /Users UniqueID | awk '$2 >= 500 {print $1}'); do
total_users=$((total_users + 1))
if [[ "$first" == true ]]; then
first=false
else
echo "," >> "$report_file"
fi
local current_config
current_config=$(get_current_menubar_config "$user")
local last_login
last_login=$(last -1 "$user" | head -1 | awk '{print $4, $5, $6, $7}' || echo 'Never')
cat >> "$report_file" << EOF
{
"username": "$user",
"current_menu_items": $current_config,
"item_count": $(echo "$current_config" | jq 'length'),
"last_login": "$last_login",
"config_file_exists": $([ -f "/Users/$user/Library/Preferences/com.apple.systemuiserver.plist" ] && echo "true" || echo "false")
}
EOF
log_action "Audited user: $user"
done
cat >> "$report_file" << EOF
],
"summary": {
"total_users": $total_users,
"available_menu_items": $(echo "${!MENU_EXTRAS[@]}" | tr ' ' '\n' | jq -R . | jq -s .),
"available_profiles": $(echo "${!MENU_PROFILES[@]}" | tr ' ' '\n' | jq -R . | jq -s .)
}
}
EOF
log_action "✅ Menu bar audit completed: $report_file"
echo "$report_file"
}
# Restore menu bar configuration from backup
restore_menubar_config() {
local backup_file="$1"
local target_user="$2"
if [[ ! -f "$backup_file" ]]; then
log_action "❌ Backup file not found: $backup_file"
return 1
fi
log_action "Restoring menu bar configuration from: $backup_file"
local users=()
if [[ -n "$target_user" ]]; then
users=("$target_user")
else
# Extract username from backup filename
local extracted_user
extracted_user=$(basename "$backup_file" | sed 's/menubar_\(.*\)_[0-9]*_[0-9]*.plist/\1/')
if [[ -n "$extracted_user" ]]; then
users=("$extracted_user")
fi
fi
for user in "${users[@]}"; do
local plist_path="/Users/$user/Library/Preferences/com.apple.systemuiserver.plist"
# Create current backup before restore
backup_user_menubar_config "$user"
# Restore configuration
if cp "$backup_file" "$plist_path" 2>/dev/null; then
chown "$user:staff" "$plist_path" 2>/dev/null
# Apply changes
run_as_user "$user" /usr/bin/killall cfprefsd 2>/dev/null
run_as_user "$user" /usr/bin/killall SystemUIServer 2>/dev/null
log_action "✅ Configuration restored for user: $user"
else
log_action "❌ Failed to restore configuration for user: $user"
return 1
fi
done
return 0
}
# Monitor menu bar changes
monitor_menubar_changes() {
local duration="${1:-3600}" # 1 hour default
local check_interval="${2:-60}" # 1 minute default
log_action "Starting menu bar monitoring for ${duration} seconds"
local monitoring_report="$REPORTS_DIR/menubar_monitoring_$(date '+%Y%m%d_%H%M%S').json"
local baseline_file="/tmp/menubar_baseline.json"
# Create baseline
generate_menubar_audit > "$baseline_file"
cat > "$monitoring_report" << EOF
{
"monitoring_metadata": {
"start_time": "$(date -Iseconds)",
"duration_seconds": $duration,
"check_interval_seconds": $check_interval,
"hostname": "$(hostname)"
},
"changes_detected": [
EOF
local start_time end_time
start_time=$(date +%s)
end_time=$((start_time + duration))
local first=true
while [[ $(date +%s) -lt $end_time ]]; do
sleep "$check_interval"
# Check for changes
local current_state="/tmp/menubar_current.json"
generate_menubar_audit > "$current_state"
if ! diff "$baseline_file" "$current_state" >/dev/null 2>&1; then
if [[ "$first" == true ]]; then
first=false
else
echo "," >> "$monitoring_report"
fi
cat >> "$monitoring_report" << EOF
{
"timestamp": "$(date -Iseconds)",
"change_type": "menu_bar_configuration_change",
"details": "Changes detected in menu bar configuration"
}
EOF
log_action "⚠️ Menu bar configuration changes detected"
cp "$current_state" "$baseline_file"
fi
rm -f "$current_state"
done
cat >> "$monitoring_report" << EOF
],
"end_time": "$(date -Iseconds)"
}
EOF
log_action "✅ Menu bar monitoring completed: $monitoring_report"
rm -f "$baseline_file"
echo "$monitoring_report"
}
# Main execution function
main() {
local action="${1:-audit}"
local parameter="$2"
local additional_param="$3"
log_action "=== MacFleet Menu Bar Management Started ==="
log_action "Action: $action"
log_action "Parameter: ${parameter:-N/A}"
setup_directories
case "$action" in
"profile")
if [[ -z "$parameter" ]]; then
echo "Available menu bar profiles:"
for profile in "${!MENU_PROFILES[@]}"; do
echo " - $profile: ${MENU_PROFILES[$profile]}"
done
echo ""
echo "Usage: $0 profile <profile_name> [username]"
exit 1
fi
apply_menubar_profile "$parameter" "$additional_param"
;;
"custom")
if [[ -z "$parameter" ]]; then
echo "Available menu items:"
for item in "${!MENU_EXTRAS[@]}"; do
echo " - $item"
done
echo ""
echo "Usage: $0 custom <item1,item2,item3> [username]"
echo "Example: $0 custom clock,battery,wifi,vpn john.doe"
exit 1
fi
configure_custom_menubar "$parameter" "$additional_param"
;;
"audit")
generate_menubar_audit
;;
"monitor")
monitor_menubar_changes "$parameter" "$additional_param"
;;
"restore")
if [[ -z "$parameter" ]]; then
echo "Usage: $0 restore <backup_file> [username]"
echo "Available backups:"
ls -la "$BACKUP_DIR"/*.plist 2>/dev/null || echo "No backups found"
exit 1
fi
restore_menubar_config "$parameter" "$additional_param"
;;
"hide-vpn")
local user
if user=$(get_current_user); then
hide_vpn_icon
else
echo "❌ No active user session"
exit 1
fi
;;
"show-vpn")
local user
if user=$(get_current_user); then
show_vpn_icon
else
echo "❌ No active user session"
exit 1
fi
;;
*)
echo "Usage: $0 {profile|custom|audit|monitor|restore|hide-vpn|show-vpn}"
echo " profile - Apply predefined menu bar profile"
echo " custom - Apply custom menu bar configuration"
echo " audit - Generate menu bar audit report"
echo " monitor - Monitor menu bar changes over time"
echo " restore - Restore from backup"
echo " hide-vpn - Hide VPN icon from menu bar"
echo " show-vpn - Show VPN icon in menu bar"
exit 1
;;
esac
log_action "=== Menu bar management completed ==="
}
# Execute main function
main "$@"
Menu Bar Profile Examples
Corporate Profile Setup
#!/bin/bash
# Apply corporate menu bar profile across all devices
deploy_corporate_profile() {
echo "=== Corporate Menu Bar Deployment ==="
# Corporate profile: clock, battery, wifi, vpn, timemachine
local corporate_items="clock,battery,wifi,vpn,timemachine"
# Get all users
for user in $(dscl . list /Users UniqueID | awk '$2 >= 500 {print $1}'); do
echo "Configuring corporate profile for: $user"
# Apply configuration
configure_custom_menubar "$corporate_items" "$user"
# Verify application
sleep 2
local current_config
current_config=$(get_current_menubar_config "$user")
echo "Applied configuration: $current_config"
done
echo "✅ Corporate profile deployed to all users"
}
Kiosk Mode Configuration
#!/bin/bash
# Minimal menu bar for kiosk environments
setup_kiosk_menubar() {
echo "=== Kiosk Menu Bar Setup ==="
# Minimal items: clock and battery only
local kiosk_items="clock,battery"
local target_user="$1"
if [[ -z "$target_user" ]]; then
echo "Usage: setup_kiosk_menubar <username>"
return 1
fi
configure_custom_menubar "$kiosk_items" "$target_user"
echo "✅ Kiosk menu bar configured for: $target_user"
}
Advanced Menu Bar Features
Dynamic Profile Switching
#!/bin/bash
# Switch between different profiles based on conditions
smart_profile_switching() {
local user="$1"
echo "=== Smart Profile Switching ==="
# Detect environment
if pgrep -f "VPN" >/dev/null; then
echo "VPN detected - applying corporate profile"
apply_menubar_profile "corporate" "$user"
elif [[ $(pmset -g batt | grep -c "Battery") -gt 0 ]]; then
echo "Battery power detected - applying minimal profile"
apply_menubar_profile "minimal" "$user"
else
echo "Standard environment - applying full profile"
apply_menubar_profile "full" "$user"
fi
}
Menu Bar Health Check
#!/bin/bash
# Verify menu bar configuration integrity
menubar_health_check() {
echo "=== Menu Bar Health Check ==="
local issues_found=0
for user in $(dscl . list /Users UniqueID | awk '$2 >= 500 {print $1}'); do
local plist_path="/Users/$user/Library/Preferences/com.apple.systemuiserver.plist"
if [[ ! -f "$plist_path" ]]; then
echo "⚠️ Missing configuration file for user: $user"
issues_found=$((issues_found + 1))
continue
fi
# Check if SystemUIServer is running
if ! pgrep -u "$(id -u "$user")" SystemUIServer >/dev/null; then
echo "⚠️ SystemUIServer not running for user: $user"
issues_found=$((issues_found + 1))
fi
# Validate plist format
if ! plutil -lint "$plist_path" >/dev/null 2>&1; then
echo "❌ Corrupted plist file for user: $user"
issues_found=$((issues_found + 1))
fi
done
if [[ $issues_found -eq 0 ]]; then
echo "✅ All menu bar configurations are healthy"
else
echo "⚠️ Found $issues_found issues requiring attention"
fi
return $issues_found
}
Best Practices
🎯 User Experience Optimization
- Consistent branding across all devices with standardized menu bar layouts
- Profile-based configurations for different user roles and environments
- Minimal kiosk setups for public or restricted-use devices
- Dynamic switching based on power state or network conditions
🔧 Enterprise Management
- Centralized deployment of menu bar configurations across device fleets
- Backup and restore capabilities for configuration management
- Audit reporting for compliance and standardization verification
- Change monitoring with automated detection and alerting
🔐 Security Considerations
- Remove sensitive indicators from public-facing devices
- Hide VPN status in environments where network information should be private
- Standardize corporate displays to prevent information leakage
- Monitor unauthorized changes to menu bar configurations
📊 Monitoring and Maintenance
- Regular health checks to ensure configuration integrity
- Automated profile enforcement to maintain consistency
- Change detection with rollback capabilities
- User experience feedback collection and optimization
Important Notes
- macOS 12+ required for optimal compatibility with modern menu extras
- User session active required for menu bar modifications
- System restart may be needed for some changes to take full effect
- Test thoroughly before deploying to production environments
- Backup configurations before making changes to allow easy rollback