Apple's Software Update server (Policy) for Big Sur Security Updates

jschank
New Contributor III

Anyone having issues attempting to update Big Sur devices via Policy and using Apple's Software Update server? The policy is not working for Big Sur including intel machines. Attempting to get the latest update (20D74).

With the terminal open, it states it downloads. It shows as completed in Jamf within the policy. Also within client history. Yet reboot does not install the update.

Mojave and Catalina devices update fine.

64 REPLIES 64

not sure if it works for you, but the API command on a 11.3.1 M1 mac mini does not work, neither does GUI, even with the new RC Jamf vers

nelsoni
Contributor II

Not sure if I am missing something, but when I attempt to use the "InstallASAP" key in my script, it still errors out saying it is an invalid key. I confirmed I am on 10.29.2

Anyone else experiencing this?

jtrant
Contributor III

@nelsoni I believe you need to use 'InstallForceRestart'. 'InstallASAP' will trigger a restart countdown notification.

nelsoni
Contributor II

"InstallForceRestart" results in the same error. I have posted my script here, maybe someone can shed some light on what I am missing.

#!/bin/sh

#API login info
apiuser="Temp User"
apipass="Temp Password"
jamfProURL="https://ORG.jamfcloud.com"

#Grab serial number, OS Version of computer and CPU Type
SerialNumber=$(system_profiler SPHardwareDataType | grep 'Serial Number (system)' | awk '{print $NF}')
macOSVersion=$(sw_vers -productVersion)
arch=$( /usr/bin/arch )

#Check macOS Major
CheckIt=$(echo $macOSVersion | cut -d . -f 1)

#Set xpath option based on macOS major version
if [[ "$CheckIt" == "11" ]]
    then
        xpath="xpath -e"
    else
        xpath="xpath"
fi

function scheduleOSUpdateViaAPI() {

    #Grab the computers JSS ID
    jamfProCompID=$( /usr/bin/curl -s -u ${apiuser}:${apipass} ${jamfProURL}/JSSResource/computers/serialnumber/${SerialNumber}/subset/general | $xpath "/computer/general/id/text()" )

    #Initiate the InstallForceRestart key (Download and install the update, and restart computers after installation), To allow for updates without user interaction on target computers with Apple silicon, Bootstrap Token for the computers must be escrowed with Jamf Pro.
    /usr/bin/curl -s -X POST -H "Content-Type: text/xml" -u ${apiuser}:${apipass} ${jamfProURL}/JSSResource/computercommands/command/ScheduleOSUpdate/action/InstallForceRestart/id/${jamfProCompID}

}

if [[ "$arch" == "arm64" ]]; then
    scheduleOSUpdateViaAPI
else
    /usr/sbin/softwareupdate --install --all --include-config-data --restart --force
fi

exit 0

from my testing, that API call AND from the GUI does nothing to the m1 air and mac mini I have.  They dont restart or install the updates at any point 😞

jtrant
Contributor III

This looks good to me, I even had Jamf PS look over it during a recent engagement and they said the same. I haven't had a chance to try it out just yet, though. We only upgraded to 10.29.2 two days ago.

nelsoni
Contributor II

I tested the action keys some more and found that pretty much every key resulted in an error. This was on both an M1 and Intel Mac.

I finally just tried the GUI MDM button on the Macs management tab and that worked, but I was logged into the Mac at the time.

beeboo
Contributor

@nelsoni where did you see that?

ecdf5e84832f4bca954604b093172687

nelsoni
Contributor II

The Mac has to be DEP enrolled and supervised for the MDM command to be an option.
5231c81e6d8f4632b2e3ca509e32891b

fredrik_virding
Contributor

@nelsoni I tried the GUI, but usually get an error of like ”unsupported os” if i recall. Not sure if there are some bugs with early Big Sur versions.

jtrant
Contributor III

I know supervision is a requirement, but I have a non-DEP M1 Mac and have the "Download and Install Updates" button available under the 'Management' tab.

I don't think this is quite the same as triggering a mass action from a search, as this workflow specifically shows the "Download and install the update, and restart computers after installation" option:

