Scripts for use with Custom Trigger Policies

jmjenki3
New Contributor II

I wanted to share some of my scripts I use in policies with Custom Triggers. My environment requires rather extensive and fine grain control of deployments, often down to the order of installs, which has required me to get a little creative.

My goal has been to have "one enrollment policy to rule them all" regardless of device type. Here is the walkthrough of the scripts I used to build my environment. I hope they are of use to someone.

First, I started with a list of all the applications I need. Once I had that, I created installation policies for each one, and created a Custom Trigger for every install, i.e. InstallChrome. To create a Custom Trigger, create a new policy, and then assign a package to install. Under the General tab, click the Custom checkbox, and assign your trigger.

Since I have several "stacks" of software to apply to different configurations, I list for all the software stacks I needed. My software stacks are determined by the device hostname prefix, so I have query the device to determine the hostname.

I then had to work out how to automatically name a device (Google is my friend). Once I had that trivial bit of information, a simple series of if/then booleans gave me most of what I need.

Since my network is built in a way that doesn't allow reverse DNS lookup for laptops of wireless, I wound up having to split my configurations into policies with the Custom Triggers of DesktopSetup and LaptopSetup.

So, my single Enrollment policy winds up running a single script to determine the hardware type:

#!/bin/sh

HWModel="$(/usr/sbin/sysctl hw.model)"

if [[ $HWModel =~ .*MacBook*. ]]
then
    /usr/local/bin/jamf policy -trigger LaptopSetup
else
    /usr/local/bin/jamf policy -trigger DesktopSetup
fi

exit 0

The LaptopSetup trigger installs a few items, and then requires a Self Service policy to configure the device, discussed later, but let's say our device is a Desktop, and fires the DesktopSetup trigger.

The script sets a lot of defaults for the environment, but the most important is setting the name of the computer to be the registered hostname by defining the terminal command hostname -s as a variable that returns just the name without the suffix (i.e. - instead of lib-1234.lib.ncsu.edu, it returns lib-1234).

#!/bin/sh
# Script to set primary configuration options for NCSU Libraries computers
# Set variables
SYSSET="/usr/sbin/systemsetup"
SYSTOOL="/usr/sbin/scutil"
DEFAULTS="/usr/bin/defaults"
JAMF="/usr/local/bin/jamf"
MACHINENAME=$(/bin/hostname -s)

# Set computer to never sleep
"$SYSSET" -setcomputersleep off
"$SYSSET" -setsleep 0

# Restart automatically if the computer freezes or power fails
"$SYSSET" -setrestartfreeze on
"$SYSSET" -setrestartpowerfailure on
"$SYSSET" -setwaitforstartupafterpowerfailure 120

##
# Set HostName, LocalHostName, and ComputerName to $MACHINENAME
##
echo "Setting computer names to $MACHINENAME"
"$SYSTOOL" --set HostName $MACHINENAME
"$SYSTOOL" --set LocalHostName $MACHINENAME
"$SYSTOOL" --set ComputerName $MACHINENAME
"$SYSSET" -setcomputername $MACHINENAME

# Stop automatic updates
echo "Disabling automatic updates."
"$DEFAULTS" write "$TVOL/Library/Preferences/com.apple.commerce" AutoUpdate -bool false
"$DEFAULTS" write "$TVOL/Library/Preferences/com.apple.commerce" AutoUpdateRestartRequired -bool false
"$DEFAULTS" write "$TVOL/Library/Preferences/com.apple.SoftwareUpdate" AutomaticCheckEnabled -bool false

#Force check-in to JSS.
echo "Updating the inventory for the JSS."
"$JAMF" recon

# Install the default desktop wallpaper.
"$JAMF" policy -trigger InstallDesktopWallpaper
exit 0

At the conclusion of the above policy, I define a terminal command to run under Files and Processes in the "EXECUTE COMMAND" field, which calls my primary configuration policy's custom trigger: jamf policy -trigger Config.

This is where my software stack custom trigger policies are called based on the hostname prefix. The config script is below.

#!/bin/sh
HNAME=$(hostname -s)
JAMF="/usr/local/bin/jamf"

# First, set the time servers.

"$JAMF" policy -trigger SetTimeServers

# Second create the user(s) for the computer.

"$JAMF" policy -trigger CreateUsers

# Next, install Java to meet requirements for all software stacks.

"$JAMF" policy -trigger InstallJava
"$JAMF" policy -trigger InstallJavaForMacOS

# Now install the software.

