Posted on 07-12-2019 08:59 AM
We're trying to upgrade our fleet of MacBook Airs to Mojave. We want to be able to send a command that will start the process on the boot of the device. We're NOT trying to erase and install, but to upgrade in place.
We have a script that works if the user is logged in, but we don't want to have to log in to all the user accounts to make it function as expected - we want the installer to begin from the login screen after an initial boot function. Is this possible?
This is the script we're using (it's slightly modified from the original):
OSInstaller="$4"
version="$5"
versionMajor=$( /bin/echo "$version" | /usr/bin/awk -F. '{print $2}' )
versionMinor=$( /bin/echo "$version" | /usr/bin/awk -F. '{print $3}' )
download_trigger="$6"
installESDChecksum="$7"
validChecksum=0
unsuccessfulDownload=0
eraseInstall="$8"
if [ "$eraseInstall" != "1" ]; then eraseInstall=0 ; fi
if [ "$versionMajor${versionMinor:=0}" -lt 134 ]; then
eraseInstall=0
fi
userDialog="$9"
if [ "$userDialog" != "1" ]; then userDialog=0 ; fi
if [ "$versionMajor" -ge 14 ]; then
# Installer of macOS 10.14 or later set cancel to auth reboot.
cancelFVAuthReboot=1
else
# Installer of macOS 10.13 or earlier try to do auth reboot.
cancelFVAuthReboot=0
fi
macOSname=$(/bin/echo "$OSInstaller" | /usr/bin/sed -E 's/(.)?Install(.).app/?/2/' | /usr/bin/xargs)
title="$macOSname Upgrade"
heading="Please wait as we prepare your computer for $macOSname..."
description="This process will take approximately 5-10 minutes.
Once completed your computer will reboot and begin the upgrade."
dldescription="We need to download $macOSname to your computer, this will
take several minutes."
dlPosition="ul"
icon="$OSInstaller/Contents/Resources/InstallAssistant.icns"
finishOSInstallScriptFilePath="/usr/local/jamfps/finishOSInstall.sh"
osinstallersetupdDaemonSettingsFilePath="/Library/LaunchDaemons/com.jamfps.cleanupOSInstall.plist"
osinstallersetupdAgentSettingsFilePath="/Library/LaunchAgents/com.apple.install.osinstallersetupd.plist"
acPowerWaitTimer="0"
declare -a sysRequirementErrors
warnIcon="/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertCautionIcon.icns"
errorIcon="/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertStopIcon.icns"
kill_process() {
processPID="$1"
if /bin/ps -p "$processPID" > /dev/null ; then
/bin/kill "$processPID"
wait "$processPID" 2>/dev/null
fi
}
wait_for_ac_power() {
local jamfHelperPowerPID
jamfHelperPowerPID="$1"
# Loop for "acPowerWaitTimer" seconds until either AC Power is detected or the timer is up
/bin/echo "Waiting for AC power..."
while [[ "$acPowerWaitTimer" -gt "0" ]]; do
if /usr/bin/pmset -g ps | /usr/bin/grep "AC Power" > /dev/null ; then
/bin/echo "Power Check: OK - AC Power Detected"
kill_process "$jamfHelperPowerPID"
return
fi
sleep 1
((acPowerWaitTimer--))
done
kill_process "$jamfHelperPowerPID"
sysRequirementErrors+=("Is connected to AC power")
/bin/echo "Power Check: ERROR - No AC Power Detected"
}
validate_power_status() {
##Check if device is on battery or ac power
##If not, and our acPowerWaitTimer is above 1, allow user to connect to power for specified time period
if /usr/bin/pmset -g ps | /usr/bin/grep "AC Power" > /dev/null ; then
/bin/echo "Power Check: OK - AC Power Detected"
else
if [[ "$acPowerWaitTimer" -gt 0 ]]; then
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -title "Waiting for AC Power Connection" -icon "$warnIcon" -description "Please connect your computer to power using an AC power adapter. This process will continue once AC power is detected." &
wait_for_ac_power "$!"
else
sysRequirementErrors+=("Is connected to AC power")
/bin/echo "Power Check: ERROR - No AC Power Detected"
fi
fi
}
validate_free_space() { ##Check if free space > 15GB (10.13) or 20GB (10.14+) osMajor=$( /usr/bin/sw_vers -productVersion | /usr/bin/awk -F. '{print $2}' ) osMinor=$( /usr/bin/sw_vers -productVersion | /usr/bin/awk -F. '{print $3}' ) if [[ $osMajor -eq 12 ]] || [[ $osMajor -eq 13 && $osMinor -lt 4 ]]; then freeSpace=$( /usr/sbin/diskutil info / | /usr/bin/grep "Available Space" | /usr/bin/awk '{print $6}' | /usr/bin/cut -c 2- ) else freeSpace=$( /usr/sbin/diskutil info / | /usr/bin/grep "Free Space" | /usr/bin/awk '{print $6}' | /usr/bin/cut -c 2- ) fi
requiredDiskSpaceSizeGB=$([ "$osMajor" -ge 14 ] && /bin/echo "20" || /bin/echo "15")
if [[ ${freeSpace%.} -ge $(( requiredDiskSpaceSizeGB 1000 1000 1000 )) ]]; then
/bin/echo "Disk Check: OK - ${freeSpace%.} Bytes Free Space Detected"
else
sysRequirementErrors+=("Has at least ${requiredDiskSpaceSizeGB}GB of Free Space")
/bin/echo "Disk Check: ERROR - ${freeSpace%.} Bytes Free Space Detected"
fi
}
verifyChecksum() {
if [ -n "$installESDChecksum" ]; then
osChecksum=$( /sbin/md5 -q "$OSInstaller/Contents/SharedSupport/InstallESD.dmg" )
if [ "$osChecksum" = "$installESDChecksum" ]; then
/bin/echo "Checksum: Valid"
validChecksum=1
return
else
/bin/echo "Checksum: Not Valid"
/bin/echo "Beginning new dowload of installer"
/bin/rm -rf "$OSInstaller"
/bin/sleep 2
downloadInstaller
fi
else
##Checksum not specified as script argument, assume true
validChecksum=1
return
fi
}
cleanExit() {
kill_process "$caffeinatePID"
## Remove Script
/bin/rm -f "$finishOSInstallScriptFilePath" 2>/dev/null
/bin/rm -f "$osinstallersetupdDaemonSettingsFilePath" 2>/dev/null
/bin/rm -f "$osinstallersetupdAgentSettingsFilePath" 2>/dev/null
exit "$1"
}
/usr/bin/caffeinate -dis &
caffeinatePID=$!
currentUser=$( /usr/bin/stat -f %Su /dev/console )
fvStatus=$( /usr/bin/fdesetup status | head -1 )
validate_power_status
validate_free_space
if [[ "${#sysRequirementErrors[@]}" -ge 1 ]]; then /bin/echo "Launching jamfHelper Dialog (Requirements Not Met)..." /Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -title "$title" -icon "$errorIcon" -heading "Requirements Not Met" -description "We were unable to prepare your computer for $macOSname. Please ensure your computer meets the following requirements:
$( /usr/bin/printf ' • %s ' "${sysRequirementErrors[@]}" )
If you continue to experience this issue, please contact the IT Support Center." -iconSize 100 -button1 "OK" -defaultButton 1
cleanExit 1
fi
/bin/mkdir -p /usr/local/jamfps
/bin/cat << EOF > "$finishOSInstallScriptFilePath"
/bin/rm -fr "$OSInstaller"
/bin/sleep 2
/usr/local/jamf/bin/jamf recon
/bin/rm -f "$osinstallersetupdAgentSettingsFilePath"
/bin/rm -f "$osinstallersetupdDaemonSettingsFilePath"
/bin/rm -fr /usr/local/jamfps
exit 0
EOF
/usr/sbin/chown root:admin "$finishOSInstallScriptFilePath"
/bin/chmod 755 "$finishOSInstallScriptFilePath"
/bin/cat << EOF > "$osinstallersetupdDaemonSettingsFilePath"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.jamfps.cleanupOSInstall</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>$finishOSInstallScriptFilePath</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
EOF
/usr/sbin/chown root:wheel "$osinstallersetupdDaemonSettingsFilePath"
/bin/chmod 644 "$osinstallersetupdDaemonSettingsFilePath"
if [ "$cancelFVAuthReboot" -eq 0 ]; then ##Determine Program Argument if [ "$osMajor" -ge 11 ]; then progArgument="osinstallersetupd" elif [ "$osMajor" -eq 10 ]; then progArgument="osinstallersetupplaind" fi
/bin/cat << EOP > "$osinstallersetupdAgentSettingsFilePath"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.apple.install.osinstallersetupd</string>
<key>LimitLoadToSessionType</key>
<string>Aqua</string>
<key>MachServices</key>
<dict>
<key>com.apple.install.osinstallersetupd</key>
<true/>
</dict>
<key>TimeOut</key>
<integer>300</integer>
<key>OnDemand</key>
<true/>
<key>ProgramArguments</key>
<array>
<string>$OSInstaller/Contents/Frameworks/OSInstallerSetup.framework/Resources/$progArgument</string>
</array>
</dict>
</plist>
EOP
##Set the permission on the file just made. /usr/sbin/chown root:wheel "$osinstallersetupdAgentSettingsFilePath" /bin/chmod 644 "$osinstallersetupdAgentSettingsFilePath"
fi
if [ "$userDialog" -eq 0 ]; then
/bin/echo "Launching jamfHelper as FullScreen..."
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType fs -title "" -icon "$icon" -heading "$heading" -description "$description" &
jamfHelperPID=$!
else
/bin/echo "Launching jamfHelper as Utility Window..."
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -title "$title" -icon "$icon" -heading "$heading" -description "$description" -iconSize 100 &
jamfHelperPID=$!
fi
if [ "$fvStatus" = "FileVault is On." ] &&
[ "$currentUser" != "root" ] &&
[ "$cancelFVAuthReboot" -eq 0 ] ; then
userID=$( /usr/bin/id -u "${currentUser}" )
/bin/launchctl bootstrap gui/"${userID}" /Library/LaunchAgents/com.apple.install.osinstallersetupd.plist
fi
/bin/echo "Launching startosinstall..."
if [ "$eraseInstall" -eq 1 ]; then
eraseopt='--eraseinstall'
/bin/echo "Script is configured for Erase and Install of macOS."
fi
osinstallLogfile="/var/log/startosinstall.log"
if [ "$versionMajor" -ge 14 ]; then
eval ""$OSInstaller/Contents/Resources/startosinstall"" "$eraseopt" --agreetolicense --nointeraction --pidtosignal "$jamfHelperPID" >> "$osinstallLogfile" 2>&1 &
else
eval ""$OSInstaller/Contents/Resources/startosinstall"" "$eraseopt" --applicationpath ""$OSInstaller"" --agreetolicense --nointeraction --pidtosignal "$jamfHelperPID" >> "$osinstallLogfile" 2>&1 &
fi
/bin/sleep 3
cleanExit 0
Posted on 07-12-2019 09:43 AM
@micah002 Note the Read Me at https://github.com/kc9wwh/macOSUpgrade:
This workflow will not work if a user is not logged in since the startosinstall binary requires a user to be logged in. Tested with macOS 10.13.4 and you will get errors in that the process couldn't establish a connection to the WindowServer.
Posted on 07-12-2019 11:21 AM
Hello, tested today with a 10.13.6 machine left at login window
If you create a recurring/correctly scoped policy to drop the full "Install macOS Mojave" installer within Applications folder (using a .dmg) and then - within the same policy - add a "Files and Processes / EXECUTE COMMAND
/Applications/Install macOS Mojave.app/Contents/Resources/startosinstall --agreetolicense --nointeraction
it works at login window although you then have to wait the installation to finish at first login (it says something like 15 mins)*
This is the same issue that used to happen with High Sierra upgrades done the same way, I am quite sure if you leave the computer on for a while (say a couple of hours), it should complete by itself but have not tested yet with Mojave
Hope it helps
Ciao
Carlo
PS: Might also work sending the one liner with ARD to the target if Mojave installer is already there but again jave not tested it
Posted on 07-12-2019 11:21 AM
Posted on 07-12-2019 11:26 AM
@carlo.anselmi If you're using FileVault 2 encryption there's no way to do an authenticated restart if no user is logged in, which is probably why startosinstall
wants a logged in user.
Posted on 07-12-2019 11:37 AM
Oh yup, that’s absolutely right
I took for granted target was not encrypted
Thank you!