ff9021708157476d89d36061c1f83ce6

JustDeWon
Contributor III

Update:

The way I'm managing this is using a custom script that was being used prior to Big Sur, which allows the users to delay softwareUpdates up to 8hours or "Install Now".. If they choose the install now button, it will kick off a policy -event that will run the API script Install key above. The dialog states along the lines of update is installing and will reboot after install(this takes up to about 7 minutes)

If they defer, once the timelimit is up, it will again, kick off the API script. This has been successfully tested and working

fredrik_virding
Contributor

@JustDeWon

Cool idea! Are you able to share that script?

JustDeWon
Contributor III

@fredrik.virding Sure, I took out alot in this script to make it the bare minimum. Just adjust for your environment.. This is the delay that I run prior to the above API script..

#!/bin/sh

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# SET THE VARIABLES
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 

## Path of branded icon
icon="/path/to/icon"

## Set the delayed restart .plist
DelayedRestartD="/Library/LaunchDaemons/com.company.rebootdelay.plist"

## Set the delayed restart warning .plist
WarningRestartD="/Library/LaunchDaemons/com.company.rebootwarning.plist"

## Set the delayed cleanup LaunchDaemon
CleanupDelayedD="/Library/LaunchDaemons/com.company.cleanupDelayedRestart.plist"

## Set the delayed restart Script
WarningRestartScript="/path/to/rebootwarning.sh"

## Set the delayed cleanup restart Script
CleanupRestartScript="/path/to/cleanupDelayedRestart.sh"

## Get the logged in user
currentUser=$(scutil <<< "show State:/Users/ConsoleUser" | awk -F': ' '/[[:space:]]+Name[[:space:]]:/ { if ( $2 != "loginwindow" ) { print $2 }}')

## Raw unix time in seconds of last boot up
lastBootTime=$(sysctl kern.boottime | awk -F'[ |,]' '{print $5}')

## Current time in unix seconds
currentTime=$(date +"%s")

## Calculation of difference between boot and current time ( total time up in seconds )
upTimeRaw=$((currentTime-lastBootTime))

## Calculation of uptime in minutes total ( uptime in seconds div by 60 )
upTimeMin=$((upTimeRaw/60))

## Calculation of uptime in hours total ( uptime in minutes div by 60 )
upTimeHours=$((upTimeMin/60))

## Calculation of uptime in whole days total ( uptime in hours  div by 24 )
upTimeDays=$((upTimeHours/24))

## Total minutes up in whole days ( [uptime in whole days x 24] x 60 minutes )
minusMinutes=$((((upTimeDays*24))*60))

## Calculation of remaining minutes to subtract
remainingMin=$((upTimeMin-minusMinutes))

## Calculation of remaining hours to subtract
remainingHrs=$((remainingMin/60))
minusHours=$((upTimeHours-remainingHrs))

## Figure out minutes value for uptime display
total1=$((upTimeDays*24*60))
total2=$((remainingHrs*60))
minusMinutes2=$((total1+total2))
remainingMinFin=$((upTimeMin-minusMinutes2))

UptimeThreshold1alt=$((UptimeThreshold1-1))
UptimeThreshold2alt=$((UptimeThreshold2-1))

#Set the log path
logpath="/path/to/logpath/"

#Ensure logFile can be written
if [ ! -d "${logpath}" ]; then
  mkdir "${logpath}"
fi

##Create the logFile
logFile="${logpath}"restartTimer.log

#Check for the company icon & if not install it
if [ ! -e "$icon" ]; then
    echo $(date) "company branding icon not installed.. installing" >> $logFile
    jamf policy -event customTrigger
else
   echo $(date) "Company branding icon is installed" >> $logFile
fi

## Determine if computer is sitting at login screen and no one is logged in. If so, run software update.
if [[ "$currentUser" == "root" ]]; then
    echo $(date) "No one is currently logged in, rebooting seat." >> $logFile
    jamf policy -event customAPITrigger
    exit 0
 else
    #Check if user is logged in, if so, proceed with dialog prompts..