if [[ "$HNAME" =~ "libstaff-" ]]
    then
        "$JAMF" policy -trigger StaffConfig

    elif [[ "$HNAME" =~ "libaskus-" ]]
        then 
            "$JAMF" policy -trigger AskUsKioskConfig

    elif [[ "$HNAME" =~ "libaudio-" ]]
        then 
            "$JAMF" policy -trigger MediaAudioConfig

    elif [[ "$HNAME" =~ "libcr-" ]]
        then 
            "$JAMF" policy -trigger ConfConfig

    elif [[ "$HNAME" =~ "libdata-" ]]
        then 
            "$JAMF" policy -trigger DataspaceConfig   

    elif [[ "$HNAME" =~ "libdes-" ]]
        then 
            "$JAMF" policy -trigger CommonsConfig

    elif [[ "$HNAME" =~ "libdvil-" ]]
        then 
            "$JAMF" policy -trigger DVILConfig

    elif [[ "$HNAME" =~ "libittc-" ]]
        then 
            "$JAMF" policy -trigger ITTCConfig

    elif [[ "$HNAME" =~ "libitstu-" ]]
        then 
            "$JAMF" policy -trigger StaffConfig

    elif [[ "$HNAME" =~ "libkiosk-" ]]
        then 
            "$JAMF" policy -trigger KioskConfig

    elif [[ "$HNAME" =~ "liblc-" ]]
        then 
            "$JAMF" policy -trigger CommonsConfig

    elif [[ "$HNAME" =~ "libgfr-" ]]
        then 
            "$JAMF" policy -trigger GFRConfig

    elif [[ "$HNAME" =~ "libgc-" ]]
        then 
            "$JAMF" policy -trigger GradCommonsConfig

    elif [[ "$HNAME" =~ "libgssr-" ]]
        then 
            "$JAMF" policy -trigger GSRConfig

    elif [[ "$HNAME" =~ "libgsr-" ]]
        then 
            "$JAMF" policy -trigger GSRConfig

    elif [[ "$HNAME" =~ "libdml-" ]]
        then
            "$JAMF" policy -trigger MediaBaseConfig

    elif [[ "$HNAME" =~ "libmake-" ]]
        then
            "$JAMF" policy -trigger MakeConfig

    elif [[ "$HNAME" =~ "libppr-" ]]
        then
            "$JAMF" policy -trigger PresentConfig

    elif [[ "$HNAME" =~ "liblend-" ]]
        then
            "$JAMF" policy -trigger LendConfig

    elif [[ "$HNAME" =~ "libslnd-" ]]
        then
            "$JAMF" policy -trigger StaffLendConfig

    elif [[ "$HNAME" =~ "libtd-" ]]
        then
            "$JAMF" policy -trigger StaffConfig

    elif [[ "$HNAME" =~ "libstudio-" ]]
        then
            "$JAMF" policy -trigger MediaHighConfig

    elif [[ "$HNAME" =~ "libteach-" ]]
        then
            "$JAMF" policy -trigger TeachConfig

    elif [[ "$HNAME" =~ "libtest-" ]]
        then
            "$JAMF" policy -trigger TestConfig

    elif [[ "$HNAME" =~ "libvid-" ]]
        then
            "$JAMF" policy -trigger MediaHighConfig

    elif [[ "$HNAME" =~ "libqp-" ]]
        then
            "$JAMF" policy -trigger QuickPrintConfig

    elif [[ "$HNAME" =~ "libserv-" ]]
        then
            "$JAMF" policy -trigger ServerConfig

    else
        echo "This computer has not been registered with a proper DNS entry."             

fi

exit 0

One of the very first things I like to do is create local administrator users with a custom trigger of CreateUsers. These policies also judge the computer type and create local admin users based on the name of the computer. Then, they assign ARD permissions to those local admin users should any technician need to remote in to trouble an issue with a deployment, or monitor deployment progress by sending a UNIX command from ARD:

tail -20 /var/log/jamf.log

That command shows the last 20 events executed from the JSS (the number can be whatever is needed), and is very useful when needing a quick look at deployment progress when the boss wants to know "what's taking so long", and needs to be reminded (for the two hundredth time) that a Full Adobe Creative Cloud install is not instant.

The user creation script applied to the policy with CreateUsers is below.

#!/bin/sh
HNAME=$(hostname -s)
JAMF="/usr/local/bin/jamf"
DEFAULTS="/usr/bin/defaults"

if [[ "$HNAME" =~ "libstaff-" ]]
    then
        "$JAMF" policy -trigger CreateUserstaffadmin

    elif [[ "$HNAME" =~ "libaudio-" ]]
        then 
            "$JAMF" policy -trigger CreateUserMedia

    elif [[ "$HNAME" =~ "libdata-" ]]
        then 
            "$JAMF" policy -trigger CreateUserdataspaceadmin

    elif [[ "$HNAME" =~ "libcr-" ]]
        then 
            "$JAMF" policy -trigger CreateUserlibconf

    elif [[ "$HNAME" =~ "libdvil-" ]]
        then 
            "$JAMF" policy -trigger CreateUserTeachingCarts

    elif [[ "$HNAME" =~ "libittc-" ]]
        then 
            "$JAMF" policy -trigger CreateUserTeachingCarts

    elif [[ "$HNAME" =~ "libitstu-" ]]
        then
            "$JAMF" policy -trigger CreateUserstaffadmin

    elif [[ "$HNAME" =~ "libkiosk-" ]]
        then 
            "$JAMF" policy -trigger CreateUserKiosk

    elif [[ "$HNAME" =~ "libaskus-" ]]
        then 
            "$JAMF" policy -trigger CreateUserlabadmin

    elif [[ "$HNAME" =~ "libgc-" ]]
        then 
            "$JAMF" policy -trigger CreateUserlabadmin

    elif [[ "$HNAME" =~ "libgfr-" ]]
        then 
            "$JAMF" policy -trigger CreateUserlabadmin

    elif [[ "$HNAME" =~ "liblc-" ]]
        then 
            "$JAMF" policy -trigger CreateUserlabadmin

    elif [[ "$HNAME" =~ "libgsr-" ]]
        then 
            "$JAMF" policy -trigger CreateUserlabadmin

    elif [[ "$HNAME" =~ "libdml-" ]]
        then
            "$JAMF" policy -trigger CreateUserlabadmin

    elif [[ "$HNAME" =~ "libmake-" ]]
        then
            "$JAMF" policy -trigger CreateUserTeachingCarts

    elif [[ "$HNAME" =~ "libppr-" ]]
        then
            "$JAMF" policy -trigger CreateUserlabadmin

    elif [[ "$HNAME" =~ "liblend-" ]]
        then
            "$JAMF" policy -trigger CreateUserlabadmin

    elif [[ "$HNAME" =~ "libslnd-" ]]
        then
            "$JAMF" policy -trigger CreateUserstaffadmin

    elif [[ "$HNAME" =~ "libtd-" ]]
        then
            "$JAMF" policy -trigger CreateUserstaffadmin

    elif [[ "$HNAME" =~ "libstudio-" ]]
        then
            "$JAMF" policy -trigger CreateUserMedia

    elif [[ "$HNAME" =~ "libteach-" ]]
        then
            "$JAMF" policy -trigger CreateUserTeachingCarts

    elif [[ "$HNAME" =~ "libvid-" ]]
        then
            "$JAMF" policy -trigger CreateUserlabadmin

    elif [[ "$HNAME" =~ "libdes-" ]]
        then
            "$JAMF" policy -trigger CreateUserlabadmin

    elif [[ "$HNAME" =~ "libqp-" ]]
        then
            "$JAMF" policy -trigger CreateUserlabadmin

    elif [[ "$HNAME" =~ "libtest-" ]]
        then
            "$JAMF" policy -trigger CreateUserlibtest

    elif [[ "$HNAME" =~ "libdvil-" ]]
        then
            "$JAMF" policy -trigger CreateUserDVIL

    else
        echo "This computer has not been registered with a proper DNS entry."             

fi

exit 0

You will notice that a lot of the if statements call the same user creation policy. Using the CreateUserlabadmin multiple repeats, I could do something like:

#!/bin/sh
if [[ "$HNAME" =~ "liblc-" ]] | [[ "$HNAME" = "libgsr" ]] | [[ "$HNAME" =~ "libgc-" ]]
    then
        "$JAMF" policy -trigger CreateUserlabadmin
    else
        echo "This computer has not been registered with a proper DNS entry."
fi

Functionally, this would do the same thing (and is really better from a scripting point of view), but because my software stacks and device configurations finds itself changing based on who is using the stack in a teaching role from year to year, there are often changes that require me to be more malleable in my deployment style. So, I tend to separate out all settings for each software stack.

Now that users are created (using the CreateUserlabadmin policy from above), this is the script used to implement permissions for ARD.

