Skip to content

Unenrolling a Mac from your Casper Suite JSS

We have two JSS’s, one for Development/Testing and one for Production. We want to be able to swap our test Macs back & forth as needed. Unfortunately, JAMF doesn’t provide an “unenroll” switch for the jamf command line tool. You can “removeFramework” but that deletes everything JAMF-related from the workstation so you cannot enroll programmatically and are stuck obtaining a new QuickAdd package and running from the GUI. So I wrote a script to get around that.

There are only two files you need to preserve prior to removing the JAMF framework so that you can re-enroll later:


Once those are saved, you’re good to roll. Here is the script:

# January 17, 2014, Kurt Tappe
# This script will swap JSS'

# Set server addresses

# Which JSS are we running from?
jss=`/usr/sbin/jamf checkJSSConnection | grep https | cut -d "/" -f3`
if [ "$jss" = "" ]; then
   echo "Error: JSS could not be determined. You may need to first enroll this Mac in a JSS using QuickAdd."
elif [ "$jss" = "" ]; then
   echo "Currently connected to $jss. Switching to Dev..."
   echo "Currently connected to $jss. Switching to Prod..."

# Preserve prefs & binary
cp /Library/Preferences/com.jamfsoftware.jamf.plist /Library/Preferences/com.jamfsoftware.jamf.backup
cp /usr/sbin/jamf /usr/sbin/jamfbackup

# Disconnect
echo "Removing framework..."
/usr/sbin/jamf removeFramework
sleep 5

# Restore old prefs and modify them with the new JSS URL
echo "Restoring prefs & enrolling..."
cp /Library/Preferences/com.jamfsoftware.jamf.backup /Library/Preferences/com.jamfsoftware.jamf.plist
cp /usr/sbin/jamfbackup /usr/sbin/jamf
defaults write /Library/Preferences/com.jamfsoftware.jamf allowInvalidCertificate -boolean true
defaults write /Library/Preferences/com.jamfsoftware.jamf jss_url -string $newjss
defaults delete /Library/Preferences/com.jamfsoftware.jamf last_management_framework_change_id

# Reconnect
/usr/sbin/jamf enroll -noRecon

One thing required for this to work is for you to not be forcing the use of certificates. Yes, we use them, but for ease of management we don’t require them for enrollment, so that we can do things like run these scripts.

Hope this helps,

Programmatically Scraping Data From your Casper Suite JSS

So let’s say you’re writing a bash script and need to glean data from your JSS about a particular Mac workstation. In this case, you have the workstation name and you want to know the Mac’s IP address. Well, JAMF has provided a means to do that via the REST API.

If you contact JAMF, they’ll show you a way to obtain info about any Mac workstation in the JSS if you already know its computer ID in the JSS:

curl -v -u USERNAME:PASSWORD https://JSS.URL:8443/JSSResource/computers/id/###/subset/general -X GET | xpath //computer/general/ip_address | sed -e 's/\<ip_address>//g; s/\<\/ip_address>//g'

This is hardly useful though, as who knows each workstation’s ID? (The ID happens to be pretty much random; it’s a chronological increment based on when the Mac was enrolled in the JSS. Your 9th Mac is “9″, your 47th is “47″, etc.)

The other problem is that both ‘curl’ and ‘xpath’ are very verbose in their output. If you’re writing a command-line utility, you don’t want them mucking up the user’s screen. Luckily there’s a better way.

curl -v -u USERNAME:PASSWORD https://JSS.URL:8443/JSSResource/computers/name/NAME/subset/General | grep -Eo '<ip_address>[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}' | cut -d ">" -f2

(Note: I attempted to use ‘sed’ instead of ‘grep’ & ‘cut’ above, but ‘sed’ just wouldn’t parse the tag-laden output. Ping me if you have a solution.)

In both examples above, you of course need to substitute your authentication into USERNAME and PASSWORD, your JSS address into JSS.URL, and in the bottom example the workstation name you’re referencing into NAME.

If you try the commands directly in Terminal you’ll see the entire transaction with the server, but don’t worry; if you pipe the output to a file or use it in a full script, the output will be clean.

I’ll post more code snippets as I develop them. Specifically, I’m trying to do the reverse of the above; take an IP address and convert it into a workstation name or (better yet) serial number. Watch this space.

Error While Updating Enterprise-packaged Adobe Creative Suite

If you’re trying to install updates or upgrades to Adobe Creative Suite, you may run across a refusal of the updater to run. The error will resemble:

Update failed, updates have been suppressed by the Administrator

Funny thing is, you actually are the likely cause of this error. Remember back when you were packaging Creative Suite using Adobe Application Manager Enterprise Edition (AAMEE)? Near the end of the series of dialog boxes you went through, you clicked “Prevent Updates”.

And there we are. Adobe remembered this and is continuing to suppress updates. It doesn’t care if you’re the admin; updates are suppressed!

The fix is easy even if the path to it is long. What you need to do is edit or delete this file:

/Library/Application Support/Adobe/AAMUpdaterInventory/1.0/AdobeUpdaterAdminPrefs.dat

Removing it is draconian but quick & easy. All you need gone / changed is the line within it that reads:


Make the “1″ a “0″ or just delete that line.

This is all documented by Adobe here, but Googling or Binging the subject is often fruitless. So I’m reposting here to make it easier for all to find.