if [[ ! "$currentUser" == "root" ]]; then
    echo $(date) "$currentUser is logged in. Proceeding with dialog prompts.." >> $logFile
 fi
fi


# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# FUNCTIONS
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
jamfHelper()
{

/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper 
-windowType hud 
-title "input title here" 
-heading "Input heading here" 
-description "A software update has been downloaded on your workstation and will reboot after install.
You can select to have the update installed now or delay for up to 8 hours.
Your machine will countdown 5 minutes prior to installation if you choose to delay.
Please make selection below." 
-icon "$icon" 
-alignDescription "left" 
-alignCountdown "right" 
-lockHUD 
-button1 "Delay" 
-button2 "Install Now" 
-showDelayOptions "900, 1800, 3600, 28800" # 8 hours

}

# variables
result=$(jamfHelper)
delayint=$(echo "$result" | /usr/bin/sed 's/.$//')
warndelayint=$(expr $delayint - 300)
defercal=$(($(/bin/date +%s) + delayint))
hour=$(/bin/date -j -f "%s" "$defercal" "+%H")
minute=$(/bin/date -j -f "%s" "$defercal" "+%M")
warndefercal=$(($(/bin/date +%s) + warndelayint))
warnhour=$(/bin/date -j -f "%s" "$warndefercal" "+%H")
warnminute=$(/bin/date -j -f "%s" "$warndefercal" "+%M")

## Create LaunchDaemon from jamfHelper delay output

delay()
{
/bin/cat <<EOF > "$DelayedRestartD"
<?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.company.rebootdelay</string>
    <key>ProgramArguments</key>
    <array>
        <string>reboot</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>$hour</integer>
        <key>Minute</key>
        <integer>$minute</integer>
    </dict>
</dict>
</plist>
EOF

echo $(date) "The timed delay has been selected by the user. Applying permissions.." >> $logFile
/usr/sbin/chown root:wheel "$DelayedRestartD"
/bin/chmod 644 "$DelayedRestartD"
echo $(date) "8 hour permissions has been applied".. >> $logFile

}

warndelay()
{

/bin/cat <<EOF > "$WarningRestartD"
<?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.company.rebootwarning</string>
    <key>ProgramArguments</key>
    <array>
        <string>sh</string>
        <string>/path/to/rebootwarning.sh</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>$warnhour</integer>
        <key>Minute</key>
        <integer>$warnminute</integer>
    </dict>
</dict>
</plist>
EOF

# set ownership on delaywarning launch daemon
/usr/sbin/chown root:wheel "$WarningRestartD"
/bin/chmod 644 "$WarningRestartD"

}

warnScript()
{

/bin/cat <<EOF > "$WarningRestartScript"
#!/bin/bash
PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/sbin


#Set the log path
logpath="/path/to/logpath/"

#Ensure logFile can be written
if [ ! -d "${logpath}" ]; then
  mkdir "${logpath}"
fi

##Create the logFile
logFile="${logpath}"restartWarning.log

#Start the jamfHelper dialog
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper 
-windowType hud 
-title "input title here" 
-heading "input heading here" 
-description "The selected delay is complete. The update will install in 5 minutes. Please save your work" 
-timeout 300 
-countdown 
-lockHUD 
-icon "$icon" 

jamf policy -event customAPITrigger
EOF

## Make reboot warning script executable
/usr/sbin/chown root:admin "$WarningRestartScript"
/bin/chmod 755 "$WarningRestartScript"
/bin/chmod +x "$WarningRestartScript"

}


