Erase and Install macOS Catalina 10.15

uiqbaljamf
New Contributor II

Based on a JAMF webinar I want to create a self serv item that would format the drive on a Mac and install Catalina.

I have created a policy that deploys Install macOS Catalina.app to /Applications
and a second policy that executes the command '/Applications/Install macOS Catalina.app/Contents/Resources/startosinstall' --eraseinstall --agreetolicense --forcequitapps --newvolumename 'Macintosh HD' --nointeraction &

5d0d57ebbf70484b9e0435d2644f4e30

In theory, this should quite all open apps, restart the computer, wipe the drive and install macOS Catalina.

All it does is create a new partition and dump the installer files in there. That's it! The Catalina package runs fine when I install it manually.
What am I doing wrong here?

006910aee28e42baa190db638b58f67f

1 ACCEPTED SOLUTION

uiqbaljamf
New Contributor II

Found out what the issue was, it was the installer package. I had used composer to make the package and I must have done something wrong there. The policy works just fine when I download the installer from the app store as opposed to pushing it out via JAMF. Now I need to figure out a way to deploy the pkg properly. Any recommendations on how to go about doing that ?

View solution in original post

27 REPLIES 27

kevinwilemon
New Contributor III

Do you still have issues if you remove the "--nointeraction" ? We use the same command as you except we only use "--nointeraction" for upgrades or when running directly from the command line to suppress the install UI. Not sure it's needed when using "--eraseinstall"

ChrisRybin
New Contributor II

My 'Install macOS Catalina.pkg' disappears after running the above policy. Am I doing something wrong?

UoYJames
New Contributor

@ChrisRybin did you manage to resolve this? I'm having the same issue. I'm packaging up Catalina (full installer downloaded from a Mojave MBP) as a .pkg using Composer, using Jamf Admin to put it on our distribution point, deploying it to a test machine, but then when I try to interact with the installer in any way, be it startosinstall from a terminal, double click the app... it just errors and deletes itself.

dross
New Contributor III

Same issue here @ChrisRybin

mojo21221
Contributor II

We used to use a method like you are trying to do. However, we moved on to using this. https://github.com/kc9wwh/macOSUpgrade/blob/master/macOSUpgrade.sh

But to answer your question try the following instead... "/Applications/Install macOS Catalina.app/Contents/Resources/startosinstall" --eraseinstall --newvolumename "Macintosh HD" --agreetolicense &

Personally I have this script run first to force quit all other apps that may prevent the restart.

#!/bin/sh

#Force quit all non-essential applications


declare -a killPIDs
killPIDs=$(ps axww -o pid,command | grep -v bash | grep [A]pplications/ | grep -v /bin/sh | grep -v [C]asper | grep -v [J]amf | grep -v [S]elf Service | grep -v grep | awk '{print $1}')
#Kill said processes.


for i in ${killPIDs[@]}
do
    echo "Killing PID $i"
    kill -9 $i
done

exit 0

dross
New Contributor III

Doh! I found a Software Restriction I forgot about in Jamf . Removed it and now works as expected. I suspect that is the same problem for others.

Jakov
New Contributor III

There is a nice App that does exactly what you want (and more) called EraseInstall

uiqbaljamf
New Contributor II

Found out what the issue was, it was the installer package. I had used composer to make the package and I must have done something wrong there. The policy works just fine when I download the installer from the app store as opposed to pushing it out via JAMF. Now I need to figure out a way to deploy the pkg properly. Any recommendations on how to go about doing that ?

Kornel
New Contributor II

Hi @uiqbaljamf,
I am not sure if that would be the correct answer, but lately I had some issues with creating a macOS installer package with Composer. Thankfully, I've found a more reliable way to make one on the Macadmins Slack.

Just simply use Terminal:

pkgbuild --component /Applications/Install macOS Catalina.app [path-to-your-package/name-of-your-package].pkg

or you can just skip the path of your pkg file to create it in the current folder.

barberd
New Contributor II

Hi @Jakov

I’m pretty new to Jamf and it’s still very confusing to me. My customer success manager suggested the erase install app to me for my computer labs, but I’m still unable to successfully erase install Catalina to my test computer. Can you tell me how to integrate the erase install app with Jamf Pro to install a clean OS?

yngve
New Contributor II

I've created a script to run in order to do a complete wipe of the Mac and a policy to call from the script to remove the device from JSS.
For us it has, so far knock on wood, worked flaw less.

