Recurring check-in vs sudo Jamf recon

Hermenegildah
New Contributor II

We have a Catalina upgrade script that allow users to defer the installation for 1, 2, and 3 hours, checks for free space on the HD and for computer power status (skips laptops not plugged in to PS and systems with less than 20GB usable space). Policy is set as recurring check-in, once every day, executed at 4PM (client-side limitations). 

The script runs, log looks good, nothing happens.

I flush the policy on that system, run sudo Jamf policy, I see the same prompts (deferral), and... the upgrade starts happening. Same script.

I have modified the policy, removing the 4PM setting, still nothing. Flushing the policy and running sudo Jamf policy works again.

The installer package works for deployment with no script (execute command, /Applications/Install\ macOS\ Catalina.app/Contents/Resources/startosinstall) so I don't think there is something wrong with it. 

 

Is there any difference between policies that are forced manually and those scheduled to run at recurring check-ins? And if yes, what is it?

I can provide the script in question, of course. Any advice will be greatly appreciated. 

 

H.

3 REPLIES 3

Hermenegildah
New Contributor II
#!/bin/zsh
#
# Utility - OS Upgrade - Deadline.sh
#
#
 
# Enforces a macOS upgrade. Requires a Jamf policy to cache an "Install macOS" app on the user's Mac.
# Before starting, checks:
#  * Current OS version does not match the target upgrade version already.
#
# Before installing, checks:
#  * If user is running on a MacBook. If so: if they are running on battery or not.
#  * If the installer app exists.
#  * If there is enough free space in GB after caching the installer app.
#  * If no user is logged in: start upgrade immediately.
#
# Creates a first-boot script and launch daemon to run the script.
# The script runs after a successful upgrade to clean up the installer app and immediately try to submit a Jamf recon.
# Jamf recon may not succeed in all cases if network is not up at the time the recon tries to run.
#
# Script logs: /Library/Logs/AG/
# OS upgrade log: /var/log/startosinstall.log
 
# Exit Codes:
#  0: Success
#  1: Generic error
#  2: OS already up to date
#  3: Failed to cache OS installer
#  4: Not enough free space
#  5: Running on MacBook without AC power
 
 
########################################
# Global Variables
########################################
osUpgradeName="${4}" # e.g. 'macOS 12 Monterey'
osUpgradeMajorVersion="${5}" # For macOS 11 and newer just use the major digit like '11' ; For older use the first two, like '10.15'
osUpgradeAppPath="${6}" # e.g. '/Applications/Install macOS Monterey.app'
requiredInstallSpace=${7} # Space needed for installation of the upgrade in GB
jamfPolicyCacheInstaller="${8}" # Jamf policy trigger to cache the desired macOS installer
 
notificationName="AG-Utility-OSUpgrade-Deadline" # Name of notification - for logging purposes
titleTxt="${osUpgradeName} Upgrade" # Notification window title and heading text
iconPath="/Library/Application Support/AGCustom/Generic_Vertical_Wordmark.ico" # Icon shown in notification window
loggedInUser=$( /bin/echo "show State:/Users/ConsoleUser" | /usr/sbin/scutil | /usr/bin/awk '/Name :/ && ! /loginwindow/ { print $3 }' ) # Get the current user - shell agnostic (sh, bash, zsh, dash)
myJamfHelper="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper" # Short variable for using jamfHelper
 
caffeinatePID="" # Set up for caffeinate later
freeSpace=$(/bin/df -g / | /usr/bin/awk 'FNR==2{print $4}') # Free space in GB
originalOsVer=$(/usr/bin/sw_vers -productVersion) # OS version for comparing to in post-upgrade script - used to determine if cleanup should be done
finishOSInstallScriptFilePath="/usr/local/jamfps/finishOSInstall.sh" # Post-upgrade script to remove the installers and recon
osinstallersetupdDaemonSettingsFilePath="/Library/LaunchDaemons/com.jamfps.cleanupOSInstall.plist" # Post-upgrade launch daemon to start script above
osinstallLogfile="/var/log/startosinstall.log" # Output from macOS installer
########################################
 
########################################
# Logging
########################################
logPath="/Library/Logs/AG"
logFile="${logPath}/${notificationName}.log"
 
if [ ! -d "$logPath" ]; then
/bin/mkdir -p "$logPath"
fi
 
WriteLog() {
logTxt=$1
/bin/echo "$(/bin/date +%Y-%m-%d_%H:%M:%S) : ${logTxt}" | /usr/bin/tee -a "$logFile"
}
########################################
 