cleanUp()
{

/bin/cat << EOF > "$CleanupRestartScript"
#!/bin/bash
PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/sbin

#Set the log path
logpath="/path/to/logpath/"

#Ensure logFile can be written
if [ ! -d "${logpath}" ]; then
  mkdir "${logpath}"
fi

##Create the logFile
logFile="${logpath}"restartCleanup.log

## First Run Script to remove the installer.
echo $(date) "Cleaning up files, phase 1.." >> $logFile
/bin/rm -f /Library/LaunchDaemons/com.company.rebootdelay.plist
/bin/rm -f /Library/LaunchDaemons/com.company.rebootwarning.plist
/bin/rm -f /path/to/rebootwarning.sh
launchctl remove com.company.rebootdelay
launchctl remove com.company.rebootwarning

/bin/sleep 2

## Update Device Inventory
echo $(date) "Running recon to update inventory" >> $logFile
jamf recon
## Remove LaunchDaemon
echo $(date) "Removing cleanup LaunchDaemon and script"
/bin/rm -f /Library/LaunchDaemons/com.company.cleanupDelayedRestart.plist
## Remove cleanup Script
/bin/rm -f /path/to/cleanupDelayedRestart.sh
echo "Cleaning Up Phase 2 complete.." >> $logFile
exit 0

EOF

## Make cleanup delayed restart script executable
/usr/sbin/chown root:admin "$CleanupRestartScript"
/bin/chmod 755 "$CleanupRestartScript"
/bin/chmod +x "$CleanupRestartScript"

## Create LaunchDaemon
cat << EOF > "$CleanupDelayedD"
<?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.company.cleanupDelayedRestart</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>-c</string>
        <string>"$CleanupRestartScript"</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>
EOF

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

}

finalPrep()
{

# unload launchd
#launchctl unload /Library/LaunchDaemons/org.company.rebootdelay.plist
#launchctl unload /Library/LaunchDaemons/org.company.rebootwarning.plist

#load launchd
launchctl load /Library/LaunchDaemons/com.company.rebootdelay.plist
launchctl load /Library/LaunchDaemons/com.company.rebootwarning.plist
echo $(date) "8 hour delay LaunchDaemon has been loaded.." >> $logFile
}

## Select action based on user input

case "$result" in
    *1 )    delay
            warndelay
            warnScript
            finalPrep
            cleanUp
            ;;
    *2 )    echo $(date) "Updating Inventory" >> $logFile
            jamf recon
            echo $(date) "Rebooting seat.." >> $logFile
            jamf policy -event customTrigger
            ;;
esac

exit 0

Based upon users response the above API script runs from a policy trigger. The only changes I made on the above script is encrypting the password for the API user..

nosaJ31
New Contributor

Was that script written from scratch by yourself ?

JustDeWon
Contributor III

beeboo
Contributor

@JustDeWon have you tried that script in conjuction with multiple OS's and hardware?

I have run the API call to an M1 + Big Sur machine (mac mini) and while the command goes through and i see a pending command under Management, it actually doesnt do the install nor reboot.
Same goes for if i click the Software Update in the Management tab of the same machine.

Im curious what your API call is and what hardware and software you have tested against.

thanks!

JustDeWon
Contributor III

@beeboo , I run my script prior to running @nelsoni script he posted above.. I am using the Install key in the API script. I'm only using the API script for macOS Big Sur(Intel/M1).. My script kicks off the -policy event that calls the API script based upon a user's selection to Delay or Install Now.. The only edit I made to his script was encrypt the api password..

#!/bin/sh

#############################################################################################
# - The purpose of this script is to allow softwareUpdates for macOS Big Sur on Intel/M1's
# - This script will resolve the need for user input during software updates
# - The password will be Encrypted for security reasons
#
# - Created by: Ian Nelson
# - Modified:   DeWon Farris    6/3/2020
# - Version:    1.1     #Encrypted API password
#############################################################################################

# Decrypt the API password
function DecryptString() {
    echo "${1}" | /usr/bin/openssl enc -aes256 -d -a -A -S "${2}" -k "${3}"
}

#API login info
apiuser="APIUser"
apipass=$(DecryptString $4 'string.goes.here' 'key.goes.here')
jamfProURL="https://company.url.here"

#Grab serial number and OS Version of computer
SerialNumber=$(system_profiler SPHardwareDataType | grep 'Serial Number (system)' | awk '{print $NF}')
macOSVersion=$(sw_vers -productVersion)

