Guys,
Help me out with the below script. Trying to send unmanage command to machine. However, I'm getting: Syntax Error Unexpected end of file
#! /bin/sh
# server connection information
jamfProURL="XXXX"
username="XXXX"
password="XXXX"
renewToken() {
# renew auth token
authToken=$( /usr/bin/curl \\
--header "Accept: application/json" \\
--header "Authorization: Bearer $token" \\
--request POST \\
--silent \\
--url "$jamfProURL/api/v1/auth/keep-alive" )
# parse auth token
token=$( /usr/bin/plutil \\
-extract token raw - <<< "$authToken" )
tokenExpiration=$( /usr/bin/plutil \\
-extract expires raw - <<< "$authToken" )
localTokenExpirationEpoch=$( TZ=UTC /bin/date -j -f "%Y-%m-%dT%T" "$tokenExpiration" "+%s" 2> /dev/null )
# update the renewal time for another 25 minutes
renewalTime=$(( $localTokenExpirationEpoch - 300 ))
}
# request auth token
authToken=$( /usr/bin/curl \\
--request POST \\
--silent \\
--url "$jamfProURL/api/v1/auth/token" \\
--user "$username:$password" )
echo "$authToken"
# parse auth token
token=$( /usr/bin/plutil \\
-extract token raw - <<< "$authToken" )
tokenExpiration=$( /usr/bin/plutil \\
-extract expires raw - <<< "$authToken" )
localTokenExpirationEpoch=$( TZ=GMT /bin/date -j \\
-f "%Y-%m-%dT%T" "$tokenExpiration" \\
+"%s" 2> /dev/null )
echo Token: "$token"
echo Expiration: "$tokenExpiration"
echo Expiration epoch: "$localTokenExpirationEpoch"
# verify auth token is valid
checkToken=$( /usr/bin/curl \\
--header "Authorization: Bearer $token" \\
--silent \\
--url "$jamfProURL/api/v1/auth" \\
--write-out "%{http_code}" )
tokenStatus=${checkToken: -3}
echo Token status: "$tokenStatus"
# subtract five minutes (300 seconds) from localTokenExpirationEpoch
renewalTime=$(( $localTokenExpirationEpoch - 300 ))
while IFS= read aDevice
do
now=$( /bin/date +"%s" )
if [[ "$renewalTime" -lt "$now" ]]; then
renewToken
fi
## get unique identifier for machine
udid=$(system_profiler SPHardwareDataType | awk '/UUID/ { print $3; }')
## get computer ID from Jamf server
compId=$( /usr/bin/curl \\
--request GET \\
--url "$jamfProURL/JSSResource/computers/udid/${udid}/subset/general" \\
--header "Accept: application/xml" \\
--header "Authorization: Bearer $token" \\
--xpath '/computer/general/id/text()' )
echo "${compID}"
## send unmanage command to machine
/usr/bin/curl \\
--header "Authorization: Bearer $token" \\
--request POST \\
--url "$jamfProURL/JSSResource/computercommands/command/UnmanageDevice/id/${compId}"
# expire auth token
/usr/bin/curl \\
--header "Authorization: Bearer $token" \\
--request POST \\
--silent \\
--url "$jamfProURL/api/v1/auth/invalidate-token"
Guys,
Help me out with the below script. Trying to send unmanage command to machine. However, I'm getting: Syntax Error Unexpected end of file
#! /bin/sh
# server connection information
jamfProURL="XXXX"
username="XXXX"
password="XXXX"
renewToken() {
# renew auth token
authToken=$( /usr/bin/curl \\
--header "Accept: application/json" \\
--header "Authorization: Bearer $token" \\
--request POST \\
--silent \\
--url "$jamfProURL/api/v1/auth/keep-alive" )
# parse auth token
token=$( /usr/bin/plutil \\
-extract token raw - <<< "$authToken" )
tokenExpiration=$( /usr/bin/plutil \\
-extract expires raw - <<< "$authToken" )
localTokenExpirationEpoch=$( TZ=UTC /bin/date -j -f "%Y-%m-%dT%T" "$tokenExpiration" "+%s" 2> /dev/null )
# update the renewal time for another 25 minutes
renewalTime=$(( $localTokenExpirationEpoch - 300 ))
}
# request auth token
authToken=$( /usr/bin/curl \\
--request POST \\
--silent \\
--url "$jamfProURL/api/v1/auth/token" \\
--user "$username:$password" )
echo "$authToken"
# parse auth token
token=$( /usr/bin/plutil \\
-extract token raw - <<< "$authToken" )
tokenExpiration=$( /usr/bin/plutil \\
-extract expires raw - <<< "$authToken" )
localTokenExpirationEpoch=$( TZ=GMT /bin/date -j \\
-f "%Y-%m-%dT%T" "$tokenExpiration" \\
+"%s" 2> /dev/null )
echo Token: "$token"
echo Expiration: "$tokenExpiration"
echo Expiration epoch: "$localTokenExpirationEpoch"
# verify auth token is valid
checkToken=$( /usr/bin/curl \\
--header "Authorization: Bearer $token" \\
--silent \\
--url "$jamfProURL/api/v1/auth" \\
--write-out "%{http_code}" )
tokenStatus=${checkToken: -3}
echo Token status: "$tokenStatus"
# subtract five minutes (300 seconds) from localTokenExpirationEpoch
renewalTime=$(( $localTokenExpirationEpoch - 300 ))
while IFS= read aDevice
do
now=$( /bin/date +"%s" )
if [[ "$renewalTime" -lt "$now" ]]; then
renewToken
fi
## get unique identifier for machine
udid=$(system_profiler SPHardwareDataType | awk '/UUID/ { print $3; }')
## get computer ID from Jamf server
compId=$( /usr/bin/curl \\
--request GET \\
--url "$jamfProURL/JSSResource/computers/udid/${udid}/subset/general" \\
--header "Accept: application/xml" \\
--header "Authorization: Bearer $token" \\
--xpath '/computer/general/id/text()' )
echo "${compID}"
## send unmanage command to machine
/usr/bin/curl \\
--header "Authorization: Bearer $token" \\
--request POST \\
--url "$jamfProURL/JSSResource/computercommands/command/UnmanageDevice/id/${compId}"
# expire auth token
/usr/bin/curl \\
--header "Authorization: Bearer $token" \\
--request POST \\
--silent \\
--url "$jamfProURL/api/v1/auth/invalidate-token"
I'm really confused on what you're doing here....as it looks like two different possibilities:
- There's a `while` loop, which isn't terminated, nor is there something it's iterating through
- This could be what's causing the Syntax Error Unexpected end of file
- Second, it seems you're unmanaging the device the script is running on.....so...that means the device has to be active to run it....
- And you're saving credentials in a script, which is bad practice
It's also important to note that sending the Unmanage MDM Command to a device means the device must be active to receive it and become unmanaged. In addition, on a Mac, this command only removes the MDM Profile (and related bits), it does not remove the Jamf Management Framework which consists of the `jamf` binary and other bits, which means the device will continue to check-in to the JPS.
I'm really confused on what you're doing here....as it looks like two different possibilities:
- There's a `while` loop, which isn't terminated, nor is there something it's iterating through
- This could be what's causing the Syntax Error Unexpected end of file
- Second, it seems you're unmanaging the device the script is running on.....so...that means the device has to be active to run it....
- And you're saving credentials in a script, which is bad practice
It's also important to note that sending the Unmanage MDM Command to a device means the device must be active to receive it and become unmanaged. In addition, on a Mac, this command only removes the MDM Profile (and related bits), it does not remove the Jamf Management Framework which consists of the `jamf` binary and other bits, which means the device will continue to check-in to the JPS.
@MLBZ521 Using this same While loop i'm able to get the list of smart groups, but not sure why unable to send the unmanage command.
I'm passing the credential to bearer token so that no need to key in credentials each and every time.
Also, I'm running this script which already managed by the Jamf. And I would like to take your last point that it would check in back to Jamf server. Will work on it. Thanks, BTW.
@MLBZ521 Using this same While loop i'm able to get the list of smart groups, but not sure why unable to send the unmanage command.
I'm passing the credential to bearer token so that no need to key in credentials each and every time.
Also, I'm running this script which already managed by the Jamf. And I would like to take your last point that it would check in back to Jamf server. Will work on it. Thanks, BTW.
@MLBZ521 My apologies MLBZ521. After I removed the 'while' loop the script executed well. I mean without any issues profiles got removed. Thanks a lot.
@MLBZ521 Using this same While loop i'm able to get the list of smart groups, but not sure why unable to send the unmanage command.
I'm passing the credential to bearer token so that no need to key in credentials each and every time.
Also, I'm running this script which already managed by the Jamf. And I would like to take your last point that it would check in back to Jamf server. Will work on it. Thanks, BTW.
Why are you using the `while` loop to get a list of Smart Groups?
But, as mentioned, the `while` loop is not terminated, so that is going to cause the script to fail.
@MLBZ521 My apologies MLBZ521. After I removed the 'while' loop the script executed well. I mean without any issues profiles got removed. Thanks a lot.
As a mentioned previously, removing the MDM Profile does not remove the Jamf Management Framework which consists of the `jamf` binary and other bits, which means the device will continue to check-in to the JPS.
Guys, anyone explain me below when I execute the unmanage script via API and Bearer token renewal in terminal I'm getting this,