#!/bin/sh
####################################################################################################
#
# Copyright (c) 2010, JAMF Software, LLC.  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 the JAMF Software, LLC 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 BY JAMF SOFTWARE, LLC "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 JAMF SOFTWARE, LLC 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.
#
####################################################################################################
#
# SUPPORT FOR THIS PROGRAM
#
#       This program is distributed "as is" by JAMF Software, LLC's Resource Kit team. For more
#       information or support for the Resource Kit, please utilize the following resources:
#
#               http://list.jamfsoftware.com/mailman/listinfo/resourcekit
#
#               http://www.jamfsoftware.com/support/resource-kit
#
#       Please reference our SLA for information regarding support of this application:
#
#               http://www.jamfsoftware.com/support/resource-kit-sla
#
####################################################################################################
#
# ABOUT THIS PROGRAM
#
# NAME
#   enableARD.sh -- Enable ARD and Configure Remote Management Settings
#
# SYNOPSIS
#   sudo enableARD.sh
#   sudo enableARD.sh <mountPoint> <computerName> <currentUsername> <targetUsername>
#
#   If the $targetUsername parameter is specified (parameter 4), this is the account that will be 
#   granted access to ARD.
#
#   If no parameter is specified for parameter 4, the hardcoded value in the script will be used.
#
# DESCRIPTION
#   This script enables and configures remote management settings for a user.  There are a number
#   of options that the script is capable of configuring, which should be specified in the privs
#   string.  Please see the kickstart man page for more information.
#
#   The following options are available in the kickstart application:
#
#       -DeleteFiles
#       -ControlObserve
#       -TextMessages
#       -ShowObserve
#       -OpenQuitApps
#       -GenerateReports
#       -RestartShutDown
#       -SendFiles
#       -ChangeSettings
#       -ObserveOnly
#       -mask
#
#   ARD access is granted and priviliges  are assigned to an individual account on computers running 
#   Mac OS X 10.3 and later. It can be used with a hardcoded value in the script, or read in as a 
#   parameter.  Since the Casper Suite defines the first three parameters as (1) Mount Point, 
#   (2) Computer Name and (3) username, we are using the forth parameter ($4) as the passable 
#   parameter.  We do not use $3 since it may not match up to the username that we want to grant
#   access for.
#
####################################################################################################
#
# HISTORY
#
#   Version: 1.1
#
#   - Created by Tedd Herman on August 5th, 2008
#   - Modified by Nick Amundsen on August 5th, 2008
#
####################################################################################################
#
# DEFINE VARIABLES & READ IN PARAMETERS
#
####################################################################################################


# HARDCODED VALUE FOR "USERNAME" IS SET HERE
targetUsername=""


# CHECK TO SEE IF A VALUE WAS PASSED IN PARAMETER 4 AND, IF SO, ASSIGN TO "USERNAME"
if [ "$4" != "" ] && [ "$targetUsername" == "" ];then
    targetUsername=$4
fi

# DEFINE WHICH PRIVILEGES WILL BE SET FOR THE SPECIFIED USER
privs="-all"

####################################################################################################
# 
# SCRIPT CONTENTS - DO NOT MODIFY BELOW THIS LINE
#
####################################################################################################

if [ "$targetUsername" != "" ]; then
    echo "Enabling Apple Remote Desktop Agent..."
    /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -activate -configure -allowAccessFor -specifiedUsers
    echo "Setting Remote Management Privileges for User: $targetUsername ..."
    /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart -configure -access -on -privs $privs -users $targetUsername
else
    echo "Error:  The parameter 'targetUsername' is blank.  Please specify a user."
fi

The script uses the Parameter 4 custom field so the script can be used on a global basis, and modified for whatever short username is desired. In the case above, "labadmin" (without the quotes) would be entered for the policy. Then the labadmin user would receive all ARD privileges.

Now that users have been created, the deployments move on to the software stacks. Here is an example of a software stack used for staff users:

#!/bin/sh

# EXAMPLE
# To SoftwareUpdateCheck an application, first call an "$#"$OSASCRIPT"" command to execute an AppleScript to display a notification in macOS's Notification Center. Make sure you modify the app you are Updating and customize the title however you like.
# Command to use: "$#"$OSASCRIPT"" -e 'display notification "Updating AppName..." with title "Software Updates"' where AppName is the name of the application you are Updating.
#
# Next, you need to call the Casper policy that has the software you want to SoftwareUpdateCheck. Make sure you create a custom trigger in the SoftwareUpdateCheck policy that matches the one after the -trigger switch.
# Command to use: "$"$JAMF"" policy -trigger SoftwareUpdateCheckAppName where AppName is the name of the Application you want to SoftwareUpdateCheck.