Keeping 10.8 Mountain Lion Updates

Back before all Apple updates came to be distributed through the App Store, it was easy to retain software updates. You just told Software Update app to Keep Updates through its menu system.

There are distinct advantages to retaining updates instead of redownloading them every time. Bandwidth, which Apple seems to think we all have in abundance, is a large one, especially considering many updates are measured in gigabytes these days. But perhaps you also want to keep an update for use in InstaDMG or Casper Suite.

With the advent of App Store, these updates are notably harder to retain. But if you’re quick, you can still keep them. Follow these steps:

1) Initiate the download of the update using App Store.

2) Once the download is complete, look in /Library/Updates/[random #]

OK, the number probably isn’t random, but you’re unlikely to find out Apple’s numbering system, so just look at the most recently updated numerical folder inside /Library/Updates. Your .pkg will be there waiting for you.


10.7_vanilla.catalog w/iTunes 10.6.1, Java Update, & Safari 5.1.5 [UPDATED]

In case any InstaDMG users are looking for a cut & pasteable 10.7_vanilla.catalog that’s set up for using a 10.7.3 InstallESD.dmg, iTunes 10.6.1, Java Update 2012-002, and Safari 5.1.5, here it is:

OS Updates:
 AirPort Utility 6.0 sha1:4e58a9e1af3ddefce843e49ef4b3f36c9c79793a
 iTunes 10.6.1 sha1:e9067bd7b8add8ef6bba0799df1e5a245c6c8187
 Safari 5.1.5 sha1:c253c54faf399ef961a962425930e410cf3fe47e
 JavaForOSX sha1:f3a37aaeace6731c427c76b277c8741d9d6e0220

(Important note: Tabs don’t translate well to WordPress. Be sure after copying to replace the spaces before the URL’s and sha1′s with tabs!)

Sure you can make one yourself in 10 minutes but I thought this might save someone some trouble.

Binding Lion To Active Directory

Active Directory binding can be a black art. Nuances abound, and they can be pitfalls for getting that first bind to actually take place. Take the following command for example:

dsconfigad -force -add "" -c MyMac -u myusername -p mypassword -ou "OU=macs,OU=computers"

No DC’s are specified, but you’d think that’s OK. After all, you told the AD plugin what the DC’s were with the -add flag. It should be able to figure them out from the “”, right?

Wrong. While third-party solutions like ADmitMac and Centrify can figure out DC’s from the supplied domain, Apple’s AD plugin cannot.

Worse, if you omit the explicit DC specification, the AD plugin won’t tell you. It will try for a while to bind, and eventually you’ll get an error, but it will be a “(10001) failure”, not something useful like “DC not specified”. And it will be at the end of a very long log of bind attempts (assuming you turned on debug logging).

So basically you must specify the domain twice, thusly:

dsconfigad -force -add "" -c MyMac -u myusername -p my password -ou "OU=macs,OU=computers" -domain "DC=ad,DC=mycompany,DC=com"

or (shorter):

dsconfigad -force -add "" -c MyMac -u myusername -p my password -ou "OU=macs,OU=computers,DC=ad,DC=mycompany,DC=com"

Specify your DC’s. Everyone will be happier.

Programatically Getting IP Address on the MacBook Air

(It’s tougher than you think.)

Obtaining the IP address on a desktop Mac with built-in ethernet ports is relatively straightforward. You can rely on en0 existing and the connection being stable. On desktops, TCP connections are maintained, even in a “CLOSE_WAIT” state:

(IP addresses changed to protect the guilty)
# netstat -p tcp
 Active Internet connections
 Proto Recv-Q Send-Q Local Address Foreign Address (state)
 tcp4 0 0 a192-17-157-46.zz.https SYN_SENT
 tcp4 0 0 my.server..micro ESTABLISHED
 tcp4 0 0 CLOSE_WAIT
 tcp4 0 0 CLOSE_WAIT

However, the MacBook Air has no built-in ethernet. To hardwire one to a network, a USB<->Ethernet transceiver must be added on. And in an effort to save power, the Air drops any unused connections after 5 seconds. Witness:

# netstat -p tcp
 Active Internet connections
 Proto Recv-Q Send-Q Local Address Foreign Address (state)

After a few seconds the very same command yields zilch:

# netstat -p tcp

So any scripts you write to grab the IP address that use netstat (the preferred method you’ll find recommended by almost all sites if you Google the topic) will yield a blank result.

You could activate the TCP connection by launching Safari or issuing a curl command and quickly reading netstat’s output within 5 seconds, but that’s a kludge.

The better answer? Good old ifconfig, which will yield the persistent IP state of the Mac no matter how long the network connections have been idle. On the same Air as above where netstat was reporting no info, you get this:

# ifconfig | grep "broadcast"
 inet netmask 0xffffff00 broadcast

Ding! (note that we limited the search to lines containing “broadcast” to prevent finding the loopback and other ports irrelevant to our current need.)

To complete the search for a usable IP, we just:

#ip=`ifconfig | grep "broadcast" | cut -d " " -f2`
#echo $ip

Now you have $ip waiting for you to act on. Examples:

if [ "$ip" = "" ]; then
   echo "No ethernet cable appears to be plugged in."


if [ "${ip:0:7}" = "169.254" ]; then
   echo "No DHCP server was found."

Have fun!


Get every new post delivered to your Inbox.