What's this?
Also, I used below to delete the entry of the computer in Jamf via API, but getting the below error message and the Id having administrator privilege.

 
Guys, anyone explain me below when I execute the unmanage script via API and Bearer token renewal in terminal I'm getting this,

What's this?
Also, I used below to delete the entry of the computer in Jamf via API, but getting the below error message and the Id having administrator privilege.

 
add:
set -x
to code after shebang, then you can see output to debug.
Guys, anyone explain me below when I execute the unmanage script via API and Bearer token renewal in terminal I'm getting this,

What's this?
Also, I used below to delete the entry of the computer in Jamf via API, but getting the below error message and the Id having administrator privilege.

 
The first question: The image indicates that `curl` is "downloading" something, for instance an ".html" file. This is what `curl` natively does. What it's downloading, you'd have to check. You can add `--output ~/Downloads/file.html` to the end of your `curl` command and see what's being transmitted.
For the second question: It's hard to know exactly without seeing the full `curl` command, but to me, it almost looks like the `curl` command is attempting to interact with a web server (aka host) named `DELETE`, which would obviously be incorrect. That should be your HTTP method, not your host. Be sure you're passing your values to the correct parameters. e.g.
curl --url <https://jamf.pro.url:8443> --request [GET | PUT | POST | DELETE ] [...<remaining parameters>...]
That said, I don't think the code is doing what you think it's doing... At least, the XML payload in that second image has nothing to do with deleting a computer record...
The first question: The image indicates that `curl` is "downloading" something, for instance an ".html" file. This is what `curl` natively does. What it's downloading, you'd have to check. You can add `--output ~/Downloads/file.html` to the end of your `curl` command and see what's being transmitted.
For the second question: It's hard to know exactly without seeing the full `curl` command, but to me, it almost looks like the `curl` command is attempting to interact with a web server (aka host) named `DELETE`, which would obviously be incorrect. That should be your HTTP method, not your host. Be sure you're passing your values to the correct parameters. e.g.
curl --url <https://jamf.pro.url:8443> --request [GET | PUT | POST | DELETE ] [...<remaining parameters>...]
That said, I don't think the code is doing what you think it's doing... At least, the XML payload in that second image has nothing to do with deleting a computer record...
@MLBZ521 Below is the curl command for the deletion of the computer entry in Jamf.
/usr/bin/curl \\
--header "Authorization: Bearer $token"\\
--request DELETE \\
--url "$jamfProURL/JSSResource/computers/udid/${compId}"
Sure, will try add output parameter and check. Thanks.
@MLBZ521 Below is the curl command for the deletion of the computer entry in Jamf.
/usr/bin/curl \\
--header "Authorization: Bearer $token"\\
--request DELETE \\
--url "$jamfProURL/JSSResource/computers/udid/${compId}"
Sure, will try add output parameter and check. Thanks.
That looks correct for what I can see. What's the value of your `$comdId` variable?
That said, that command and the screenshot for the results of that command do not match at all.
Guys, help me with the script,
Requirements:
1. Once the jamf unenroll script executed, I would like to let the customer must get notification "system is going to reboot in 2 mins" and after 2 mins should restart automatically.
2. Another script to remove the CA certificate profile from Profile tab in system preference.
Thanks in advance.
I believe you have a misunderstanding of how things work...
1. Unless you're running this on the device that is being unenrolled...this isn't possible....and if you are doing that....that's a very bad idea... Either way, why are you bothering with rebooting them?
2. Not sure what you're referring to here... This sounds like a custom Profile and you can't locally remove Profiles installed via MDM when the device is ADE Enrolled...
I believe you have a misunderstanding of how things work...
1. Unless you're running this on the device that is being unenrolled...this isn't possible....and if you are doing that....that's a very bad idea... Either way, why are you bothering with rebooting them?
2. Not sure what you're referring to here... This sounds like a custom Profile and you can't locally remove Profiles installed via MDM when the device is ADE Enrolled...
The reason to reboot the machine is our customer is moving out of jamf and getting the enrolled in Intune. After the Jamf profile removed, we were unable to enroll it intune unless we reboot the machine.
2. As soon as we enroll the device in Jamf, unlike the other profiles, customer created a profile to CA certificate install. Normal script isn't remove the CA Profile.
I believe you have a misunderstanding of how things work...
1. Unless you're running this on the device that is being unenrolled...this isn't possible....and if you are doing that....that's a very bad idea... Either way, why are you bothering with rebooting them?
2. Not sure what you're referring to here... This sounds like a custom Profile and you can't locally remove Profiles installed via MDM when the device is ADE Enrolled...
Guys, Please find the below screenshot and let me know how to remove this profile.. It's not getting removed by running remove jamf framework command.

