Skip to main content

In light of the new "Force a Computer Restart to Install macOS Updates" feature in Jamf Pro 10.38.0, I've decided to create a bash function that should make life easier for admins that want to force updates on M1 machines.

Please refer to the screenshot below for more information on this feature.

New to bearer tokens? Don't worry about it, I've already done the work for you. Simply fill in your api account data and let the function take care of the rest. 

 

#!/bin/bash

# Server connection information
URL="https://url.jamfcloud.com"
username="apiusername"
password="apipassword"

# Determine Serial Number
serialNumber=$(system_profiler SPHardwareDataType | awk '/Serial Number/{print $4}')

initializeSoftwareUpdate(){
# create base64-encoded credentials
encodedCredentials=$( printf "${username}:${password}" | /usr/bin/iconv -t ISO-8859-1 | /usr/bin/base64 -i - )

# Generate new auth token
authToken=$( curl -X POST "${URL}/api/v1/auth/token" -H "accept: application/json" -H "Authorization: Basic ${encodedCredentials}" )

# parse authToken for token, omit expiration
token=$(/usr/bin/awk -F \\" 'NR==2{print $4}' <<< "$authToken" | /usr/bin/xargs)

echo ${token}

# Determine Jamf Pro device id
deviceID=$(curl -s -H "Accept: text/xml" -H "Authorization: Bearer ${token}" ${URL}/JSSResource/computers/serialnumber/"$serialNumber" | xmllint --xpath '/computer/general/id/text()' -)

echo ${deviceID}

# Execute software update
curl -X POST "${URL}/api/v1/macos-managed-software-updates/send-updates" -H "accept: application/json" -H "Authorization: Bearer ${token}" -H "Content-Type: application/json" -d "{\\"deviceIds\\":[\\"${deviceID}\\"],\\"maxDeferrals\\":0,\\"version\\":\\"12.3.1\\",\\"skipVersionVerification\\":true,\\"applyMajorUpdate\\":true,\\"updateAction\\":\\"DOWNLOAD_AND_INSTALL\\",\\"forceRestart\\":true}"

# Invalidate existing token and generate new token
curl -X POST "${URL}/api/v1/auth/keep-alive" -H "accept: application/json" -H "Authorization: Bearer ${token}"
}

initializeSoftwareUpdate

 

Upload this script in combination with a user interaction / jamfhelper dialog policy to start forcing updates again!!!

 

Lessons Learned 06/01/2022: 

1. The update can take an extremely long time to kick off. I'm talking 1-2 hours +

2. While the Jamf Pro GUI can do full OS upgrades, it doesn't seem to be supported in the API.

 

Lessons Learned 06/23/2022:

1. I cannot recommend putting this into production. While my jamf helper script does guide the user through the update, The targeted device does not restart in a reasonable time.

2. At this time, the best options for Monterey updates and upgrades seems to be using Nudge or the startosinstall executable that comes packaged with macOS installers: Solved: Re: macOS installer script not working for Apple S... - Jamf Nation Community - 249859

 

@MPL Below is the second part of my jamf helper process. It's basically a set of dialogs that guides the user through the updates. logs are created to track how many times the user has deferred the prompt. Simply create a custom policy with the trigger "updateOS". The launch daemon in part A will run this at the desired time interval.

 

 

 

#!/bin/bash
# Server connection information
URL="https://url.jamfcloud.com"
username="$4"
password="$5"

# Operatitng system information
osVersion="$6"
osName="$7"
osFullName="$7 "${osVersion}""
serialNumber=$(system_profiler SPHardwareDataType | awk '/Serial Number/{print $4}')

# Log information
currentUser=$( scutil <<< "show State:/Users/ConsoleUser" | awk '/Name 😕 && ! /loginwindow/ { print $3 }' )
logFolder="/Users/"${currentUser}"/logs/"
deferlog="${logFolder}/deferlog.txt"
lastDeferralCount=$(cat "${deferlog}")

# Jamf helper information
jamfHelper="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"

description1="${osFullName} is now available. Select 'Start Now' to initialize the update. Select 'Defer' to complete the update at another time.

Once the update is initiated, it may take 25-60 minutes for the computer to restart.

Remaining Deferral Attempts: 3"

description2="${osFullName} is now available. Select 'Start Now' to initialize the update. Select 'Defer' to complete the update at another time.

Once the update is initiated, it may take 25-60 minutes for the computer to restart.

Remaining Deferral Attempts: 2"

description3="${osFullName} is now available. Select 'Start Now' to initialize the software update. Select 'Defer' to initialize the update at another time.

Once the update is initiated, it may take 25-60 minutes for the computer to restart.

Remaining Deferral Attempts: 1"

finalDescription="The maximum deferral limit has been reached. Select 'Start Now' to initialize the ${osFullName} update. Otherwise, use the remaining time to sync any unsaved changes to OneDrive.

Remaining Deferral Attempts: 0"


waitDescription="Processing software update. Please wait for the computer to restart. Ensure that the power adapter is connected. This process may take awhile."

timeout="$8"

########Functions###########

initializeSoftwareUpdate(){
# create base64-encoded credentials
encodedCredentials=$( printf "${username}:${password}" | /usr/bin/iconv -t ISO-8859-1 | /usr/bin/base64 -i - )

# Generate new auth token
authToken=$( curl -X POST "${URL}/api/v1/auth/token" -H "accept: application/json" -H "Authorization: Basic ${encodedCredentials}" )

# parse authToken for token, omit expiration
token=$(/usr/bin/awk -F \\" 'NR==2{print $4}' <<< "$authToken" | /usr/bin/xargs)

echo ${token}

# Determine Jamf Pro device id
deviceID=$(curl -s -H "Accept: text/xml" -H "Authorization: Bearer ${token}" ${URL}/JSSResource/computers/serialnumber/"$serialNumber" | xmllint --xpath '/computer/general/id/text()' -)

echo ${deviceID}

# Execute software update
curl -X POST "${URL}/api/v1/macos-managed-software-updates/send-updates" -H "accept: application/json" -H "Authorization: Bearer ${token}" -H "Content-Type: application/json" -d "{\\"deviceIds\\":[\\"${deviceID}\\"],\\"maxDeferrals\\":0,\\"version\\":\\"${osVersion}\\",\\"skipVersionVerification\\":true,\\"applyMajorUpdate\\":true,\\"updateAction\\":\\"DOWNLOAD_AND_INSTALL\\",\\"forceRestart\\":true}"

# Invalidate existing token and generate new token
curl -X POST "${URL}/api/v1/auth/keep-alive" -H "accept: application/json" -H "Authorization: Bearer ${token}"
}

pleaseWait(){

buttonClicked=$("$jamfHelper" -windowType hud -lockHUD -windowPosition "lr" -title "Software Update" -description "$waitDescription" -alignDescription "Left")

}

firstPrompt(){

# First Prompt
buttonClicked=$("$jamfHelper" -windowType hud -lockHUD -windowPosition "lr" -title "Software Update" -description "${description1}" -alignDescription "Left" -button1 "Start Now" -button2 "Defer")

echo $buttonClicked

if [[ $buttonClicked == 0 ]]; then
echo "User clicked Start Now"
echo "Initializing Software Update"
rm -rf "${logFolder}"
launchctl unload /private/tmp/osUpdate.plist
rm -rf /private/tmp/osUpdate.plist
initializeSoftwareUpdate
pleaseWait
elif [[ $buttonClicked == 2 ]]; then

# Declare what the user clicked
echo "User Clicked Defer"

# Set deferralCount to 0
deferralCount="1"

#Append current number of deferrals to deferlog.txt
echo $deferralCount > $deferlog

exit 0
fi
}

secondPrompt(){

# Second Prompt
buttonClicked=$("$jamfHelper" -windowType hud -lockHUD -windowPosition "lr" -title "Software Update" -description "${description2}" -alignDescription "Left" -icon -button1 "Start Now" -button2 "Defer")

echo $buttonClicked

if [[ $buttonClicked == 0 ]]; then
echo "User clicked Start Now"
echo "Initializing Software Update"
rm -rf "${logFolder}"
launchctl unload /private/tmp/osUpdate.plist
rm -rf /private/tmp/osUpdate.plist
initializeSoftwareUpdate
pleaseWait
elif [[ $buttonClicked == 2 ]]; then
echo "User Clicked Defer"

# Set deferralCount to 0
deferralCount="2"

#Append current number of deferrals to deferlog.txt
echo $deferralCount > $deferlog

exit 0
fi
}

thirdPrompt(){

# Third Prompt
buttonClicked=$("$jamfHelper" -windowType hud -lockHUD -windowPosition "lr" -title "Software Update" -description "${description3}" -alignDescription "Left" -button1 "Start Now" -button2 "Defer")

echo $buttonClicked

if [[ $buttonClicked == 0 ]]; then
echo "User clicked Start Now"
echo "Initializing Software Update"
rm -rf "${logFolder}"
launchctl unload /private/tmp/osUpdate.plist
rm -rf /private/tmp/osUpdate.plist
initializeSoftwareUpdate
pleaseWait
elif [[ $buttonClicked == 2 ]]; then
echo "User Clicked Defer"

# Set deferralCount to 0
deferralCount="3"

#Append current number of deferrals to deferlog.txt
echo $deferralCount > $deferlog

exit 0
fi
}


finalPrompt(){

buttonClicked=$("$jamfHelper" -windowType hud -lockHUD -windowPosition "lr" -title "Software Update" -description "$finalDescription" -alignDescription "Left" -button1 "Start Now" -timeout $timeout -countdown -alignCountdown "Center")
if [[ $buttonClicked == 0 ]]; then
echo "User clicked Start Now"
echo "Initializing Software Update"
rm -rf "${logFolder}"
launchctl unload /private/tmp/osUpdate.plist
rm -rf /private/tmp/osUpdate.plist
initializeSoftwareUpdate
pleaseWait
fi

}

# First Prompt
if [[ ! -f $deferlog ]]; then

echo "Initializing deferral process"

# Create a logs folder
mkdir $logFolder

# Create a deferlog.txt to track deferral count
touch "$deferlog"

# Set deferralCount to 0
deferralCount="1"

#Append current number of deferrals to deferlog.txt
echo $deferralCount > $deferlog


# Initialize First Prompt
firstPrompt

# Second Prompt
elif [[ ${lastDeferralCount} = "1" ]]; then
echo "Initializing Second Prompt"
secondPrompt

# Third Prompt
elif [[ ${lastDeferralCount} = "2" ]]; then
echo "Initializing Third Prompt"
thirdPrompt

elif [[ ${lastDeferralCount} = "3" ]]; then
echo "Initializing Final Prompt"
finalPrompt

fi

exit 0 ## Success
exit 1 ## Failure

 

 

 

 


Hi @bwoods I have encountered an issue and I'm hoping you might be able to help me out. 

When I let the launchdaemon script from policy A runs after the specified time it will show the Jamf helper prompt, and when click "Start Now" nothing seems to happen except the script removing the log folder from the /Users. Also, no logs are showing up in deferral policy B logs that the user has selected "Start Now".  However, If I select "Defer" it works and I can see the logs showing up in policy B that the user has deferred.    

The interesting thing is if I manually run sudo jamf policy -event updaeOS on the test machine the "Start Now" from the jamf helper works fine! 

 

Any advice would be appreciated greatly. 


@moussabl Here are the JAMF account permissions I use for this script and it works for me.  

Account
Access Level - Full Access
Privilege Set - Custom


Privileges (everything is unchecked except for the following):
JAMF Pro Server Objects - Computers - Create+Read

JAMF Pro Server Actions - Send Computer Remote Command to Download and Install macOS Update - Checked


Awesome, Thank you @stutz 


Hi @bwoods I have encountered an issue and I'm hoping you might be able to help me out. 

When I let the launchdaemon script from policy A runs after the specified time it will show the Jamf helper prompt, and when click "Start Now" nothing seems to happen except the script removing the log folder from the /Users. Also, no logs are showing up in deferral policy B logs that the user has selected "Start Now".  However, If I select "Defer" it works and I can see the logs showing up in policy B that the user has deferred.    

The interesting thing is if I manually run sudo jamf policy -event updaeOS on the test machine the "Start Now" from the jamf helper works fine! 

 

Any advice would be appreciated greatly. 


@moussabl , ensure that the custom triggers are exactly the same. In your post above, see "updaeOS" instead of "updateOS". The trigger in the launch daemon must mach the trigger in the policy. Also, I designed the script to self destruct after "Start Now" is selected. The logs only work if the user clicks defer.  

For testing I suggest using Code Runner to run Part A over and over again until you're satisfied with the results.

For instance, have the code for Part A in code Runner and Part B in Jamf. Manually set the timeout in Part A to something reasonable like 10 seconds to test the code. 


Was playing around with this and granting the account read access to Computers, it outputs some information that's questionable. management_password, list of users, software detected, etc

 

Not the fastest to get ID. But it won't expose more information than what's needed.

deviceID=$(jamf recon | grep "computer_id" | xmllint --xpath '/computer_id/text()' -)
 
Account just has this permission: Send Computer Remote Command to Download and Install macOS Update

One note, the section below can mostly likely be turned into a variable or parameter to edit the version number on the fly in the future. Otherwise, have at it. Help me improve this thing.

 


I ran the above script on one of our M1's and it says completed with the below details. The user's MacBook was locked.

Script result: Serial number: XYVH2K0FVH
% Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 100 391 0 391 0 0 905 0 --:--:-- --:--:-- --:--:-- 922 Auth Token: { "token" : "eyJhbGciOiJIUzI1NiJ9.eyJhdXRoZW50aWNhdGVkLWFwcCI6IkdFTkVSSUMiLCJhdXRoZW50aWNhdGlvbi10eXBlIjoiSlNTIiwiZ3JvdXBzIjpbXSwic3ViamVjdC10eXBlIjoiSlNTX1VTRVJfSUQiLCJ0b2tlbi11dWlkIjoiNmQwNDQwMmQtZjgyOC00Mzk2LWEwNjItNjRkOWQ2NjU4ZTJmIiwibGRhcC1zZXJ2ZXItaWQiOi0xLCJzdWIiOiIxIiwiZXhwIjoxNjU5MjIwMTkxfQ.IMGqlh89pAIIGNOI0Fnn4bKKLkZwZb__G6b4p1nH6Z0", "expires" : "2022-07-30T22:29:51.103Z" } Token: eyJhbGciOiJIUzI1NiJ9.eyJhdXRoZW50aWNhdGVkLWFwcCI6IkdFTkVSSUMiLCJhdXRoZW50aWNhdGlvbi10eXBlIjoiSlNTIiwiZ3JvdXBzIjpbXSwic3ViamVjdC10eXBlIjoiSlNTX1VTRVJfSUQiLCJ0b2tlbi11dWlkIjoiNmQwNDQwMmQtZjgyOC00Mzk2LWEwNjItNjRkOWQ2NjU4ZTJmIiwibGRhcC1zZXJ2ZXItaWQiOi0xLCJzdWIiOiIxIiwiZXhwIjoxNjU5MjIwMTkxfQ.IMGqlh89pAIIGNOI0Fnn4bKKLkZwZb__G6b4p1nH6Z0 Device ID: 107 % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 100 392 0 224 100 168 539 404 --:--:-- --:--:-- --:--:-- 982 { "responses" : [ { "id" : "e84835e9-7ee3-4884-9c01-2a032512cbfb", "href" : "https://lplfinancial.jamfcloud.com/lplfinancial/api/v1/mdm/commands?uuids=e84835e9-7ee3-4884-9c01-2a032512cbfb" } ], "errors" : [ ] } % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 100 391 0 391 0 0 854 0 --:--:-- --:--:-- --:--:-- 884 { "token" : "eyJhbGciOiJIUzI1NiJ9.eyJhdXRoZW50aWNhdGVkLWFwcCI6IkdFTkVSSUMiLCJhdXRoZW50aWNhdGlvbi10eXBlIjoiSlNTIiwiZ3JvdXBzIjpbXSwic3ViamVjdC10eXBlIjoiSlNTX1VTRVJfSUQiLCJ0b2tlbi11dWlkIjoiNmQwNDQwMmQtZjgyOC00Mzk2LWEwNjItNjRkOWQ2NjU4ZTJmIiwibGRhcC1zZXJ2ZXItaWQiOi0xLCJzdWIiOiIxIiwiZXhwIjoxNjU5MjIwMTkzfQ.cpkwPxLRneGh3uI9THNzys7rl8cHjFnEEmu8sTevuoA", "expires" : "2022-07-30T22:29:53.276Z" }


@moussabl , ensure that the custom triggers are exactly the same. In your post above, see "updaeOS" instead of "updateOS". The trigger in the launch daemon must mach the trigger in the policy. Also, I designed the script to self destruct after "Start Now" is selected. The logs only work if the user clicks defer.  

For testing I suggest using Code Runner to run Part A over and over again until you're satisfied with the results.

For instance, have the code for Part A in code Runner and Part B in Jamf. Manually set the timeout in Part A to something reasonable like 10 seconds to test the code. 


Thanks heaps @bwoods! It's working for us now.

Sorry, final question. would you know if there's a way to postponed the Jamf helper notification when a user is active in a meeting on MS Teams or Zoom call?

Thank you very much.


Thanks heaps @bwoods! It's working for us now.

Sorry, final question. would you know if there's a way to postponed the Jamf helper notification when a user is active in a meeting on MS Teams or Zoom call?

Thank you very much.


@moussabl If the notification is a system notification, teach the users to enable Do not disturb on the computer at the beginning of a meeting (and how to disable it afterwards). This process is slightly different depending on the macOS version:

  • In macOS 10.15 Catalina and earlier, the quickest way was to go to the Notification centre (itemised hamburger menu on the right of the menu bar) and scroll up to find the Do Not Disturb switch.
  • In macOS 11 Big Sur and newer, go to the Control Centre menu and change the Focus (usually top right of the Control Centre).

Thanks, I actually got it working last night (tried so many things I don't remember what fixed it). Here is the final script I'm using:

#!/bin/bash

# Server connection information
URL="xxxxxxxx"
username="xxxxxxxx"
password="xxxxxxxx"

# Determine Serial Number
serialNumber=$(system_profiler SPHardwareDataType | awk '/Serial Number/{print $4}')

echo "Serial number: ${serialNumber}"

initializeSoftwareUpdate(){
# create base64-encoded credentials
encodedCredentials=$( printf "${username}:${password}" | /usr/bin/iconv -t ISO-8859-1 | /usr/bin/base64 -i - )

# Generate new auth token
authToken=$( curl -X POST "${URL}/api/v1/auth/token" -H "accept: application/json" -H "Authorization: Basic ${encodedCredentials}" )

# parse authToken for token, omit expiration
token=$(/usr/bin/awk -F \\" 'NR==2{print $4}' <<< "$authToken" | /usr/bin/xargs)

echo "Token: ${token}"

# Determine Jamf Pro device id
deviceID=$(curl -s -H "Accept: text/xml" -H "Authorization: Bearer ${token}" ${URL}/JSSResource/computers/serialnumber/"$serialNumber" | xmllint --xpath '/computer/general/id/text()' -)

echo "Device ID: ${deviceID}"

# Execute software update
curl -X POST "${URL}/api/v1/macos-managed-software-updates/send-updates" -H "accept: application/json" -H "Authorization: Bearer ${token}" -H "Content-Type: application/json" -d "{\\"deviceIds\\":[\\"${deviceID}\\"],\\"maxDeferrals\\":0,\\"version\\":\\"12.4\\",\\"skipVersionVerification\\":true,\\"applyMajorUpdate\\":true,\\"updateAction\\":\\"DOWNLOAD_AND_INSTALL\\",\\"forceRestart\\":true}"

# Invalidate existing token and generate new token
curl -X POST "${URL}/api/v1/auth/keep-alive" -H "accept: application/json" -H "Authorization: Bearer ${token}"
}

initializeSoftwareUpdate

 Thanks for your help, I really appreciate it!


For this to work, does the user have to be logged on. I tested it on one our users, but the device was locked and it never updated.


Thanks heaps @bwoods! It's working for us now.

Sorry, final question. would you know if there's a way to postponed the Jamf helper notification when a user is active in a meeting on MS Teams or Zoom call?

Thank you very much.


Hmmm...my first instinct would be to create a conditional statement to check if Zoom or Teams is open, but I don't know how to tell if the user is on a call. Simply checking for open apps may result in the process never starting because messaging apps are usually always open.


From going through this thread, are 3 policies needed to deploy the update? From what I'm reading it looks like one is needed for the update script, Part A and Part B?


From going through this thread, are 3 policies needed to deploy the update? From what I'm reading it looks like one is needed for the update script, Part A and Part B?


Only two policies are needed. One to load the launch daemon and the other to run the script.


Only two policies are needed. One to load the launch daemon and the other to run the script.


Sorry for the ignorance on this, very new to Jamf. I'm just creating part A(daemon) and then part B for the script?


Sorry for the ignorance on this, very new to Jamf. I'm just creating part A(daemon) and then part B for the script?


Correct, part A is creating and loading a launch daemon that then runs the script in part B.


Correct, part A is creating and loading a launch daemon that then runs the script in part B.


I must be doing something wrong, because Part A shows installed, but it's not kicking off B. 


I must be doing something wrong, because Part A shows installed, but it's not kicking off B. 


Ensure that you've configured the parameter for part A and that the custom trigger in part B matches the trigger in part A. Are you familiar with positional parameters / custom triggers in Jamf?


Ensure that you've configured the parameter for part A and that the custom trigger in part B matches the trigger in part A. Are you familiar with positional parameters / custom triggers in Jamf?


I'm familiar with custom triggers, but not so much regarding positional parameters. I have the trigger in part B set to updateOS.


I'm familiar with custom triggers, but not so much regarding positional parameters. I have the trigger in part B set to updateOS.


Also, if the user hit's start now and then locks there Mac, will the process finish or does it need to be logged on to?


Also, if the user hit's start now and then locks there Mac, will the process finish or does it need to be logged on to?


Sorry for all the questions. Is it possible to set part B to deploy without any user interaction? Trying to be more like our SCCM side of things. We typically send out communications informing users of updates that are coming at night. If possible, it would cool to set this to deploy at night without any user interaction.


Sorry for all the questions. Is it possible to set part B to deploy without any user interaction? Trying to be more like our SCCM side of things. We typically send out communications informing users of updates that are coming at night. If possible, it would cool to set this to deploy at night without any user interaction.


If you don't want to notify the user first, you can send a mass action command via Jamf. More info below: 

Updating macOS Using a Mass Action - Managing macOS Updates | Jamf


If you don't want to notify the user first, you can send a mass action command via Jamf. More info below: 

Updating macOS Using a Mass Action - Managing macOS Updates | Jamf


That was hit miss when we tested it.  Your script worked, so I think we'll roll with that and just have to have some user interaction.


That was hit miss when we tested it.  Your script worked, so I think we'll roll with that and just have to have some user interaction.


That's very strange because my script is basically an MDM Command in API form. If you don't want any interaction you can copy my original function above and just trow it in a policy.


That's very strange because my script is basically an MDM Command in API form. If you don't want any interaction you can copy my original function above and just trow it in a policy.


When running either the Part B or your original function, does the user have to be logged on for the update complete?


These couple of functions may help you add some safety checks in.

batteryCheck(){
powerType=$(pmset -g batt | head -n 1 | cut -c19- | rev | cut -c 2- | rev)

if [[ "$powerType" == "Battery Power" ]]; then
echo "Machine is on Battery Power."
bat=$(pmset -g batt | grep 'InternalBattery' | awk '{print $3}' | tr -d '%'';')
if (( $bat > 60 )); then
echo "Battery Level OK, Continuing Update..."
else
echo "Battery Level Insufficient for this update"
return 1
fi
else
echo "Machine is on AC Power."
fi
}

bootstrapTokenCheck(){
bootstrap=$(profiles status -type bootstraptoken)
if [[ $bootstrap == *"escrowed to server: YES"* ]]; then
echo "Bootstrap escrowed"
else
echo "Bootstrap not escrowed. Cannot update with MDM commands."
return 1
fi
}

freeSpaceRequirement(){
freespace=$(df -h -m | grep -m 1 /System/Volumes/Data | awk '{print $4}')
if [[ $freespace -lt 15000 ]]; then
echo "Insufficient free space"
return 1
else
echo "Machine has at least 15 GBs of free space"
fi
}

verifyUpdateNeeded(){
# Check for available updates
availableUpdates=`/usr/libexec/mdmclient AvailableOSUpdates`

# Updates available
if [[ "$availableUpdates" == *"=== OS Update Item ==="* ]]; then
echo "Updates Available"
# Updates not available
else
echo "No Updates Available"
return 1
fi
}

# Verifying update is needed and requirements have been met.
verifyUpdateNeeded || exit 0
bootstrapTokenCheck || exit 0
freeSpaceRequirement || exit 0
batteryCheck || exit 0

 Also if I'm not mistaken you can streamline your update command so you don't have to fiddle with it as much next time around.  If you don't specify a specific version, it will download the newest one based on device eligibility.

curl -X POST "${URL}/api/v1/macos-managed-software-updates/send-updates" \\
-H "accept: application/json" \\
-H "Authorization: Bearer ${token}" \\
-H "Content-Type: application/json" \\
-d "{\\"deviceIds\\":[\\"${deviceID}\\"],\\"skipVersionVerification\\":false,\\"applyMajorUpdate\\":false,\\"updateAction\\":\\"DOWNLOAD_AND_INSTALL\\",\\"forceRestart\\":true}"

 


can the bootstrap token check be skipped if not using mobile accounts? or is it a requirement that would stop it working?


can the bootstrap token check be skipped if not using mobile accounts? or is it a requirement that would stop it working?


@tkimpton The account type doesn't matter, a bootstrap token is required for any MDM triggered updates


just FYI you need to change ${osFullName} to $osFullName in the variables otherwise you will get 403 Access Denied when trying to edit the script


Reply