JAMF="/usr/local/bin/jamf"
OSASCRIPT="/usr/bin/osascript"

# Adobe Acrobat Pro
"$OSASCRIPT" -e 'display notification "Installing Adobe Acrobat Pro..." with title "Software Installation"'
"$JAMF" policy -trigger InstallAcrobatPro
"$JAMF" policy -trigger VerifyAcrobatPro

# WebEx
"$OSASCRIPT" -e 'display notification "Installing Cisco WebEx..." with title "Software Installation"'
"$JAMF" policy -trigger InstallWebEx
"$JAMF" policy -trigger VerifyWeEx

# AnyConnect VPN
"$OSASCRIPT" -e 'display notification "Installing Cisco AnyConnect VPN..." with title "Software Installation"'
"$JAMF" policy -trigger InstallAnyConnectVPN
"$JAMF" policy -trigger VerifyAnyConnectVPN

# Jabber
"$OSASCRIPT" -e 'display notification "Installing Cisco Jabber..." with title "Software Installation"'
"$JAMF" policy -trigger InstallJabber
"$JAMF" policy -trigger VerifyJabber

# Kramer VIA
"$OSASCRIPT" -e 'display notification "Installing Kramer VIA..." with title "Software Installation"'
"$JAMF" policy -trigger InstallVia
"$JAMF" policy -trigger VerifyVia

# VirtualBox
"$OSASCRIPT" -e 'display notification "Installing Oracle VirtualBox..." with title "Software Installation"'
"$JAMF" policy -trigger InstallVirtualBox
"$JAMF" policy -trigger VerifyVirtualBox

# VMWare Horizon
"$OSASCRIPT" -e 'display notification "Installing VMWare Horizon Viewer..." with title "Software Installation"'
"$JAMF" policy -trigger InstallHorizon
"$JAMF" policy -trigger VerifyHorizon

# Zoom
"$OSASCRIPT" -e 'display notification "Installing Zoom..." with title "Software Installation"'
"$JAMF" policy -trigger InstallZoom
"$JAMF" policy -trigger VerifyZoom

# NoMAD
"$OSASCRIPT" -e 'display notification "Installing NoMAD..." with title "Software Installation"'
"$JAMF" policy -trigger InstallNoMAD
"$JAMF" policy -trigger VerifyNoMAD

# Staff Printers
"$OSASCRIPT" -e 'display notification "Installing Staff Printers..." with title "Software Installation"'
"$JAMF" policy -trigger InstallPrinters

exit 0

Each of the software stack deployment scripts calls the primary install policy, which just installs the software, then the Verify policy run an install verification just in case there is a network hiccup, or some other oddity that causes an install to fail, the Verify policy runs a sanity check to make sure there is actually a problem with the deployment.

#!/bin/bash

# Variables for use.
APPPATH="/$4"
JAMF="/usr/local/bin/jamf"
INSTALLFAIL="/Users/Shared/$5-Error.app"

# Check to see if the install error file does't exist
if [ ! -e "$INSTALLFAIL" ]; then
    # Check to see if the applications exist.
    if [ -x "$APPPATH" ]; then

    echo "$5 has been successfully installed." 

    else

    # If the install has failed, write an error file.
    touch "$INSTALLFAIL"

    # Call the installer one more time.
    echo "Install failed. Attempting install one more time."
    "$JAMF" policy -trigger $6
    fi

else
# If the install fails again, this is the three strikes and you're out notice.
echo "$5 install has already been attempted and failed too many times. Something is wrong."
rm -Rf "$INSTALLFAIL"

fi

exit 0

The script uses the 4, 5, and 6 parameters in the script GUI interface. Once I add the script to the policy, I can add the Application install path to check in 4, the Application name in 5 and the custom install trigger in 6. The script will check for AppName-Error.app where AppName is defined in parameter 5, and if it doesn't exist, check to see if the Application path, defined in parameter 4, exists. If both of those conditions are true, the script reports a verified install success. Otherwise, the script creates an install fail condition and tries to install the application again.

Assuming there are no issues, all software should now be installed, but then came the inevitable third party software update tsunami where not everything the deployment supported was in the patch policies. So, thus was born the software update check script that runs fairly similar to the software stack script. Like software stacks, the software update scripts depend on the device hostname. Using a staff configuration again. An example of the primary check script is below.

#!/bin/sh
HNAME=$(hostname -s)
JAMF="/usr/local/bin/jamf"