########################################
# Support Functions
########################################
# Kill a process by PID if it is found
KillProcess() {
    processPID="$1"
    if /bin/ps -p "$processPID" > /dev/null ; then
        /bin/kill "$processPID"
        wait "$processPID" 2>/dev/null
    fi
}
 
# Clean up processes and files and exit with the given exit code
CleanExit() {
    WriteLog "INFO: Running clean exit with exit code ($1)."
    # Kill caffeinate process
    if [ -n "$caffeinatePID" ]; then
        WriteLog "INFO: Killing caffeinate process ($caffeinatePID)."
        KillProcess "$caffeinatePID"
    fi
    # Remove first time login daemon and script
    if [ -f "$finishOSInstallScriptFilePath" ]; then
        WriteLog "INFO: Deleting ($finishOSInstallScriptFilePath)."
        /bin/rm -f "$finishOSInstallScriptFilePath"
    fi
    if [ -f "$osinstallersetupdDaemonSettingsFilePath" ]; then
        WriteLog "INFO: Deleting ($osinstallersetupdDaemonSettingsFilePath)."
        /bin/rm -f "$osinstallersetupdDaemonSettingsFilePath"
    fi
    exit "$1"
}
 
# If previous processes remain for some reason, the installation will freeze, so kill those proccesses.
ProcessCleanup() {
    killingProcesses=("caffeinate" "startosinstall" "osinstallersetupd")
    for processName in "${killingProcesses[@]}"; do
        [ -z "$processName" ] && continue
        WriteLog "INFO: Killing ($processName) processes."
        /usr/bin/killall "$processName" 2>&1 || true
    done
}
 
# Used to show an error message to the user appended with standard information.
# Parameter should be a string of text you want to show the user.
ShowError()
{
    if [ "$loggedInUser" != "" ] && [ "$loggedInUser" != "_mbsetupuser" ] && [ "$loggedInUser" != "root" ] && [ "$loggedInUser" != "loginwindow" ]; then
        errorTxt=$1
        /usr/bin/osascript -e "display dialog \"$errorTxt 
}

Hermenegildah
New Contributor II
#!/bin/zsh
#
# Utility - OS Upgrade - Deadline.sh
#
#
 
# Enforces a macOS upgrade. Requires a Jamf policy to cache an "Install macOS" app on the user's Mac.
# Before starting, checks:
#  * Current OS version does not match the target upgrade version already.
#
# Before installing, checks:
#  * If user is running on a MacBook. If so: if they are running on battery or not.
#  * If the installer app exists.
#  * If there is enough free space in GB after caching the installer app.
#  * If no user is logged in: start upgrade immediately.
#
# Creates a first-boot script and launch daemon to run the script.
# The script runs after a successful upgrade to clean up the installer app and immediately try to submit a Jamf recon.
# Jamf recon may not succeed in all cases if network is not up at the time the recon tries to run.
#
# Script logs: /Library/Logs/AG/
# OS upgrade log: /var/log/startosinstall.log
 
# Exit Codes:
#  0: Success
#  1: Generic error
#  2: OS already up to date
#  3: Failed to cache OS installer
#  4: Not enough free space
#  5: Running on MacBook without AC power
 
 
########################################
# Global Variables
########################################
osUpgradeName="${4}" # e.g. 'macOS 12 Monterey'
osUpgradeMajorVersion="${5}" # For macOS 11 and newer just use the major digit like '11' ; For older use the first two, like '10.15'
osUpgradeAppPath="${6}" # e.g. '/Applications/Install macOS Monterey.app'
requiredInstallSpace=${7} # Space needed for installation of the upgrade in GB
jamfPolicyCacheInstaller="${8}" # Jamf policy trigger to cache the desired macOS installer
 
notificationName="AG-Utility-OSUpgrade-Deadline" # Name of notification - for logging purposes
titleTxt="${osUpgradeName} Upgrade" # Notification window title and heading text
iconPath="/Library/Application Support/AGCustom/XXXXXXX_XXXX_Vertical_Wordmark.ico" # Icon shown in notification window
loggedInUser=$( /bin/echo "show State:/Users/ConsoleUser" | /usr/sbin/scutil | /usr/bin/awk '/Name :/ && ! /loginwindow/ { print $3 }' ) # Get the current user - shell agnostic (sh, bash, zsh, dash)
myJamfHelper="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper" # Short variable for using jamfHelper
 
