Help testing my script to automatically enforce JSS computer names.

Newton
New Contributor

I am new to JSS and generally impressed with what I find. However I was disappointed to see that admin users can change their machine names. Since I have no authority to remove admin rights here and wanted to use smart groups based on names to dictate policy it concerned me.

I devised a script that uses a spare editable field in JSS (barcode_2) to store a backup of the desired computer name and enforces it as the active computer name. In this way of the name it changed, it gets changed back at login (or other trigger point).

It's my first bash script in a while and my first foray into the JSS API.

I'd like help from the community testing and improving it please. I'm unfamiliar with JAMFNation. What is the best/accepted approach for this? Do I post the script here? Or somewhere else?
After it is concluded I'm happy to upload to the general scripts tab, if allowed.

Specifically I'm looking for error condition checking, what versions of OSX it can be made to work on (dependency checking) and general improvements. How should I go about this?

Best Wishes
Matt

Edit reason: typo.

5 REPLIES 5

mm2270
Legendary Contributor III

You can enter the script here in your post and preferably format it using the buttons above the post field (highlight just the script and click the script button, looks like a ">_" symbol)

But, before you script out something like this, you may not even need to do that since Casper has some functionality built in to rename a Mac based on the name the JSS has for its record. In some cases, you can run into an odd timing issue whereby the policy to rename it back may not run until after the Mac has already recon'ed itself (submitted inventory) back to the JSS with the new computer name, which means the JSS won't know what it should name it back to. But the basic functionality is built in, if you wanted to explore that first.

Also, you don't need to remove admin rights to stop the majority of renames from happening. Just lock the Sharing Preference Pane using either MCX or a Config Profile and users won't be able to rename it by simply going into the Sharing Pref Pane and plunking in a new name. This approach has its pluses and minuses. The plus is it will stop the majority of renames from happening*. The minuses are that if you need users to be able to do anything else in the Sharing pane they will be locked out of those operations completely. Edit- The other minus is that using the older "whitelist" method for this creates the headache of needing to add in additional 3rd party Pref Panes to the allowed list for users to be able to use them. I'd recommend looking at the new "Blacklist" approach introduced by Apple recently, but Casper isn't currently supporting that model to my knowledge.

Keep in mind someone with admin rights can still easily rename their mac from the command line with the requisite knowledge. Locking Sharing will only stop the casual renamers.*

damienbarrett
Valued Contributor

Casper has this feature built-in, but it is a bit hard to find. Assuming you're running Casper 9.x --

1) Go to Policies
2) Open up the "Update Inventory" policy, which is built into every Casper installation
3) Click Edit
4) Click on the Options tab if it's not selected
5) Click on "Maintenance"
6) Make sure that "Reset Computer Names" is checked.
7) Save the changes

Make sure all your computer names are correct when you enroll into JSS and this inventory update policy will keep them in line as long as the machines keep checking in.

A good idea is to use a naming syntax for your computer names so it's easy to search for any computer in the JSS that might not have the correct syntax. For instance, my computers all have either our students Year of Graduation (e.g. 2015) or our campus prefix (e.g. US, MS, PS) in them, so I can easily create a smart group that looks for any computers that do not have one of these syntax identifiers. I get an email that a machine has fallen out of our computer-naming syntax and I track it down to find out why.

Newton
New Contributor

Thanks Chaps.

Restrict sharing: Restricting sharing prefs is a good idea I will pass on, so that helps, thank you. I doubt that the team will accept it as it does restrict functionality I suspect is expected.

Naming Syntax: Couldn't agree more. For better or worse our naming convention is fixed in place, discussion is no longer entertained and it includes owner/user names. Ho humm. It is better than it was.

Reset Computer names: I already have a policy in place once a day to reset them to JSS names using the Reset Computer Names. However this still falls short in a small respect.

Here is where it broke down for us.

We have a large influx of new systems. We want to take them out of the box install the agent (with non-tech but authorized help) and do little else other than wait for the agent to then install software, apply policy, tweak the system and so on.