if [[ "$HNAME" =~ "libstaff-" ]]
    then
        "$JAMF" policy -trigger SoftwareUpdateStaff

    elif [[ "$HNAME" =~ "libaudio-" ]]
        then 
            "$JAMF" policy -trigger SoftwareUpdateMediaAudio

    elif [[ "$HNAME" =~ "libcr-" ]]
        then 
            "$JAMF" policy -trigger SoftwareUpdateConf

    elif [[ "$HNAME" =~ "libdvil-" ]]
        then 
            "$JAMF" policy -trigger SoftwareUpdateDVIL

    elif [[ "$HNAME" =~ "libittc-" ]]
        then 
            "$JAMF" policy -trigger SoftwareUpdateITTC

    elif [[ "$HNAME" =~ "libitstu-" ]]
        then 
            "$JAMF" policy -trigger SoftwareUpdateStaff

    elif [[ "$HNAME" =~ "libkiosk-" ]]
        then 
            "$JAMF" policy -trigger SoftwareUpdateKiosk

    elif [[ "$HNAME" =~ "liblc-" ]]
        then 
            "$JAMF" policy -trigger SoftwareUpdateCommons

    elif [[ "$HNAME" =~ "libgc-" ]]
        then 
            "$JAMF" policy -trigger SoftwareUpdateGradCommons

    elif [[ "$HNAME" =~ "libgfr-" ]]
        then 
            "$JAMF" policy -trigger SoftwareUpdateGroundFloorReading

    elif [[ "$HNAME" =~ "libgssr-" ]]
        then 
            "$JAMF" policy -trigger SoftwareUpdateGSSR

    elif [[ "$HNAME" =~ "libdml-" ]]
        then
            "$JAMF" policy -trigger SoftwareUpdateMediaBase

    elif [[ "$HNAME" =~ "libmake-" ]]
        then
            "$JAMF" policy -trigger SoftwareUpdateMakerspace

    elif [[ "$HNAME" =~ "libppr-" ]]
        then
            "$JAMF" policy -trigger SoftwareUpdatePPR

    elif [[ "$HNAME" =~ "liblend-" ]]
        then
            "$JAMF" policy -trigger SoftwareUpdateLending

    elif [[ "$HNAME" =~ "libslnd-" ]]
        then
            "$JAMF" policy -trigger SoftwareUpdateStaff

    elif [[ "$HNAME" =~ "libtd-" ]]
        then
            "$JAMF" policy -trigger SoftwareUpdateStaff

    elif [[ "$HNAME" =~ "libstudio-" ]]
        then
            "$JAMF" policy -trigger SoftwareUpdateMediaHigh

    elif [[ "$HNAME" =~ "libteach-" ]]
        then
            "$JAMF" policy -trigger SoftwareUpdateTeach

    elif [[ "$HNAME" =~ "libvid-" ]]
        then
            "$JAMF" policy -trigger SoftwareUpdateMediaHigh

    elif [[ "$HNAME" =~ "libdes-" ]]
        then
            "$JAMF" policy -trigger SoftwareUpdateDesign      

    else
        echo "This computer has not been registered with a proper DNS entry."             

fi

exit 0

As with the software stacks, the Software update stack scripts walk through specific software with a custom trigger of SoftwareUpdateCheckAppName, where AppName is the name of the application. Continuing with a staff stack, an example of the script is below.

#!/bin/sh

# EXAMPLE
# To SoftwareUpdateCheck an application, first call an "$"$OSASCRIPT"" command to execute an AppleScript to display a notification in macOS's Notification Center. Make sure you modify the app you are Updating and customize the title however you like.
# Command to use: "$"$OSASCRIPT"" -e 'display notification "Updating AppName..." with title "Software Updates"' where AppName is the name of the application you are Updating.
#
# Next, you need to call the Casper policy that has the software you want to SoftwareUpdateCheck. Make sure you create a custom trigger in the SoftwareUpdateCheck policy that matches the one after the -trigger switch.
# Command to use: "$"$JAMF"" policy -trigger SoftwareUpdateCheckAppName where AppName is the name of the Application you want to SoftwareUpdateCheck.

JAMF="/usr/local/bin/jamf"
OSASCRIPT="/usr/bin/osascript"

# Adobe Acrobat Pro
"$OSASCRIPT" -e 'display notification "Updating Adobe Acrobat Pro..." with title "Software Updates"'
"$JAMF" policy -trigger SoftwareUpdateCheckAcrobatPro

# WebEx
"$OSASCRIPT" -e 'display notification "Updating Cisco WebEx..." with title "Software Updates"'
"$JAMF" policy -trigger SoftwareUpdateCheckWebEx

