283 lines
12 KiB
Bash
Executable File
283 lines
12 KiB
Bash
Executable File
#!/bin/bash
|
|
##########################################################################
|
|
# Shellscript : macOS Update with deferral
|
|
# Autor : Andreas Vogel, NEXT Enterprise gmbh, 2021
|
|
##########################################################################
|
|
# set -x
|
|
setDeferral (){
|
|
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 "Incorrect deferral type used."
|
|
exit 14
|
|
fi
|
|
}
|
|
|
|
OSMajorVersion="$(/usr/bin/sw_vers -productVersion | /usr/bin/cut -d '.' -f 1)"
|
|
DeferralPlist="/Library/Application Support/JAMF/com.custom.deferrals.plist"
|
|
BundleID="com.apple.SoftwareUpdate"
|
|
DeferralType="count"
|
|
|
|
DeferralValue="${4}"
|
|
if [[ -z "$DeferralValue" ]]; then
|
|
DeferralValue=3
|
|
fi
|
|
|
|
TimeOutinSec="${5}"
|
|
if [[ -z "$TimeOutinSec" ]]; then
|
|
TimeOutinSec="900"
|
|
fi
|
|
|
|
ITContact="${6}"
|
|
|
|
if [[ -z "$ITContact" ]]; then
|
|
ITContact="IT"
|
|
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"
|
|
|
|
AppleSUIcon="/System/Library/CoreServices/Install Command Line Developer Tools.app/Contents/Resources/SoftwareUpdate.icns"
|
|
|
|
CURRENT_USER=$(/usr/bin/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");')
|
|
realname=$(dscl . read /Users/$CURRENT_USER RealName | tail -n1)
|
|
|
|
ListOfSoftwareUpdates="/tmp/ListOfSoftwareUpdates"
|
|
|
|
|
|
######################### Messages #################################################
|
|
######################### find Language ############################################
|
|
Language=$(/usr/libexec/PlistBuddy -c 'print AppleLanguages:0' "/Users/${CURRENT_USER}/Library/Preferences/.GlobalPreferences.plist")
|
|
if [[ $Language = de* ]]; then
|
|
UserLanguage="de"
|
|
else
|
|
UserLanguage="en"
|
|
fi
|
|
|
|
######################### Message Box DE ###########################################
|
|
no_ac_power_de="Der Computer läuft derzeit im Akkubetrieb und ist nicht an eine Stromquelle angeschlossen. Schließen Sie den Computer an eine Stromquelle an und versuchen Sie es erneut."
|
|
|
|
StandardUpdatePrompt_de="Hallo, $realname für deinen Mac ist ein Betriebssystem-Update verfügbar. Klicken auf Fortfahren, um mit der Softwareaktualisierung fortzufahren und die Aktualisierung auszuführen. Wenn du den Vorgang zu diesem Zeitpunkt nicht starten kannst, kannst du den Vorgang um einen Tag verschieben. Wenn die Anzahl der Aufschübe aufgebraucht ist, wird die Aktualisierung erzwungen. Die Aktualisierung dauert etwa 30 Minuten.
|
|
|
|
Du kannst das macOS Software-Update jederzeit installieren
|
|
|
|
gehe hierzu > System Preferences > Software Update
|
|
|
|
|
|
Verbleibende Anzahl der Verschiebungen: $CurrentDeferralValue"
|
|
|
|
ForcedUpdatePrompt_de="Hallo, $realname für deinen Mac sind Betriebssystem-Updates verfügbar, die einen Neustart erfordern. Du hast die Aktualisierungen bereits so oft wie möglich verschoben.
|
|
Bitte speichern deine Arbeit und klicken Sie auf Aktualisieren. Andernfalls wird diese Meldung ausgeblendet und der Computer wird automatisch neu gestartet."
|
|
|
|
PasswordInvalid_de="Passwort ungültig"
|
|
PasswordInvalidDescription_de="Du hast dreimal versucht, ein falsches Passwort einzugeben"
|
|
|
|
button1_de="Fortfahren"
|
|
button2_de="verschieben"
|
|
|
|
######################### Message Box EN ###########################################
|
|
no_ac_power_en="The computer is currently running on battery power and is not connected to a power source. Connect the computer to a power source and try again."
|
|
|
|
StandardUpdatePrompt_en="Hello, $realname for your Mac is an operating system update available. Click Continue to proceed with the software update and run the update. If you are unable to start the process at this time, you can postpone the process for one day. When the number of postponements is used up, the update will be forced. The update takes about 30 minutes to complete.
|
|
|
|
You can install the macOS software update at any time
|
|
|
|
by navigate > System Preferences > Software Update
|
|
|
|
|
|
Remaining number of shifts: $CurrentDeferralValue"
|
|
|
|
ForcedUpdatePrompt_en="Hello, $realname for your Mac operating system updates are available that require a reboot. You have already moved the updates as many times as possible.
|
|
Please save your work and click Update. Otherwise, this message will disappear and the computer will restart automatically."
|
|
|
|
PasswordInvalid_en="Password Invalid"
|
|
PasswordInvalidDescription_en="You made three incorrect password attempts"
|
|
|
|
button1_en="Continue"
|
|
button2_en="delay"
|
|
|
|
|
|
|
|
|
|
no_ac_power=no_ac_power_${UserLanguage}
|
|
StandardUpdatePrompt=StandardUpdatePrompt_${UserLanguage}
|
|
ForcedUpdatePrompt=ForcedUpdatePrompt_${UserLanguage}
|
|
PasswordInvalid=PasswordInvalid_${UserLanguage}
|
|
PasswordInvalidDescription=PasswordInvalidDescription_${UserLanguage}
|
|
button1=button1_${UserLanguage}
|
|
button2=button2_${UserLanguage}
|
|
|
|
|
|
## 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
|
|
}
|
|
|
|
runsoftwareupdate () {
|
|
expect <<EOF
|
|
spawn /usr/sbin/softwareupdate -iaR
|
|
expect "Password*"
|
|
send "$USER_PASS\r"
|
|
expect "Password:"
|
|
send "$USER_PASS\r"
|
|
expect EOF
|
|
EOF
|
|
}
|
|
|
|
updateCLI (){
|
|
LoggedInUser=`ls -l /dev/console | cut -d " " -f4`
|
|
archType=$(/usr/bin/arch)
|
|
CURRENT_USER="$(stat -f%Su /dev/console)"
|
|
USER_ID=$(id -u "$CURRENT_USER")
|
|
jamfHelper="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"
|
|
|
|
runAsUser() {
|
|
if [ "$LoggedInUser" != "loginwindow" ]; then
|
|
launchctl asuser "$uid" sudo -u "$LoggedInUser" "$@"
|
|
else
|
|
echo "no user logged in"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
#Prompting logged in user for password
|
|
USER_PASS="$(launchctl asuser "$USER_ID" osascript -e 'display dialog "Please enter your Mac password to begin the Update:" default answer "" with title "macOS Update" giving up after 86400 with text buttons {"OK"} default button 1 with hidden answer' -e 'return text returned of result')"
|
|
|
|
|
|
TRY=1
|
|
until dscl /Search -authonly "$CURRENT_USER" "$USER_PASS" &>/dev/null; do
|
|
(( TRY++ ))
|
|
echo "Prompting $CURRENT_USER for their Mac password (attempt $TRY)..."
|
|
USER_PASS="$(launchctl asuser "$USER_ID" osascript -e 'display dialog "Please enter your Mac password to begin the Update:" default answer "" with title "macOS Update" giving up after 86400 with text buttons {"OK"} default button 1 with hidden answer' -e 'return text returned of result')"
|
|
if (( TRY >= 3 )); then
|
|
echo "[ERROR] Password prompt unsuccessful after 3 attempts."
|
|
|
|
HELPER=$("$jamfHelper" -windowType utility -title "${!PasswordInvalid}" -description "${!PasswordInvalidDescription}" -button1 "OK" -defaultButton 1 -timeout "300" -countdown -alignCountdown "right")
|
|
echo "Jamf Helper Exit Code: $HELPER"
|
|
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
if [ $archType == "i386" ]; then
|
|
echo "Running updates for Intel Mac"
|
|
/usr/bin/su -l $LoggedInUser -c "echo $USER_PASS | sudo -S /usr/sbin/softwareupdate -iaR --verbose" | tee -a /var/log/jamf.log
|
|
else
|
|
echo "Running updates for Apple Silicon Mac"
|
|
runsoftwareupdate
|
|
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 "The encryption process is not yet complete."
|
|
echo "$FVStatus"
|
|
exit 13
|
|
fi
|
|
}
|
|
|
|
|
|
# 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="$(/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
|
|
|
|
##################################################################################################
|
|
############################ RUN Update ##########################################################
|
|
##################################################################################################
|
|
if [[ "$LoggedInUser" == "" ]]; then
|
|
echo "NO one is logged in"
|
|
exit 0
|
|
else
|
|
powerCheck
|
|
# 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.
|
|
HELPER=$("$jamfHelper" -windowType utility -icon "$AppleSUIcon" -title "Apple Software Updates" -description "${!StandardUpdatePrompt}" -button1 "${!button1}" -button2 "${!button2}" -cancelButton "2" -defaultButton 2 -timeout "$TimeOutinSec" -countdown -alignCountdown "right")
|
|
echo "Jamf Helper Exit Code: $HELPER"
|
|
|
|
if [ "$HELPER" == "0" ]; then
|
|
updateCLI
|
|
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 [[ "$HELPER" == "0" ]] || [[ "$HELPER" == "239" ]]; then
|
|
updateCLI
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
|
|
exit 0 |