caffeinatePID="" # Set up for caffeinate later
freeSpace=$(/bin/df -g / | /usr/bin/awk 'FNR==2{print $4}') # Free space in GB
originalOsVer=$(/usr/bin/sw_vers -productVersion) # OS version for comparing to in post-upgrade script - used to determine if cleanup should be done
finishOSInstallScriptFilePath="/usr/local/jamfps/finishOSInstall.sh" # Post-upgrade script to remove the installers and recon
osinstallersetupdDaemonSettingsFilePath="/Library/LaunchDaemons/com.jamfps.cleanupOSInstall.plist" # Post-upgrade launch daemon to start script above
osinstallLogfile="/var/log/startosinstall.log" # Output from macOS installer
########################################
 
########################################
# Logging
########################################
logPath="/Library/Logs/AG"
logFile="${logPath}/${notificationName}.log"
 
if [ ! -d "$logPath" ]; then
/bin/mkdir -p "$logPath"
fi
 
WriteLog() {
logTxt=$1
/bin/echo "$(/bin/date +%Y-%m-%d_%H:%M:%S) : ${logTxt}" | /usr/bin/tee -a "$logFile"
}
########################################
 
########################################
# Support Functions
########################################
# Kill a process by PID if it is found
KillProcess() {
    processPID="$1"
    if /bin/ps -p "$processPID" > /dev/null ; then
        /bin/kill "$processPID"
        wait "$processPID" 2>/dev/null
    fi
}
 
# Clean up processes and files and exit with the given exit code
CleanExit() {
    WriteLog "INFO: Running clean exit with exit code ($1)."
    # Kill caffeinate process
    if [ -n "$caffeinatePID" ]; then
        WriteLog "INFO: Killing caffeinate process ($caffeinatePID)."
        KillProcess "$caffeinatePID"
    fi
    # Remove first time login daemon and script
    if [ -f "$finishOSInstallScriptFilePath" ]; then
        WriteLog "INFO: Deleting ($finishOSInstallScriptFilePath)."
        /bin/rm -f "$finishOSInstallScriptFilePath"
    fi
    if [ -f "$osinstallersetupdDaemonSettingsFilePath" ]; then
        WriteLog "INFO: Deleting ($osinstallersetupdDaemonSettingsFilePath)."
        /bin/rm -f "$osinstallersetupdDaemonSettingsFilePath"
    fi
    exit "$1"
}
 
# If previous processes remain for some reason, the installation will freeze, so kill those proccesses.
ProcessCleanup() {
    killingProcesses=("caffeinate" "startosinstall" "osinstallersetupd")
    for processName in "${killingProcesses[@]}"; do
        [ -z "$processName" ] && continue
        WriteLog "INFO: Killing ($processName) processes."
        /usr/bin/killall "$processName" 2>&1 || true
    done
}
 
# Used to show an error message to the user appended with standard information.
# Parameter should be a string of text you want to show the user.
ShowError()
{
    if [ "$loggedInUser" != "" ] && [ "$loggedInUser" != "_mbsetupuser" ] && [ "$loggedInUser" != "root" ] && [ "$loggedInUser" != "loginwindow" ]; then
        errorTxt=$1
        /usr/bin/osascript -e "display dialog \"$errorTxt 
    
The ${osUpgradeName} upgrade script has failed. Please submit a ticket to your XXXX OIT Service Area for assistance at https://help.xxxx.xxxxxxx.edu
 
The XXXX OIT ticket submission form will open in your default browser after you exit this dialog.\" buttons {\"Exit\"} default button \"Exit\" with title \"Error: Upgrade Script Failed\" with icon POSIX file \"$iconPath\""
    
        /usr/bin/sudo -u "$loggedInUser" /usr/bin/open 
    fi
}
 
 
### Create First Boot Script ###
 
# Creates a first boot script under /usr/local/jamfps
# Script cleans up macOS installer app if an upgrade succeeded and also runs a recon
# Deletes itself and the launch daemon that calls it
 
