Restart iPads in a specific smart group

maxbedford
New Contributor

I've been fumbling around with this for a bit now and I'm feeling pretty stuck.
I'm trying to get all the iOS devices in a specific SmartGroup to reboot via a script.

groupid=104
jssAddress="https://myurl:8443"

devices=$(curl -sku "${jssAPIUsername}:${jssAPIPassword}"  -H "Content-Type: text/xml" "${jssAddress}/JSSResource/mobiledevicegroups/id/${groupid}" | xmllint --xpath //id -  | sed  -e "s/<id>${groupid}</id><id>-1</id>//"  | sed  -e 's/</id><id>/,/g' | sed -e 's/id/id_list/g' )

#devices=$(curl -sku "${jssAPIUsername}:${jssAPIPassword}"  -H "Content-Type: text/xml" "${jssAddress}/JSSResource/mobiledevicegroups/id/${groupid}" | xmllint --xpath //id - | sed  -e "s/<id>${groupid}</id>//" | sed -e 's/<id>-1</>//' )
#devices="<id>8</id>"

data="<mobile_device_command><general><command>RestartDevice</command></general><mobile_devices><mobile_device><id>${devices}</id></mobile_device></mobile_devices></mobile_device_command>"

#echo $devices
echo "~~~~~~~~~~~~"
echo $data
curl -sku "${jssAPIUsername}:${jssAPIPassword}" -H "Content-Type: text/xml" "${jssAddress}/JSSResource/mobiledevicecommands/command" -d $data -X POST

I get "Bad Request" "Unable to match mobile device"

It doesn't appear to have pushed to any of the devices.

Does anyone have some experience with this that wants to lend a set of eyes? The troubling part is getting the devices selected. Operating on a single device isn't a problem.

Thanks in advance for any help provided.

9 REPLIES 9

koalatee
Contributor II

Probably going to have to put the devices in an array and then have a foreach and send the command to each device

maxbedford
New Contributor

yuck, really?

Below is pretty much verbatim what the API example shows and returns "Bad Request" "Unable to match mobile_device"

curl -sku "${jssAPIUsername}:${jssAPIPassword}" -H "accept: application/xml" -X POST "${jssAddress}/JSSResource/mobiledevicecommands/command/RestartDevice/id/8%2C18%2C39%2C40%2C43%2C44%2C45%2C46%2C47%2C48%2C49%2C50%2C51%2C65%2C112%2C116%2C127%2C132%2C141%2C193%2C206%2C222%2C271%2C321%2C328%2C367%2C375%2C418%2C426%2C428%2C433%2C439%2C452%2C470%2C475%2C479%2C489%2C500%2C502%2C528%2C534%2C544%2C545%2C574%2C585%2C592%2C596%2C598%2C601%2C615%2C632%2C635%2C661%2C667%2C682%2C690%2C700%2C703%2C707%2C736%2C752%2C1182%2C1183%2C1184%2C1185%2C1186%2C1187%2C1188%2C1189%2C1190%2C1191%2C1192%2C1201%2C1202%2C1203%2C1252%2C1253%2C1254%2C1255%2C1256%2C1257%2C1258%2C1259%2C1260%2C1261%2C1262%2C1264%2C1265%2C1266%2C1267%2C1268%2C1269%2C1270%2C1271%2C1272%2C1273%2C1274%2C1275%2C1276%2C1277%2C1278%2C1279%2C1280%2C1281%2C1339%2C2030%2C2084%2C2136%2C2143%2C2726%2C2727%2C2728%2C2729%2C2730%2C2731%2C2732%2C2733%2C2734%2C2735%2C2736%2C2737%2C2738%2C2739%2C2740%2C2741%2C2742%2C2743%2C2744%2C2745%2C2746%2C2747%2C2748%2C2749%2C2750%2C2751%2C2752%2C2753%2C2754%2C2755%2C2756%2C2757%2C2758%2C2759%2C2760%2C2761%2C2762%2C2763%2C2764%2C2765%2C2766%2C2767%2C2768%2C2769%2C2770%2C2771%2C2772%2C2773%2C2774%2C2775%2C2776%2C2777%2C2778%2C2779%2C2780%2C2781%2C2782%2C2783%2C2784%2C2785%2C2786%2C2787%2C2788%2C2789%2C2790%2C2791%2C2792%2C2793%2C2794%2C2795%2C2796%2C2797%2C2798%2C2799%2C2800%2C2801%2C2802%2C2803%2C2804%2C2805%2C2806%2C2807%2C2808%2C2809%2C2810%2C2811%2C2812%2C2813%2C2814%2C2815%2C2817%2C3303%2C3304%2C3305%2C3306%2C3307%2C3308%2C3309%2C3310%2C3311%2C3312%2C3313%2C3360%2C3361%2C3362%2C3363%2C3364%2C3365%2C3366%2C3367%2C3368%2C3369%2C3370%2C3371%2C3372%2C3373%2C3374%2C3375%2C3376%2C3377%2C3378%2C3379%2C3380%2C3381%2C3382%2C3383%2C3384%2C3385%2C3386%2C3387%2C3388%2C3389%2C3390%2C3391%2C3392%2C3393%2C3394%2C3395%2C3396%2C3397%2C3398%2C3399%2C3400%2C3401%2C3402%2C3403%2C3404%2C3405%2C3406%2C3407%2C3408%2C3409"

maxbedford
New Contributor

Well dumping them into an array and running a bash for loop did the trick. It looks like there are still devices getting that error, but it isn't stopping the rest of them.

jssAPIUsername="USER"
jssAPIPassword="PASS"
groupid=32
jssAddress="https://myjamf:8443"

devices=$(curl -sku "${jssAPIUsername}:${jssAPIPassword}"  -H "Content-Type: text/xml" "${jssAddress}/JSSResource/mobiledevicegroups/id/${groupid}" | xmllint --xpath //id -  | sed  -e "s/<id>${groupid}</id><id>-1</id>//"  | sed  -e 's/</id><id>/,/g'   | sed -e 's/<[/]?id>//g' )

echo "~~~~~~~~~~~~"

IFS=',' read -ra devIDs <<< "$devices"
for x in "${devIDs[@]}"; 
do
    data="<mobile_device_command><general><command>RestartDevice</command></general><mobile_devices><mobile_device><id>${x}</id></mobile_device></mobile_devices></mobile_device_command>"
    curl -sku "${jssAPIUsername}:${jssAPIPassword}" -H "Content-Type: text/xml" "${jssAddress}/JSSResource/mobiledevicecommands/command" -d $data -X POST
    echo ""
    echo $x
    echo "~~~~~~~~~~~~"
    sleep 5
done

Needs lots of cleanup, but here it is.

cdenesha
Valued Contributor III

I've done something similar with a Blank Push command.
Let's see.. you are POSTING so get rid of the -H section. The ID list needs to be comma separated not %2C.. assuming that is what you get in $devices I would try something like this:

curl -sku "${jssAPIUsername}:${jssAPIPassword}" "${jssAddress}/JSSResource/mobiledevicecommands/command/RestartDevice/id/"$devices -X POST

talkingmoose
Moderator
Moderator

@maxbedford, I haven't fully vetted your command, but try setting your XML data to something like the following:

<mobile_device_command>
     <general>
         <command>RestartDevice</command>
     </general>
     <mobile_devices>
         <mobile_device>
             <udid>DBD8086A-C09D-52CF-9C98-FA0E9B9CFB87</udid>
         </mobile_device>
         <mobile_device>
             <udid>EDAE4A5C-0E22-4BB1-BD0B-08BA89916113</udid>
         </mobile_device>
         <mobile_device>
             <udid>63620D2A-DFD1-42DF-9DC4-3FEE808B1C17</udid>
         </mobile_device>
     </mobile_devices>
</mobile_device_command>

or

<mobile_device_command>
    <general>
        <command>RestartDevice</command>
    </general>
    <mobile_devices>
        <mobile_device>
            <id>247</id>
        </mobile_device>
        <mobile_device>
            <id>358</id>
        </mobile_device>
        <mobile_device>
            <id>400</id>
        </mobile_device>
    </mobile_devices>
</mobile_device_command>

If you put the pretty XML into a variable such as "THExml", you can use xmllint to flatten it just before you submit to the Jamf Pro Classic API:

flatXML=$( /usr/bin/xmllint --noblanks - <<< "$THExml" )

yoopersteeze
New Contributor II

Try this @maxbedford

#!/bin/sh
jamfProURL="https://acme.jamfcloud.com"            
jamfUser=""                                           
jamfPass=""                                       
mobileDeviceGroupID=""                            

deviceIDs=( $( curl -k -s -u "$jamfUser":"$jamfPass" $jamfProURL/JSSResource/mobiledevicegroups/id/$mobileDeviceGroupID -H "Accept: application/xml" -X GET | xpath "//mobile_device_group/mobile_devices" | /usr/bin/perl -lne 'BEGIN{undef $/} while (/<id>(.*?)</id>/sg){print $1}' ) )

for i in ${deviceIDs[@]}; do
    echo "Sending Restart Command to Device ID: $i..."
    /usr/bin/curl -sk -u "$jamfUser":"$jamfPass" -H "Content-Type: text/xml" -d "<?xml version="1.0" encoding="ISO-8859-1"?><mobile_device_command><general><command>RestartDevice</command>></general><mobile_devices><mobile_device><id>$i</id></mobile_device></mobile_devices></mobile_device_command>" $jamfProURL/JSSResource/mobiledevicecommands/command/RestartDevice -X POST > /dev/null
    if [[ "$?" == "0" ]]; then
        echo "   Command Processed Successfully"
    else
        echo "   Error Processing Command"
    fi
    echo ""
done

exit 0

maxbedford
New Contributor

@cdenesha Thanks %2C is the ASCII code for a comma and somewhere I had seen reports of using that instead of a comma to fix issues.

@talkingmoose Thanks I tried a couple of different ways. At this point, I'm thinking about the solution that requires minimal parsing. It is good to know what XML format works.

@yoopersteeze Thanks! A lot of good pieces in there. I don't really want to bring Perl into this ATM.

I got it working and tested. Looking at using it to force iOS updates to our student population. That is the next step.

ryan_ball
Valued Contributor

This does assume that the iPad is being used at the time that the command gets sent? Otherwise if the iPad is sleeping in a cart or locker or something, when the user turns it on next it will just immediately restart.

maxbedford
New Contributor

At no point in time have I made any statements on the assumption of the current state of the iPad,

I do understand that the iPad doesn't get the command immediately and that they get them when the iPad wakes back up, which as you point out maybe when the user next turns it on, it also maybe 2 minutes into a user waking up the iPad.