#!/bin/bash

#  ReInstallmacOSCatalina.sh
#  
#
#  Created by Yngve Åström on 2020-04-30.
#

## Check that we are running macOS 10.15
Version=$(sw_vers | grep ProductVersion | awk '{ print $2}' | awk -F. '{ print $2 }')
if [ "$Version" != "15" ] ; then
echo "This will only run on macOS 10.15 Catalina"
echo "Upgrade your macOS to Catalina"
open https://apps.apple.com/se/app/macos-catalina/id1466841314?l=en&mt=12
echo "Exiting now!"
exit
fi
## Check that we run with elevated privs
ScriptPath="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
if [ $(whoami) != "root" ]
then
    /bin/echo "You need to be root to do this, so use: sudo $ScriptPath/ReInstallmacOSCatalina.sh"
    exit 0;
fi

## Warn the user and give the user a choise to abort.
echo "WARNING!"
echo "This will completly erase your data from the machine."
echo "Your computer will be fully reset all your data will be erased."
echo "Are you sure you want to continue, have you backed up all your important files?"
read -p "Continue (y/n)?" choice
case "$choice" in
  y|Y ) echo "yes";;
  n|N ) echo "no";;
  * ) echo "invalid";;
esac

if ! [[ "$choice" =~ [y,Y] ]] ; then
   echo "Aborting, system untouched"
   exit
fi
echo "Continuing.."
echo "Downloading installer this might take a while"

## Download installer exit if the download fails.
if ! /usr/sbin/softwareupdate --fetch-full-installer --full-installer-version 10.15.4 ;
     then
     echo "Error: Failed to download the installer, please try running the script once more"
     echo "Disk untouched, exiting now."
     exit 1
fi

## Check for the installer. Abort if we can't find it in the expected place.
macOSInstallerPath=$(ls -t /Applications/ | grep -m 1 Install macOS Catalina)
if [ "$macOSInstallerPath" = "" ] ; then
    echo "Error: Couldn't find macOS Catalina installer"
    echo "Exiting, disk untouched, please try running the script once more"
    exit 1
fi

## Remove the computer from JSS
echo "Continuing.."
echo "Removing computer from JSS"
jamf policy -trigger delete_computer_from_jss

## Install macOS 10.15 with the erase drive option.
echo "Continuing.."
echo "Erasing drive and installing macOS Catalina"
echo "This might take a while usually about 15-20min."
echo "Once the mac reboots, follow the instructions on screen"

/Applications/"$macOSInstallerPath"/Contents/Resources/startosinstall --agreetolicense --forcequitapps --eraseinstall --newvolumename "Macintosh HD"
exit 0

msample
Contributor II

@yngve How did you insert this script to make the Erase and Install Catalina work? After I've completed all of the steps from:https://www.jamf.com/blog/reinstall-a-clean-macos-with-one-button/...when I engage my erase and install button, it quickly runs through the process, rushes to "finishing" and "done" and nothing happens.

I'd like to make your script work.

msample
Contributor II

@yngve I was wondering do I attach this script along with the policy for erase and install and if not there just exactly where the placement of the script would go. Thanks in advance.

yngve
New Contributor II

I run the script through a policy that is available in Self Service
00df9790866741e28f483b2da6f82b8a
Since Apple released 10.15.15 the script needs to be updated to reflect that change

--full-installer-version 10.15.4
to --full-installer-version 10.15.5

I would suggest to only make it available to Macs with sufficient disk space as well and only Macs running Catalina.

amyers2223
New Contributor

@yngve What does your policy look like that is removing the computer from the JSS?

Remove the computer from JSS

echo "Continuing.."
echo "Removing computer from JSS"
jamf policy -trigger delete_computer_from_jss

I have a similar script but at times it seems that it is deleting it from the JSS and not proceeding with the script to finished erasing the machine.

daniel_ross
Contributor III

Were using the below script in Jamf with great success and it's so easy to use!

https://github.com/grahampugh/erase-install

Were also now leveraging it for our Catalina upgrades for users that held off on upgrading rather than uploading a new installer each time Apple releases one.

ryan_ball
Valued Contributor

Agree with @daniel_ross, we use @grahamrpugh's erase-install both for upgrades and erasures.

yngve
New Contributor II

@amyers2223 It is just a small script that runs