# Because the parent shell script creates a new shell script using HEREDOC ( this << EOF >that ),
# use a backslash before the dollar sign to avoid evaluating the variable (or command) as part of creating the script.
# Note: we WANT to evaluate some variables from this parent script (e.g. the daemon settings path)
CreateFirstBootScript() {
    WriteLog "INFO: Creating first boot script to run a Jamf recon and delete installer app on successful upgrade."
    /bin/mkdir -p /usr/local/jamfps
 
    /bin/cat << EOF > "$finishOSInstallScriptFilePath"
#!/bin/zsh
## First Run Script to remove the installer.
## Wait until /var/db/.AppleUpgrade disappears
while [ -e /var/db/.AppleUpgrade ];
do
echo "\$(date "+%a %h %d %H:%M:%S"): Waiting for /var/db/.AppleUpgrade to disappear." >> /usr/local/jamfps/firstbootupgrade.log
    /bin/sleep 60
done
## Wait until the upgrade process completes
INSTALLER_PROGRESS_PROCESS=\$(/usr/bin/pgrep -l "Installer Progress")
until [ "\$INSTALLER_PROGRESS_PROCESS" = "" ];
do
echo "\$(date "+%a %h %d %H:%M:%S"): Waiting for Installer Progress to complete." >> /usr/local/jamfps/firstbootupgrade.log
    /bin/sleep 1
    INSTALLER_PROGRESS_PROCESS=\$(/usr/bin/pgrep -l "Installer Progress")
done
 
## Clean up installer app and recon if we upgraded
osVer=\$(/usr/bin/sw_vers -productVersion)
if [ "\$osVer" != "$originalOsVer" ]; then
    if [ -e "$osUpgradeAppPath" ]; then
        /bin/rm -rf "$osUpgradeAppPath"
    fi
    /usr/local/jamf/bin/jamf recon
fi
 
## Remove LaunchDaemon
if [ -e "$osinstallersetupdDaemonSettingsFilePath" ]; then
    /bin/rm -f "$osinstallersetupdDaemonSettingsFilePath"
fi
## Remove Script
if [ -e "$finishOSInstallScriptFilePath" ]; then
    /bin/rm -f "$finishOSInstallScriptFilePath"
fi
 
exit 0
EOF
 
    /usr/sbin/chown root:admin "$finishOSInstallScriptFilePath"
    /bin/chmod 755 "$finishOSInstallScriptFilePath"
}
 
### Create Launch Daemon ###
 
# Create launch daemon to kick off our first boot script above
CreateLaunchDaemon() {
    /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/zsh</string>
        <string>-c</string>
        <string>$finishOSInstallScriptFilePath</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>
EOF
 
    # Set the permission on the file just made.
    /usr/sbin/chown root:wheel "$osinstallersetupdDaemonSettingsFilePath"
    /bin/chmod 644 "$osinstallersetupdDaemonSettingsFilePath"
}
########################################
 
########################################
# Pre-check
########################################
# If we're already on our target OS version - stop
# Check if the full detected OS version starts with our desired major OS version number
if echo "$originalOsVer" | /usr/bin/grep -q "^$osUpgradeMajorVersion"; then
    WriteLog "WARN: Upgrade target is macOS ${osUpgradeMajorVersion}. Mac is running macOS ${originalOsVer}. Stopping."
    CleanExit 2
fi
########################################
 
########################################
# Main Script
########################################
# Reset ignored software updates
WriteLog "INFO: Resetting ignored software updates."
/usr/sbin/softwareupdate --reset-ignored

Hermenegildah
New Contributor II
### Start upgrade checks ###
 
WriteLog "INFO: Starting upgrade checks."
# Installer available check
WriteLog "INFO: Checking if (${osUpgradeAppPath}) exists."
if [ ! -e "$osUpgradeAppPath" ]; then
    WriteLog "WARN: (${osUpgradeAppPath}) not found."
    WriteLog "INFO: Calling Jamf policy to cache ${osUpgradeName} app."
    /usr/local/bin/jamf policy --event "$jamfPolicyCacheInstaller"
    if [ ! -e "$osUpgradeAppPath" ]; then
        WriteLog "ERROR: Jamf policy to cache ${osUpgradeName} app failed. Stopping."
        CleanExit 3
    fi
else
    WriteLog "INFO: Found (${osUpgradeAppPath}). Continuing."
fi
 
# Free space check
WriteLog "INFO: Checking if ${requiredInstallSpace} GB of free space is available."
if [ $((freeSpace)) -lt $((requiredInstallSpace)) ]; then
    WriteLog "WARN: Less than ${requiredInstallSpace} GB free space. Cannot install. Stopping."
    CleanExit 4
else
    WriteLog "INFO: ${freeSpace} GB available. Continuing."
fi
 
