Background
While we're waiting for @NightFlight's Extention Attribute Execution Frequency feature request to be implimented, here's my two cents, which was inspired by @brad's approach for only occasionally capturing the status of a computer's Recovery HD.
Approach
As one of the first steps of an Extension Attribute script, you pass the name of the Extension Attribute and the desired execution frequency (in days) to a client-side function. A client-side plist stores the epoch and the result.
During subsequent inventory updates, if the current epoch is less than the given frequency, it just reads the previous result from the plist instead of executing the entire Extension Attribute script.
For example, I have an EA for “Model Name”; how many times do you need to run that Extension Attribute? (Once per quarter? Once per year? Certainly not every time.)
Results
Early tests show an overall inventory collection that is 1.6x faster, using the following as a gauge before and after:time -p sudo jamf recon -verbose
(Some individual EA scripts which curl external Web sites or query the Sophos AV binary have realized a 113x increase!)
Scripts
Client-side Functions
You'll need to install the following functions client-side (i.e., when enrollment in complete) and update the path for "organizationalPlist".
#!/bin/sh
####################################################################################################
#
# ABOUT
#
# Standard functions which are imported into other scripts
#
####################################################################################################
#
# HISTORY
#
# Version 1.2, 26-Apr-2017, Dan K. Snelson
# Added Extension Attribute Execution Frequency & Results
#
####################################################################################################
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# LOGGING
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
## Variables
logFile="/var/log/com.company.log"
alias now="/bin/date '+%Y-%m-%d %H:%M:%S'"
## Check for / create logFile
if
! -f "${logFile}" ]; then
# logFile not found; Create logFile ...
/usr/bin/touch "${logFile}"
/bin/echo "`/bin/date +%Y-%m-%d %H:%M:%S` *** Created log file via function ***" >>"${logFile}"
fi
## I/O Redirection to client-side log file
exec 3>&1 4>&2 # Save standard output (stdout) and standard error (stderr) to new file descriptors
exec 1>>"${logFile}" # Redirect standard output, stdout, to logFile
exec 2>>"${logFile}" # Redirect standard error, stderr, to logFile
function ScriptLog() { # Write to client-side log file ...
/bin/echo "`/bin/date +%Y-%m-%d %H:%M:%S` ${1}"
}
function jssLog() { # Write to JSS ...
ScriptLog "${1}" # Write to the client-side log ...
## I/O Redirection to JSS
exec 1>&3 3>&- 2>&4 4>&- # Restore standard output (stdout) and standard error (stderr)
/bin/echo "${1}" # Record output in the JSS
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# JAMF Display Message
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function jamfDisplayMessage() {
ScriptLog "${1}"
/usr/local/jamf/bin/jamf displayMessage -message "${1}" &
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# Extension Attribute Execution Frequency
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function eaFrequency() {
# Validate parameters
if -z "$1" ] || -z "$2" ] ; then
ScriptLog "Error calling "eaFrequency" function: One or more parameters are blank; exiting."
exit 1
fi
# Variables
organizationalPlist="/client-side/path/to/com.company.plist"
plistKey="$1" # Supplied plistKey
frequency="$2" # Supplied frequency in days
frequencyInSeconds=$((frequency * 86400)) # There are 86,400 seconds in 1 day
# Check for / create plist ...
if ! -f "${organizationalPlist}" ]; then
ScriptLog "The plist, "${organizationalPlist}", does NOT exist; create it ..."
/usr/bin/touch "${organizationalPlist}"
/usr/sbin/chown root:wheel "${organizationalPlist}"
/bin/chmod 0600 "${organizationalPlist}"
fi
# Query for the given plistKey; suppress any error message, if key not found.
plistKeyTest=$( /usr/libexec/PlistBuddy -c 'print "'"${plistKey} Epoch"'"' ${organizationalPlist} 2>/dev/null )
# Capture the exit code, which indicates success v. failure
exitCode=$?
if "${exitCode}" != 0 ]; then
ScriptLog "The key, "${plistKey} Epoch", does NOT exist; create it with a value of zero ..."
/usr/bin/defaults write "${organizationalPlist}" "${plistKey} Epoch" "0"
fi
# Read the last execution time ...
lastExecutionTime=$( /usr/bin/defaults read "${organizationalPlist}" "${plistKey} Epoch" )
# Calculate the elapsed time since last execution ...
elapsedTimeSinceLastExecution=$(( $(date +%s) - ${lastExecutionTime} ))
# If the elapsed time is less than the frequency, read the previous result ...
if "${elapsedTimeSinceLastExecution}" -lt "${frequencyInSeconds}" ]; then
ScriptLog "Elapsed time since last execution for "$plistKey", $elapsedTimeSinceLastExecution, is less than $frequencyInSeconds; read previous result."
eaExecution="No"
eaResult "${plistKey}" # Obtain the current result
else
# If the elapsed time is less than the frequency, read the previous result ...
ScriptLog "Elapsed time since last execution for "$plistKey", $elapsedTimeSinceLastExecution, is greater than $frequencyInSeconds; execute the Extension Attribute."
/usr/bin/defaults write "${organizationalPlist}" "${plistKey} Epoch" "`/bin/date +%s`"
eaExecution="Yes"
fi
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# Extension Attribute Result
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function eaResult() {
# Validate parameters
if -z "$1" ] ; then
ScriptLog "Error calling "eaResult" function: Parameter 1 is blank; exiting."
exit 1
fi
# Variables
organizationalPlist="/client-side/path/to/com.company.plist"
plistKey="$1"
result="$2"
if -z "$2" ] ; then
# If the function is called with a single parameter, then just read the previously recorded result
returnedResult=$( /usr/bin/defaults read "${organizationalPlist}" "${plistKey} Result" )
else
# If the function is called with two parameters, then write / read the new result
/usr/bin/defaults write "${organizationalPlist}" "${plistKey} Result" ""${result}""
returnedResult=$( /usr/bin/defaults read "${organizationalPlist}" "${plistKey} Result" )
fi
}
Extension Attribute Modifications
Make the following modifications to your most time-consuming EA scripts, which output their results to a variable called results
. (Also, update the source
path for your environment.)
We just looked for time-consuming EAs while running the following:time -p sudo jamf recon -verbose
#!/bin/sh
####################################################################################################
# Import client-side functions
source /client-side/path/to/functions.sh
####################################################################################################
# Variables
eaName="Friendly name for Extension Attribute" # Name of Extension Attribute
eaDays="30" # Number of days between executions
# Check for Extension Attribute execution
eaFrequency "${eaName}" "${eaDays}"
if
${eaExecution} == "Yes" ]; then
#
#
#
# Insert your Current Extension Attribute script here.
#
#
#
eaResult "${eaName}" "${result}"
else
eaResult "${eaName}"
fi
jssLog "<result>${returnedResult}</result>"
exit 0