#!/bin/sh
jssURL="url to your jss"
id=$(/usr/local/bin/jamf recon | grep '<computer_id>' | awk -F'>' {' print $2 '} | tr -d '</computer_id>')
curl -s -k -u "$4:$5" "${jssURL}/JSSResource/computers/id/$id" -X DELETE
jamf removeMdmProfile
jamf removeFramework
rm /var/db/.AppleSetupDone

Have to agree that @grahamrpugh scripts looks a lot better than mine.

typeraj
New Contributor III

Has anyone tried using this on 10.15.5 and .6? From what I can tell the --fetch-full-installer option is completely broken. Running it manually from CLI just results in this error:

softwareupdate --fetch-full-installer --full-installer-version 10.15.5 Downloading and installing 10.15.5 installer SUPreferenceManager: Failed to set object of class: __NSCFConstantString for key: LastRecommendedMajorOSBundleIdentifier with error: Error Domain=SUPreferenceManagerErrorDomain Code=1 "(null)" SUPreferenceManager: Failed to set object of class: __NSCFConstantString for key: LastRecommendedMajorOSBundleIdentifier with error: Error Domain=SUPreferenceManagerErrorDomain Code=1 "(null)" Install failed with error: Update not found

fgonzale
Contributor

@typeraj from a forum thread somewhere I implemented the following and it solved my issues with fetching the installer:

rm /Library/Preferences/com.apple.SoftwareUpdate.plist
killall cfprefsd
/usr/sbin/softwareupdate --fetch-full-installer

fgonzale
Contributor