# AC power check
# Check battery state if we're on a MacBook
WriteLog "INFO: Checking if we're running on a MacBook. If so, checking we have AC power."
if /usr/sbin/system_profiler SPHardwareDataType | /usr/bin/grep -q "Book"; then # Fairly quick system_profiler query due to limiting to SPHardwareDataType
    WriteLog "INFO: Running on a MacBook. Checking AC power state."
    onACPower=$(/usr/sbin/ioreg -l | /usr/bin/grep ExternalConnected | /usr/bin/cut -d"=" -f2 | /usr/bin/sed -e 's/ //g') # Returns "Yes" or "No"
    if [ "$onACPower" = "No" ]; then
        WriteLog "WARN: Not on AC power. Stopping."
        CleanExit 5
    fi
else
    WriteLog "INFO: Not running on a MacBook. Skipping AC power state check."
fi
 
### End upgrade checks ###
 
# If no console user logged in start silent upgrade process
if [ "$loggedInUser" = "" ] || [ "$loggedInUser" = "_mbsetupuser" ] || [ "$loggedInUser" = "root" ] || [ "$loggedInUser" = "loginwindow" ]; then
WriteLog "INFO: No active console user. Starting upgrade."
 
    if [ -f "$osinstallLogfile" ]; then
        WriteLog "INFO: Clearing out old OS install log file."
        /bin/rm -f "$osinstallLogfile"
    fi
 
    # Cleanup any old upgrade processes
    ProcessCleanup
 
    # Caffeinate to prevent screen sleep during upgrade
    # Capture the caffeinate process ID for ending later
    /usr/bin/caffeinate -dis &!
    caffeinatePID=$!
 
    # Create our boot script and corresponding launch daemon to recon and clean up if we successfully upgrade
    CreateFirstBootScript
    CreateLaunchDaemon
    
    #set -m
    "${osUpgradeAppPath}/Contents/Resources/startosinstall" --agreetolicense --nointeraction >> $osinstallLogfile 2>&1 &!
    #set +m
 
    WriteLog "INFO: startosinstall started and detached from script. Log will be written to (${osinstallLogfile}) Exiting script."
    exit 0
fi
 
# Pull down our logo if it isn't cached
if [ ! -f "$iconPath" ]; then
    WriteLog "WARN: Icon file missing. Calling Jamf policy to download icon file."
    /usr/local/bin/jamf policy --event agLogo
fi
 
# Show initial dialog with delay choices
WriteLog "INFO: Starting macOS upgrade deadline notification for user (${loggedInUser})."
notificationResponse=$("$myJamfHelper" -windowType utility -button1 OK -showDelayOptions "0, 3600, 7200, 10800, 14400" -timeout 14400 -countdown -alignCountdown left -icon "${iconPath}" -iconSize 200 -title "${titleTxt}" -heading "${titleTxt}" -description "Your Mac will now upgrade to ${osUpgradeName}. To prevent any unintended data loss, please save your work and quit any running apps before clicking OK.
 
Do not manually power off or sleep your Mac until the upgrade is complete. You must remain connected to the internet to complete the upgrade.
 
You may delay the upgrade for up to 4 hours. After your selected delay expires the upgrade will automatically begin and quit any running apps.")
 
WriteLog "INFO: jamfHelper return code was (${notificationResponse})"
 
# Return code with showDelayOption AND timeout should be 1 for both: no delay choice and notification timeout
# Other return codes will be the timeout with "1" appended, e.g. 18001 for 1800 seconds
# No other return codes should happen
# For delays: subtract 30 minutes (1800 seconds) from the sleep to show a final countdown dialog later
if [ "$notificationResponse" = "1" ]; then
    WriteLog "INFO: (${loggedInUser}) chose to start upgrade immediately."
#set -m
    "$myJamfHelper" -windowType utility -icon "${iconPath}" -iconSize 200 -title "${titleTxt}" -heading "Upgrade Starting" -description "The ${osUpgradeName} upgrade has started. Your Mac will automatically restart to continue. This may take up to 1 hour. Do not manually restart or shut down.
 
Please wait." &!
    
    jamfHelperPID=$! # Capture the jamfHelper process ID to feed to 'startosinstall' as the process ID to end once reboot is about to begin
 
    if [ -f "$osinstallLogfile" ]; then
        WriteLog "INFO: Clearing out old OS install log file."
        /bin/rm -f "$osinstallLogfile"
    fi
 
    # Cleanup any old upgrade processes
    ProcessCleanup
 
    # Caffeinate to prevent screen sleep during upgrade
    # Capture the caffeinate process ID for ending later
    /usr/bin/caffeinate -dis &!
    caffeinatePID=$!
 
    # Create our boot script and corresponding launch daemon to recon and clean up if we successfully upgrade
    CreateFirstBootScript
    CreateLaunchDaemon
 
    "${osUpgradeAppPath}/Contents/Resources/startosinstall" --agreetolicense --forcequitapps --pidtosignal $jamfHelperPID >> $osinstallLogfile 2>&1 &!
    #set +m
    /bin/sleep 3
    WriteLog "INFO: startosinstall started and detached from script. Log will be written to (${osinstallLogfile}) Exiting script."
    exit 0
elif [ "$notificationResponse" = "36001" ]; then
WriteLog "INFO: (${loggedInUser}) chose 1 hour delay. Sleeping for 1800 seconds and showing final countdown at 30 minutes remaining."
    /bin/sleep 2700
elif [ "$notificationResponse" = "72001" ]; then
WriteLog "INFO: (${loggedInUser}) chose 2 hour delay. Sleeping for 5400 seconds and showing final countdown at 30 minutes remaining."
    /bin/sleep 5400
elif [ "$notificationResponse" = "108001" ]; then
WriteLog "INFO: (${loggedInUser}) chose 3 hour delay. Sleeping for 9000 seconds and showing final countdown at 30 minutes remaining."
    /bin/sleep 9000
elif [ "$notificationResponse" = "144001" ]; then
WriteLog "INFO: (${loggedInUser}) chose 4 hour delay. Sleeping for 12600 seconds and showing final countdown at 30 minutes remaining."
    /bin/sleep 12600
else
    WriteLog "ERROR: Unexpected return code (${notificationResponse}) from jamfHelper. Exiting."
    ShowError "A notification window exited in an unexpected way."
    CleanExit 1
fi
 
# If a delay was chosen, show a follow-up dialog with 30 minutes (1800 seconds) remaining
WriteLog "INFO: Delay expired. Showing final notification."
notificationResponse=$("$myJamfHelper" -windowType utility -button1 "Upgrade" -timeout 1800 -countdown -alignCountdown right -icon "${iconPath}" -iconSize 200 -title "${titleTxt}" -heading "${titleTxt}" -description "Your Mac will now upgrade to ${osUpgradeName}. To prevent any unintended data loss, please save your work and quit any running apps before clicking Upgrade.
 
Do not power off or sleep your Mac until the upgrade is complete. You must remain connected to the internet to complete the upgrade.")
WriteLog "INFO: Result was (${notificationResponse})"
 
# Return code with only a timeout should be 0 for both timeout and upgrade button click
# No other return codes should happen without an error
# 'set -m' forces subsequent backgrounded commands to detach from the parent script and continue running after script exit
# More reliable than 'nohup' in varied circumstances
if [ "$notificationResponse" = "0" ]; then
    WriteLog "INFO: Delay expired or (${loggedInUser}) clicked 'Upgrade'."
    #set -m
    "$myJamfHelper" -windowType utility -icon "${iconPath}" -iconSize 200 -title "${titleTxt}" -heading "Upgrade Starting" -description "The ${osUpgradeName} upgrade has started. Your Mac will automatically restart to continue. This may take up to 1 hour. Do not manually restart or shut down.
 
Please wait." &!
jamfHelperPID=$!
    if [ -f "$osinstallLogfile" ]; then
        WriteLog "INFO: Clearing out old OS install log file."
        /bin/rm -f "$osinstallLogfile"
    fi
 
    # Cleanup any old upgrade processes
    ProcessCleanup
 
    # Caffeinate to prevent screen sleep during upgrade
    # Capture the caffeinate process ID for ending later
    /usr/bin/caffeinate -dis &!
    caffeinatePID=$!
 
    # Create our boot script and corresponding launch daemon to recon and clean up if we successfully upgrade
    CreateFirstBootScript
    CreateLaunchDaemon
 
    "${osUpgradeAppPath}/Contents/Resources/startosinstall" --agreetolicense --nointeraction --pidtosignal $jamfHelperPID >> $osinstallLogfile 2>&1 &
    #set +m
    WriteLog "INFO: startosinstall started and detached from script. Log will be written to (${osinstallLogfile}) Exiting script."
else
    WriteLog "ERROR: Unexpected return code (${notificationResponse}) from jamfHelper. Exiting."
    ShowError "A notification window exited in an unexpected way."
    CleanExit 1
fi
 
/bin/sleep 3
 
exit 0