Management account used EA?

bentoms
Release Candidate Programs Tester

Anyone got an EA that reports back the management account used?

Or a way to smart group machines based on management account?

1 ACCEPTED SOLUTION

mm2270
Legendary Contributor III

Yep.
But... the only way (currently) is to use the Casper API. The information exists in the API for every Mac, but can't be accessed any other way.
So first things first is to set up a 'read-only' API account on your JSS if you haven't done so already. Then plug that info along with your JSS address into the below script

#!/bin/sh

apiURL="https://your.casper.jss:8443/JSSResource/computers/macaddress/"
apiUser="apiusername"
apiPass="apipassword"
MacAdd=$( networksetup -getmacaddress en0 | awk '{ print $3 }' | sed 's/:/./g' )

ManAccount=$( curl -s -u $apiUser:$apiPass "$apiURL$MacAdd" | xpath /computer/general/remote_management/management_username[1] | sed 's/<management_username>//;s/</management_username>//' )
if [[ "$ManAccount" != "" ]]; then
    echo "<result>$ManAccount</result>"
else
    exit 0
fi

In case you're wondering why the if/then, exit stuff, our JSS is clustered with a limited access server in the DMZ so Macs can connect in outside the network. problem is, the API uses Tomcat which is disabled on the external server (Limited Access JSS) so we had some issues early on where if it couldn't read the information back during inventory while outside, it would blank out the value previously assigned. Since we had a policy that was attempting to correct the management account with a QuickAdd.pkg, it was causing those Macs to be re-enrolled. Not what we wanted and caused a few issues, like some policies re-running.

Truth is though, I'm not sure if the if/then, else exit is actually working as EA's operate a bit differently than regular scripts. Our account rep at JAMF thinks they must upload 'something' regardless of how you designed the script, and I have a feeling that is the case. We had disabled the above referenced policy anyway for other reasons.

View solution in original post

26 REPLIES 26

mm2270
Legendary Contributor III

Yep.
But... the only way (currently) is to use the Casper API. The information exists in the API for every Mac, but can't be accessed any other way.
So first things first is to set up a 'read-only' API account on your JSS if you haven't done so already. Then plug that info along with your JSS address into the below script

#!/bin/sh

apiURL="https://your.casper.jss:8443/JSSResource/computers/macaddress/"
apiUser="apiusername"
apiPass="apipassword"
MacAdd=$( networksetup -getmacaddress en0 | awk '{ print $3 }' | sed 's/:/./g' )

ManAccount=$( curl -s -u $apiUser:$apiPass "$apiURL$MacAdd" | xpath /computer/general/remote_management/management_username[1] | sed 's/<management_username>//;s/</management_username>//' )
if [[ "$ManAccount" != "" ]]; then
    echo "<result>$ManAccount</result>"
else
    exit 0
fi

In case you're wondering why the if/then, exit stuff, our JSS is clustered with a limited access server in the DMZ so Macs can connect in outside the network. problem is, the API uses Tomcat which is disabled on the external server (Limited Access JSS) so we had some issues early on where if it couldn't read the information back during inventory while outside, it would blank out the value previously assigned. Since we had a policy that was attempting to correct the management account with a QuickAdd.pkg, it was causing those Macs to be re-enrolled. Not what we wanted and caused a few issues, like some policies re-running.

Truth is though, I'm not sure if the if/then, else exit is actually working as EA's operate a bit differently than regular scripts. Our account rep at JAMF thinks they must upload 'something' regardless of how you designed the script, and I have a feeling that is the case. We had disabled the above referenced policy anyway for other reasons.

bentoms
Release Candidate Programs Tester

Awesome. Thanks again Mike.

I'll give it a whirl tomorrow.

We're in the throws of a change over & so far 150 out of 190 clients have the new account.

That's @ least 2 beers I'll hopefully buy you @ JNUC 2013!

bentoms
Release Candidate Programs Tester

Worked well.. many thanks!

ericjboyd
Contributor

anyone else getting an error with xpath?

not well-formed (invalid token) at line 1, column 0, byte 0:
[huge block of xml deleted]
^ at /System/Library/Perl/Extras/5.16/darwin-thread-multi-2level/XML/Parser.pm line 187

jennifer
Contributor

Disregard.

The error was my own, the EA is working well.

ctangora
Contributor III

Just to throw in an additional feature, we are getting the account info in a similar way, but then comparing the result to the local directory, and then returning a "Missing" if it is not there.

if [[ $(dscl . -search /Users RecordName $JSSAdmin) ]]; then
echo "<result>OK - $JSSAdmin</result>"
else
echo "<result>MISSING - $JSSAdmin</result>"
fi

smamdani
New Contributor II

Running this on Yosemite, I get:

no element found at line 1, column 0, byte 0:

^
 at /System/Library/Perl/Extras/5.18/darwin-thread-multi-2level/XML/Parser.pm line 187.

Any thoughts, folks?

mm2270
Legendary Contributor III