Because of which unable to enroll it in another MDM solution. Kindly help. Thanks.
that CA is installed at user level.. so you can remove that by clicking on the minus button, or via terminal / script
It looks like this device was enrolled via UIE and not ADE. If that the case, you don't even need the API script to unmanage the device. Simply running `jamf removeFramework` would remove the MDM Profile and the Jamf Pro management Framework.
If that Profile isn't being removed with the MDM Profile, then, as jamf-42 eluded to, you can remove it via the `profiles` command. You'll just need to identify which Profile it is to remove it.
It looks like this device was enrolled via UIE and not ADE. If that the case, you don't even need the API script to unmanage the device. Simply running `jamf removeFramework` would remove the MDM Profile and the Jamf Pro management Framework.
If that Profile isn't being removed with the MDM Profile, then, as jamf-42 eluded to, you can remove it via the `profiles` command. You'll just need to identify which Profile it is to remove it.
The reason why I've API is to unmanage machine from Jamf side thatsy..
The reason why I've API is to unmanage machine from Jamf side thatsy..
Are you wanting to unmanage the device in Jamf Pro when it is being "unmanaged" (Framework and such) on the device, in other words, at the same time?
If so, I have a little project I wrote years ago to migrate devices from one Jamf Pro instance to another. It could adapt the code from it for your needs: https://github.com/MLBZ521/jamfMigrator
@rcoleman , very nice script! Worked for me. We've got a couple hundred Macs on stock shelves so it's not practical to fire each one up to run the script locally. Neither is it all that practical to deselect "Allow Jamf Pro to perform management tasks" for each record individually. I'd like to make a smart group of Macs not checked in in, say, 60 days and listed as unassigned and have those each pulled out of management by having this script run on their JSS records.
I can't figure out how to trigger that. Without those shelved Macs checking in, how would I prompt this policy to run? As they hit the 60 day mark or if someone marks their inventory record as "unassigned" I'd like the script to run.
Any ideas to make this happen?
Thanks,
- Scott
@rcoleman , very nice script! Worked for me. We've got a couple hundred Macs on stock shelves so it's not practical to fire each one up to run the script locally. Neither is it all that practical to deselect "Allow Jamf Pro to perform management tasks" for each record individually. I'd like to make a smart group of Macs not checked in in, say, 60 days and listed as unassigned and have those each pulled out of management by having this script run on their JSS records.
I can't figure out how to trigger that. Without those shelved Macs checking in, how would I prompt this policy to run? As they hit the 60 day mark or if someone marks their inventory record as "unassigned" I'd like the script to run.
Any ideas to make this happen?
Thanks,
- Scott
Glad it helps! Sorry, I'm not exactly sure what you mean when you say 'unassigned'? If I'm understanding correctly, you want to create a smart group for machines that have not checked in within 60 days, and then have this script execute and disable the 'Allow Jamf Pro to perform management tasks' checkbox for all devices in the smart group?
I'm certain there would be a better way to go about this but all I can think of at the moment is to create the smart group, then use an API call to obtain a list of all the devices in the smart group and then iterate through them and disable the option. You would still need to manually run the script once every day though, unless you create a LaunchDaemon to run the script every day using the 'StartCalendarInterval' option. Performing it this way though would require passwords to be entered into the script (needed for the API call), either in plain text or obfuscated, which of course is not ideal.
Yes, that was it. "Unassigned" means not currently attached to a user. On a stock room shelf.
I opted to just export the contents of a smarlist periodically (two or three times a year maybe) and copy paste those SNs into your script as an array and run that locally from my Mac. Works very nicely. Thanks again for it!
- Scott
Looks like this:
#!/bin/bash
# This can't be run from Jamf. We're just storing it here.
# Download it to your Mac and run it in Terminal.
echo "Enter JSS username:"
read USERNAME
echo "Enter JSS password:"
read -s PASSWORD
TOKEN_EXPIRATION_EPOCH="0"
function getBearerToken() {
OS_MAJOR_VERSION=$(sw_vers -buildVersion | cut -c 1-2)
echo "OS Major Version: $OS_MAJOR_VERSION"
if [ "$OS_MAJOR_VERSION" -lt 21 ]; then
# Get the token info
BEARER_TOKEN=$(echo $RESPONSE | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["token"]')
# Get the expiration date
TOKEN_EXPIRATION=$(echo $RESPONSE | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["expires"]')
# If we are running Monterey or later then we can use plutil to parse json
else
# Get the token info
BEARER_TOKEN=$(echo "$RESPONSE" | plutil -extract token raw -)
# Get the token expiration date
TOKEN_EXPIRATION=$(echo "$RESPONSE" | plutil -extract expires raw - | awk -F . '{print $1}')
fi
TOKEN_EXPIRATION_EPOCH=$(date -j -f "%Y-%m-%dT%T" "$TOKEN_EXPIRATION" +"%s")
}
function checkTokenExpiration() {
NOW_EPOCH_UTC=$(date -j -f "%Y-%m-%dT%T" "$(date -u +"%Y-%m-%dT%T")" +"%s")
if [[ TOKEN_EXPIRATION_EPOCH -gt NOW_EPOCH_UTC ]]
then
echo "Token valid until the following epoch time: " "$TOKEN_EXPIRATION_EPOCH"
else
echo "No valid token available, getting new token"
getBearerToken
fi
}
function invalidateToken() {
if [[ ${RESPONSE_CODE} == 204 ]]
then
echo "Token successfully invalidated"
BEARER_TOKEN=""
TOKEN_EXPIRATION_EPOCH="0"
elif [[ ${RESPONSE_CODE} == 401 ]]
then
echo "Token already invalid"
else
echo "An unknown error occurred invalidating the token"
fi
}
echo "Getting API token..."
checkTokenExpiration
# Paste in a list of Mac SNs to be removed from management:
unmanage=(
SN#####
SN#####
SN#####
)
for SERIAL in ${unmanage[@]}
do
# This next commented code is to get the serial number of the Mac from which the script
# is running in the case of performing the script on this local Mac to remove it from management.
# I've turned it off in favour of using an array of provided SNs of other Macs. See above.
# to remove from management.
# Get local serial number:
# SERIAL=$(system_profiler SPHardwareDataType | awk '/Serial/ {print $4}')
# /bin/echo "Serial number is $SERIAL"
# Get JAMF ID of device from API looked by SN found locally or provided in
# $unmanage array:
# API call to de-select "Allow Jamf Pro to perform management tasks" in the JSS for this device:
/bin/echo "JAMF ID for $SERIAL is $JAMF_ID and it is now unmanaged in the JSS"
done
# Bin the token
/bin/echo "Invalidating API token..."
invalidateToken
/bin/echo "Done."
exit 0;