Skip to content

Instantly share code, notes, and snippets.

@arekdreyer
Last active April 5, 2024 21:36
Show Gist options
  • Star 22 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save arekdreyer/a7af6eab0646a77b9684b2e620b59e1b to your computer and use it in GitHub Desktop.
Save arekdreyer/a7af6eab0646a77b9684b2e620b59e1b to your computer and use it in GitHub Desktop.
Postinstall script for Jamf Composer to install DEPNotify with supporting scripts and a LaunchDaemon
#!/bin/zsh
## postinstall
pathToScript=$0
pathToPackage=$1
targetLocation=$2
targetVolume=$3
# This postinstall script for Composer creates the following
# A LaunchDaemon that starts a separate script to run a Jamf Pro policy command
# A script to wait for Jamf Pro enrollment to complete
# - then triggers a Jamf Pro policy that triggers DEPNotify
# A script that is designed to be called by a Jamf Pro policy
# - to unload the LaunchDaemon then remove the LaunchDaemon and script
#
# Q: Why not just call the `jamf policy -event` command
# from the PreStage Enrollment package postinstall script?
# A: Because the PreStage Enrollment package is installed
# before the jamf binary is installed.
#
# Q: Why not just have the postinstall script wait until jamf enrollment is complete?
# A: Because the postinstall script won't exit while it waits, which prevents enrollment
#
# Q: Why not just include the DEPNotify.sh script in the PreStage Enrollment package?
# A: Because every time you update it, for instance POLICY_ARRAY,
# you'd need to re-build and re-upload the package
#
# Q: Why not distribute the extra scripts and LaunchDaemons somewhere else,
# instead of embedding them in this funky postinstall script?
# A: This way you only have to download and maintain one extra thing.
#
# One approach is to use the following locations and files:
# LaunchDaemon:
# /Library/LaunchDaemons/com.arekdreyer.DEPNotify-prestarter.plist
#
# Temporary folder for the installer and scripts:
# /usr/local/depnotify-with-installers/
#
# Scripts:
# /usr/local/depnotify-with-installers/com.arekdreyer.DEPNotify-prestarter-installer.zsh
# /usr/local/depnotify-with-installers/com.arekdreyer.DEPNotify-prestarter-uninstaller.zsh
# This script must be run as root or via Jamf Pro.
# The resulting Script and LaunchDaemon will be run as root.
# Update this when the DEPNotify installer package is updated; earlier name was DEPNotifyInstallerName=DEPNotify-1.1.4.pkg
DEPNotifyInstallerName=DEPNotify.pkg
#
# You can change this if you have a better location to use.
# I haven't tested this with any path that has a space in the name.
TempUtilitiesPath=/usr/local/depnotify-with-installers
#
# You can change any of these:
InstallerBaseString=com.arekdreyer.DEPNotify-prestarter
InstallerScriptName=${InstallerBaseString}-installer.zsh
InstallerScriptPath=${TempUtilitiesPath}/${InstallerScriptName}
UnInstallerScriptName=${InstallerBaseString}-uninstaller.zsh
UnInstallerScriptPath=${TempUtilitiesPath}/${UnInstallerScriptName}
# Best to use /Library/LaunchDaemons for the LaunchDaemon
LaunchDaemonName=${InstallerBaseString}.plist
LaunchDaemonPath="/Library/LaunchDaemons"/${LaunchDaemonName}
DEPNOTIFYSTARTER_TRIGGER=start-depnotify
# Install the package
/usr/sbin/installer -pkg ${TempUtilitiesPath}/${DEPNotifyInstallerName} -target $3
# The following will create a script that triggers the DEPNotify script to start. Be sure the contents are between the two "ENDOFINSTALLERSCRIPT" lines.
# NOTE: Make sure to leave a full return at the end of the Script content before the last "ENDOFINSTALLERSCRIPT" line.
echo "Creating ${InstallerScriptPath}."
(
cat <<ENDOFINSTALLERSCRIPT
#!/bin/zsh
until [ -f /var/log/jamf.log ]
do
echo "Waiting for jamf log to appear"
sleep 1
done
until ( /usr/bin/grep -q enrollmentComplete /var/log/jamf.log )
do
echo "Waiting for jamf enrollment to be complete."
sleep 1
done
/usr/local/jamf/bin/jamf policy -event ${DEPNOTIFYSTARTER_TRIGGER}
exit 0
ENDOFINSTALLERSCRIPT
) > "${InstallerScriptPath}"
echo "Setting permissions for ${InstallerScriptPath}."
chmod 755 "${InstallerScriptPath}"
chown root:wheel "${InstallerScriptPath}"
#-----------
# The following will create the LaunchDaemon file that starts the script that waits for Jamf Pro enrollment
# then runs the jamf policy -event command to run your DEPNotify.sh script.
echo "Creating ${LaunchDaemonPath}."
(
cat <<ENDOFLAUNCHDAEMON
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>${InstallerBaseString}</string>
<key>RunAtLoad</key>
<true/>
<key>UserName</key>
<string>root</string>
<key>ProgramArguments</key>
<array>
<string>/bin/zsh</string>
<string>${InstallerScriptPath}</string>
</array>
<key>StandardErrorPath</key>
<string>/var/tmp/${InstallerScriptName}.err</string>
<key>StandardOutPath</key>
<string>/var/tmp/${InstallerScriptName}.out</string>
</dict>
</plist>
ENDOFLAUNCHDAEMON
) > "${LaunchDaemonPath}"
echo "Setting permissions for ${LaunchDaemonPath}."
chmod 644 "${LaunchDaemonPath}"
chown root:wheel "${LaunchDaemonPath}"
echo "Loading ${LaunchDaemonName}."
launchctl load "${LaunchDaemonPath}"
#-----------
# The following will create the script file to uninstall the LaunchDaemon and installer script.
# You can create a Jamf Pro policy with the following characteristics:
# General settings:
# --Name: Cleanup DEPNotify Installers
# --Trigger: Custom Trigger: cleanup-depnotify-preinstaller
# --Scope: All Computers
# --Frequency: Once per Computer
# Files and Processes settings:
# --Execute Command: Whatever your $UnInstallerScriptPath is set to.
#
# In your DEPNotify.sh script, include the policy near the end of your POLICY_ARRAY.
#
# Paste your script's contents between the two "ENDOFUNINSTALLERSCRIPT" lines.
# NOTE: Make sure to leave a full return at the end of the Script content before the last "ENDOFUNINSTALLERSCRIPT" line.
echo "Creating ${UnInstallerScriptPath}."
(
cat <<ENDOFUNINSTALLERSCRIPT
#!/bin/zsh
# This is meant to be called by a Jamf Pro policy via trigger
# Near the end of your POLICY_ARRAY in your DEPNotify.sh script
rm ${TempUtilitiesPath}/${DEPNotifyInstallerName}
rm ${InstallerScriptPath}
#Note that if you unload the LaunchDaemon this will immediately kill the depNotify.sh script
#Just remove the underlying plist file, and the LaunchDaemon will not run after next reboot/login.
rm ${LaunchDaemonPath}
rm ${UnInstallerScriptPath}
rmdir ${TempUtilitiesPath}
exit 0
exit 1
ENDOFUNINSTALLERSCRIPT
) > "${UnInstallerScriptPath}"
echo "Setting permissions for ${UnInstallerScriptPath}."
chmod 644 "${UnInstallerScriptPath}"
chown root:wheel "${UnInstallerScriptPath}"
exit 0 ## Success
exit 1 ## Failure
@arekdreyer
Copy link
Author