Our naming convention (for better or worse, there is no discussion on the matter (any more) includes building codes and truncated staff names.

We're going to pre-populate Casper with computer objects that correspond to the still boxed new hardware. Complete with correct computer names, asset tags, building codes

Trouble is the agent overwrites the computer name with whatever the value is at agent install. So essentially a lot of room for error for "non-technical helpers" who would have to get it correct at time of install. Or if we install remotely via ARD or similar then removing the need to enter hundreds of individual computer names. Much easier to create in a spreadsheet from a formula and upload once.

This script is to address that need. By enforcing the pre-populated correct computer names thus allowing them to survive an agent install or later renaming.

Here it is. Comments welcome.

#!/bin/sh
####################################################################################################
#
# Copyright (c) 2014, Matthew Newton.  All rights reserved.
#
#       Redistribution and use in source and binary forms, with or without
#       modification, are permitted provided that the following conditions are met:
#               * Redistributions of source code must retain the above copyright
#                 notice, this list of conditions and the following disclaimer.
#               * Redistributions in binary form must reproduce the above copyright
#                 notice, this list of conditions and the following disclaimer in the
#                 documentation and/or other materials provided with the distribution.
#               * Neither the name of Matthew Newton nor the
#                 names of its contributors may be used to endorse or promote products
#                 derived from this software without specific prior written permission.
#
#       THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
#       INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
#       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHEW NEWTON
#       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
#       CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
#       GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
#       HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
#       LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
#       THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
####################################################################################################
#
# ABOUT THIS PROGRAM
#
# NAME
#   enforceJSSComputerNames.sh -- Enforces the JSS computer name using the JSS barcode_2
#
# SYNOPSIS
#   enforceJSSComputerNames.sh
#
# DESCRIPTION
#   This script reads computer information from a CSV file and imports the data into the JSS.
#
####################################################################################################
#
# HISTORY
#
#   Version: 1.0
#
#   - Created by Matthew Newton on July 30, 2014
#
####################################################################################################
apiUsername="myname"
apiPassword="mypassword"
jssBase="jssurl"

#Get info and compare
serialNumber=`system_profiler SPHardwareDataType | grep Serial | awk '{print $NF}'`
jssMobileID=`curl -sS -k -u $apiUsername:$apiPassword $jssBase/JSSResource/computers/match/$serialNumber | xpath //computers/computer/id | sed -e 's/<id>//g; s/</id>//g'`

if [ "$jssMobileID" != "" ]; then
#   echo "JSS Computer ID : $jssMobileID" >> $outFile
#   Get JSS name and Barcode2 value
    jssName=`curl -sS -k -u $apiUsername:$apiPassword $jssBase/JSSResource/computers/id/$jssMobileID | xpath //computer/general/name | sed -e 's/<name>//g; s/</name>//g'`

    jssBarcode2=`curl -sS -k -u $apiUsername:$apiPassword $jssBase/JSSResource/computers/id/$jssMobileID | xpath //computer/general/barcode_2[1] | sed -e 's/<barcode_2>//g; s/</barcode_2>//g'`

#echo We found serialNumber of $serialNumber
#echo We got jssMobileID of $jssMobileID
#echo and jssName of $jssName

    if [ "$jssBarcode2" == "" ] || [ "$jssBarcode2" == "<barcode_2 />" ] ; then 
    # Must be a computer not previously on our enforce name system.
    # Set the empty JSS barcode_2 value to the JSS computer name and update var.
#   echo The jssBarcode2 is default or empty.
#   echo Must be a computer not previously on the enforce names system.
#   echo Putting it on now. Setting jssBarcode2 value to jssName of $jssName
        curl -sS -k -i -u $apiUsername:$apiPassword -X PUT -H "Content-Type: text/xml" -d "<?xml version="1.0" encoding="ISO-8859-1"?><computer><general><barcode_2>$jssName</barcode_2></general></computer>" $jssBase/JSSResource/computers/id/$jssMobileID
        jssBarcode2=`curl -sS -k -u $apiUsername:$apiPassword $jssBase/JSSResource/computers/id/$jssMobileID | xpath //computer/general/barcode_2[1] | sed -e 's/<barcode_2>//g; s/</barcode_2>//g'`
#       echo Updated jssBarcode2 to $jssBarcode2
    fi
#echo and now jssBarcode2 of $jssBarcode2


sysComputerName=`scutil --get ComputerName`
sysLocalHostName=`scutil --get LocalHostName`
##sysHostName=`scutil --get HostName`
#echo We have system ComputerName of $sysComputerName
#echo We have LocalHostName of$sysLocalHostName
##echo and system HostName of $sysHostName

if [ "$jssName" != "$jssBarcode2" ] || [ "$sysComputerName" != "$jssBarcode2" ] || [ "$sysLocalHostName" != "$jssBarcode2" ] ; then

#   echo Humm, jssName of $jssName is different from jssBarcode2 of $jssBarcode2
#   echo That should not be in our system. So enforcing computer name to be as jssBarcode2.
#   echo Changing computer name from $jssName to $jssBarcode2

#   If JSS name different from barcode2 then it has been improperly changed, so lets enforce the original name.
    /usr/sbin/jamf setComputerName -name $jssBarcode2
#   echo Since we made changes, let us update the inventory.
    /usr/sbin/jamf recon
fi  

else
    echo "Unable to locate a computer with this serial number, name or MAC, aborting...."
fi
#echo Nothing more to do. Bye Bye.

mm2270
Legendary Contributor III

I haven't looked through your whole script, but I see a few items that could be adjusted to make the script a little more efficient. By no means though is anything I see being done wrong. These are just some tips to help the script run a little faster or more efficiently.

First, consider getting the Mac's serial number from ioreg instead of calling system_profiler, which is very slow.

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

Two, if you need to pull multiple items from the API record, you'd be better off pulling it once and piping it into a local file and then using xpath against that file, rather than calling curl against the API several times. For example, do this-

curl -sS -k -u $apiUsername:$apiPassword $jssBase/JSSResource/computers/match/$serialNumber > "/tmp/$serialNumber"

Then later use xpath against it, like

xpath //computer/general/name < "/tmp/${serialNumber}.xml"

You can also use an awk trick to pull the item you want without needing several sed commands to clean up the output, for example:

jssName=`xpath //computer/general/name < "/tmp/${serialNumber}.xml" 2>&1 | awk -F'[>|<]' '/name/{print $3}'`

Also, with any curl commands against the API, I recommend adding the -f flag to them. The -f flag allows curl to evaluate whether it was actually successful. The problem with normal curl operations is that if curl gets any response at all from a server, even an error response, it considers it to be successful and will exit with exit code 0. So, if your API username and password don't work for some reason, or the record can't be found, it will still think it worked. This makes is very hard to test for successful curl operations in the script if you chose to do that. The -f flag will make it exit with code 22 if it could not get the information you requested.
So, rewriting the first example above, I'd consider doing it like this-

curl -sS -kfu $apiUsername:$apiPassword $jssBase/JSSResource/computers/match/$serialNumber > "/tmp/${serialNumber}.xml"

Hope the above tips help some. Beyond those minor things it looks OK at first glance. Its a nice way to address the issue of timing whereby the JSS grabs a renamed Mac's name and the built in rename function no longer works the way you'd expect.

Newton
New Contributor

That's REALLY helpful. Thank you!

Could I trouble you to glance at the revised version incorporating your marvelous tips.

Any other tips? Anyone else?

#!/bin/sh
####################################################################################################
#
# Copyright (c) 2014, Matthew Newton.  All rights reserved.
#
#       Redistribution and use in source and binary forms, with or without
#       modification, are permitted provided that the following conditions are met:
#               * Redistributions of source code must retain the above copyright
#                 notice, this list of conditions and the following disclaimer.
#               * Redistributions in binary form must reproduce the above copyright
#                 notice, this list of conditions and the following disclaimer in the
#                 documentation and/or other materials provided with the distribution.
#               * Neither the name of Matthew Newton nor the
#                 names of its contributors may be used to endorse or promote products
#                 derived from this software without specific prior written permission.
#
#       THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
#       INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
#       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHEW NEWTON
#       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
#       CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
#       GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
#       HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
#       LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
#       THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
####################################################################################################
#
# ABOUT THIS PROGRAM
#
# NAME
#   enforceJSSComputerNames.sh -- Enforces the JSS computer name using the JSS barcode_2
#
# SYNOPSIS
#   enforceJSSComputerNames.sh
#
# DESCRIPTION
#   This script reads computer information from a CSV file and imports the data into the JSS.
#
####################################################################################################
#
# HISTORY
#
#   Version: 1.0.1
#
#   - Created by Matthew Newton on July 30, 2014
#   - Assistance from Mike (mm2270)
#
####################################################################################################
apiUsername="myname"
apiPassword="mypassword"
jssBase="myjssurl"

#Collect sys serial
#serialNumber=`system_profiler SPHardwareDataType | grep Serial | awk '{print $NF}'`
#New and improved method
serialNumber=$(ioreg -rd1 -c IOPlatformExpertDevice | awk -F'"' '/IOPlatformSerialNumber/{print $4}')
#Collect JSS info
#jssMobileID=`curl -sS -kfu $apiUsername:$apiPassword $jssBase/JSSResource/computers/match/$serialNumber | xpath //computers/computer/id | sed -e 's/<id>//g; s/</id>//g'`
curl -sS -kfu $apiUsername:$apiPassword $jssBase/JSSResource/computers/match/$serialNumber > "/tmp/$serialNumber"

if [ "$jssMobileID" != "" ]; then

#   echo "JSS Computer ID : $jssMobileID" >> $outFile
#   Get JSS name and Barcode2 value
#   jssName=`curl -sS -ku $apiUsername:$apiPassword $jssBase/JSSResource/computers/id/$jssMobileID | xpath //computer/general/name | sed -e 's/<name>//g; s/</name>//g'`
#   jssBarcode2=`curl -sS -ku $apiUsername:$apiPassword $jssBase/JSSResource/computers/id/$jssMobileID | xpath //computer/general/bar_code_2[1] | sed -e 's/<barcode_2>//g; s/</bar_code_2>//g'`
    # New and improved method
    jssName=`xpath //computers/name[1] < "/tmp/${serialNumber}.xml" 2>&1 | awk -F'[>|<]' '/name/{print $3}'`
    jssBarcode2=`xpath //computers/bar_code_2[1] < "/tmp/${serialNumber}.xml" 2>&1 | awk -F'[>|<]' '/bar_code_2/{print $3}'`

#echo We found serialNumber of $serialNumber
#echo We got jssMobileID of $jssMobileID
#echo and jssName of $jssName
#echo Initial jssBarcode2 of $jssBarcode2
    if [ "$jssBarcode2" == "" ] || [ "$jssBarcode2" == "<bar_code_2 />" ] ; then 
    # Must be a computer not previously on our enforce name system.
    # Set the empty JSS barcode_2 value to the JSS computer name and update var.
#   echo The jssBarcode2 is default or empty.
#   echo Must be a computer not previously on the enforce names system.
#   echo Putting it on now. Setting jssBarcode2 value to jssName of $jssName
        curl -sS -k -i -u $apiUsername:$apiPassword -X PUT -H "Content-Type: text/xml" -d "<?xml version="1.0" encoding="ISO-8859-1"?><computer><general><bar_code_2>$jssName</bar_code_2></general></computer>" $jssBase/JSSResource/computers/id/$jssMobileID
        jssBarcode2=`curl -sS -kfu $apiUsername:$apiPassword $jssBase/JSSResource/computers/id/$jssMobileID | xpath //computer/general/bar_code_2[1] | sed -e 's/<bar_code_2>//g; s/</bar_code_2>//g'`
#       echo Updated jssBarcode2 to $jssBarcode2
    fi
#echo and now jssBarcode2 of $jssBarcode2


sysComputerName=`scutil --get ComputerName`
sysLocalHostName=`scutil --get LocalHostName`
##sysHostName=`scutil --get HostName`
#echo We have system ComputerName of $sysComputerName
#echo We have LocalHostName of$sysLocalHostName
##echo and system HostName of $sysHostName

if [ "$jssName" != "$jssBarcode2" ] || [ "$sysComputerName" != "$jssBarcode2" ] || [ "$sysLocalHostName" != "$jssBarcode2" ] ; then

#   echo Humm, jssName of $jssName is different from jssBarcode2 of $jssBarcode2
#   echo That should not be in our system. So enforcing computer name to be as jssBarcode2.
#   echo Changing computer name from $jssName to $jssBarcode2

#   If JSS name different from barcode2 then it has been improperly changed, so lets enforce the original name.
    /usr/sbin/jamf setComputerName -name $jssBarcode2
#   echo Since we made changes, let us update the inventory.
    /usr/sbin/jamf recon
fi  

else
    echo "Unable to locate a computer with this serial number, name or MAC, aborting...."
fi
#echo Nothing more to do. Bye Bye.