Hi @smamdani][/url - Not sure if you're talking about my script or another one, but if you mean mine, try the following. New and improved.

#!/bin/sh

apiURL="https://your.casper.jss:8443/JSSResource/computers/macaddress"
apiUser="apiusername"
apiPass="apipassword"
MacAdd=$(networksetup -getmacaddress en0 | awk '{ print $3 }' | sed 's/:/./g')

ManAccount=$(curl -H "Accept: application/xml" -skfu $apiUser:$apiPass "${apiURL}/${MacAdd}/subset/general" | xmllint --format -  2>/dev/null | awk -F'>|<' '/management_username/{print $3}')
if [ ! -z "$ManAccount" ]; then
    echo "<result>$ManAccount</result>"
else
    echo "<result>N/A</result>"
fi

ericjboyd
Contributor

any chance you could suggest the changes needed for this script to use xmllint? https://jamfnation.jamfsoftware.com/discussion.html?id=8667

mm2270
Legendary Contributor III

@ejboyd][/url][/url - using xmllint isn't strictly necessary. It just makes looking at and parsing the resulting xml a little easier since it formats it.
Anyway, here is a rework of the script posted on that thread using both xmllint and also forcing the API to return xml instead of JSON, which can happen under some circumstances now..

#!/bin/sh

apiURL="https://yourjss:8443/JSSResource/computers/macaddress/"
apiUser="username"
apiPass="password"
MacAdd=$( /usr/sbin/networksetup -getmacaddress en0 | /usr/bin/awk '{ print $3 }' | /usr/bin/sed 's/:/./g' )

siteName=$( /usr/bin/curl -H "Accept: application/xml" -skfu $apiUser:$apiPass "$apiURL${MacAdd}/subset/general" | xmllint --format - | xpath //site 2>&1 | awk -F'>|<' '/name/{print $3}' )
if [[ "$siteName" != "" ]]; then
    echo "<result>$siteName</result>"
else
    echo "<result>Not Available</result>"
fi

Josh_Smith
Contributor III

This is great, thanks Mike.

FYI There was one typo I had to correct to get the EA for the management account to work (the new and improved version listed 3 posts above this post). There is a trailing slash in the apiURL variable, but also an additional slash when the apiURL variable is called when defining the ManAccount variable....you need to lose one or the other to get it to work.

.....
apiURL="https://yourjss:8443/JSSResource/computers/macaddress**/**"
.....
...."${apiURL}**/**${MacAdd}/subset/general".......

mm2270
Legendary Contributor III

@Josh.Smith - Thanks for the catch! You're absolutely right. One too many forward slashes. The EA we use for this is slightly different so this was a modification on what we use, hence the typo as it got translated.

chlaird
Contributor

Hey @mm2270][/url , I upgraded to 9.64 this morning and suddenly my old EA to get the management account didn't work anymore. I looked at your updated version quick and it's returning a value of N/A for me on every computer, so maybe it needs to be updated again, or who knows. I'm not going to dig into that, because I modified your $sitename EA to get a working version.

For anyone interested, here it is. It's working for me, so hopefully it works for y'all too. All credit to @mm2270][/url for the original.

#!/bin/sh

apiURL="https://your.jss.address:8443/JSSResource/computers/macaddress/"
apiUser="YourAPIusername"
apiPass="YourAPIpassword"

MacAdd=$( /usr/sbin/networksetup -getmacaddress en0 | /usr/bin/awk '{ print $3 }' | /usr/bin/sed 's/:/./g' )


ManAccount=$( /usr/bin/curl -H "Accept: application/xml" -skfu $apiUser:$apiPass "$apiURL${MacAdd}/subset/general" | xmllint --format - | xpath //remote_management 2>&1 | awk -F'>|<' '/management_username/{print $3}' )
if [[ "$ManAccount" != "" ]]; then
    echo "<result>$ManAccount</result>"
else
    exit 0
fi

mm2270
Legendary Contributor III

@chlaird][/url - Thanks for the info! The problem it seems with most of my posts above is that I had an extra forward slash in the URL variable. For example, change the apiURL line to end with macaddress instead of macaddress/ and it should start working. @Josh.Smith][/url had pointed out the error above and I just never got around to changing any of the above posts to correct it.

Edit: Corrections now applied to several posts above.

chlaird
Contributor

@mm2270 -- I see you corrected your posts, I thought I was going crazy! I was looking for what you said, and it wasn't there suddenly.

Anyway, you're correct-- the extra / in your 12/17/2014 post broke it for me. With your new edit, it now works.

If you're interested, your 12/30/14 post is now broken because it's missing a / , since the address ends with macaddress and you didn't include it in "$apiURL${MacAdd}/subset/general"

mm2270
Legendary Contributor III

Oops! Haha. You're right. I was looking close enough at that script when I edited it. I added the slash back in.

At some point I may edit them all to make them more consistent across the various versions.
Thanks again for the catches!

spraguga
Contributor

What access is everyone providing to the API User for this particular lookup? JSS : Computers : Read Only?

mm2270
Legendary Contributor III

@spraguga - Yes, the API account should really only need access to read Computer objects and nothing else. Its just pulling the Computer record to get the Management account information from its record.
That said, we use our standard API account that can read all objects from the JSS for this, but its not necessary to have that level of access.

ericjboyd
Contributor

Ok, finally got this working. xmlpath and xmllint were choking on something returning from the server incorrectly. Our JSS is on Tomcat/Windows Server.
The fix was to edit:

C:Program FilesJSSTomcatincatalina.bat

and adding the line:
```
set JAVA_OPTS=%JAVA_OPTS% -Dfile.encoding=UTF8

rvanhardeveld
New Contributor

@mm2270 Mike, do you know if something is changed since then so you can get the management account information from de EA? The api of our limited jss is disabled, and I would like to see that the management account information can be acquired.

mm2270
Legendary Contributor III

Hi @rvanhardeveld I'm not sure I understand what you mean by:

The api of our limited jss is disabled

If its disabled, there isn't really a way to get the Management Account via script. As far as I know its still not a built in criteria item in a computer record that can be displayed. Its part of the computer's record details in the xml, but that's it.
Or am I misunderstanding what you're meaning by that statement?

rvanhardeveld
New Contributor

No Mike, you understood it correctly:) Thank you for your answer, I was hoping there was a work-around for this:)

tlarkin
Honored Contributor

Hi Everyone,

Just a friendly reminder, if you are going to deploy API scripts client side, you probably want to use a read only API account. I would just like everyone to take that into consdieration since they will contain a password. Alternatively you can check out Bryson's GitHub example of passing encrypted strings in positional parameters in a script (which you can't do in an EA unfortunately but you can in a policy) at this link:

Encrypted Strings example

Another option over XML is JSON when doing GET request, and it is sometimes a lot easier to parse. There is a FOSS binary called jq which you can download off their GitHub site

A quick example to get the management account in a one liner, you can use jq like so:

bash-3.2$ serial=$(system_profiler SPHardwareDataType | awk '/Serial Number/ { print $4}')

bash-3.2$ curl -sk -H "Accept: application/json" -u tlarkin https://casper9gm.local:8443/JSSResource/computers/serialnumber/${serial} | jq '.computer.general.remote_management.management_username'
Enter host password for user 'tlarkin':
"_sshdaemon"

Of course Python has built in modules that can parse XML/JSON, but there are some decent ones for bash. XMLstartlet and jq are two you should look up. One pitfall of jq though is there is no edit in place with the binary, you have to write to a file. Since the JSS doesn't support PUT/POST in JSON anyway that should not be a huge deal currently. XMLStartlet is another FOSS project that uses xpath and can do in place editing when doing API work. This should reduce the amount of awk and sed pipes, although in my experience nothing does regex that well in bash as sed does.

Hope this was somewhat helpful to you all.

Thanks,
Tom

mm2270
Legendary Contributor III

Thanks for the information on jq @tlarkin.
FWIW, I still prefer working with xml myself and my API scripts no longer use all that xpath, awk and sed stuff up above (some of it, but less). For example, getting the management account now is:

#!/bin/sh

serial=$(ioreg -rd1 -c IOPlatformExpertDevice | awk -F'"' '/IOPlatformSerialNumber/{print $4}')

management_account=$(curl -H "Accept: application/xml" -sfku apiuser:apipass https://jss.company.com:8443/JSSResource/computers/serialnumber/${serial} -X GET | xmllint --format - | awk -F'>|<' '/<management_username>/{print $3}')

So basically, pass the xml result thru xmllint to format it and use awk field separator and regex matching together to pull the field between the tags. Just two pipes, so not terrible. Seems to work well for me. I will take a look at the extra tools you mentioned though. Can't have too many tools in your toolbox.

tlarkin
Honored Contributor

@mm2270

That is the beauty of the plethora of tools available, and the methods you use. You have options. I prefer xpath when working with XML in bash. Like this example:

$ curl -sk -u api:api https://casper9gm.local:8443/JSSResource/computers/id/10 | xpath /computer/general/remote_management/management_username
Found 1 nodes:
-- NODE --
<management_username>_sshdaemon</management_username>

from there I just strip out the xml tags with sed and regex. However, that is not the only way to accomplish the same goal, so whatever methods you prefer is really up to you. I like xpath and the -d switch with curl so I can just GET/PUT/POST whatever small piece of XML I am working, but that is literally just my personal preference. It is by no means the only way to do this.

I personally enjoy seeing everyone's solutions on here because it always gives me new ideas. :-)

Cheers,
Tom

Taylor_Armstron
Valued Contributor

Probably worth submitting a feature request to simply expose this in the GUI without an EA. Unfortunately we have an ITSec policy prohibiting us from storing passwords in scripts (yes, I know, I didn't write it). We use Recon to "catch" systems that we've missed, but that enrolls with an existing admin account, while we created a new one to use with Casper. It would be nice to create a policy to re-enroll with the new account, scoped to systems using the old one.