Creating a Policy through the API?

chadlawson
Contributor
Contributor

Hi,

Has anyone here created a policy via the API before? I've made other script to create objects via the API (buildings, departments, etc.) but these are my first attempts at creating a policy and it isn't working.

My goal is to have a script I can run against a client server that will make a policy to cache all of their packages. I plan to use a custom trigger. I'm not even trying to scope it in this, just create a policy that contains all the packages with an action to cache them. Then I can go in and and scope to a test machine.

I'm including my code below in case someone sees an obvious error, or if any of it is of value to someone else's efforts.

#!/bin/bash

jamfurl="https://xxx.jamfcloud.com"
jamfuser="APIUSER"
jamfpass="APIPASS"

packageList=$(curl -ksu "${jamfuser}:${jamfpass}" -H "Accept: text/xml" "${jamfurl}/JSSResource/packages")
packageCount=$(echo ${packageList} | xmllint --xpath '/packages/size/text()' -)
packageIDs=$(echo ${packageList} | xmllint --format - | awk -F'[<>]' '/<id>/{print $3}')

for packageID in $packageIDs; do
    package=$(curl -ksu "${jamfuser}:${jamfpass}" -H "Accept: text/xml" "${jamfurl}/JSSResource/packages/id/${packageID}")
    packageNumber=$(echo $package | xmllint --xpath '/package/id/text()' -)
    packageName=$(echo $package | xmllint --xpath '/package/name/text()' -)
    packageBlob="<package><id>${packageNumber}</id><name>${packageName}</name><action>Cache</action></package>"
    packagesXML="${packagesXML}${packageBlob}"
done

uploadXML="<policy><general><name>Download Packages</name><enabled>true</enabled><trigger>EVENT</trigger><trigger_other>downloadTest</trigger_other><frequency>Ongoing</frequency></general><scope><all_computers>true</all_computers></scope><package_configuration><packages><size>${packageCount}</size>${packagesXML}</packages></package_configuration></policy>"

curl -ksu "${jamfuser}:${jamfpass}" -H "Content-Type: text/xml" "${jamfurl}/JSSResource/policies/id/0" -X POST -d ${uploadXML}
7 REPLIES 7

chadlawson
Contributor
Contributor

The other idea I had was to create a local file repository that is on my local network and then use Jamf Admin on my test machine to replicate to it. It's "hacky" but it would work too.

I'd much rather use the script approach, if I can get it working.

Mauricio
Contributor III

@chadlawson

Check if the for loop is getting the individual ID.
I've run a quick test and packageIDs is one big string.
A test from your script:

packageIDs=$(echo ${packageList} | xmllint --format - | awk -F'[<>]' '/<id>/{print $3}')

for packageID in $packageIDs; do
  echo "/JSSResource/packages/id/${packageID})"
done

I got:

/JSSResource/packages/id/265 1329 1738 235 1318 2146 352 637 1156 1383)

Changing the packageIDs line to:

packageIDs=($(echo ${packageList} | xmllint --format - | awk -F'[<>]' '/<id>/{print $3}'))

for packageID in $packageIDs; do
  echo "/JSSResource/packages/id/${packageID})"
done

I got (shortened from 411 packages )

JSSResource/packages/id/265) /JSSResource/packages/id/1329) /JSSResource/packages/id/1738) /JSSResource/packages/id/235) /JSSResource/packages/id/1318) /JSSResource/packages/id/2146) /JSSResource/packages/id/352) /JSSResource/packages/id/637) /JSSResource/packages/id/1156) /JSSResource/packages/id/1649)

I haven't looked the rest of your script but this could be a reason of the issue.
Regards
Mauricio

chadlawson
Contributor
Contributor

Thanks for replying, @Mauricio, but the issue seems to be with the XML format for creating the policy. The list of packages seem fine.

This is the XML I am sending:

<?xml version="1.0" encoding="UTF-8"?>
<policy>
   <general>
      <name>Download Packages</name>
      <enabled>true</enabled>
      <trigger>EVENT</trigger>
      <trigger_other>downloadTest</trigger_other>
      <frequency>Ongoing</frequency>
   </general>
   <scope>
      <all_computers>true</all_computers>
   </scope>
   <package_configuration>
      <packages>
         <size>2</size>
         <package>
            <id>8</id>
            <name>CompanyAssets.pkg</name>
            <action>Cache</action>
         </package>
         <package>
            <id>7</id>
            <name>DEPNotify.pkg</name>
            <action>Cache</action>
         </package>
      </packages>
   </package_configuration>
</policy>

And this is the error message I am getting back:

<html>
<head>
   <title>Status page</title>
</head>
<body style="font-family: sans-serif;">
<p style="font-size: 1.2em;font-weight: bold;margin: 1em 0px;">Bad Request</p>
<p>Error in XML file.  Possible mismatch between resource specified in the URL and XML file</p>
<p>You can get technical details <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">here</a>.<br>
Please continue your visit at our <a href="/">home page</a>.
</p>
</body>
</html>

My suspicion (and fear) is that unlike other JSS objects in the API, I might have include much more of the policy XML rather than just the items I'm interested in setting.

mm2270
Legendary Contributor III

@chadlawson I'm wondering if you need to have the <id>0</id> line in your xml near the top under the general section. I don't see it in your final xml. Maybe that's the issue? Or have you tried that previously and it didn't help?

Mauricio
Contributor III

@chadlawson I managed to create the policy OK here using your xml structure with a few changes.

Package name(s) can be omitted, as far you have the ID that is what matters. Same goes for Packages <size>.

The XML structure:

<?xml version="1.0" encoding="UTF-8"?>
<policy>
    <general>
        <name>Download Packages</name>
        <enabled>false</enabled>
        <trigger>EVENT</trigger>
        <trigger_other>downloadTest</trigger_other>
        <frequency>Ongoing</frequency>
    </general>
    <scope>
        <all_computers>true</all_computers>
    </scope>
    <package_configuration>
        <packages>
        <package>
            <id>401</id>
            <action>Cache</action>
        </package>
        <package>
            <id>475</id>
            <action>Cache</action>
        </package>
        </packages>
    </package_configuration>
</policy>

I use Postman for developing/testing my APIs. A good way to validate the process.

8f3fc34e797b47a4b23f292c9538c767

chadlawson
Contributor
Contributor

Oh, @Mauricio! I can not tell you how much I needed your reply!

Once I saw yours working, I knew it had to be something stupid simple, and it was! If I had posted my code and not just the XML you would probably have seen it right away. In my first version I was using a heredoc to build the main XML for readability, but when that didn't work I condensed it down into a single line... and failed to escape the quotes in my '<?xml' header line.

Once I did that, it worked perfect!

Thank you for confirming I wasn't way off base. That forced me to look at the little things and there it was.

Find me at next year's JNUC and I'll buy you a drink!

Mauricio
Contributor III

@chadlawson Glad that is working now. Sometimes small details can bite back hard but you got there in the end.