Posted on 02-04-2021 07:26 AM
(Please pardon the piecemeal post; I'm presuming partial information is better than nothing.)
Thanks to AppleCare pointing out that:
When running the softwareupdate
command in a root shell on Apple Silicon users are being prompted for a password.
This is expected behavior and the recommendation is to use the Schedule an OS Update command via MDM. This is the method to use if you want to update Apple Silicon Macs without requiring user credentials.
In other words:
if [[ "$arch" == "arm64" ]]; then
scheduleOSUpdateViaAPI
else
/usr/sbin/softwareupdate --install --all --include-config-data --restart --force
fi
In my limited testing, users are still prompted:
####################################################################################################
#
# Variables
#
####################################################################################################
jamfProURL="https://company.jamfcloud.com" # No trailing forward slash
apiUsername="${5}"
apiPasswordEncrypted="${6}"
computerSerialNumber=$( /usr/sbin/system_profiler SPHardwareDataType | /usr/bin/grep Serial | /usr/bin/awk '{print $NF}' )
arch=$( /usr/bin/arch )
####################################################################################################
#
# Functions
#
####################################################################################################
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# xpath tool changes in Big Sur
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function xpath() {
# https://scriptingosx.com/2020/10/dealing-with-xpath-changes-in-big-sur/
# Thanks, Armin!
if [[ $(sw_vers -buildVersion) > "20A" ]]; then
/usr/bin/xpath -e "$@"
else
/usr/bin/xpath "$@"
fi
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Decrypt Password
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function decryptPassword() {
/bin/echo "${1}" | /usr/bin/openssl enc -aes256 -d -a -A -S "${2}" -k "${3}"
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Schedule OS Update via the API
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function scheduleOSUpdateViaAPI() {
echo "Schedule OS Update via the API …"
apiPassword=$( decryptPassword ${apiPasswordEncrypted} ${Salt} ${Passphrase} )
jamfProCompID=$( /usr/bin/curl -s -u ${apiUsername}:${apiPassword} ${jamfProURL}/JSSResource/computers/serialnumber/${computerSerialNumber}/subset/general | xpath "/computer/general/id/text()" )
# /usr/bin/curl -s -X POST -H "Content-Type: text/xml" -u ${apiUsername}:${apiPassword} ${jamfProURL}/JSSResource/computercommands/command/ScheduleOSUpdate/action/InstallForceRestart/id/${jamfProCompID}
/usr/bin/curl -s -X POST -H "Content-Type: text/xml" -u ${apiUsername}:${apiPassword} ${jamfProURL}/JSSResource/computercommands/command/ScheduleOSUpdate/action/Default/id/${jamfProCompID}
}
####################################################################################################
#
# Program
#
####################################################################################################
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Force Software Update Snippet only
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
if [[ "$arch" == "arm64" ]]; then
scheduleOSUpdateViaAPI
else
/usr/sbin/softwareupdate --install --all --include-config-data --restart --force
fi
10-22-2021 11:41 AM - edited 07-01-2022 03:47 PM
I went ahead and deployed to a classroom last night and successfully updated 34 out of 36 stations from 11.5.2 to 11.6 while at the login window. I'm still at a loss as to why it's not 100%, but hopefully this will improve with Monterey. As best I can tell the Jamf side and API command is functioning, it just seems like something on the workstation(s) themselves related to softwareupdated, or other process is hindering the update. I'm still digging though logs, but I'm not coming up with anything yet.
fwiw- if it helps anyone following along, here's how I'm initiating the command via check-in from a Jamf policy script (sanitized for public view & using optional script parameters for testing... I would recommend using encrypted script parameters for production).
api user permissions are only:
Jamf Pro Server Objects: Computer: Create & Read
Jamf Pro Server Actions: Send Computer Remote Command to Download and Install macOS Update
- see newer script in thread below using updated API
Posted on 05-06-2022 10:46 AM
Love this script, thx for sharing. Working well for my tests so far with Monterey, but I have to manually click "Restart Now" in Software Update to get this to complete. Is that as intended or should this download, install and then restart automatically?
05-06-2022 11:48 AM - edited 05-06-2022 11:49 AM
Thanks Tom! My only thought is are you following up with a policy 'Restart Options' payload specifically set to 'MDM Restart with Kernel Cache Rebuild'?
I recently updated a few hundred lab stations to 12.3.1 at the login window so I know it's still working. However as I mentioned previously, I almost always have a few stations that despite receiving the mdm command never complete the update!? I suspect an Apple bug with softwareupdated, and lately I've been adding this earlier in the script. I feel like it's helping but I can't honestly say for sure. The word is that Apple patched some issues with softwareupdated in 12.3 but I've not hat time to follow up & test.
/bin/launchctl kickstart -k system/com.apple.softwareupdated
### give it 5 minutes to check for updates & sort itself out
sleep 300
Posted on 05-06-2022 12:48 PM
Ah I did not have the restart payload set in my policy! Testing now, first computer did not work, will keep working on it. Using this policy with the "Enrollment complete" trigger so when when start getting new laptops ready, one of the first things that happens is updating to latest macOS.
Where did you apply:
/bin/launchctl kickstart -k system/com.apple.softwareupdated ### give it 5 minutes to check for updates & sort itself out sleep 300
Posted on 05-06-2022 01:14 PM
@jonw actually it did work just now! Heading out for the wkd and letting another run now, will report back Monday.
05-06-2022 01:44 PM - edited 05-06-2022 01:46 PM
@TomDay Awesome! Glad to hear it.
I run the softwareupdated kickstart before everything else. I also should have mentioned the whole update process governed by api/mdm is annoyingly 'mysterious' at times, whether triggered via script or by the native Jamf action. I've seen it take up to an hour past the command being sent to see any obvious activity other than the Jamf log saying the command was successfully sent.
Posted on 02-10-2022 05:21 AM
Has anyone found a Jamf API to include the new deferral option when running the update MDM command?
03-10-2022 03:31 PM - edited 03-12-2022 02:30 PM
judging by the API commands I'm seeing, it doesn't seem like we can do this yet. =(
Posted on 03-12-2022 02:29 PM
Correction, just been noodling, and found it in the API docs.
Wrote up an advancedsearch to find machines needing 12.2.1, and then some quick powershell to grab the computers, and build the query. It SEEMS to work, but will need to test more.
Posted on 05-18-2022 12:39 PM
/bin/launchctl kickstart -k system/com.apple.softwareupdated ### give it 5 minutes to check for updates & sort itself out sleep 300
This seems to have made a big difference in the effectiveness of the ScheduleOSUpdate command for me.
Posted on 05-18-2022 01:58 PM
@jwaltonen Glad to hear it!
By the way, a new feature in the recently released Jamf 10.38.0 that looks really promising! "You can now force computers to immediately restart and install an available macOS update using the /v1/macos-managed-software-updates/send-updates endpoint via the Jamf Pro API."
I'm going to start testing asap.
Posted on 05-20-2022 07:18 AM
Maybe I spoke too soon.
With Monterey:
${jss}/JSSResource/computercommands/command/ScheduleOSUpdate/action/install/id/${jamfID}
Seemed to work pretty good on its own.
Adding
/bin/launchctl kickstart -k system/com.apple.softwareupdated
before the call seemed to make it work even better.
However it has done nothing for improving the situation on Big Sur endpoints.
/v1/macos-managed-software-updates/send-updates
In my limited testing this is working well for Monterey but it still very sporadic, leaning toward mostly not working for Big Sur endpoints.
I guess the solution is obvious, upgrade everything to Monterey.
07-01-2022 11:28 AM - edited 07-01-2022 11:30 AM
Hi! With Monterrey pretty sure you want to POST to
https://instance.jamfcloud.com/api/v1/macos-managed-software-updates/send-updates
https://developer.jamf.com/jamf-pro/reference/post_v1-macos-managed-software-updates-send-updates
We have tested and it works. But the thing is it's kind of just a random wait until it starts.... Wish there was a way to intercept the process start (downloading update + starting preinstall before logout) from the syslog or such other and show a notification that it's starting. Could be anywhere from 30 seconds to 15 minutes before the machine randomly reboots on the user.
07-01-2022 11:45 AM - edited 07-05-2022 03:08 PM
@BlackGloveEng1 Funny you should mention, I've actually been working on an improved script that uses the new endpoint you mention (my version above was built prior to this) which also incorporates the use of an auth bearer token.
Here's what I'm testing now. It seems pretty solid, but I'm happy to receive any constructive criticism. The only issue I've noticed is the same as before, periodically a computer will go through the motions, but no update actually occurs. I can see in the logs the API call was successfully sent, so I think this is an Apple/MDM issue, not Jamf. Running a second time almost always fixes it up. Obviously use at your own risk, you can see I only schedule this to run off hours in our unique student lab environment, ymmv.
#!/bin/bash
### Execute macOS update via MDM API - LAB
### latest update 2022.06.30 -JonW
### Use auth bearer token and new /v1/macos-managed-software-updates/send-updates endpoint found in Jamf Pro API 10.38.0+
### This is primarily intended for off hours, scheduled macOS updates while at the login window.
### Script will warn any logged-in user with a 5 minute timer, but will NOT provide an option to defer.
### Updating in this fashion eliminates the requirement for a local admin password on Silicon and also works with Intel.
### Adapting a few ideas & tips from deflounder, @talkingmoose, & @bwoods - THANK YOU!
### https://derflounder.wordpress.com/2022/01/04/basic-authentication-deprecated-for-the-jamf-pro-classic-api/
### https://community.jamf.com/t5/jamf-pro/force-a-computer-restart-to-install-macos-updates/td-p/265982
### required API permissions
### Jamf Pro Server Objects: Computer (read only - to obtain device ID)
### Jamf Pro Server Actions: Send Computer Remote Command to Download and Install macOS Update
### Ensure policy uses MDM Restart with Kernel Cache Rebuild payload
### SECURITY NOTE!!!
### We're calling the API from the local computer, and passing the API username & password...
### which can potentially expose them to a savvy local user looking at system processes!
### In this case, risk is minimized by running only while at the login window and using a unique API user & pass with limited permissions.
#################################################################################
### Jamf policy script parameters
#################################################################################
jss="$4"
apiUser="$5"
apiPass="$6"
macOSversion="$7" ### OPTIONAL - specify a macOS version to install (if not specified, latest available will apply)
applyMajorUpdate="$8" ### OPTIONAL - allow major macOS version upgrade (specify true or false)
#################################################################################
### If a user is logged in, warn to save & logout within 5 minutes!
### As we only run late on weekends, chances are slim the user is actually active,
### more likely a stuck app has halted the automatic logout process, but just to be sure...
#################################################################################
loggedInUser=$( scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ && ! /loginwindow/ { print $3 }' )
if [[ -n "$loggedInUser" ]]; then
### Open JamfHelper dialog
buttonClicked=$(/Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper \
-windowType utility \
-title "Weekly MacOS Update Policy" \
-heading "Attention! " \
-description "Automatic logout to perform updates will occur in 5 minutes! To prevent data loss, save all files and logout immediately." \
-defaultButton 1 \
-button2 "OK" \
-countdown \
-timeout 300 \
-alignCountdown center)
if [ $buttonClicked == 2 ]; then
### Button 2 clicked - user warning acknowledged
echo "User acknowledged, waiting 5 minutes for them to save & logout"
sleep 300
### Is user still logged in?
loggedInUser=$( scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ && ! /loginwindow/ { print $3 }' )
if [[ -n "$loggedInUser" ]]; then
echo "User acknowledged, but has yet to logout after 5 minutes, force logout now"
killall loginwindow ### No need to be delicate
sleep 30 ### wait for loginwindow to reload
else
echo "proceeding, user has logged out"
fi
else
### No click default = 1, the 5 minute countdown timed out
echo "proceeding, no user acknowledgement after 5 minutes, force logout now"
killall loginwindow ### No need to be delicate
sleep 30 ### wait for loginwindow to reload
fi
fi
#################################################################################
### kick softwareupdated, not required, but does 'seem' to help with success rate
#################################################################################
/bin/launchctl kickstart -k system/com.apple.softwareupdated
sleep 120 ### give it a few minutes to sort itself out
#################################################################################
### Generate Authorization Bearer Token
### (valid 30 minutes or until we invalidate)
#################################################################################
### (thanks again Der Flounder & @talkingmoose for the slick one-liner & parsing methods!)
if [[ $(/usr/bin/sw_vers -productVersion | awk -F . '{print $1}') -lt 12 ]]; then
### Pre Monterey, grab token & parse to remove quotes & expiration details
token=$(/usr/bin/curl -s "${jss}/api/v1/auth/token" -u "${apiUser}:${apiPass}" -X POST | python -c 'import sys, json; print json.load(sys.stdin)["token"]')
else
### As of Monterey, grab token and use updated plutil to pull raw token directly - no parsing necessary
token=$(/usr/bin/curl -s "${jss}/api/v1/auth/token" -s -u "${apiUser}:${apiPass}" -X POST | plutil -extract token raw -)
fi
echo "$token"
#################################################################################
### Determine Jamf Pro device id from the computer serial number (ID is required by /v1/macos-managed-software-updates endpoint)
### Note, I'm still unable to find a way to pull a device ID from the Pro API, continue using Classic for now.
#################################################################################
serialNumber=$(system_profiler SPHardwareDataType | awk '/Serial Number/{print $4}')
deviceID=$(/usr/bin/curl -s "${jss}/JSSResource/computers/serialnumber/${serialNumber}" -H "Authorization: Bearer ${token}" -H "Accept: text/xml" | xmllint --xpath '/computer/general/id/text()' -)
echo "Device ID" ${deviceID}
#################################################################################
### Check current macOS, and attempt to apply updates
##################################################################################
### If optional specified macOS version is equal to the current macOS, we're already up to date, exit 0
current_macOS=$( sw_vers -productVersion )
if [[ "$7" == "$current_macOS" ]]; then
echo "Specified macOS is $7"
echo "current macOS is $current_macOS"
echo "No update necessary, macOS is already at our specified version"
exit 0
fi
### If optional script parameter $8 (allow major macOS upgrade) is not explicitely set to true, force default to be false
if [[ "$8" != "true" ]]; then
applyMajorUpdate="false"
echo "allow major macOS upgrade? $applyMajorUpdate"
else
echo "allow major macOS upgrade? $applyMajorUpdate"
fi
### If the optionally specified macOS version is NOT EQUAL to the current macOS, apply it, else apply the most recently available update.
### Note! We're not validating cases where the specified version may be lower than the currently installed version.
### The internal update process 'should' automatically ignore these attempts, but I've not yet tested the theory!
if [[ $7 != "" ]]; then
echo "attempting to apply specified macOS version $7"
/usr/bin/curl -s -X POST \
--url "${jss}/api/v1/macos-managed-software-updates/send-updates" \
--header "Authorization: Bearer ${token}" \
--header "accept: application/json" \
--header "Content-Type: application/json" \
--data @<(cat <<EOF
{
"deviceIds": [
"$deviceID"
],
"maxDeferrals": 0,
"version": "$macOSversion",
"skipVersionVerification": true,
"applyMajorUpdate": $applyMajorUpdate,
"updateAction": "DOWNLOAD_AND_INSTALL",
"forceRestart": true
}
EOF
)
else
echo "attempting to apply the latest available macOS version"
/usr/bin/curl -s -X POST \
--url "${jss}/api/v1/macos-managed-software-updates/send-updates" \
--header "Authorization: Bearer ${token}" \
--header "accept: application/json" \
--header "Content-Type: application/json" \
--data @<(cat <<EOF
{
"deviceIds": [
"$deviceID"
],
"maxDeferrals": 0,
"skipVersionVerification": true,
"applyMajorUpdate": $applyMajorUpdate,
"updateAction": "DOWNLOAD_AND_INSTALL",
"forceRestart": true
}
EOF
)
fi
#################################################################################
### Invalidate our token
#################################################################################
/usr/bin/curl -s "${jss}/api/v1/auth/invalidate-token" -H "Authorization: Bearer ${token}" -X POST
Posted on 08-16-2022 04:58 PM
Man, you are a rock star! This was next on my list to tackle. I have a great script to do erase and install, but getting user boxes updated when the user isn't an admin has been a giant pain in the butt. I am thinking I can scope this to an item in Self Service so the user can click on the item, have it display the dialog to save any work, then have it do the update...that would be awesome. For the labs, run it on a schedule.
08-26-2022 08:11 AM - edited 08-26-2022 08:13 AM
@kwoodard Thanks! Sorry for the late response, it's been crazy busy around here. But yeah, my intention for this was unattended lab stations. It would need some tweaking for standard users due to the heavy-handed logout, and despite working well late at night in labs, there's no real indication that the MDM call is going to take effect. I still see about 10% of my computers randomly ignore the command and need a 2nd attempt. The command is successfully sent & logged, the update just doesn't kick off. I would probably suggest something else to notify/allow your standard users to kick off updates (upgrades are another issue).