Logging & Retrieving Imaging Configurations with Jamf

While there have been many write-ups and presentations on the impending doom of imaging, it’s not quite dead yet …

NotDeadYet

If you’re still an imaging shop, you may know that of all the wonderful things that jamf logs during the imaging process, the actual imaging configuration that is used during imaging is not one of them.

This has been a feature request on Jamfnation since 2012.  Though as of this writing the feature is listed as “Planned”, there’s no reason you couldn’t implement a workflow that accomplishes this right now.

There are two methods:

  1. A script that runs in an imaging configuration workflow
  2. A policy that runs following imaging & enrollment

There are likely other workflows already out there that accomplish this in a similar way, but this is the process I’ve come up with for my environment.  At the very least, I hope this gives you something to work from in developing your own solution.

I also have a couple optional additions that I include at the end as part of this larger solution, which you can choose to incorporate if you wish.  This includes:

  • A modified script that runs during an imaging workflow that includes writing additional User & Location data to a local PLIST for inventory collection.
  • An inventory collection policy that runs a custom command in order to automate the collection of User & Location data (user, department, building, and room) in the JSS.
  • A couple of scripts to calculate the total time (hours:minutes:seconds) it takes to image a machine.

Click below for more details.

Solution Overview

We need a couple things to be able to collect the imaging configuration used on machines in the JSS.

  1. A script that writes the imaging configuration to the jamf.log, as this log does not get deleted even if you choose the “Erase target drive” option in Casper Imaging
  2. An extension attribute which reads the applicable info from the jamf.log

Option 1: Logging Imaging Configurations with a Script During Imaging

This is what I currently have implemented in our environment.

The challenge with this option is needing to have a separate script for each of your imaging configurations.  If you’re like us and have many different smart configurations, it can pose a challenge initially trying to ensure the right script gets added to the right configuration.

Screen Shot 2017-05-11 at 5.34.36 PM.png

Be mindful of the fact that adding scripts to parent configurations could result in two different scripts running, since child configurations do everything their parents do.  In the grand scheme of things this shouldn’t matter as the extension attribute (later) used to collect the imaging configuration name will only read the last (most recent) configuration in the jamf.log.

Below is a template ImagingConfig script.  This script is also available on my Github.

#!/bin/bash

#########################################
# Created by AP Orlebeke - 01/27/17
# 
# Script designed to be used in Casper Imaging workflow, or as a part of a separate policy
# depending on how you've configured your environment.
#
# As of this writing, there is no way to automatically gather the selected imaging
# configuration used on a machine. The result is this script, which must be assigned
# to each individual imaging configuration (or policy). 
#
# Writes the desired imaging configuration name to /var/log/jamf.log
# along with a date & time stamp. Subsequent imaging will append (not replace) this file.
#
# NOTE: Because of how scripts are treated in Casper Imaging vs. in policies, there are
# two different potential log paths. For use with Casper Imaging, you need to explicitly
# include the $1 variable to point to the internal hard drive volume.
#
# (Optional) Use with a JSS extension attribute and tail the last line of the file
# to read it. You can find this EA here: 
# https://github.com/apizz/JSS_Extension_Attributes/blob/master/JSS_Imaging_Configuration_EA/JSS_Imaging_Config_EA.sh
#########################################

# Imaging Configuration Name
IMG_CFG="ENTER IMAGING CONFIGURATION NAME HERE"

# Date & Time Stamp
DATE=$(date "+%Y-%m-%d %H:%M:%S")

# Path to jamf.log for imaging workflow
LOG="$1/var/log/jamf.log"
# Path to Log File for use with a policy
# LOG="/var/log/jamf.log"

# Appends Date & Time Stamp along with config name to log file
/bin/echo "$DATE IMAGINGCONFIG $IMG_CFG" >> "$LOG"

exit

Using the Script

IMG_CFG  –  enter the name of your imaging configuration here.

Because this script is being run during imaging, the mount point of the internal volume ($1) has to be explicitly included in the LOG path.

To have this script run before everything else during imaging, set the script priority to Before.  To have it run at the end of Casper Imaging choose After.  If you want it to run at the very very end, after locally copied packages are installed, choose At Reboot.