# AnyConnect VPN
"$OSASCRIPT" -e 'display notification "Updating Cisco AnyConnect VPN..." with title "Software Updates"'
"$JAMF" policy -trigger SoftwareUpdateCheckAnyConnectVPN
# Jabber
"$OSASCRIPT" -e 'display notification "Updating Cisco Jabber..." with title "Software Updates"'
"$JAMF" policy -trigger SoftwareUpdateCheckJabber

# Kramer VIA
"$OSASCRIPT" -e 'display notification "Updating Kramer Via..." with title "Software Updates"'
"$JAMF" policy -trigger SoftwareUpdateCheckVia

# NoMAD
"$OSASCRIPT" -e 'display notification "Updating NoMAD..." with title "Software Updates"'
"$JAMF" policy -trigger SoftwareUpdateCheckNoMAD

# VirtualBox
"$OSASCRIPT" -e 'display notification "Updating Oracle VirtualBox..." with title "Software Updates"'
"$JAMF" policy -trigger SoftwareUpdateCheckVirtualBox

# VMWare Horizon
"$OSASCRIPT" -e 'display notification "Updating VMWare Horizon Viewer..." with title "Software Updates"'
"$JAMF" policy -trigger SoftwareUpdateCheckHorizon

# Zoom
"$OSASCRIPT" -e 'display notification "Updating Zoom..." with title "Software Updates"'
"$JAMF" policy -trigger SoftwareUpdateCheckZoom

exit 0

Each SoftwareUpdateCheck policy runs a single script to check for the Application path, the version of the application, and compare the installed version with the needed version. If the installed version doesn't match the needed version, the install policy is called to update the app.

#!/bin/sh
# Script to check the version of an application against a specified version variable. If the versions do not much, run the software update policy from jamf Pro.

# Defined variables for use:

# Application name set to $4 variable so it can be defined in the script policy tab.
APPNAME="$4"

# Application info.plist path set to $5 variable so it can be defined in the script policy tab.
APPINSTALLPATH="$5"

# Application needed version number set to $6 variable so it can be defined in the script policy tab.
NEEDEDAPPVER="$6"

# Application install command set to $7 variable so it can be defined in the script policy tab.
APPINSTALL="$7"

# Application install path set to $8 variable so it can be defined in the script policy tab.
INFOPLIST="$APPINSTALLPATH/Contents/Info.plist"

# Application Version command as a variable
CURRENTAPPVER=$(defaults read "$INFOPLIST" CFBundleShortVersionString)

# jamf binary path
JAMF="/usr/local/bin/jamf"

# Get current host name for the computer.
HNAME=$(hostname -s)

# Custom commands for other needs
CUSTOM1="$8"
CUSTOM2="$9"

# Run custom commands i.e. remove old version of app

if [[ ! -z "$8" ]]
then
    echo "Executing Custom Command 1..."
    "$8"
else
    echo "Custom Command 1 has not been specified. Checking for other custom commands..."
fi

if [[ ! -z "$9" ]]
then
    echo "Executing Custom Command 2..."
    "$9"
else
    echo "Custom Command 2 has not been specified. Checking for other custom commands..."
fi

# First check if the application is installed. If it is, check that the version is up to date. If not, skip it and proceed.
if [[ "$APPINSTALLPATH" ]]; then
    echo "$APPNAME is installed on $HNAME. Checking that $APPNAME has the latest version..."

# Compare needed application version and current application version. If they don't match, run the update command.
if [[ "$CURRENTAPPVER" != "$NEEDEDAPPVER" ]]; then
    echo "$APPNAME is not up to date for $HNAME. Executing update command..."
    "$JAMF" policy -trigger "$APPINSTALL"
else
    echo "$APPNAME is up to date on $HNAME. Continuing with other updates..."
fi

else
    echo "$APPNAME is not installed on $HNAME and it should be. Executing install command for latest version..."
    "$JAMF" policy -trigger "$APPINSTALL"
fi

exit 0

There is some legwork involved to keep track of the latest versions for specific applications, but tools like AutoPKGr are a godsend. As patch policies become more mature, however, the day is coming when the above script will be no longer needed.

So, those are the building bricks used in my custom trigger deployment. I hope they are of use, and welcome any feedback or suggestions for improvements.

4 REPLIES 4

lisacherie
Contributor II

ada3121cee7a4342af16141c1cf1f6ac
Validating a cached file
905075d4f1c641d0ad9e3f8b99c4c94f

#!/usr/bin/perl

use warnings;
use strict;