Added the "exit 0" and "exit 1" lines at the end of the script to uninstall everything.

@arekdreyer
Copy link
Author

Removed the line that used launchctl to unload the LaunchDaemon, and added comments about why you shouldn't unload the LaunchDaemon.

@arekdreyer
Copy link
Author

arekdreyer commented Jul 16, 2019

Line 73: Updated description in comment.
Line 11: Changed "Jame" to "Jamf"
Lines 42 and 43: Corrected shell script names
Thanks Christopher Stout.

@arekdreyer
Copy link
Author

Changed instances of -trigger (legacy) to -event.

@arekdreyer
Copy link
Author

Now that https://gitlab.com/Mactroll/DEPNotify/-/tags names the package DEPNotify.pkg by default, instead of with the version name (like DEPNotify.pkg-1.1.4.pkg), updated the value of $DEPNotifyInstallerName.

@rickgmac
Copy link

Love your script.
between line 88 & 89 worth adding this or similar. Will ensure DEPNotify does not launch until desktop is present for new user accounts.

dockStatus=$(pgrep -x Dock)
log "Waiting for Desktop"
while [ "$dockStatus" == "" ]; do
log "Desktop is not loaded. Waiting."
sleep 2
dockStatus=$(pgrep -x Dock)
done

@dev-yeet
Copy link

if you're using Jamf, i would change those log commands to echo. Policy logs don't like the log command

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment