368 lines
18 KiB
Bash
368 lines
18 KiB
Bash
#!/bin/bash
|
|
|
|
# This script is meant to be used with Jamf Pro and makes use of Jamf Helper.
|
|
# The idea behind this script is that it alerts the user that there are required OS
|
|
# updates that need to be installed. Rather than forcing updates to take place through the
|
|
# command line using "softwareupdate", the user is encouraged to use the GUI to update.
|
|
# In recent OS versions, Apple has done a poor job of testing command line-based workflows
|
|
# of updates and failed to account for scenarios where users may or may not be logged in.
|
|
# The update process through the GUI has not suffered from these kind of issues. The
|
|
# script will allow end users to postpone/defer updates X amount of times and then will
|
|
# give them one last change to postpone.
|
|
# This script should work rather reliably going back to 10.12 and maybe further, but at
|
|
# this point the real testing has only been done on 10.14.
|
|
# Please note, that this script does NOT cache updates in advance. The reason for this is
|
|
# that sometimes Apple releases updates that get superseded in a short time frame.
|
|
# This can result in downloaded updates that are in the /Library/Updates path that cannot
|
|
# be removed in 10.14+ due to System Integrity Protection.
|
|
#
|
|
# JAMF Pro Script Parameters:
|
|
# Parameter 4: Optional. Number of postponements allowed. Default: 3
|
|
# Parameter 5: Optional. Number of seconds dialog should remain up. Default: 900 seconds
|
|
#
|
|
# Here is the expected workflow with this script:
|
|
# If no user is logged in, the script will install updates through the command line and
|
|
# shutdown/restart as required.
|
|
# If a user is logged in and there are updates that require a restart, the user will get
|
|
# prompted to update or to postpone.
|
|
# If a user is logged in and there are no updates that require a restart, the updates will
|
|
# get installed in the background (unless either Safari or iTunes are running.)
|
|
#
|
|
# There are a few exit codes in this script that may indicate points of failure:
|
|
# 11: No power source detected while doing CLI update.
|
|
# 12: Software Update failed.
|
|
# 13: FV encryption is still in progress.
|
|
# 14: Incorrect deferral type used.
|
|
|
|
# Potential feature improvement
|
|
# Allow user to postpone to a specific time with a popup menu of available times
|
|
|
|
########################## Deutsch #####################################################
|
|
|
|
# Dieses Skript ist für die Verwendung mit Jamf Pro vorgesehen und verwendet Jamf Helper.
|
|
# Die Idee hinter diesem Skript ist, dass der Benutzer darauf hingewiesen wird, dass ein Betriebssystem erforderlich ist
|
|
# Updates, die installiert werden müssen. Anstatt Aktualisierungen zu erzwingen, die über das Internet erfolgen
|
|
# Kommandozeile Mit "softwareupdate" wird der Benutzer aufgefordert, die GUI zum Aktualisieren zu verwenden.
|
|
# In neueren Betriebssystemversionen hat Apple befehlszeilenbasierte Workflows nur unzureichend getestet
|
|
# Anzahl der Aktualisierungen und nicht berücksichtigte Szenarien, in denen Benutzer möglicherweise angemeldet sind oder nicht.
|
|
# Der Aktualisierungsprozess über die GUI hat unter solchen Problemen nicht gelitten. Das Mit dem Skript
|
|
# können Endbenutzer Aktualisierungen x-mal verschieben oder verschieben
|
|
# Gib ihnen eine letzte Änderung, um sie zu verschieben.
|
|
# Dieses Skript sollte ziemlich zuverlässig funktionieren und zurück zu 10.12 und vielleicht weiter, aber um
|
|
# An diesem Punkt wurden die eigentlichen Tests erst am 10.14 durchgeführt.
|
|
# Bitte beachten Sie, dass dieses Skript KEINE Updates im Voraus zwischenspeichert. Der Grund dafür ist
|
|
# dass Apple manchmal Updates veröffentlicht, die in kurzer Zeit ersetzt werden.
|
|
# Dies kann dazu führen, dass Updates heruntergeladen werden, die sich im Pfad / Library / Updates befinden und nicht heruntergeladen werden können
|
|
# Wurde in 10.14+ aufgrund des Schutzes der Systemintegrität entfernt.
|
|
#
|
|
# JAMF Pro Script Parameter:
|
|
# Parameter 4: Optional. Anzahl der zulässigen Verschiebungen. Voreinstellung: 3
|
|
# Parameter 5: Optional. Das Dialogfeld für die Anzahl der Sekunden sollte geöffnet bleiben. Voreinstellung: 900 Sekunden
|
|
#
|
|
# Hier ist der erwartete Workflow mit diesem Skript:
|
|
# Wenn kein Benutzer angemeldet ist, installiert das Skript Updates über die Befehlszeile und
|
|
# Herunterfahren / Neustarten nach Bedarf.
|
|
# Wenn ein Benutzer angemeldet ist und es Updates gibt, die einen Neustart erfordern, erhält der Benutzer
|
|
# aufgefordert, zu aktualisieren oder zu verschieben.
|
|
# Wenn ein Benutzer angemeldet ist und keine Updates vorhanden sind, die einen Neustart erfordern, werden die Updates ausgeführt
|
|
# im Hintergrund installiert werden (es sei denn, Safari oder iTunes werden ausgeführt.)
|
|
#
|
|
# Es gibt einige Exit-Codes in diesem Skript, die auf Fehlerstellen hinweisen können:
|
|
# 11: Während der CLI-Aktualisierung wurde keine Stromquelle erkannt.
|
|
# 12: Softwareaktualisierung fehlgeschlagen.
|
|
# 13: Die FV-Verschlüsselung wird noch durchgeführt.
|
|
# 14: Falscher Stundungstyp verwendet.
|
|
|
|
# Mögliche Funktionsverbesserung
|
|
# Benutzer kann mit einem Popup-Menü mit verfügbaren Zeiten auf einen bestimmten Zeitpunkt verschoben werden
|
|
set -x
|
|
###### ACTUAL WORKING CODE BELOW #######
|
|
setDeferral (){
|
|
# Notes: PlistBuddy "print" will print stderr to stdout when file is not found.
|
|
# File Doesn't Exist, Will Create: /path/to/file.plist
|
|
# There is some unused code here with the idea that at some point in the future I can
|
|
# extend functionality of this script to support hard and relative dates.
|
|
BundleID="${1}"
|
|
DeferralType="${2}"
|
|
DeferralValue="${3}"
|
|
DeferralPlist="${4}"
|
|
|
|
if [[ "$DeferralType" == "date" ]]; then
|
|
DeferralDate="$(/usr/libexec/PlistBuddy -c "print :$BundleID:date" "$DeferralPlist" 2>/dev/null)"
|
|
# Set deferral date
|
|
if [[ -n "$DeferralDate" ]] && [[ ! "$DeferralDate" =~ "File Doesn't Exist" ]]; then
|
|
# /usr/libexec/PlistBuddy -c "set :$BundleID:date '07/04/2019 11:21:51 +0000'" "$DeferralPlist"
|
|
/usr/libexec/PlistBuddy -c "set :$BundleID:date $DeferralValue" "$DeferralPlist" 2>/dev/null
|
|
else
|
|
# /usr/libexec/PlistBuddy -c "add :$BundleID:date date '07/04/2019 11:21:51 +0000'" "$DeferralPlist"
|
|
/usr/libexec/PlistBuddy -c "add :$BundleID:date date $DeferralValue" "$DeferralPlist" 2>/dev/null
|
|
fi
|
|
elif [[ "$DeferralType" == "count" ]]; then
|
|
DeferralCount="$(/usr/libexec/PlistBuddy -c "print :$BundleID:count" "$DeferralPlist" 2>/dev/null)"
|
|
# Set deferral count
|
|
if [[ -n "$DeferralCount" ]] && [[ ! "$DeferralCount" =~ "File Doesn't Exist" ]]; then
|
|
/usr/libexec/PlistBuddy -c "set :$BundleID:count $DeferralValue" "$DeferralPlist" 2>/dev/null
|
|
else
|
|
/usr/libexec/PlistBuddy -c "add :$BundleID:count integer $DeferralValue" "$DeferralPlist" 2>/dev/null
|
|
fi
|
|
else
|
|
echo "Falscher Stundungstyp verwendet"
|
|
exit 14
|
|
fi
|
|
}
|
|
|
|
|
|
OSMajorVersion="$(/usr/bin/sw_vers -productVersion | /usr/bin/cut -d '.' -f 2)"
|
|
OSMinorVersion="$(/usr/bin/sw_vers -productVersion | /usr/bin/cut -d '.' -f 3)"
|
|
DeferralPlist="/Library/Application Support/JAMF/com.custom.deferrals.plist"
|
|
BundleID="com.apple.SoftwareUpdate"
|
|
DeferralType="count"
|
|
DeferralValue="${4}"
|
|
|
|
if [[ -z "$DeferralValue" ]]; then
|
|
DeferralValue=3
|
|
fi
|
|
|
|
CurrentDeferralValue="$(/usr/libexec/PlistBuddy -c "print :$BundleID:count" "$DeferralPlist" 2>/dev/null)"
|
|
|
|
# Set up the deferral value if it does not exist already
|
|
if [[ -z "$CurrentDeferralValue" ]] || [[ "$CurrentDeferralValue" =~ "File Doesn't Exist" ]]; then
|
|
setDeferral "$BundleID" "$DeferralType" "$DeferralValue" "$DeferralPlist"
|
|
CurrentDeferralValue="$(/usr/libexec/PlistBuddy -c "print :$BundleID:count" "$DeferralPlist" 2>/dev/null)"
|
|
fi
|
|
|
|
jamfHelper="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"
|
|
jamf="/usr/local/bin/jamf"
|
|
TimeOutinSec="${5}"
|
|
|
|
if [[ -z "$DeferralValue" ]]; then
|
|
TimeOutinSec="900"
|
|
fi
|
|
|
|
# Path to temporarily store list of software updates. Avoids having to re-run the softwareupdate command multiple times.
|
|
# Pfad zum temporären Speichern der Liste der Softwareupdates. Vermeidet, dass der Befehl softwareupdate mehrmals ausgeführt werden muss
|
|
ListOfSoftwareUpdates="/tmp/ListOfSoftwareUpdates"
|
|
|
|
# Set appropriate Software Update icon depending on OS version
|
|
# Stellen Sie das entsprechende Software-Aktualisierungssymbol je nach Betriebssystemversion ein
|
|
if [[ "$OSMajorVersion" -gt 13 ]]; then
|
|
AppleSUIcon="/System/Library/CoreServices/Software Update.app/Contents/Resources/SoftwareUpdate.icns"
|
|
elif [[ "$OSMajorVersion" -eq 13 ]]; then
|
|
AppleSUIcon="/System/Library/CoreServices/Install Command Line Developer Tools.app/Contents/Resources/SoftwareUpdate.icns"
|
|
elif [[ "$OSMajorVersion" -ge 8 ]] && [[ "$OSMajorVersion" -le 12 ]]; then
|
|
AppleSUIcon="/System/Library/CoreServices/Software Update.app/Contents/Resources/SoftwareUpdate.icns"
|
|
elif [[ "$OSMajorVersion" -lt 8 ]]; then
|
|
AppleSUIcon="/System/Library/CoreServices/Software Update.app/Contents/Resources/Software Update.icns"
|
|
fi
|
|
|
|
## Verbiage For Messages ##
|
|
## Sprachausgabe für Nachrichten ##
|
|
# Message to guide user to Software Update process
|
|
# Nachricht, die den Benutzer zum Software-Update-Vorgang führt
|
|
if [[ "$OSMajorVersion" -ge 14 ]]; then
|
|
#SUGuide="Klicken Sie im Apple-Menü auf "Systemeinstellungen" und dann auf "Software-Update", um alle verfügbaren Updates zu installieren."
|
|
SUGuide="by navigating to:
|
|
> System Preferences > Software Update"
|
|
else
|
|
#SUGuide="Öffnen Sie den App Store im Ordner "Programme" und klicken Sie auf die Registerkarte "Updates", um alle verfügbaren Updates zu installieren."
|
|
SUIGuide="by navigating to:
|
|
> App Store > Updates tab"
|
|
fi
|
|
|
|
# Message to let user to contact IT
|
|
# Nachricht, damit der Benutzer Kontakt mit der IT aufnehmen kann
|
|
ITContact=""
|
|
|
|
if [[ -z "$ITContact" ]]; then
|
|
ITContact="IT"
|
|
fi
|
|
|
|
ContactMsg="Bei der Installation der Updates ist anscheinend ein Fehler aufgetreten. Sie können es erneut versuchen $SUGuide
|
|
Wenn der Fehler weiterhin besteht, wenden Sie sich bitte an $ITContact."
|
|
|
|
# Message to display when computer is running off battery
|
|
# Meldung, die angezeigt wird, wenn der Akku des Computers leer ist
|
|
no_ac_power="Der Computer ist derzeit im Akkubetrieb und nicht an eine Stromquelle angeschlossen. Schließen Sie den Computer an eine Stromquelle an und versuchen Sie es erneut."
|
|
|
|
# Standard Update Message
|
|
# Standard Update Nachricht
|
|
StandardUpdatePrompt="Für Ihren Mac ist ein Betriebssystem-Update verfügbar. Klicken Sie auf Weiter, um mit dem Software-Update fortzufahren und dieses Update auszuführen. Wenn Sie den Vorgang zu diesem Zeitpunkt nicht starten können, können Sie den Vorgang um einen Tag verschieben.
|
|
Versuche bleiben zu verschieben: $CurrentDeferralValue
|
|
Sie können jederzeit MacOS-Software-Updates installieren $SUGuide"
|
|
|
|
# Forced Update Message
|
|
# Erzwungene Aktualisierungsnachricht
|
|
ForcedUpdatePrompt="Für Ihren Mac sind Software-Updates verfügbar, die einen Neustart erfordern. Sie haben Aktualisierungen bereits so oft wie möglich verschoben.
|
|
Bitte speichern Sie Ihre Arbeit und klicken Sie auf Aktualisieren. Anderenfalls verschwindet diese Meldung und der Computer wird automatisch neu gestartet."
|
|
|
|
# Message shown when running CLI updates
|
|
# Meldung, die beim Ausführen von CLI-Updates angezeigt wird
|
|
HUDMessage="Bitte speichern Sie Ihre Arbeit und beenden Sie alle anderen Anwendungen. Im Hintergrund werden MacOS-Software-Updates installiert. Schalten Sie diesen Computer während dieser Zeit nicht aus. Diese Meldung wird ausgeblendet, wenn die Aktualisierungen abgeschlossen sind. Wenn Sie sie schließen, wird der Aktualisierungsvorgang nicht abgebrochen.
|
|
Wenn Sie das Gefühl haben, dass zu viel Zeit vergangen ist, wenden Sie sich bitte an $ITContact
|
|
"
|
|
|
|
## Functions ##
|
|
powerCheck (){
|
|
# This is meant to be used when doing CLI update installs.
|
|
# Updates through the GUI can already determine its own requirements to proceed with
|
|
# the update process.
|
|
# Let's wait 5 minutes to see if computer gets plugged into power.
|
|
for (( i = 1; i <= 5; ++i )); do
|
|
if [[ "$(/usr/bin/pmset -g ps | /usr/bin/grep "Battery Power")" = "Now drawing from 'Battery Power'" ]]; then
|
|
echo "$no_ac_power"
|
|
/bin/sleep 60
|
|
else
|
|
return 0
|
|
fi
|
|
done
|
|
exit 11
|
|
}
|
|
|
|
|
|
updateCLI (){
|
|
# Install all software updates
|
|
/usr/sbin/softwareupdate -ia --verbose 2>&1 >> "$ListOfSoftwareUpdates" &
|
|
|
|
## Get the Process ID of the last command run in the background ($!) and wait for it to complete (wait)
|
|
SUPID=$(echo "$!")
|
|
|
|
wait $SUPID
|
|
|
|
SU_EC=$?
|
|
|
|
ShutdownRequired=$(/bin/cat "$ListOfSoftwareUpdates" | /usr/bin/grep -E "halt|shut down" | /usr/bin/wc -l | /usr/bin/awk '{ print $1 }')
|
|
|
|
return $SU_EC
|
|
}
|
|
|
|
|
|
updateRestartAction (){
|
|
# On T2 hardware, we need to shutdown on certain updates
|
|
if [[ "$ShutdownRequired" == "1" ]] && [[ "$SEPType" ]]; then
|
|
if [[ "$OSMajorVersion" -eq 13 ]] && [[ "$OSMinorVersion" -ge 4 ]] || [[ "$OSMajorVersion" -ge 14 ]]; then
|
|
/sbin/shutdown -h now
|
|
exit 0
|
|
fi
|
|
fi
|
|
# If no shutdown is required then let's go ahead and restart
|
|
/sbin/shutdown -r now
|
|
exit 0
|
|
}
|
|
|
|
|
|
updateGUI (){
|
|
# Update through the GUI
|
|
if [[ "$OSMajorVersion" -ge 14 ]]; then
|
|
/usr/bin/open "/System/Library/CoreServices/Software Update.app"
|
|
elif [[ "$OSMajorVersion" -ge 8 ]] && [[ "$OSMajorVersion" -le 13 ]]; then
|
|
/usr/bin/open macappstore://showUpdatesPage
|
|
fi
|
|
}
|
|
|
|
|
|
fvStatusCheck (){
|
|
# Check to see if the encryption process is complete
|
|
FVStatus="$(/usr/bin/fdesetup status)"
|
|
if [[ $(/usr/bin/grep -q "Encryption in progress" <<< "$FVStatus") ]]; then
|
|
echo "Der Verschlüsselungsprozess ist noch nicht abgeschlossen."
|
|
echo "$FVStatus"
|
|
exit 13
|
|
fi
|
|
}
|
|
|
|
|
|
runUpdates (){
|
|
"$jamfHelper" -windowType hud -lockhud -title "Apple Software Update" -description "$HUDMessage""START TIME: $(/bin/date +"%b %d %Y %T")" -icon "$AppleSUIcon" &>/dev/null &
|
|
|
|
## We'll need the pid of jamfHelper to kill it once the updates are complete
|
|
JHPID=$(echo "$!")
|
|
|
|
## Run the jamf policy to insall software updates
|
|
updateCLI
|
|
|
|
## Kill the jamfHelper. If a restart is needed, the user will be prompted. If not the hud will just go away
|
|
/bin/kill -s KILL "$JHPID" &>/dev/null
|
|
|
|
if [[ "$SU_EC" == 0 ]]; then
|
|
updateRestartAction
|
|
else
|
|
echo "/usr/bin/softwareupdate failed. Exit Code: $SU_EC"
|
|
|
|
"$jamfHelper" -windowType utility -icon "$AppleSUIcon" -title "Apple Software Updates" -description "$ContactMsg" -button1 "OK"
|
|
exit 12
|
|
fi
|
|
|
|
exit 0
|
|
}
|
|
|
|
|
|
# Store list of software updates in /tmp which gets cleared periodically by the OS and on restarts
|
|
/usr/sbin/softwareupdate -l 2>&1 > "$ListOfSoftwareUpdates"
|
|
|
|
UpdatesNoRestart=$(/bin/cat "$ListOfSoftwareUpdates" | /usr/bin/grep recommended | /usr/bin/grep -v restart | /usr/bin/cut -d , -f 1 | /usr/bin/sed -e 's/^[[:space:]]*//')
|
|
RestartRequired=$(/bin/cat "$ListOfSoftwareUpdates" | /usr/bin/grep restart | /usr/bin/grep -v '\*' | /usr/bin/cut -d , -f 1 | /usr/bin/sed -e 's/^[[:space:]]*//')
|
|
|
|
# Determine Secure Enclave version
|
|
SEPType="$(/usr/sbin/system_profiler SPiBridgeDataType | /usr/bin/awk -F: '/Model Name/ { gsub(/.*: /,""); print $0}')"
|
|
|
|
# Determine currently logged in user
|
|
#LoggedInUser=$(python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "\n");')
|
|
LoggedInUser="$(/usr/sbin/scutil <<< "show State:/Users/ConsoleUser" | /usr/bin/awk '/Name :/ && ! /loginwindow/ { print $3 }')"
|
|
|
|
# Let's make sure FileVault isn't encrypting before proceeding any further
|
|
fvStatusCheck
|
|
|
|
# If there are no system updates, reset timer and exit script
|
|
if [[ "$UpdatesNoRestart" == "" ]] && [[ "$RestartRequired" == "" ]]; then
|
|
echo "No updates at this time."
|
|
setDeferral "$BundleID" "$DeferralType" "$DeferralValue" "$DeferralPlist"
|
|
exit 0
|
|
fi
|
|
|
|
# If we get to this point, there are updates available.
|
|
# If there is no one logged in, let's try to run the updates.
|
|
if [[ "$LoggedInUser" == "" ]]; then
|
|
powerCheck
|
|
updateCLI
|
|
updateRestartAction
|
|
else
|
|
# Someone is logged in. Prompt if any updates require a restart ONLY IF the update timer has not reached zero
|
|
if [[ "$RestartRequired" != "" ]]; then
|
|
if [[ "$CurrentDeferralValue" -gt 0 ]]; then
|
|
# Reduce the timer by 1. The script will run again the next day
|
|
let CurrTimer=$CurrentDeferralValue-1
|
|
setDeferral "$BundleID" "$DeferralType" "$CurrTimer" "$DeferralPlist"
|
|
|
|
# If someone is logged in and they have not canceled $DeferralValue times already, prompt them to install updates that require a restart and state how many more times they can press 'cancel' before updates run automatically.
|
|
# Wenn jemand angemeldet ist und $ DeferralValue noch nicht abgebrochen hat, fordern Sie ihn auf, Updates zu installieren, die einen Neustart erfordern, und anzugeben, wie oft er "Abbrechen" drücken kann, bevor Updates automatisch ausgeführt werden
|
|
HELPER=$("$jamfHelper" -windowType utility -icon "$AppleSUIcon" -title "Apple Software Updates" -description "$StandardUpdatePrompt" -button1 "Fortsetzen" -button2 "Verschieben" -cancelButton "2" -defaultButton 2 -timeout "$TimeOutinSec")
|
|
echo "Jamf Helper Exit Code: $HELPER"
|
|
|
|
# If they click "Update" then take them to the software update preference pane
|
|
if [ "$HELPER" == "0" ]; then
|
|
updateGUI
|
|
fi
|
|
|
|
exit 0
|
|
else
|
|
HELPER=$("$jamfHelper" -windowType utility -icon "$AppleSUIcon" -title "Apple Software Update" -description "$ForcedUpdatePrompt" -button1 "Update" -defaultButton 1 -timeout "$TimeOutinSec" -countdown -alignCountdown "right")
|
|
echo "Jamf Helper Exit Code: $HELPER"
|
|
# If they click Install Updates then run the updates
|
|
# Looks like someone tried to quit jamfHelper or the jamfHelper screen timed out
|
|
# The Timer is already 0, run the updates automatically, the end user has been warned!
|
|
if [[ "$HELPER" == "0" ]] || [[ "$HELPER" == "239" ]]; then
|
|
runUpdates
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Install updates that do not require a restart
|
|
# Future Fix: Might want to see if Safari and iTunes are running as sometimes these apps sometimes do not require a restart but do require that the apps be closed
|
|
# A simple stop gap to see if either process is running.
|
|
if [[ "$UpdatesNoRestart" != "" ]] && [[ ! "$(/bin/ps -axc | /usr/bin/grep -e Safari$)" ]] && [[ "$(/bin/ps -axc | /usr/bin/grep -e iTunes$)" ]]; then
|
|
powerCheck
|
|
updateCLI
|
|
fi
|
|
|
|
exit 0 |