#Check macOS Major
CheckIt=$(echo $macOSVersion | cut -d . -f 1)

#Set xpath option based on macOS major version
if [[ "$CheckIt" == "11" ]]
    then
        xpath="xpath -e"
    else
        xpath="xpath"
fi

## Curl API to get to install software Updates ASPAP after user chooses to delay or install now
jamfProCompID=$( /usr/bin/curl -s -u ${apiuser}:${apipass} ${jamfProURL}/JSSResource/computers/serialnumber/${SerialNumber}/subset/general | $xpath "/computer/general/id/text()" )
echo $jamfProCompID
/usr/bin/curl -s -X POST -H "Content-Type: text/xml" -u ${apiuser}:${apipass} ${jamfProURL}/JSSResource/computercommands/command/ScheduleOSUpdate/action/installASAP/id/${jamfProCompID}


exit 0

is this with the latest version of Jamf? i think 10.32 or whatnot?

I tested the GUI iteration of this when i was RC'ing 10.32 and it worked, but it ended up rebooting at an undetermined time, thus out of the control of the user and myself.

Also couldnt do a schedule or anything, it just ran it as a management task.

Lastly it didnt give me any options aside from I think the type of update, like all or some aspects of the available updates (gotta double this part).

Based on that testing scheme, I dont have a reliable way to allow a user to get the update and reboot without a random interruption. I think the API would work the same or at least similarly to the GUI, but I am still left to the mercy of "random" reboot.

dmichels
Contributor

Still looking for a way to run Apple Security Updates now version 11.5 on computers with no user intervention? Running Jamf Pro Version 10.30 nothing working Softwareupdate -i -a -R says is works, but downloads and nothing happens. Any workarounds, why is Jamf not resolving this issue, its been months?

ardrake
New Contributor III

We began prepping our lifecycle replacements in earnest over the last two weeks, and I have become keenly aware of this problem.  The Management Command method of running updates is unreliable at best, and having to do so one machine at a time is not reasonable.  The ability to schedule updates (not just manually initiate them via a mass action) needs to be a priority.

I have tried using softwareupdate -i -a -R and User Interaction with some success, but still not good enough.  The issue is the user is not notified a reboot will occur (even after adding in the Reboot Policy) . Once the update is downloaded the device just reboots. The download is really slow (as mentioned elsewhere  here) and end users are not sure if it is actually working.  
This leads into another issue.  The time is UTC time which is challenging when you are dealing with Macs on a global scale.

I then resorted to this Babodee.   It took me a long time to understand what it does and had various results.  Perhaps it was the VM not working liking the updates and snap shot restoring.  I am still struggling with Big Sur Updates (from 11.2.3, 11.3.x and 11.4) and snuggling to understand the how to configure the script.

It really has been a challenge trying to find a method which allows the users to defer for 48hrs then its forced...and work consistently. 

Mass emails asking users to update has work little bit.  I now have a Jamf Notification Message which appears on users devices 1x per day informing them to update their machine.

Finally, one of our techs is messaging people directly telling them to update (50 or so)  This method, although manual and slow is having the biggest affect.

Let's hope Monterey resolves updating.

jschank
New Contributor III

I have been trying Nudge.  As we need to provide some info to end user before we move with an update.  Still in testing phase for us.

https://community.jamf.com/t5/jamf-pro/nudge-macos-updates/m-p/243467#M228760

 

 

How is Nudge working out for you? We are reluctant to add another agent on a device for running updates. So far one of our techs is messaging people directly.  It seems to work better than 2 communication emails.  We don't have 100s of Macs yet and only about 51 people were on 11.5.1 or less but all on Big Sur.

beeboo
Contributor

so this works, tested on Big Sur M1 machines

 

https://{{url}}/JSSResource/computercommands/command/ScheduleOSUpdate/action/install/id/:id

and while the command respects comma seperated values, anyone know how to get the information from an array to then do a one liner with commas using bash?

 

example: https://{{url}}/JSSResource/computercommands/command/ScheduleOSUpdate/action/install/id/9,100,92,1,500