Posted on
03-18-2019
08:58 AM
- last edited
2 weeks ago
by
kh-richa_mig
Hello,
I'm tying to batch create new Extension Attributes via API using curl. I got my xml
template by pulling down an EA by GET
ting an EA by ID and running through xmllint
.
I'm very new to using API calls. Hopefully someone can see what is going on here.
When I run my code I get:
curl: (6) Could not resolve host: POST
curl: (6) Could not resolve host:
HTTP/1.1 415 Unsupported Media Type
Accept-Ranges: bytes
Cache-Control: no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0
Content-Type: text/html;charset=UTF-8
Date: Mon, 18 Mar 2019 15:47:45 GMT
Server: Jamf Cloud Node
Set-Cookie: APBALANCEID=aws.std-pagetia12-tc-5; path=/;HttpOnly;Secure;
X-FRAME-OPTIONS: SAMEORIGIN
Content-Length: 554
Connection: keep-alive
<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;">Unsupported Media Type</p>
<p>The server is refusing to service the request because the entity of the request is in a format not supported by the requested resource for the requested method</p>
<p>You can get technical details <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.16">here</a>.<br>
Please continue your visit at our <a href="/">home page</a>.
</p>
</body>
</html>
Here is my code:
#!/bin/bash -x
_password=''
_user=''
_credentials="$(printf "$_user:$_password" | iconv -t ISO-8859-1 | base64 -i -)"
_urlPrefix="https://orgname.jamfcloud.com/JSSResource"
_urlRequest="$(echo computerExtensionAttributes | tr '[A-Z]' '[a-z]')/id"
_xmlPath="${HOME}/.orgname/Scratch/JAMF/extAttributes"
attribute_names=( 'finanace' 'development'
'facilities' 'front_office'
'technology' 'faculty' 'staff'
'student' 'blt' 'admissions'
'communications' 'heads' 'humanresources'
'art' 'spanish' 'physed' 'pka' 'pkb' 'ka' 'kb'
'pa' 'pb' 'pc' 'pd' '3a' '3b' 'jua' 'jub' 'jud'
'juc' 'ms6' 'ms7' 'ms8' )
_count=2
for _name in "${attribute_names[@]}"; do
touch ${_xmlPath}/${_name}-ea.xml
printf "%s" "<?xml version="1.0" encoding="UTF-8"?>
<computer_extension_attribute>
<id>${_count}</id>
<name>Group File ${_name}?</name>
<description>looks for /usr/local/TPS/.${_name}</description>
<data_type>String</data_type>
<input_type>
<type>script</type>
<platform>Mac</platform>
<script>
#!/bin/bash
id_path=/usr/local/TPS
id_file=.${_name}
if [[ -f "$id_path"/"$id_file" ]]; then
echo '<result>true</result>'
fi
</script>
</input_type>
<inventory_display>Extension Attributes</inventory_display>
<recon_display>Extension Attributes</recon_display>
</computer_extension_attribute>" > ${_xmlPath}/${_name}-ea.xml
curl --header "Authorization: Basic ${_credentials}"
--header "Accept: text/xml"
--request POST
--data-binary
--upload-file "@${_xmlPath}/${_name}-ea.xml" "${_urlPrefix}"/"${_urlRequest}"/"${_count}"
_count=$(($_count + 1))
done
Solved! Go to Solution.
Posted on 03-19-2019 07:00 AM
Ok, I figured it out...
I didn't think until I was speaking with a friend last night to add the --verbose
flag to curl. The Content-Type
was showing Content-Type: application/x-www-form-urlencoded
, so I added another --header
:
Content-Type: application/xml
Boom. See screenshots.
I think just using a GET
requested 'template' extension attribute would have been fine (as it has all the encoding), but I need to test this.
Thanks for all your help @mm2270
EDIT: added new screenshots...couldn't see test in my original ones.
Posted on 03-18-2019 10:59 AM
I haven't had the chance to look over your entire script and digest it, but I see two things that immediately stand out to me.
One, if you are iterating numbers to use for the IDs for the new Extension Attributes as it loops with the _count
variable, which is what it looks like, that's not the proper method. To create any new object in Jamf Pro using the API, use an ID of 0
. This tells the server that it should assign a new ID to it as it creates it. The server figures out what the next ID is it can use on its own.
Two, I believe the script itself must be encoded, to convert characters that are illegal in xml to ones that are legal. I may be wrong, but when I look at any script based Extension Attributes on our Jamf Pro 10.10.1 instance, they are encoded. For example, any hard returns in the script show as
, and the <
and >
symbols turn into <
and >
respectively. And there are others besides those two. So I have a feeling that might be necessary to accept the XML during a POST.
Unfortunately I can't guide you on that conversion, if that's necessary to do. The perl command I use for similar encoding only works on single strings, not whole files, and doesn't do the same type of encoding anyway.
Posted on 03-18-2019 11:09 AM
Thanks a bunch!
EDIT: I can just pull down some new xml. It has all the encoding, I removed it.
Posted on 03-18-2019 11:41 AM
In case you need more, I actually found a command that does the trick. Again using perl, but a different one than the one I mentioned above.
This works on a whole file. In my testing, what I did was output a script into /tmp/ and then in the same script I did this to create an encoded variable for the entire script
ENCODED_SCRIPT=$(perl -p -e 'BEGIN { use CGI qw(escapeHTML); } $_ = escapeHTML($_);' /tmp/SCRIPT_1.sh)
Obviously change /tmp/SCRIPT_1.sh
to another variable or name as needed.
From there, I used that to create the XML file, like so:
cat << EOEA > /tmp/EA_1.xml
<computer_extension_attribute>
<id>0</id>
<name>Example EA</name>
<description>Example</description>
<data_type>String</data_type>
<input_type>
<type>script</type>
<platform>Mac</platform>
<script>
${ENCODED_SCRIPT}
</script>
</input_type>
<inventory_display>Extension Attributes</inventory_display>
<recon_display>Extension Attributes</recon_display>
</computer_extension_attribute>
EOEA
Note the ${ENCODED_SCRIPT}
variable where the actual script would go.
I then used a POST command to upload it, and voila! It shows up in my Jamf Pro console correctly.
It's just an example to make sure it actually works. Would still need to be tested against more complex scripts, but I think this should work.
Posted on 03-18-2019 01:09 PM
A couple few questions?
1. Can I see the encoded output?
2. Can I see your curl expression (or Python expressions) for uploading to your JSS instance?
After figuring out how to install CPAN and installing the CGI module, the encoding part seems to-- maybe-- work ok, though I noticed in the raw xml from a GET
request with curl, that line breaks were encoded (like you mentioned). Getting Unsupported media type
error for the upload to JSS.
EDIT: I'm not getting a numerical status error, just some html telling me so.
Encode example:
#!/bin/bash
id_path=/usr/local/ORG
id_file=.ms8
if [[ -f "$id_path"/"$id_file" ]]; then
echo <result>true</result>
fi
What my code is doing:
- iterating over array of attribute_names
- printing the extension attribute script body
to a file. attribute_names
are used to make the script unique to a group
- encode the extension attribute script body
file in a variable
- POST
the actual extension attribute not as a file (not required, just emphasized). attribute_name
is used to make the actual extension attribute unique to group
Tried:
- Accept: application/xml
, Accept: text/xml
- --data
, --data-binary
With curl
Code:
#!/bin/bash -x
_password=''
_user=''
_credentials="$(printf "$_user:$_password" | iconv -t ISO-8859-1 | base64 -i -)"
_urlPrefix="https://orgname.jamfcloud.com/JSSResource"
_urlInclude="$(echo computerExtensionAttributes | tr '[A-Z]' '[a-z]')/id"
_xmlPath="${HOME}/.tps/Scratch/JAMF/extAttributes"
attribute_names=( 'finanace' 'development'
'facilities' 'front_office'
'technology' 'faculty' 'staff'
'student' 'blt' 'admissions'
'communications' 'heads' 'humanresources'
'art' 'spanish' 'physed' 'pka' 'pkb' 'ka' 'kb'
'pa' 'pb' 'pc' 'pd' '3a' '3b' 'jua' 'jub' 'jud'
'juc' 'ms6' 'ms7' 'ms8' )
for _name in "${attribute_names[@]}"; do
printf "%s
"
"#!/bin/bash
id_path=/usr/local/ORG
id_file=.${_name}
if [[ -f "$id_path"/"$id_file" ]]; then
echo <result>true</result>
fi" > ${_xmlPath}/${_name}_ea.sh
_encoded_script=$(perl -p -e 'BEGIN { use CGI qw(escapeHTML); } $_ = escapeHTML($_);'
${_xmlPath}/${_name}_ea.sh)
curl -X POST
"${_urlPrefix}"/"${_urlInclude}"/0
--header "Authorization: Basic ${_credentials}"
--header "Accept: application/xml"
--data "<computer_extension_attribute>
<id>0</id>
<name>Group File ${_name}?</name>
<description>looks for /usr/local/TPS/.${_name}</description>
<data_type>String</data_type>
<input_type>
<type>script</type>
<platform>Mac</platform>
<script>
${_encoded_script}
</script>
</input_type>
<inventory_display>Extension Attributes</inventory_display>
<recon_display>Extension Attributes</recon_display>
</computer_extension_attribute>"
done
Posted on 03-19-2019 07:00 AM
Ok, I figured it out...
I didn't think until I was speaking with a friend last night to add the --verbose
flag to curl. The Content-Type
was showing Content-Type: application/x-www-form-urlencoded
, so I added another --header
:
Content-Type: application/xml
Boom. See screenshots.
I think just using a GET
requested 'template' extension attribute would have been fine (as it has all the encoding), but I need to test this.
Thanks for all your help @mm2270
EDIT: added new screenshots...couldn't see test in my original ones.
Posted on 03-19-2019 07:50 AM
Hey @zetaomegagon An example curl command I use for uploading an xml file to the JSS would be like this:
curl -u username:password https://your.jamf.instance.com/JSSResource/computerextensionattributes/id/0 -X POST -T /tmp/file.xml
The -T
flag lets you specify a file to use as the upload, but you can also use --data
flag for uploading direct data as you have it. I don't often has as much luck using a direct data upload though, so I tend to use a file.
As for the encoded script, here is the one I used in the test I mentioned above
#!/bin/bash
if [ -e "/Applications/Safari.app" ]; then
echo "<result>Installed</result>"
else
echo "<result>Not Installed</result>"
fi
Interestingly, the encoding here looks a bit different than the way it would appear in the Jamf Pro server, but, despite the differences, it accepted the XML and worked to create the EA for me. I guess there's no hard rule on the encoding, as long as it's something that is valid for inclusion in an XML file.
Hope the above helps.
Posted on 03-19-2019 08:02 AM
Cool. That is basically how my encoding looks.
At least for curl 7.61.1
, you can use --data
or --data-binary
and reference a file like this: @/path/to/file
. I had been using the -T
option as --upload-file
. I like to use long options if possible, for reference, because I will have to train other members of my staff to use cli/shell in the future.
Please note: in my original script, I had tried about half a dozen options and some jenky stuff made it into the script (like: --data-binary
followed by --upload-file
). Original script should be considered alpha
quality.
EDIT: Ah. I see --data @/path/to/file
will just read the contents, where as -T
|--upload-file
will actually upload the file. My bad.