my $file = $ARGV[3];
my $hash = $ARGV[4];

unless ($file =~ /a-zA-Z/) { exit; }
unless ($hash =~ /a-zA-Z/) { exit; }

my $command = "shasum -a 512 $file | cut -d ' ' -f 1";

my $downloadhash = `$command`;
chomp $downloadhash;

unless ("$hash" eq "$downloadhash") {
    printf "hash doesn't match - removing file.
";
    unlink $file;
}

lisacherie
Contributor II

Logic script for a simple erase and install with custom triggers

#!/usr/bin/perl

use strict;
use warnings;

my $appfilepath = "/Applications/Install macOS High Sierra.app";
my $cachefilepath = "/Library/Application Support/JAMF/Waiting Room/Install macOS High Sierra.dmg”;

# If Applications file already exists remove it

if (-e "$appfilepath") {

    printf "Install app already exists - removing
";
    system "rm -rf "$appfilepath"";
}

# Execute custom trigger policy to cache the installer
#Policy Caches package
#Validates package checksum with after script priority

printf "Caching the Installer
";

system "jamf policy -event cacheHS";

#If the cache file is missing exit - as it failed to cache

unless (-e "$cachefilepath") {

    printf "no cache - exiting
";

    #show popup message there is no cache
    system "jamf displayMessage -message ”Unable to cache OS files."";

    exit 0;
}

# Begin Erase & Install
#Policy installs cached package
#Policy executes install command

printf "Beginning the install
";

system "jamf policy -event installHS";

exit 0;

The actual erase and install command:

/Applications/Install macOS High Sierra.app/Contents/Resources/startosinstall --eraseinstall --agreetolicense --nointeraction &

jmjenki3
New Contributor II

From the 2020 How self service saved the day - Zero-touch rapid provisioning for deployment in any galaxy (JNUC308) session. Here is the script we use for Google Drive User Data backup in the event a device has to be wiped remotely. Note that Google Drive File Stream must be installed and logged in to use this method.

This should also not be considered as a full backup solution, but one of a one-off for emergency data backup.

#!/bin/sh
# User backup script.
# by: Joseph Jenkins
# Version 1.0 - 20170629
# Version 2.0 - 20191222

# Variables for use
JAMF="/usr/local/bin/jamf"
RSYNC="/usr/bin/rsync"
HNAME=$(hostname -s)
VOLBKUPFLDR="/Volumes/GoogleDrive/My Drive"
USER=$(ls -l /dev/console | awk '/ / { print $3 }')
DESKTOP="/Users/$USER/Desktop"
DOCUMENTS="/Users/$USER/Documents"
DATE=$(date "+%Y%m%d")

# Check to see if the backup folder exists. If not, create a backup folder with the device name and today's date
if [[ -d "$VOLBKUPFLDR/$HNAME-Backup-$DATE" ]]
then
    echo "$HNAME-Backup-$DATE exists. Beginning backup operation..."
else
    mkdir "$VOLBKUPFLDR/$HNAME-Backup-$DATE"
fi

# Now backup folders to Google Drive. By default, only Desktopa and Documents are backed up, but other paths may be added.
# Including the jamf script variables for script execution that are commented out, but can be used withi paths filled out in the script webform.
"$RSYNC" -a -v "$DESKTOP" "$VOLBKUPFLDR/$HNAME-Backup-$DATE"
"$RSYNC" -a -v "$DOCUMENTS" "$VOLBKUPFLDR/$HNAME-Backup-$DATE"
#"$RSYNC" -a -v "$4" "$VOLBKUPFLDR/$HNAME-Backup-$DATE"
#"$RSYNC" -a -v "$5" "$VOLBKUPFLDR/$HNAME-Backup-$DATE"
#"$RSYNC" -a -v "$6" "$VOLBKUPFLDR/$HNAME-Backup-$DATE"
#"$RSYNC" -a -v "$7" "$VOLBKUPFLDR/$HNAME-Backup-$DATE"
#"$RSYNC" -a -v "$8" "$VOLBKUPFLDR/$HNAME-Backup-$DATE"
#"$RSYNC" -a -v "$9" "$VOLBKUPFLDR/$HNAME-Backup-$DATE"

ETI_Admin
New Contributor III

I relatively new to Jamf and on a steep learning curve. I've implemented a lot of what you've posted here and I am very appreciative that you share this with the JNUC. This collection of scripts is the best I've seen, so thank you x 1000.

If you told me in 1986 when I was getting my degree in computer science that I'd STILL be using awk and grep commands in 2020 I would not have believed you!