@typeraj actually, if you are interested in my script this one will try the download a few times in case it doesn't complete on the first try (It usually works with removing the plist but I've seen it fail on the first try at least once). Also, I put in safety feature to not run the policy unless the Self Service app is running. This is to prevent a résumé updating incident where a bad scope or bad trigger configuration blows aways all your Macs (maybe we wouldn't do this but a less trained Site admin...):

#!/bin/sh
#prevent computer from sleeping for the next 3 hours. Prevents computer from falling asleep when on battery;
#obtain variable on whether the Self Service app is running;
caffeinate -t 10800 & selfservice=$(pgrep -x "Self Service");

#determine whether self service app is running and whether the macOS Catalina installer is already there;

#if the installer is not already there and self servie app is running then begin download;
if [ ! -z "$selfservice" ] && [ ! -d "/Applications/Install macOS Catalina.app" ];
then echo "Self Service is running but no Installer exists. Proceed with Download and Erase and Install";

#if it doesn't get it on the first try then try again a few times as sometimes it doesn't work on the first try;
counter=$((counter + 1));
until [[ -d "/Applications/Install macOS Catalina.app" ]] || [[ $counter == 4 ]];
#to avoid download issues remove the softwareupdate plist file;
do rm /Library/Preferences/com.apple.SoftwareUpdate.plist;
#kill the prefs daemon process to reload preferences;
killall cfprefsd;

#download installer; 
/usr/sbin/softwareupdate --fetch-full-installer;
counter=$((counter + 1));
done; 
echo $counter;
echo "Installer was successfully downloaded or try counter reached 4...";
#erase and reinstall machine;
'/Applications/Install macOS Catalina.app/Contents/Resources/startosinstall' --eraseinstall --agreetolicense --forcequitapps --newvolumename 'Macintosh HD';
#if the installer is already there and self servie app is running then just start installing; 
elif [ ! -z "$selfservice" ] && [ -d "/Applications/Install macOS Catalina.app" ];
then echo "Self Service is running and Installer already exists. Proceed with Erase and Install";
'/Applications/Install macOS Catalina.app/Contents/Resources/startosinstall' --eraseinstall --agreetolicense --forcequitapps --newvolumename 'Macintosh HD';
#as a safety feature to not blow away your company due to a bad scope or wrong trigger in Jamf the reinstall will not happen unless the self service app is running;
else echo "Self Service is stopped. Will not proceed with Erase and Install.";
fi

As well, to obfuscate a bit more I don't usually put the above in the Scripts section as I don't want this to be a selectable item. I generally put this into the policy's execute command in Files and Processes. Though you have to have the script as a single line:

caffeinate -t 10800 & selfservice=$(pgrep -x "Self Service"); if [ ! -z "$selfservice" ] && [ ! -d "/Applications/Install macOS Catalina.app" ]; then echo "Self Service is running but no Installer exists. Proceed with Download and Erase and Install"; counter=$((counter + 1)); until [[ -d "/Applications/Install macOS Catalina.app" ]] || [[ $counter == 4 ]]; do rm /Library/Preferences/com.apple.SoftwareUpdate.plist; killall cfprefsd; /usr/sbin/softwareupdate --fetch-full-installer; counter=$((counter + 1)); done; echo $counter; echo "Installer was successfully downloaded or try counter reached 4..."; '/Applications/Install macOS Catalina.app/Contents/Resources/startosinstall' --eraseinstall --agreetolicense --forcequitapps --newvolumename 'Macintosh HD'; elif [ ! -z "$selfservice" ] && [ -d "/Applications/Install macOS Catalina.app" ]; then echo "Self Service is running and Installer already exists. Proceed with Erase and Install"; '/Applications/Install macOS Catalina.app/Contents/Resources/startosinstall' --eraseinstall --agreetolicense --forcequitapps --newvolumename 'Macintosh HD'; else echo "Self Service is stopped. Will not proceed with Erase and Install."; fi

Jason33
Contributor III

I've built a new package for 10.15.7 and trying to deploy it to /Applications. Policy runs just fine, finishes, but the installer is not placed in /Applications, and no errors to indicate anything went wrong. Previously, I had the 10.15.4 installer being deployed to /private/var and it worked with no issues. I've checked the console logs and dont see the policy to cache the installer was run, and no errors afterwards.

jlococo
New Contributor II

So I am using the following jamf script below and everything works except at some point I noticed Self Service would fail to quit preventing the OSInstall process. It's hit or miss and sometimes works and sometimes doesn't. I'm guessing I could just insert a kill command for self service in here, but what would that look like and where should I put it? Not really a scripting guru.

#!/bin/bash

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# Copyright (c) 2019 Jamf.  All rights reserved.
#
#       Redistribution and use in source and binary forms, with or without
#       modification, are permitted provided that the following conditions are met:
#               * Redistributions of source code must retain the above copyright
#                 notice, this list of conditions and the following disclaimer.
#               * Redistributions in binary form must reproduce the above copyright
#                 notice, this list of conditions and the following disclaimer in the
#                 documentation and/or other materials provided with the distribution.
#               * Neither the name of the Jamf nor the names of its contributors may be
#                 used to endorse or promote products derived from this software without
#                 specific prior written permission.
#
#       THIS SOFTWARE IS PROVIDED BY JAMF SOFTWARE, LLC "AS IS" AND ANY
#       EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#       WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
#       DISCLAIMED. IN NO EVENT SHALL JAMF SOFTWARE, LLC BE LIABLE FOR ANY
#       DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
#       (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
#       LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#       ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#       (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#       SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# This script was designed to be used in a Self Service policy to ensure specific
# requirements have been met before proceeding with an inplace upgrade of the macOS,
# as well as to address changes Apple has made to the ability to complete macOS upgrades
# silently.
#
# REQUIREMENTS:
#           - Jamf Pro
#           - macOS Clients running version 10.10.5 or later
#           - macOS Installer 10.12.4 or later
#           - eraseInstall option is ONLY supported with macOS Installer 10.13.4+ and client-side macOS 10.13+
#           - Look over the USER VARIABLES and configure as needed.
#
#
# For more information, visit https://github.com/kc9wwh/macOSUpgrade
#
# Written by: Joshua Roskos | Jamf
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# APS Updates added 11/28/2019
#
# Updated "validate_free_space" to work with Catalina – freeSpace
# Added "Print :APFSContainerFree"
# /usr/libexec/PlistBuddy -c "Print :APFSContainerFree" /dev/stdin <<< "$diskInfoPlist" 2>/dev/null || /usr/libexec/PlistBuddy -c "Print :FreeSpace" /dev/stdin <<< "$diskInfoPlist" 2>/dev/null || /usr/libexec/PlistBuddy -c "Print :AvailableSpace" /dev/stdin <<< "$diskInfoPlist" 2>/dev/null
# 
# Updated loopCout to work with Stub/Lite installer – Stub/Lite installer does not specify a macOS version.
# Check to see if the installer version matches, or if the installer does not have InstallInfo.plist.
# if [ "$currentInstallerVersion" = "$installerVersion" ] || [[ ! -e "$OSInstaller/Contents/SharedSupport/InstallInfo.plist" ]]; then
#
# Updated variable "currentUser"
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# USER VARIABLES
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

## Specify path to OS installer – Use Parameter 4 in the JSS, or specify here.
## Parameter Label: Path to the macOS installer
## Example: /Applications/Install macOS High Sierra.app
OSInstaller="/Applications/Install macOS Catalina.app"

## Version of Installer OS. Use Parameter 5 in the JSS, or specify here.
## Parameter Label: Version of macOS
## Example Command: /usr/libexec/PlistBuddy -c 'Print :"System Image Info":version' "/Applications/Install macOS High Sierra.app/Contents/SharedSupport/InstallInfo.plist"
## Example: 10.12.5
installerVersion="10.15.2"
installerVersionMajor=$( /bin/echo "$installerVersion" | /usr/bin/awk -F. '{print $2}' )
installerVersionMinor=$( /bin/echo "$installerVersion" | /usr/bin/awk -F. '{print $3}' )

## Custom Trigger used for download – Use Parameter 6 in the JSS, or specify here.
## Parameter Label: Download Policy Trigger
## This should match a custom trigger for a policy that contains just the
## MacOS installer. Make sure that the policy is scoped properly
## to relevant computers and/or users, or else the custom trigger will
## not be picked up. Use a separate policy for the script itself.
## Example trigger name: download-sierra-install
download_trigger="cache-catalina-installer"

## MD5 Checksum of InstallESD.dmg – Use Parameter 7 in the JSS.
## Parameter Label: installESD Checksum (optional)
## This variable is OPTIONAL
## Leave the variable BLANK if you do NOT want to verify the checksum (DEFAULT)
## Example Command: /sbin/md5 /Applications/Install macOS High Sierra.app/Contents/SharedSupport/InstallESD.dmg
## Example MD5 Checksum: b15b9db3a90f9ae8a9df0f81741efa2b
installESDChecksum="$7"

## Valid Checksum?  O (Default) for false, 1 for true.
validChecksum=0

## Unsuccessful Download?  0 (Default) for false, 1 for true.
unsuccessfulDownload=0

## Erase & Install macOS (Factory Defaults)
## Requires macOS Installer 10.13.4 or later
## Disabled by default
## Options: 0 = Disabled / 1 = Enabled
## Use Parameter 8 in the JSS.
## Parameter Label: Upgrade or Erase (0 or 1)
eraseInstall="1"
if [ "$eraseInstall" != "1" ]; then eraseInstall=0 ; fi
# macOS Installer 10.13.3 or ealier set 0 to it.
if [ "$installerVersionMajor${installerVersionMinor:=0}" -lt 134 ]; then
    eraseInstall=0
fi

## Enter 0 for Full Screen, 1 for Utility window (screenshots available on GitHub)
## Full Screen by default
## Use Parameter 9 in the JSS.
## Parameter Label: Full Screen or Dialog Box (0 or 1)
userDialog="1"
if [ "$userDialog" != "1" ]; then userDialog=0 ; fi

# Control for auth reboot execution.
if [ "$installerVersionMajor" -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

## Title of OS
macOSname=$(/bin/echo "$OSInstaller" | /usr/bin/sed -E 's/(.+)?Install(.+).app/?/2/' | /usr/bin/xargs)

## Title to be used for userDialog (only applies to Utility Window)
title="$macOSname Upgrade"

## Heading to be used for userDialog
heading="Please wait as we prepare your computer for $macOSname..."

## Title to be used for userDialog
description="Your computer will reboot in 5-10 minutes and begin the upgrade.
This process will take approximately 30-40 minutes."

## Description to be used prior to downloading the OS installer
dldescription="We need to download $macOSname to your computer, this will 
take several minutes."

## Jamf Helper HUD Position if macOS Installer needs to be downloaded
## Options: ul (Upper Left); ll (Lower Left); ur (Upper Right); lr (Lower Right)
## Leave this variable empty for HUD to be centered on main screen
dlPosition="ul"

## Icon to be used for userDialog
## Default is macOS Installer logo which is included in the staged installer package
icon="$OSInstaller/Contents/Resources/InstallAssistant.icns"

## First run script to remove the installers after run installer
finishOSInstallScriptFilePath="/usr/local/jamfps/finishOSInstall.sh"

## Launch deamon settings for first run script to remove the installers after run installer
osinstallersetupdDaemonSettingsFilePath="/Library/LaunchDaemons/com.jamfps.cleanupOSInstall.plist"

## Launch agent settings for filevault authenticated reboots
osinstallersetupdAgentSettingsFilePath="/Library/LaunchAgents/com.apple.install.osinstallersetupd.plist"

## Amount of time (in seconds) to allow a user to connect to AC power before moving on
## If null or 0, then the user will not have the opportunity to connect to AC power
acPowerWaitTimer="0"

## Declare the sysRequirementErrors array
declare -a sysRequirementErrors=()

## Icon to display during the AC Power warning
warnIcon="/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertCautionIcon.icns"

## Icon to display when errors are found
errorIcon="/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertStopIcon.icns"

## The startossinstall log file path
osinstallLogfile="/var/log/startosinstall.log"

## caffeinatePID
caffeinatePID=""

## The startossinstall command option array
declare -a startosinstallOptions=()

## Determine binary name
binaryNameForOSInstallerSetup=$([ "$installerVersionMajor" -ge 11 ] && /bin/echo "osinstallersetupd" || /bin/echo "osinstallersetupplaind")

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# FUNCTIONS
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

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"
}

downloadInstaller() {
    /bin/echo "Downloading macOS Installer..."
    /Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper 
        -windowType hud -windowPosition $dlPosition -title "$title" -alignHeading center -alignDescription left -description "$dldescription" 
        -lockHUD -icon "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/SidebarDownloadsFolder.icns" -iconSize 100 &
    ## Capture PID for Jamf Helper HUD
    jamfHUDPID=$!
    ## Run policy to cache installer
    /usr/local/jamf/bin/jamf policy -event "$download_trigger"
    ## Kill Jamf Helper HUD post download
    kill_process "$jamfHUDPID"
}

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() {
    local installerMajor diskInfoPlist freeSpace requiredDiskSpaceSizeGB installerPath installerSizeBytes

    installerMajor="$1"
    installerPath="$2"

    diskInfoPlist=$(/usr/sbin/diskutil info -plist /)
    ## 10.13.4 or later, diskutil info command output changes key from 'AvailableSpace' to 'Free Space' about disk space.
    ## 10.15.0 or later, diskutil info command output changes key from 'APFSContainerFree' to 'Free Space' about disk space.
    freeSpace=$(
    /usr/libexec/PlistBuddy -c "Print :APFSContainerFree" /dev/stdin <<< "$diskInfoPlist" 2>/dev/null || /usr/libexec/PlistBuddy -c "Print :FreeSpace" /dev/stdin <<< "$diskInfoPlist" 2>/dev/null || /usr/libexec/PlistBuddy -c "Print :AvailableSpace" /dev/stdin <<< "$diskInfoPlist" 2>/dev/null
    )

    ## The free space calculation also includes the installer, so it is excluded.
    if [ -e "$installerPath" ]; then
        installerSizeBytes=$(/usr/bin/du -s "$installerPath" | /usr/bin/awk '{print $1}' | /usr/bin/xargs)
        freeSpace=$((freeSpace + installerSizeBytes))
    fi

    ## Check if free space > 15GB (install 10.13) or 20GB (install 10.14+)
    requiredDiskSpaceSizeGB=$([ "$installerMajor" -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() {
    if [ -n "$caffeinatePID" ]; then
      kill_process "$caffeinatePID"
    fi
    ## Remove Script
    /bin/rm -f "$finishOSInstallScriptFilePath"
    /bin/rm -f "$osinstallersetupdDaemonSettingsFilePath"
    /bin/rm -f "$osinstallersetupdAgentSettingsFilePath"
    exit "$1"
}

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# SYSTEM CHECKS
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

## If previous processes remain for some reason, the installation will freeze, so kill it.
killingProcesses=("caffeinate" "startosinstall" "$binaryNameForOSInstallerSetup")
for processName in "${killingProcesses[@]}"; do
    [ -z "$processName" ] && continue
    /bin/echo "Killing $processName processes."
    /usr/bin/killall "$processName" 2>&1 || true
done

## Caffeinate
/usr/bin/caffeinate -dis &
caffeinatePID=$!

##Get Current User
currentUser=$(/bin/echo 'show State:/Users/ConsoleUser' | /usr/sbin/scutil | /usr/bin/awk '/Name / { print $3 }')

## Check if FileVault Enabled
fvStatus=$( /usr/bin/fdesetup status | /usr/bin/head -1 )

## Run system requirement checks
validate_power_status
validate_free_space "$installerVersionMajor" "$OSInstaller"

## Don't waste the users time, exit here if system requirements are not met
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

## Check for existing OS installer
loopCount=0
while [ "$loopCount" -lt 3 ]; do
    if [ -e "$OSInstaller" ]; then
        /bin/echo "$OSInstaller found, checking version."
        currentInstallerVersion=$(/usr/libexec/PlistBuddy -c 'Print :"System Image Info":version' "$OSInstaller/Contents/SharedSupport/InstallInfo.plist")
        /bin/echo "Found macOS installer for version $currentInstallerVersion."
        ## Check to see if the installer version matches, or if the installer does not have InstallInfo.plist.
        if [ "$currentInstallerVersion" = "$installerVersion" ] || [[ ! -e "$OSInstaller/Contents/SharedSupport/InstallInfo.plist" ]]; then
            /bin/echo "Installer found, version matches. Verifying checksum..."
            verifyChecksum
        else
            ## Delete old version.
            /bin/echo "Installer found, but old. Deleting..."
            /bin/rm -rf "$OSInstaller"
            /bin/sleep 2
            downloadInstaller
        fi
        if [ "$validChecksum" -eq 1 ]; then
            unsuccessfulDownload=0
            break
        fi
    else
        downloadInstaller
    fi
    unsuccessfulDownload=1
    ((loopCount++))
done

if [ "$unsuccessfulDownload" -eq 1 ]; then
    /bin/echo "macOS Installer Downloaded 3 Times - Checksum is Not Valid"
    /bin/echo "Prompting user for error and exiting..."
    /Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -title "$title" -icon "$errorIcon" -heading "Error Downloading $macOSname" -description "We were unable to prepare your computer for $macOSname. Please contact the IT Support Center." -iconSize 100 -button1 "OK" -defaultButton 1
    cleanExit 0
fi

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# CREATE FIRST BOOT SCRIPT
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

/bin/mkdir -p /usr/local/jamfps

/bin/cat << EOF > "$finishOSInstallScriptFilePath"
#!/bin/bash
## First Run Script to remove the installer.
## Clean up files
/bin/rm -fr "$OSInstaller"
## Update Device Inventory
/usr/local/jamf/bin/jamf recon
## Remove LaunchAgent and LaunchDaemon
/bin/rm -f "$osinstallersetupdAgentSettingsFilePath"
/bin/rm -f "$osinstallersetupdDaemonSettingsFilePath"
## Remove Script
/bin/rm -fr /usr/local/jamfps
exit 0
EOF

/usr/sbin/chown root:admin "$finishOSInstallScriptFilePath"
/bin/chmod 755 "$finishOSInstallScriptFilePath"

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# LAUNCH DAEMON
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

/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

## Set the permission on the file just made.
/usr/sbin/chown root:wheel "$osinstallersetupdDaemonSettingsFilePath"
/bin/chmod 644 "$osinstallersetupdDaemonSettingsFilePath"

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# LAUNCH AGENT FOR FILEVAULT AUTHENTICATED REBOOTS
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
if [ "$cancelFVAuthReboot" -eq 0 ]; then
    /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/$binaryNameForOSInstallerSetup</string>
    </array>
</dict>
</plist>
EOP

    ## Set the permission on the file just made.
    /usr/sbin/chown root:wheel "$osinstallersetupdAgentSettingsFilePath"
    /bin/chmod 644 "$osinstallersetupdAgentSettingsFilePath"

fi

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# APPLICATION
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

## Launch jamfHelper
jamfHelperPID=""
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

## Load LaunchAgent
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

## Set required startosinstall options
startosinstallOptions+=(
"--agreetolicense"
"--nointeraction"
"--pidtosignal $jamfHelperPID"
)

## Set version specific startosinstall options
if [ "$installerVersionMajor" -lt 14 ]; then
    # This variable may have space. Therefore, escape value with duble quotation
    startosinstallOptions+=("--applicationpath "$OSInstaller"")
fi

## Check if eraseInstall is Enabled
if [ "$eraseInstall" -eq 1 ]; then
    startosinstallOptions+=("--eraseinstall")
    /bin/echo "Script is configured for Erase and Install of macOS."
fi

## Begin Upgrade
startosinstallCommand=""$OSInstaller/Contents/Resources/startosinstall" ${startosinstallOptions[*]} >> $osinstallLogfile 2>&1 &"
/bin/echo "Running a command as '$startosinstallCommand'..."
eval "$startosinstallCommand"

/bin/sleep 3

exit 0

chase_g
New Contributor III

@jlococo Looks like the version you have of that script is old. There were some updates to it a while back that added(line 533) when installing an os greater than 10.14 it would utilize the --forcequitapps option and that should fix the issue with Self Service not quitting and preventing the reboot. Get the updated version on kc9wwh's Github page

TomRWSmith
New Contributor III

I've been trying to get this erase and install script working for the last few days now, I've followed the Webinar and the blog by Bill Smith and got stuck on the erasing policy in Self Service. First this was getting errors within the policy logs this is because I copied and pasted the script from the blog and then I typed out the script and saved the policy which then prompted for the end user to enter the admin password. I did some more research and tried few things and finally got this working with the below Files and Processes. This now wipes the Mac and reinstalls the OS X with both the Macintosh HD & the Macintosh HD - Data which is normal for Catalina.
/Applications/Install macOS Catalina.app/Contents/Resources/startosinstall --agreetolicense --eraseinstall && reboot

51305f9177e34b669713eeccd6ea43b7

jlococo
New Contributor II

@chase.garcia Thanks! the updated script worked perfectly

rcole
Contributor

Has anyone had issues like this with the https://github.com/kc9wwh/macOSUpgrade/blob/master/macOSUpgrade.sh script?

Script result: installerVersion 10.15.7
installerVersion_Full_Integer 101507
installerVersion_Major_Integer 1015
Killing caffeinate processes.
Killing startosinstall processes.
No matching processes were found
Killing osinstallersetupd processes.
No matching processes were found
Power Check: OK - AC Power Detected
Disk Check: OK - 144709979400 Bytes Free Space Detected
Installer check: Target version is ok (10.15.7).
Installer check: DMG file check: Skipped.
Installer check: PASSED
Launching jamfHelper as Utility Window.
Running a command as '"/Applications/Install macOS Catalina.app//Contents/Resources/startosinstall" --agreetolicense --nointeraction --pidtosignal 7897 --forcequitapps >> /var/log/startosinstall.log 2>&1 &’..

I like this script, but it seems to fail for strange reasons after a certain step. Also, these other scripts seem great, but what if you simply want to upgrade a user to Catalina instead of Wiping/Erasing the Device?

I’ve also tried editing this script:

!/bin/sh

prevent computer from sleeping for the next 3 hours. Prevents computer from falling asleep when on battery;

obtain variable on whether the Self Service app is running;

caffeinate -t 10800 & selfservice=$(pgrep -x "Self Service");

determine whether self service app is running and whether the macOS Catalina installer is already there;

if the installer is not already there and self service app is running then begin download;

if [ ! -z "$selfservice" ] && [ ! -d "/Applications/Install macOS Catalina.app" ];
then echo "Self Service is running but no Installer exists. Proceed with Download and Install";

if it doesn't get it on the first try then try again a few times as sometimes it doesn't work on the first try;

counter=$((counter + 1));
until [[ -d "/Applications/Install macOS Catalina.app" ]] || [[ $counter == 4 ]];

to avoid download issues remove the softwareupdate plist file;

do rm /Library/Preferences/com.apple.SoftwareUpdate.plist;

kill the prefs daemon process to reload preferences;

killall cfprefsd;

download installer;

/usr/sbin/softwareupdate --fetch-full-installer;
counter=$((counter + 1));
done; echo $counter;
echo "Installer was successfully downloaded or try counter reached 4...";

update and reinstall machine;

'/Applications/Install macOS Catalina.app/Contents/Resources/startosinstall' --agreetolicense --forcequitapps;

if the installer is already there and self servie app is running then just start installing;

elif [ ! -z "$selfservice" ] && [ -d "/Applications/Install macOS Catalina.app" ];
then echo "Self Service is running and Installer already exists. Proceed with Install";
'/Applications/Install macOS Catalina.app/Contents/Resources/startosinstall' --agreetolicense --forcequitapps;

as a safety feature to not blow away your company due to a bad scope or wrong trigger in Jamf the reinstall will not happen unless the self service app is running;

else echo "Self Service is stopped. Will not proceed with Install.";
fi

But, this script also fails. Any assistance you all might be able to provide would be greatly appreciated.