Personally, I set the script priority to After.

Option 2: Logging Imaging Configurations with a Post-Imaging Policy

If you opt to run this script in a policy, you’ll want to comment out the current LOG variable and remove the comment in the second LOG variable.

The only difference is the $1 from the LOG and PLIST variable paths.

Imaging Configuration Name Extension Attribute

This is how you collect the imaging configuration name the script writes to the jamf.log.  I’ve also made this EA available on my Github.

The EA reads the log file and collects the last entry in the log that contains IMAGINGCONFIG.  If you care about more than just the most recent imaging configuration, simply remove the tail portion of the command in the RESULT variable.

#!/bin/bash

########################
# Created by AP Orlebeke - 1/27/17
#
# For use with my JSS_Imaging_Config_Log bash script for writing the imaging configuration used to image
# a machine. This bash script can be found here:
# https://github.com/apizz/Mac_Scripts/blob/master/JSS_Imaging_Configuration_Log/JSS_Imaging_Config_Log.sh
########################

LOG="/var/log/jamf.log"
RESULT=$(/bin/cat "$LOG" | /usr/bin/grep IMAGINGCONFIG | /usr/bin/tail -1 | /usr/bin/sed -n -e 's/^.*IMAGINGCONFIG //p')

if [ "$RESULT" = "" ]; then
 RESULT="No Imaging Config Log Exists"
fi

/bin/echo "<result>$RESULT</result>"

exit

Imaging Configuration Date Extension Attribute

This EA will look in the jamf.log just like the previous EA, but instead pull out the date and time, rather than the name of the configuration used.

Since I also write this info to a local PLIST on the machine, if for whatever reason this info isn’t in the jamf.log I call the imagingdate key from the PLIST.

#!/bin/bash

# Looks to /var/jamf.log first for imaging info, but if it doesn't exist
# looks to the local PLIST for this info.

LOG="/var/log/jamf.log"
PLIST="/Library/Receipts/TMSJSSData.plist"
RESULT=$(/bin/cat "$LOG" | /usr/bin/grep IMAGINGCONFIG | /usr/bin/tail -1 | /usr/bin/awk '{print $1,$2}')

if [ "$RESULT" = "" ]; then
    if [ -f "$PLIST" ]; then
        RESULT=$(/usr/bin/defaults read "$PLIST" imagingdate)
        
        if [ $? != 0 ]; then
            RESULT="No Last Imaging Date Exists"
        fi
    fi
fi

/bin/echo "<result>$RESULT</result>"

exit

Optional Additions to the Process

At the time I developed this solution, I was also trying to find a way to automatically collect User & Location data as part of inventory collection.

From the 2015 JNUC Solving Real Needs with the Command Line I had learned about the additional command flags that are available as part of the jamf recon command and how the -endUsername flag could be used to automatically assign the last logged in user to a machine.  Since we give all of our faculty a laptop, utilizing this greatly simplified the process of assigning users to their respective computer.  But I wanted to go further.

We have many imaging configurations centered around departments, as well as laptop carts and labs that are in specific buildings and rooms.  So I also incorporated additional variables in my script which are then written to a PLIST on the local machine.  These include:

  • DEPT  –  optionally enter the name of the department to whom the machine belongs.  To have the JSS grab this as part of inventory collection it must match the exact name of a department you have in your JSS.
  • BUILDING – optionally enter the name of the building where the machine is located.  To have the JSS grab this as part of inventory collection it must match the exact name of a building you have in your JSS.
  • ROOM – optionally enter a room to be grabbed as part of inventory collection.  Since this is just a textfield and not a dropdown menu like the department and building fields, this can be whatever you want.
#!/bin/bash

# Name of imaging configuration
IMG_CFG=""
# DEPARTMENT name must match name in JSS
DEPT=""
# BUILDING name must match name in JSS
BUILDING=""
ROOM=""
DATE=$(date "+%Y-%m-%d %H:%M:%S")
LOG="$1/var/log/jamf.log"
PLIST="$1/Library/Receipts/JSSData.plist"

# Write imagingconfig to LOG
/bin/echo "$DATE IMAGINGCONFIG $IMG_CFG" >> "$LOG"

# Write imagingconfig to PLIST
/usr/bin/defaults write "$PLIST" imagingconfig "$IMG_CFG"

# Write imagingdate to PLIST
/usr/bin/defaults write "$PLIST" imagingdate "$DATE"

# Write department to PLIST
/usr/bin/defaults write "$PLIST" department "$DEPT"

# Write building to PLIST
/usr/bin/defaults write "$PLIST" building "$BUILDING"

# Write room to PLIST
/usr/bin/defaults write "$PLIST" room "$ROOM"

exit

Custom Inventory Collection Policy

If you decide to use the above script, how do you then collect this additional data as part of JSS inventory collection?

You’ll need to utilize the Files & Processes payload in jamf policies, specifically the Execute Command field to specify the additional recon flags.  Using the “Update Inventory” checkbox will not be enough here.

Screenshot 2017-05-11 19.58.54.png

The full Execute Command is below.

sudo jamf recon -endUsername "$(defaults read /Library/Preferences/com.apple.loginwindow lastUserName)"\ -department "$(defaults read /Library/Receipts/JSSData.plist department)" -building "$(defaults read /Library/Receipts/JSSData.plist building)" -room "$(defaults read /Library/Receipts/JSSData.plist room)"

The good news is that if no data is entered for any of the DEPT, BUILDING, or ROOM variables, running this command manually or via policy doesn’t overwrite what already may be entered for the machine in the JSS!

Calculate Total Imaging Time

All you need here are two scripts – one that runs at the very beginning of imaging and a second that runs at the very end – which write the date and time to the jamf.log.  With this information an EA can calculate the difference between the two times.

Script #1 – Write Imaging Start Time

The first script that writes the start time to the jamf.log has its priority set to Before.  This way it runs before anything is installed, even if you choose to erase the target drive.

#!/bin/bash

DATE=$(date "+%Y-%m-%d %H:%M:%S")
LOG="$1/var/log/jamf.log"

# Write imagingstart date and time to LOG
/bin/echo "$DATE IMAGINGSTART" >> "$LOG"

exit

Script #2 – Write Imaging End Time

The second script that writes the end time to the jamf.log has its priority set to At Reboot.  This way the script runs at the very very end.

#!/bin/bash

DATE=$(date "+%Y-%m-%d %H:%M:%S")
LOG="$1/var/log/jamf.log"
PLIST="$1/Library/Receipts/JSSData.plist"
START=$(cat "$LOG" | grep IMAGINGSTART | tail -1 | awk '{print $1,$2}')

# Write imagingend to LOG
/bin/echo "$DATE IMAGINGEND" >> "$LOG"

# Write imaging start time to PLIST
/usr/bin/defaults write "$PLIST" imagingstart "$START"

# Write imaging end time to PLIST
/usr/bin/defaults write "$PLIST" imagingend "$DATE"

exit

EA – Calculate Imaging Duration

Lastly, an extension attribute reads the imagingstart and imagingend keys from the PLIST on the local machine and calculates the difference in time.  If the PLIST doesn’t exist, it instead reads from the jamf.log.

#!/bin/bash

LOG="/var/log/jamf.log"
PLIST="/Library/Receipts/JSSData.plist"

if [ -f "$PLIST" ]; then
    START=$(defaults read "$PLIST" imagingstart)
    END=$(defaults read "$PLIST" imagingend)
else
    START=$(cat "$LOG" | grep IMAGINGSTART | tail -1 | awk '{print $1,$2}')
    END=$(cat "$LOG" | grep IMAGINGEND | tail -1 | awk '{print $1,$2}')
fi

if [ "$START" = "" ] || [ "$END" = "" ]; then
    TIME="Insufficient Time Data"
else
    START_SEC=$(date -j -f '%Y-%m-%d %H:%M:%S' "$START" '+%s')
    END_SEC=$(date -j -f '%Y-%m-%d %H:%M:%S' "$END" '+%s')
    TIMECALC=$(expr $END_SEC - $START_SEC)
    TIME=$(date -u -r "$TIMECALC" '+%T')
fi

# Time in HH:MM:SS
/bin/echo "<result>$TIME</result>"

exit
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s