Posted on 09-27-2018 07:29 AM
Similar questions have been asked, but I can't seem to find an answer.
We run a few different management systems, some of which can perform duplicate functions, but usually don't. In this case, we want one of those systems (BigFix) to verify a system is enrolled in Jamf, and vice-versa. We have the Jamf-verifying-BigFix-registration done, but still need to get BigFix to enroll a system in Jamf if it isn't already.
BigFix can install packages and run scripts, but it runs in the background with no user interaction. Since this is going to be running frequently on systems that are remote, going to each one (or connecting to each one) isn't feasible.
What I'm looking for is a script that will check for the existence of the Jamf files (this part's pretty easy), download the QuickAdd.pkg from our Jamf server, and install it (this part's also pretty easy). It can't ask for a username or password though, or ask for any unique information (like an enrollment ID), unless it retrieves one dynamically.
Posted on 09-27-2018 07:35 AM
@omatsei maybe some of the work @rtrouton did for Caspercheck might help you?
Posted on 09-27-2018 07:46 AM
Ah, that was the part I was missing... the QuickAdd created from Recon can enroll unlimited machines. I can just push that out in BigFix then. Thanks!
Posted on 09-27-2018 08:37 AM
Any chance you can share the other side of this, @omatsei? I'd love a better way of making sure our Macs have BigFix installed - been a pain keeping up with it here.
Posted on 09-27-2018 08:50 AM
Absolutely. I'm testing it now... I'll post the whole workflow as soon as I'm reasonably sure it works.
Posted on 09-27-2018 09:12 AM
Here's what I've got...
Step 1. Create a QuickAdd.pkg using Recon. I entered my username and password, and accepted the defaults.
Step 2. Compress the QuickAdd.pkg to become QuickAdd.pkg.zip.
Step 3. Put the QuickAdd.pkg.zip onto a file server. The Jamf Suite is installed on a Windows server with IIS. Our server runs exclusively on HTTPS (I can't remember if Jamf even works on HTTP), so HTTP was open for use. The only thing existing on it was an index.html file that redirected incoming traffic to https://casperserver.url:8443. So I just put it at the base of the HTTP webroot, so the URL to the file is http://casperserver.url/QuickAdd.pkg.zip.
Step 4. Create a new fixlet in BigFix.
Step 5. Add the following lines at the beginning, so it creates the script as a file on the client system:
// Delete file, in case it exists delete createfile createfile until __EOF
Step 6. Paste the newest version of the Casper Check script and edit it, changing the Casper URL and the QuickAdd URL. Also since the single open bracket is a protected character in BigFix, change ALL the { to {{ (single open bracket to double open bracket). (I also had to change the quotation marks around the server name, since they were a different font.)
Step 7. Add the following lines at the end of the fixlet:
EOF wait chmod 555 "{(client folder of current site as string) & "/__createfile"}" delete "/tmp/caspercheck.sh" move "{(client folder of current site as string) & "/__createfile"}" "/tmp/caspercheck.sh" run /bin/sh -c "trap '' 15; /tmp/caspercheck.sh 2>&1 > /var/log/caspercheck.log"
Step 8. "Take action" in BigFix, pointed to the system(s) that you'd like to enroll.
Please note that I've tested this on one system, for less than an hour... so it's definitely not what I'd call production-ready, but it worked on my test system.
Posted on 09-27-2018 09:14 AM
Thanks - if you get a chance, what I'm looking for is the opposite - using Casper to make sure BigFix is installed! ;). We've got a few machines that just seem to drop back out of BigFix regularly, would love to clean that up and be able to do some basic reporting on it from Casper.
Posted on 09-27-2018 09:18 AM
Just in case you want the full fixlet contents, here it is:
// Delete file, in case it exists
delete __createfile
createfile until __EOF__
#!/bin/bash
#
# User-editable variables
#
# For the fileURL variable, put the complete address
# of the zipped Casper QuickAdd installer package
fileURL="http://url.to.quickadd.package/QuickAdd.pkg.zip"
# For the jss_server_address variable, put the complete
# fully qualified domain name address of your Casper server
jss_server_address="casperserver.whatever.com"
# For the jss_server_address variable, put the port number
# of your Casper server. This is usually 8443; change as
# appropriate.
jss_server_port="8443"
# For the log_location variable, put the preferred
# location of the log file for this script. If you
# don't have a preference, using the default setting
# should be fine.
log_location="/var/log/caspercheck.log"
#
# The variables below this line should not need to be edited.
# Use caution if doing so.
#
quickadd_dir="/var/root/quickadd"
quickadd_zip="$quickadd_dir/quickadd.zip"
quickadd_installer="$quickadd_dir/casper.pkg"
quickadd_timestamp="$quickadd_dir/quickadd_timestamp"
#
# Begin function section
# =======================
#
# Function to provide custom curl options
myCurl () {{ /usr/bin/curl -k -L --retry 3 --silent --show-error "$@"; }
# Function to provide logging of the script's actions to
# the log file defined by the log_location variable
ScriptLogging(){{
DATE=`date +%Y-%m-%d %H:%M:%S`
LOG="$log_location"
echo "$DATE" " $1" >> $LOG
}
CheckForNetwork(){{
# Determine if the network is up by looking for any non-loopback network interfaces.
local test
if [[ -z "${{NETWORKUP:=}" ]]; then
test=$(ifconfig -a inet 2>/dev/null | sed -n -e '/127.0.0.1/d' -e '/0.0.0.0/d' -e '/inet/p' | wc -l)
if [[ "${{test}" -gt 0 ]]; then
NETWORKUP="-YES-"
else
NETWORKUP="-NO-"
fi
fi
}
CheckSiteNetwork (){
# CheckSiteNetwork function adapted from Facebook's check_corp function script.
# check_corp script available on Facebook's IT-CPE Github repo:
#
# check_corp:
# This script verifies a system is on the corporate network.
# Input: CORP_URL= set this to a hostname on your corp network
# Optional ($1) contains a parameter that is used for testing.
# Output: Returns a check_corp variable that will return "True" if on
# corp network, "False" otherwise.
# If a parameter is passed ($1), the check_corp variable will return it
# This is useful for testing scripts where you want to force check_corp
# to be either "True" or "False"
# USAGE:
# check_corp # No parameter passed
# check_corp "True" # Parameter of "True" is passed and returned
site_network="False"
ping=`host -W .5 $jss_server_address`
# If the ping fails - site_network="False"
[[ $? -eq 0 ]] && site_network="True"
# Check if we are using a test
[[ -n "$1" ]] && site_network="$1"
}
#
# The update_quickadd function checks the timestamp of the fileURL variable and compares it against a locally
# cached timestamp. If the hosted file's timestamp is newer, then the Casper
# QuickAdd installer gets downloaded and extracted into the target directory.
#
# This function uses the myCurl function defined at the top of the script.
#
update_quickadd () {{
# Create the destination directory if needed
if [[ ! -d "$quickadd_dir" ]]; then
mkdir "$quickadd_dir"
fi
# If needed, remove existing files from the destination directory
if [[ -d "$quickadd_dir" ]]; then
/bin/rm -rf "$quickadd_dir"/*
fi
# Get modification date of fileURL
modDate=$(myCurl --head $fileURL 2>/dev/null | awk -F': ' '/Last-Modified/{{print $2}')
# Downloading Casper agent installer
ScriptLogging "Downloading Casper agent installer from server."
myCurl --output "$quickadd_zip" $fileURL
# Check to make sure download occurred
if [[ ! -f "$quickadd_zip" ]]; then
ScriptLogging "$quickadd_zip not found. Exiting CasperCheck."
ScriptLogging "======== CasperCheck Finished ========"
exit 0
fi
# Verify that the downloaded zip file is a valid zip archive.
zipfile_chk=`/usr/bin/unzip -tq $quickadd_zip > /dev/null; echo $?`
if [ "$zipfile_chk" -eq 0 ]; then
ScriptLogging "Downloaded zip file appears to be a valid zip archive. Proceeding."
else
ScriptLogging "Downloaded zip file appears to be corrupted. Exiting CasperCheck."
ScriptLogging "======== CasperCheck Finished ========"
rm "$quickadd_zip"
exit 0
fi
# Unzip the Casper agent install into the destination directory
# and remove the __MACOSX directory, which is created as part of
# the uncompression process from the destination directory.
/usr/bin/unzip "$quickadd_zip" -d "$quickadd_dir";/bin/rm -rf "$quickadd_dir"/__MACOSX
# Rename newly-downloaded installer to be casper.pkg
mv "$(/usr/bin/find $quickadd_dir -maxdepth 1 ( -iname *.pkg -o -iname *.mpkg ))" "$quickadd_installer"
# Remove downloaded zip file
if [[ -f "$quickadd_zip" ]]; then
/bin/rm -rf "$quickadd_zip"
fi
# Add the quickadd_timestamp file to the destination directory.
# This file is used to help verify if the current Casper agent
# installer is already cached on the machine.
if [[ ! -f "$quickadd_timestamp" ]]; then
echo $modDate > "$quickadd_timestamp"
fi
}
CheckTomcat (){{
# Verifies that the JSS's Tomcat service is responding via its assigned port.
tomcat_chk=`nc -z -w 5 $jss_server_address $jss_server_port > /dev/null; echo $?`
if [ "$tomcat_chk" -eq 0 ]; then
ScriptLogging "Machine can connect to $jss_server_address over port $jss_server_port. Proceeding."
else
ScriptLogging "Machine cannot connect to $jss_server_address over port $jss_server_port. Exiting CasperCheck."
ScriptLogging "======== CasperCheck Finished ========"
exit 0
fi
}
CheckInstaller (){{
# Compare timestamps and update the Casper agent
# installer if needed.
modDate=$(myCurl --head $fileURL 2>/dev/null | awk -F': ' '/Last-Modified/{{print $2}')
if [[ -f "$quickadd_timestamp" ]]; then
cachedDate=$(cat "$quickadd_timestamp")
if [[ "$cachedDate" == "$modDate" ]]; then
ScriptLogging "Current Casper installer already cached."
else
update_quickadd
fi
else
update_quickadd
fi
}
CheckBinary (){{
# Identify location of jamf binary.
#
# If the jamf binary is not found, this check will return a
# null value. This null value is used by the CheckCasper
# function, in the "Checking for the jamf binary" section
# of the function.
jamf_binary=`/usr/bin/which jamf`
if [[ "$jamf_binary" == "" ]] && [[ -e "/usr/sbin/jamf" ]] && [[ ! -e "/usr/local/bin/jamf" ]]; then
jamf_binary="/usr/sbin/jamf"
elif [[ "$jamf_binary" == "" ]] && [[ ! -e "/usr/sbin/jamf" ]] && [[ -e "/usr/local/bin/jamf" ]]; then
jamf_binary="/usr/local/bin/jamf"
elif [[ "$jamf_binary" == "" ]] && [[ -e "/usr/sbin/jamf" ]] && [[ -e "/usr/local/bin/jamf" ]]; then
jamf_binary="/usr/local/bin/jamf"
fi
}
InstallCasper () {{
# Check for the cached Casper QuickAdd installer and run it
# to fix problems with Casper being able to communicate with
# the Casper server
if [[ ! -e "$quickadd_installer" ]] ; then
ScriptLogging "Casper installer is missing. Downloading."
/bin/rm -rf "$quickadd_timestamp"
update_quickadd
fi
if [[ -e "$quickadd_installer" ]] ; then
ScriptLogging "Casper installer is present. Installing."
/usr/sbin/installer -dumplog -verbose -pkg "$quickadd_installer" -target /
ScriptLogging "Casper agent has been installed."
fi
}
CheckCasper () {{
# CheckCasper function adapted from Facebook's jamf_verify.sh script.
# jamf_verify script available on Facebook's IT-CPE Github repo:
# Link: https://github.com/facebook/IT-CPE
# Checking for the jamf binary
CheckBinary
if [[ "$jamf_binary" == "" ]]; then
ScriptLogging "Casper's jamf binary is missing. It needs to be reinstalled."
InstallCasper
CheckBinary
fi
# Verifying Permissions
/usr/bin/chflags noschg $jamf_binary
/usr/bin/chflags nouchg $jamf_binary
/usr/sbin/chown root:wheel $jamf_binary
/bin/chmod 755 $jamf_binary
# Verifies that the JSS is responding to a communication query
# by the Casper agent. If the communication check returns a result
# of anything greater than zero, the communication check has failed.
# If the communication check fails, reinstall the Casper agent using
# the cached installer.
jss_comm_chk=`$jamf_binary checkJSSConnection > /dev/null; echo $?`
if [[ "$jss_comm_chk" -eq 0 ]]; then
ScriptLogging "Machine can connect to the JSS on $jss_server_address."
elif [[ "$jss_comm_chk" -gt 0 ]]; then
ScriptLogging "Machine cannot connect to the JSS on $jss_server_address."
ScriptLogging "Reinstalling Casper agent to fix problem of Casper not being able to communicate with the JSS."
InstallCasper
CheckBinary
fi
# Checking if machine can run a manual trigger
# This section will need to be edited if the policy
# being triggered has different options than the policy
# described below:
#
# Trigger: iscasperup
# Plan: Run Script iscasperonline.sh
#
# The iscasperonline.sh script contains the following:
#
# | #!/bin/sh
# |
# | echo "up"
# |
# | exit 0
#
jamf_policy_chk=`$jamf_binary policy -trigger iscasperup | grep "Script result: up"`
# If the machine can run the specified policy, exit the script.
if [[ -n "$jamf_policy_chk" ]]; then
ScriptLogging "Casper enabled and able to run policies"
# If the machine cannot run the specified policy,
# reinstall the Casper agent using the cached installer.
elif [[ ! -n "$jamf_policy_chk" ]]; then
ScriptLogging "Reinstalling Casper agent to fix problem of Casper not being able to run policies"
InstallCasper
CheckBinary
fi
}
#
# End function section
# ====================
#
# The functions and variables defined above are used
# by the section below to check if the network connection
# is live, if the machine is on a network where
# the Casper JSS is accessible, and if the Casper agent on the
# machine can contact the JSS and run a policy.
#
# If the Casper agent on the machine cannot run a policy, the appropriate
# functions run and repair the Casper agent on the machine.
#
ScriptLogging "======== Starting CasperCheck ========"
# Wait up to 60 minutes for a network connection to become
# available which doesn't use a loopback address. This
# condition which may occur if this script is run by a
# LaunchDaemon at boot time.
#
# The network connection check will occur every 5 seconds
# until the 60 minute limit is reached.
ScriptLogging "Checking for active network connection."
CheckForNetwork
i=1
while [[ "${{NETWORKUP}" != "-YES-" ]] && [[ $i -ne 720 ]]
do
sleep 5
NETWORKUP=
CheckForNetwork
echo $i
i=$(( $i + 1 ))
done
# If no network connection is found within 60 minutes,
# the script will exit.
if [[ "${{NETWORKUP}" != "-YES-" ]]; then
ScriptLogging "Network connection appears to be offline. Exiting CasperCheck."
fi
if [[ "${{NETWORKUP}" == "-YES-" ]]; then
ScriptLogging "Network connection appears to be live."
# Sleeping for 120 seconds to give WiFi time to come online.
ScriptLogging "Pausing for two minutes to give WiFi and DNS time to come online."
sleep 120
CheckSiteNetwork
if [[ "$site_network" == "False" ]]; then
ScriptLogging "Unable to verify access to site network. Exiting CasperCheck."
fi
if [[ "$site_network" == "True" ]]; then
ScriptLogging "Access to site network verified"
CheckTomcat
CheckInstaller
CheckCasper
fi
fi
ScriptLogging "======== CasperCheck Finished ========"
exit 0
__EOF__
wait chmod 555 "{(client folder of current site as string) & "/__createfile"}"
delete "/tmp/caspercheck.sh"
move "{(client folder of current site as string) & "/__createfile"}" "/tmp/caspercheck.sh"
run /bin/sh -c "trap '' 15; /tmp/caspercheck.sh 2>&1 > /var/log/caspercheck.log"
Posted on 09-27-2018 09:42 AM
Ah... I have an Extension Attribute called "BES Agent Existence" that checks whether /Library/BESAgent exists or not.
Extension Attribute (String data, from a script):
#!/bin/sh
if [ -e /Library/BESAgent ]
then
echo "<result>True</result>"
else
echo "<result>False</result>"
fi
I then have a Smart Group with membership criteria as "BES Agent Existence" is "False".
Then I have a policy that runs at check-in time that installs the BES agent. This won't help if the agent is already installed and they just disappear from BigFix though...
Posted on 09-27-2018 09:55 AM
Thanks. Yeah... was hoping you'd discovered some additional logic. We have about a dozen different departments, which all need their own BigFix installer (different config files for sorting on the server) so it just gets to be a pain. Slowly making progress... but I still haven't put a basic "if not present, re-install" policy, so I should at the least follow your example there.
Posted on 10-01-2018 02:28 AM
Assuming the JAMF QuickAdd.pkg can be run as root to do the install, then something like this should be sufficient for the BigFix actionscript:
prefetch QuickAdd.pkg.zip sha1:??????????? size:0000000 http://url.to.quickadd.package/QuickAdd.pkg.zip sha256:??????
delete /tmp/QuickAdd.pkg.zip
copy __Download/QuickAdd.pkg.zip /tmp/QuickAdd.pkg.zip
wait /usr/bin/unzip -qn /tmp/QuickAdd.pkg.zip -d /tmp
wait /usr/sbin/installer -pkg "/tmp/QuickAdd.pkg" -target /
You just need to set the SHA1, Size, SHA256, and URL to the correct values.
The other part would be a relevance statement to detect when JAMF is not installed and use that as the relevance for the fixlet / task deployed through BigFix, which would then install JAMF if and only if it is not already installed.
Based upon the above, I think this BigFix applicability relevance would work:
not exists (files "/usr/sbin/jamf"; files "/usr/local/bin/jamf")
This would be TRUE and run the fixlet / task if both files were missing, it would be FALSE and not run if either file was present. (you could be more sophisticated than that if you wanted)