-
-
Save colorenz/ec4f96883698c12163cbd5b4ba70f5ac to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/zsh | |
## postinstall | |
pathToScript=$0 | |
pathToPackage=$1 | |
targetLocation=$2 | |
targetVolume=$3 | |
# Optionally replace the value of this variable with the name of your organization. | |
organizationIdentifier=com.arekdreyer | |
waitMessage="Please wait a moment while your Mac completes enrolling with your organization's mobile device management solution." | |
# | |
# After Setup Assistant exits, if jamf enrollment isn't complete, | |
# this is how many seconds to wait complete before exiting with an error: | |
enrollmentTimeout=120 | |
# This postinstall script for Composer creates the following: | |
# A LaunchDaemon that starts a separate script to run a Jamf Pro policy command | |
# A LaunchAgent that runs BigHonkingText soon as the first user logs in | |
# 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 | |
# | |
# The HEREDOC portions of this script that creates additional scripts | |
# uses the backslash character (\) to prevent commands from being run and | |
# to prevent variables from being interpreted. | |
# NOTE: Make sure to leave a full return at the end of HEREDOC | |
# content before the last line that defines the end of the HEREDOC content. | |
# | |
# This script must be run as root or via Jamf Pro. | |
# The resulting Script and LaunchDaemon will be run as root. | |
# | |
# Update this any of these are changed; | |
# The earlier package installer name was DEPNotifyInstallerName=DEPNotify-1.1.4.pkg | |
depNotifyInstallerName=DEPNotify.pkg | |
depNotifyLog="/var/tmp/depnotify.log" | |
depNotifyAppPath="/Applications/Utilities/DEPNotify.app/Contents/MacOS/DEPNotify" | |
# | |
# 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=${organizationIdentifier}.DEPNotify-prestarter | |
installerScriptName=${installerBaseString}-installer.zsh | |
installerScriptPath=${tempUtilitiesPath}/${installerScriptName} | |
uninstallerScriptName=${installerBaseString}-uninstaller.zsh | |
uninstallerScriptPath=${tempUtilitiesPath}/${uninstallerScriptName} | |
depNotifyStarter_Trigger=start-depnotify | |
# Comment the following out to not open the initial DEPNotify in full screen | |
depNotifyAppFullScreen="--args -fullScreen" | |
# It's probably best to not update any of the rest of the script without extensive testing. | |
# | |
launchDaemonName=${installerBaseString}.plist | |
launchDaemonPath="/Library/LaunchDaemons"/${launchDaemonName} | |
# | |
# Install the package | |
/usr/sbin/installer -pkg ${tempUtilitiesPath}/${depNotifyInstallerName} -target / | |
# The following creates a script that triggers the DEPNotify script to start. | |
# Leave a full return at the end of the content before the last "ENDOFINSTALLERSCRIPT" line. | |
echo "Creating ${installerScriptPath}." | |
( | |
cat <<ENDOFINSTALLERSCRIPT | |
#!/bin/zsh | |
# First and most simple test: if enrollment is complete, just run the policy. | |
# It doesn't matter at that point if someone is logged in or not. | |
# Don't try to grep a file if it doesn't yet exist. | |
if [[ -f /var/log/jamf.log ]]; | |
then | |
if \$( /usr/bin/grep -q enrollmentComplete /var/log/jamf.log ) | |
then | |
/usr/local/jamf/bin/jamf policy -event ${depNotifyStarter_Trigger} | |
exit 0 | |
fi | |
fi | |
# If enrollment isn't complete, and no one has logged in yet, we can wait around indefinitely. | |
# /var/db/.AppleSetupDone is created after any of these events happen: | |
# • The MDM solution creates a manaded MDM administrator account | |
# • The user creates a computer account in Setup Assistant | |
# That's not enough though, we should wait until they complete Setup Assistant. | |
# After they make their last Setup Assistant choice, | |
# /var/db/.AppleDiagnosticsSetupDone is created. | |
# | |
until [[ -f /var/db/.AppleDiagnosticsSetupDone ]]; | |
do | |
echo "Waiting for someone to complete Setup Assistant." | |
sleep 1 | |
done | |
# At this point, a user is logged in. | |
# That may have given enough time to complete enrollment. | |
# Do a quick check to see if enrollment is complete. | |
# | |
if [[ -f /var/log/jamf.log ]]; | |
then | |
if \$( /usr/bin/grep -q enrollmentComplete /var/log/jamf.log ) | |
then | |
/usr/local/jamf/bin/jamf policy -event ${depNotifyStarter_Trigger} | |
exit 0 | |
fi | |
fi | |
# At this point, a user is logged in, but enrollment isn't complete. | |
# Display a message that they need to wait, but don't display it forever. | |
# Set up a temporary message for DEPNotify to use | |
# | |
echo "Command: MainTitle: Please Wait" >> ${depNotifyLog} | |
echo "Command: MainText: ${waitMessage}" >> ${depNotifyLog} | |
echo "Status: Waiting to complete enrollment" >> ${depNotifyLog} | |
# Assume that because we waited for /var/db/.AppleDiagnosticsSetupDone to exist, | |
# we are logged in as a real user instead of _mbsetutp user. | |
# | |
currentUser=\$( /usr/bin/stat -f %Su /dev/console ) | |
sudo -u \${currentUser} open -a ${depNotifyAppPath} ${depNotifyAppFullScreen} | |
timeoutCounter=0 | |
until [[ -f /var/log/jamf.log ]] | |
do | |
if [[ \$timeoutCounter -ge $enrollmentTimeout ]]; | |
then | |
echo "Gave up waiting for the jamf log to appear." | |
killall DEPNotify | |
exit 1 | |
else | |
echo "Waiting for the jamf log to appear. Timeout counter: \${timeoutCounter} of ${enrollmentTimeout}." | |
((timeoutCounter++)) | |
sleep 1 | |
fi | |
done | |
until ( /usr/bin/grep -q enrollmentComplete /var/log/jamf.log ) | |
do | |
if [[ \$timeoutCounter -ge $enrollmentTimeout ]]; | |
then | |
echo "Gave up waiting for enrollment to complete." | |
killall DEPNotify | |
exit 1 | |
else | |
echo "Waiting for jamf enrollment to complete: Timeout counter: \${timeoutCounter} of ${enrollmentTimeout}." | |
((timeoutCounter++)) | |
sleep 1 | |
fi | |
done | |
# At this point, we can assume: | |
# 1. A real user is logged in | |
# 2. jamf enrollment is complete | |
# 3. DEPNotify is running with a generic wait message | |
# | |
# Stop DEPNotify so the real DEPNotify can start | |
# /usr/bin/killall DEPNotify | |
# Remove the DEPNotify log otherwise DEPNotify will fail to open | |
/bin/rm ${depNotifyLog} | |
# Run the policy to call the DEPNotify starter script. | |
/usr/local/jamf/bin/jamf policy -event ${depNotifyStarter_Trigger} | |
ENDOFINSTALLERSCRIPT | |
) > "${installerScriptPath}" | |
echo "Setting permissions for ${installerScriptPath}." | |
chmod 755 "${installerScriptPath}" | |
chown root:wheel "${installerScriptPath}" | |
#----------- | |
# The following creates 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. | |
# Leave a full return at the end of the content before the last "ENDOFLAUNCHDAEMON" line. | |
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>${launchDaemonName}</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 creates the script 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. | |
# | |
# Leave a full return at the end of the 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} | |
rm /var/tmp/${installerScriptName}.err | |
rm /var/tmp/${installerScriptName}.out | |
ENDOFUNINSTALLERSCRIPT | |
) > "${uninstallerScriptPath}" | |
echo "Setting permissions for ${uninstallerScriptPath}." | |
chmod 644 "${uninstallerScriptPath}" | |
chown root:wheel "${uninstallerScriptPath}" | |
exit 0 ## Success | |
exit 1 ## Failure |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment