diff --git a/README.md b/README.md index 0714ec5..fbb53ed 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ [Setup Your Mac (1.14.0)](https://snelson.us/sym) -# Setup Your Mac (1.14.0) with SYM-Helper (1.1.1) via swiftDialog (2.4.0) +# Setup Your Mac (1.15.0) with SYM-Helper (1.1.1) via swiftDialog (2.4.0) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/dan-snelson/Setup-Your-Mac?display_name=tag) ![GitHub issues](https://img.shields.io/github/issues-raw/dan-snelson/Setup-Your-Mac) ![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/dan-snelson/Setup-Your-Mac) ![GitHub pull requests](https://img.shields.io/github/issues-pr-raw/dan-snelson/Setup-Your-Mac) ![GitHub closed pull requests](https://img.shields.io/github/issues-pr-closed-raw/dan-snelson/Setup-Your-Mac) -> Optimized to leverage SYM-Helper (1.1.1), Setup Your Mac (1.14.0) leverages new features of swiftDialog (2.4.0) +> Optimized to leverage SYM-Helper (1.1.1), Setup Your Mac (1.15.0) leverages new features of swiftDialog (2.4.0) [Setup Your Mac (1.14.0)](https://snelson.us/sym) diff --git a/Setup-Your-Mac-via-Dialog.bash b/Setup-Your-Mac-via-Dialog.bash index f78efe9..5848225 100755 --- a/Setup-Your-Mac-via-Dialog.bash +++ b/Setup-Your-Mac-via-Dialog.bash @@ -10,18 +10,8 @@ # # HISTORY # -# Version 1.14.0, 05-Feb-2024 -# - Updated Vimeo ID -# - Corrected omission of [SYM-Helper] for `moveableInProduction` -# - Updated "Microsoft Office 365" to "Microsoft 365" -# - Added OS Build number to webhook output [Pull Request No. 124](https://github.com/dan-snelson/Setup-Your-Mac/pull/124); thanks, @drtaru! -# - Changed filepath validation test from `-f` (i.e., "True if file exists and is a regular file") to `-e` (i.e., "True if file exists (regardless of type)."); thanks for the inspiration, @mrmte! [Issue 19](https://github.com/BIG-RAT/SYM-Helper/issues/19); thanks for the code suggestion, @bartreardon! -# - Updates to `README.md`, `CONTRIBUTORS.md` and `CONTRIBUTING.md` [Pull Request No. 128](https://github.com/setup-your-mac/Setup-Your-Mac/pull/128); thanks, @robjschroeder! -# - Refactored the way `brandingBanner` variable is checked [Pull Request No. 131](https://github.com/setup-your-mac/Setup-Your-Mac/pull/131); thanks, @drtaru! -# - Increased minimum required version of swiftDialog to 2.4.0.4750 -# - Leveraged the new `listitem: subtitle` option with a dedicated `subtitle` field in `policyJSON` -# - Corrected misspelling of "policies" in log entries [Issue No. 134](https://github.com/setup-your-mac/Setup-Your-Mac/issues/134); thanks, @Honestpuck! -# - Updated `brandingBanner` to [image by benzoix on Freepik](https://www.freepik.com/author/benzoix) +# Version 1.15.0, 06-Feb-2024 +# - Added logging functions # #################################################################################################### @@ -37,7 +27,7 @@ # Script Version and Jamf Pro Script Parameters # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -scriptVersion="1.14.0" +scriptVersion="1.15.0" export PATH=/usr/bin:/bin:/usr/sbin:/sbin scriptLog="${4:-"/var/log/org.churchofjesuschrist.log"}" # Parameter 4: Script Log Location [ /var/log/org.churchofjesuschrist.log ] (i.e., Your organization's default location for client-side logs) debugMode="${5:-"verbose"}" # Parameter 5: Debug Mode [ verbose (default) | true | false ] @@ -55,6 +45,8 @@ swiftDialogMinimumRequiredVersion="2.4.0.4750" # Various Feature Variables # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +humanReadableScriptName="Setup Your Mac" # Script Human-readable Name +organizationScriptName="sym" # Organization's Script Name debugModeSleepAmount="3" # Delay for various actions when running in Debug Mode failureDialog="true" # Display the so-called "Failure" dialog (after the main SYM dialog) [ true | false ] @@ -162,2597 +154,2646 @@ configurationThreeDescription="Recommended apps, Adobe Acrobat Reader and Google configurationThreeSize="106" # Configuration Three in Gibibits (i.e., Total File Size in Gigabytes * 7.451) configurationThreeInstallBuffer="0" # Buffer time added to estimates to include installation time of packages, in seconds. Set to 0 to disable. - - #################################################################################################### # -# Pre-flight Checks +# Functions # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Client-side Logging +# Client-side Logging # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -if [[ ! -f "${scriptLog}" ]]; then - touch "${scriptLog}" -fi +function updateScriptLog() { + echo -e "${organizationScriptName} ($scriptVersion): $( date +%Y-%m-%d\ %H:%M:%S ) - ${1}" | tee -a "${scriptLog}" +} +function preFlight() { + updateScriptLog "[PRE-FLIGHT] ${1}" +} +function welcomeDialog() { + updateScriptLog "[WELCOME DIALOG] ${1}" +} -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Client-side Script Logging Function -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +function error() { + updateScriptLog "[ERROR] ${1}" +} -function updateScriptLog() { - echo -e "$( date +%Y-%m-%d\ %H:%M:%S ) - ${1}" | tee -a "${scriptLog}" +function fatal() { + updateScriptLog "[FATAL ERROR] ${1}" + exit 1 } +function info() { + updateScriptLog "[INFO] ${1}" +} +function updateSetupYourMacDialog() { + updateScriptLog "[SETUP YOUR MAC DIALOG] ${1}" +} -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Current Logged-in User Function -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +function updateFailureDialog() { + updateScriptLog "[FAILURE DIALOG] ${1}" +} -function currentLoggedInUser() { - loggedInUser=$( echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ { print $3 }' ) - updateScriptLog "PRE-FLIGHT CHECK: Current Logged-in User: ${loggedInUser}" +function updateSuccessDialog() { + updateScriptLog "[SUCCESS] ${1}" +} + +function finaliseUserExperience() { + updateScriptLog "[FINALISE USER EXPERIENCE] ${1}" } +function completionActionOut() { + updateScriptLog "[COMPLETION ACTION] ${1}" +} +function quitOut() { + updateScriptLog "[QUIT SCRIPT] ${1}" +} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Logging Preamble +# Output Line Number in `verbose` Debug Mode (thanks, @bartreardon!) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -updateScriptLog "\n\n###\n# Setup Your Mac (${scriptVersion})\n# https://snelson.us/sym\n###\n" -updateScriptLog "PRE-FLIGHT CHECK: Initiating …" +function outputLineNumberInVerboseDebugMode() { + if [[ "${debugMode}" == "verbose" ]]; then updateScriptLog "# # # SETUP YOUR MAC VERBOSE DEBUG MODE: Line No. ${BASH_LINENO[0]} # # #" ; fi +} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Confirm script is running under bash +# Run command as logged-in user (thanks, @scriptingosx!) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -if [[ "$BASH" != "/bin/bash" ]] ; then - updateScriptLog "PRE-FLIGHT CHECK: This script must be run under 'bash', please do not run it using 'sh', 'zsh', etc.; exiting." - exit 1 -fi - - +function runAsUser() { -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Confirm script is running as root -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + info "Run \"$@\" as \"$loggedInUserID\" … " + launchctl asuser "$loggedInUserID" sudo -u "$loggedInUser" "$@" -if [[ $(id -u) -ne 0 ]]; then - updateScriptLog "PRE-FLIGHT CHECK: This script must be run as root; exiting." - exit 1 -fi +} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Validate Setup Assistant has completed +# Calculate Free Disk Space +# Disk Usage with swiftDialog (https://snelson.us/2022/11/disk-usage-with-swiftdialog-0-0-2/) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -while pgrep -q -x "Setup Assistant"; do - updateScriptLog "PRE-FLIGHT CHECK: Setup Assistant is still running; pausing for 2 seconds" - sleep 2 -done +function calculateFreeDiskSpace() { + + freeSpace=$( diskutil info / | grep -E 'Free Space|Available Space|Container Free Space' | awk -F ":\s*" '{ print $2 }' | awk -F "(" '{ print $1 }' | xargs ) + freeBytes=$( diskutil info / | grep -E 'Free Space|Available Space|Container Free Space' | awk -F "(\\\(| Bytes\\\))" '{ print $2 }' ) + diskBytes=$( diskutil info / | grep -E 'Total Space' | awk -F "(\\\(| Bytes\\\))" '{ print $2 }' ) + freePercentage=$( echo "scale=2; ( $freeBytes * 100 ) / $diskBytes" | bc ) + diskSpace="$freeSpace free (${freePercentage}% available)" + + diskMessage=$(echo "Disk Space: ${diskSpace}") -updateScriptLog "PRE-FLIGHT CHECK: Setup Assistant is no longer running; proceeding …" +} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Confirm Dock is running / user is at Desktop +# Update the "Welcome" dialog # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -until pgrep -q -x "Finder" && pgrep -q -x "Dock"; do - updateScriptLog "PRE-FLIGHT CHECK: Finder & Dock are NOT running; pausing for 1 second" - sleep 1 -done - -updateScriptLog "PRE-FLIGHT CHECK: Finder & Dock are running; proceeding …" +function dialogUpdateWelcome(){ + # welcomeDialog "$1" + echo "$1" >> "$welcomeCommandFile" +} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Validate Logged-in System Accounts +# Update the "Setup Your Mac" dialog # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -updateScriptLog "PRE-FLIGHT CHECK: Check for Logged-in System Accounts …" -currentLoggedInUser - -counter="1" +function dialogUpdateSetupYourMac() { + updateSetupYourMacDialog "$1" + echo "$1" >> "$setupYourMacCommandFile" +} -until { [[ "${loggedInUser}" != "_mbsetupuser" ]] || [[ "${counter}" -gt "180" ]]; } && { [[ "${loggedInUser}" != "loginwindow" ]] || [[ "${counter}" -gt "30" ]]; } ; do - updateScriptLog "PRE-FLIGHT CHECK: Logged-in User Counter: ${counter}" - currentLoggedInUser - sleep 2 - ((counter++)) -done +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Update the "Failure" dialog +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -loggedInUserFullname=$( id -F "${loggedInUser}" ) -loggedInUserFirstname=$( echo "$loggedInUserFullname" | sed -E 's/^.*, // ; s/([^ ]*).*/\1/' | sed 's/\(.\{25\}\).*/\1…/' | awk '{print ( $0 == toupper($0) ? toupper(substr($0,1,1))substr(tolower($0),2) : toupper(substr($0,1,1))substr($0,2) )}' ) -loggedInUserID=$( id -u "${loggedInUser}" ) -updateScriptLog "PRE-FLIGHT CHECK: Current Logged-in User First Name: ${loggedInUserFirstname}" -updateScriptLog "PRE-FLIGHT CHECK: Current Logged-in User ID: ${loggedInUserID}" +function dialogUpdateFailure(){ + updateFailureDialog "$1" + echo "$1" >> "$failureCommandFile" +} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Validate Operating System Version and Build +# Finalise User Experience # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -if [[ "${requiredMinimumBuild}" == "disabled" ]]; then - - updateScriptLog "PRE-FLIGHT CHECK: 'requiredMinimumBuild' has been set to ${requiredMinimumBuild}; skipping OS validation." - updateScriptLog "PRE-FLIGHT CHECK: macOS ${osVersion} (${osBuild}) installed" - -else +function finalise(){ - # Since swiftDialog requires at least macOS 12 Monterey, first confirm the major OS version - if [[ "${osMajorVersion}" -ge 12 ]] ; then + outputLineNumberInVerboseDebugMode - updateScriptLog "PRE-FLIGHT CHECK: macOS ${osMajorVersion} installed; checking build version ..." + if [[ "${configurationDownloadEstimation}" == "true" ]]; then - # Confirm the Mac is running `requiredMinimumBuild` (or later) - if [[ "${osBuild}" > "${requiredMinimumBuild}" ]]; then + outputLineNumberInVerboseDebugMode + calculateFreeDiskSpace + finaliseUserExperience "${diskMessage}" - updateScriptLog "PRE-FLIGHT CHECK: macOS ${osVersion} (${osBuild}) installed; proceeding ..." + fi - # When the current `osBuild` is older than `requiredMinimumBuild`; exit with error - else - updateScriptLog "PRE-FLIGHT CHECK: The installed operating system, macOS ${osVersion} (${osBuild}), needs to be updated to Build ${requiredMinimumBuild}; exiting with error." - osascript -e 'display dialog "Please advise your Support Representative of the following error:\r\rExpected macOS Build '${requiredMinimumBuild}' (or newer), but found macOS '${osVersion}' ('${osBuild}').\r\r" with title "Setup Your Mac: Detected Outdated Operating System" buttons {"Open Software Update"} with icon caution' - updateScriptLog "PRE-FLIGHT CHECK: Executing /usr/bin/open '${outdatedOsAction}' …" - su - "${loggedInUser}" -c "/usr/bin/open \"${outdatedOsAction}\"" - exit 1 + if [[ "${jamfProPolicyTriggerFailure}" == "failed" ]]; then + outputLineNumberInVerboseDebugMode + updateFailureDialog "Failed policies detected …" + if [[ -n "${webhookURL}" ]]; then + updateFailureDialog "Display Failure dialog: Sending webhook message" + webhookStatus="Failures detected" + webHookMessage fi - # The Mac is running an operating system older than macOS 12 Monterey; exit with error - else - - updateScriptLog "PRE-FLIGHT CHECK: swiftDialog requires at least macOS 12 Monterey and this Mac is running ${osVersion} (${osBuild}), exiting with error." - osascript -e 'display dialog "Please advise your Support Representative of the following error:\r\rExpected macOS Build '${requiredMinimumBuild}' (or newer), but found macOS '${osVersion}' ('${osBuild}').\r\r" with title "Setup Your Mac: Detected Outdated Operating System" buttons {"Open Software Update"} with icon caution' - updateScriptLog "PRE-FLIGHT CHECK: Executing /usr/bin/open '${outdatedOsAction}' …" - su - "${loggedInUser}" -c "/usr/bin/open \"${outdatedOsAction}\"" - exit 1 - - fi + if [[ "${failureDialog}" == "true" ]]; then -fi + outputLineNumberInVerboseDebugMode + updateFailureDialog "Display Failure dialog: ${failureDialog}" + killProcess "caffeinate" + if [[ "${brandingBannerDisplayText}" == "true" ]] ; then dialogUpdateSetupYourMac "title: Sorry ${loggedInUserFirstname}, something went sideways"; fi + dialogUpdateSetupYourMac "icon: SF=xmark.circle.fill,weight=bold,colour1=#BB1717,colour2=#F31F1F" + dialogUpdateSetupYourMac "progresstext: Failures detected. Please click Continue for troubleshooting information." + dialogUpdateSetupYourMac "button1text: Continue …" + dialogUpdateSetupYourMac "button1: enable" + dialogUpdateSetupYourMac "progress: reset" + + # Wait for user-acknowledgment due to detected failure + wait + dialogUpdateSetupYourMac "quit:" + eval "${dialogFailureCMD}" & sleep 0.3 -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Ensure computer does not go to sleep during SYM (thanks, @grahampugh!) -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + updateFailureDialog "\n\n# # #\n# FAILURE DIALOG\n# # #\n" + updateFailureDialog "Jamf Pro Policy Name Failures:" + updateFailureDialog "${jamfProPolicyNameFailures}" -symPID="$$" -updateScriptLog "PRE-FLIGHT CHECK: Caffeinating this script (PID: $symPID)" -caffeinate -dimsu -w $symPID & + failureMessage="A failure has been detected, ${loggedInUserFirstname}. \n\nPlease complete the following steps:\n1. Reboot and login to your ${modelName} \n2. Login to Self Service \n3. Re-run any failed policy listed below \n\nThe following failed: \n${jamfProPolicyNameFailures}" + + if [[ -n "${supportTeamName}" ]]; then + supportContactMessage+="If you need assistance, please contact the **${supportTeamName}**: \n" + if [[ -n "${supportTeamPhone}" ]]; then + supportContactMessage+="- **Telephone:** ${supportTeamPhone}\n" + fi -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Toggle `jamf` binary check-in (thanks, @robjschroeder!) -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + if [[ -n "${supportTeamEmail}" ]]; then + supportContactMessage+="- **Email:** $supportTeamEmail\n" + fi -function toggleJamfLaunchDaemon() { - - jamflaunchDaemon="/Library/LaunchDaemons/com.jamfsoftware.task.1.plist" + if [[ -n "${supportTeamWebsite}" ]]; then + supportContactMessage+="- **Web**: ${supportTeamHyperlink}\n" + fi - if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then + if [[ -n "${supportKB}" ]]; then + supportContactMessage+="- **Knowledge Base Article:** $supportTeamErrorKB\n" + fi + + fi - if [[ $(/bin/launchctl list | grep com.jamfsoftware.task.E) ]]; then - updateScriptLog "PRE-FLIGHT CHECK: DEBUG MODE: Normally, 'jamf' binary check-in would be temporarily disabled" - else - updateScriptLog "QUIT SCRIPT: DEBUG MODE: Normally, 'jamf' binary check-in would be re-enabled" - fi + failureMessage+="\n\n${supportContactMessage}" - else + dialogUpdateFailure "message: ${failureMessage}" - while [[ ! -f "${jamflaunchDaemon}" ]] ; do - updateScriptLog "PRE-FLIGHT CHECK: Waiting for installation of ${jamflaunchDaemon}" - sleep 0.1 - done + dialogUpdateFailure "icon: SF=xmark.circle.fill,weight=bold,colour1=#BB1717,colour2=#F31F1F" + dialogUpdateFailure "button1text: ${button1textCompletionActionOption}" - if [[ $(/bin/launchctl list | grep com.jamfsoftware.task.E) ]]; then + # Wait for user-acknowledgment due to detected failure + wait - updateScriptLog "PRE-FLIGHT CHECK: Temporarily disable 'jamf' binary check-in" - /bin/launchctl bootout system "${jamflaunchDaemon}" + dialogUpdateFailure "quit:" + quitScript "1" else - updateScriptLog "QUIT SCRIPT: Re-enabling 'jamf' binary check-in" - updateScriptLog "QUIT SCRIPT: 'jamf' binary check-in daemon not loaded, attempting to bootstrap and start" - result="0" + outputLineNumberInVerboseDebugMode + dialogUpdateFailure "Display Failure dialog: ${failureDialog}" - until [ $result -eq 3 ]; do + killProcess "caffeinate" + if [[ "${brandingBannerDisplayText}" == "true" ]] ; then dialogUpdateSetupYourMac "title: Sorry ${loggedInUserFirstname}, something went sideways"; fi + dialogUpdateSetupYourMac "icon: SF=xmark.circle.fill,weight=bold,colour1=#BB1717,colour2=#F31F1F" + dialogUpdateSetupYourMac "progresstext: Failures detected." + dialogUpdateSetupYourMac "button1text: ${button1textCompletionActionOption}" + dialogUpdateSetupYourMac "button1: enable" + dialogUpdateSetupYourMac "progress: reset" + dialogUpdateSetupYourMac "progresstext: Errors detected; please ${progressTextCompletionAction// and } your ${modelName}, ${loggedInUserFirstname}." - /bin/launchctl bootstrap system "${jamflaunchDaemon}" && /bin/launchctl start "${jamflaunchDaemon}" - result="$?" + quitScript "1" - if [ $result = 3 ]; then - updateScriptLog "QUIT SCRIPT: Staring 'jamf' binary check-in daemon" - else - updateScriptLog "QUIT SCRIPT: Failed to start 'jamf' binary check-in daemon" - fi + fi - done + else + outputLineNumberInVerboseDebugMode + updateSuccessDialog "All policies executed successfully" + if [[ -n "${webhookURL}" ]]; then + webhookStatus="Successful" + updateSuccessDialog "Sending success webhook message" + webHookMessage fi + if [[ "${brandingBannerDisplayText}" == "true" ]] ; then dialogUpdateSetupYourMac "title: ${loggedInUserFirstname}‘s ${modelName} is ready!"; fi + dialogUpdateSetupYourMac "icon: SF=checkmark.circle.fill,weight=bold,colour1=#00ff44,colour2=#075c1e" + dialogUpdateSetupYourMac "progresstext: Complete! Please ${progressTextCompletionAction}enjoy your new ${modelName}, ${loggedInUserFirstname}!" + dialogUpdateSetupYourMac "progress: complete" + dialogUpdateSetupYourMac "button1text: ${button1textCompletionActionOption}" + dialogUpdateSetupYourMac "button1: enable" + + quitScript "0" + fi } -toggleJamfLaunchDaemon - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Validate / install swiftDialog (Thanks big bunches, @acodega!) +# Parse JSON via osascript and JavaScript # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function dialogInstall() { +function get_json_value() { + # set -x + JSON="$1" osascript -l 'JavaScript' \ + -e 'const env = $.NSProcessInfo.processInfo.environment.objectForKey("JSON").js' \ + -e "JSON.parse(env).$2" + # set +x +} - # Get the URL of the latest PKG From the Dialog GitHub repo - dialogURL=$(curl -L --silent --fail "https://api.github.com/repos/swiftDialog/swiftDialog/releases/latest" | awk -F '"' "/browser_download_url/ && /pkg\"/ { print \$4; exit }") - # Expected Team ID of the downloaded PKG - expectedDialogTeamID="PWA5E9TQ59" - updateScriptLog "PRE-FLIGHT CHECK: Installing swiftDialog..." +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Parse JSON via osascript and JavaScript for the Welcome dialog (thanks, @bartreardon!) +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Create temporary working directory - workDirectory=$( /usr/bin/basename "$0" ) - tempDirectory=$( /usr/bin/mktemp -d "/private/tmp/$workDirectory.XXXXXX" ) +function get_json_value_welcomeDialog() { + # set -x + for var in "${@:2}"; do jsonkey="${jsonkey}['${var}']"; done + JSON="$1" osascript -l 'JavaScript' \ + -e 'const env = $.NSProcessInfo.processInfo.environment.objectForKey("JSON").js' \ + -e "JSON.parse(env)$jsonkey" + # set +x +} - # Download the installer package - /usr/bin/curl --location --silent "$dialogURL" -o "$tempDirectory/Dialog.pkg" - # Verify the download - teamID=$(/usr/sbin/spctl -a -vv -t install "$tempDirectory/Dialog.pkg" 2>&1 | awk '/origin=/ {print $NF }' | tr -d '()') - - # Install the package if Team ID validates - if [[ "$expectedDialogTeamID" == "$teamID" ]]; then - - /usr/sbin/installer -pkg "$tempDirectory/Dialog.pkg" -target / - sleep 2 - dialogVersion=$( /usr/local/bin/dialog --version ) - updateScriptLog "PRE-FLIGHT CHECK: swiftDialog version ${dialogVersion} installed; proceeding..." - - else - - # Display a so-called "simple" dialog if Team ID fails to validate - osascript -e 'display dialog "Please advise your Support Representative of the following error:\r\r• Dialog Team ID verification failed\r\r" with title "Setup Your Mac: Error" buttons {"Close"} with icon caution' - completionActionOption="Quit" - exitCode="1" - quitScript - - fi - - # Remove the temporary working directory when done - /bin/rm -Rf "$tempDirectory" - -} +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Execute Jamf Pro Policy Custom Events (thanks, @smithjw) +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +function run_jamf_trigger() { -function dialogCheck() { + outputLineNumberInVerboseDebugMode - # Output Line Number in `verbose` Debug Mode - if [[ "${debugMode}" == "verbose" ]]; then updateScriptLog "PRE-FLIGHT CHECK: # # # SETUP YOUR MAC VERBOSE DEBUG MODE: Line No. ${LINENO} # # #" ; fi + trigger="$1" - # Check for Dialog and install if not found - if [ ! -e "/Library/Application Support/Dialog/Dialog.app" ]; then + if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then - updateScriptLog "PRE-FLIGHT CHECK: swiftDialog not found. Installing..." - dialogInstall + updateSetupYourMacDialog "DEBUG MODE: TRIGGER: $jamfBinary policy -event $trigger ${suppressRecon}" + sleep "${debugModeSleepAmount}" else - dialogVersion=$(/usr/local/bin/dialog --version) - if [[ "${dialogVersion}" < "${swiftDialogMinimumRequiredVersion}" ]]; then - - updateScriptLog "PRE-FLIGHT CHECK: swiftDialog version ${dialogVersion} found but swiftDialog ${swiftDialogMinimumRequiredVersion} or newer is required; updating..." - dialogInstall - - else - - updateScriptLog "PRE-FLIGHT CHECK: swiftDialog version ${dialogVersion} found; proceeding..." + updateSetupYourMacDialog "RUNNING: $jamfBinary policy -event $trigger" + eval "${jamfBinary} policy -event ${trigger} ${suppressRecon}" # Add comment for policy testing + # eval "${jamfBinary} policy -event ${trigger} ${suppressRecon} -verbose | tee -a ${scriptLog}" # Remove comment for policy testing - fi - fi } -dialogCheck - - - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Validate `supportTeam` variables are populated -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -if [[ -z $supportTeamName ]]; then - updateScriptLog "PRE-FLIGHT CHECK: 'supportTeamName' must be populated to proceed; exiting" - exit 1 -fi - -if [[ -z $supportTeamPhone && -z $supportTeamEmail && -z $supportKB ]]; then - updateScriptLog "PRE-FLIGHT CHECK: At least ONE 'supportTeam' variable must be populated to proceed; exiting" - exit 1 -fi - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Complete +# Confirm Policy Execution # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -updateScriptLog "PRE-FLIGHT CHECK: Complete" - +function confirmPolicyExecution() { + outputLineNumberInVerboseDebugMode -#################################################################################################### -# -# Dialog Variables -# -#################################################################################################### + trigger="${1}" + validation="${2}" + updateSetupYourMacDialog "Confirm Policy Execution: '${trigger}' '${validation}'" + if [ "${suppressReconOnPolicy}" == "true" ]; then suppressRecon="-forceNoRecon"; fi -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# infobox-related variables -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + case ${validation} in -macOSproductVersion="$( sw_vers -productVersion )" -macOSbuildVersion="$( sw_vers -buildVersion )" -serialNumber=$( ioreg -rd1 -c IOPlatformExpertDevice | awk -F'"' '/IOPlatformSerialNumber/{print $4}' ) -timestamp="$( date '+%Y-%m-%d-%H%M%S' )" -dialogVersion=$( /usr/local/bin/dialog --version ) + */* ) # If the validation variable contains a forward slash (i.e., "/"), presume it's a path and check if that path exists on disk + outputLineNumberInVerboseDebugMode + if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then + updateSetupYourMacDialog "Confirm Policy Execution: DEBUG MODE: Skipping 'run_jamf_trigger ${trigger}'" + sleep "${debugModeSleepAmount}" + elif [[ -e "${validation}" ]]; then + updateSetupYourMacDialog "Confirm Policy Execution: ${validation} exists; skipping 'run_jamf_trigger ${trigger}'" + previouslyInstalled="true" + else + updateSetupYourMacDialog "Confirm Policy Execution: ${validation} does NOT exist; executing 'run_jamf_trigger ${trigger}'" + previouslyInstalled="false" + run_jamf_trigger "${trigger}" + fi + ;; + "None" | "none" ) -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Reflect Debug Mode in `infotext` (i.e., bottom, left-hand corner of each dialog) -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + outputLineNumberInVerboseDebugMode + updateSetupYourMacDialog "Confirm Policy Execution: ${validation}" + if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then + sleep "${debugModeSleepAmount}" + else + run_jamf_trigger "${trigger}" + fi + ;; -case ${debugMode} in - "true" ) scriptVersion="DEBUG MODE | Dialog: v${dialogVersion} • Setup Your Mac: v${scriptVersion}" ;; - "verbose" ) scriptVersion="VERBOSE DEBUG MODE | Dialog: v${dialogVersion} • Setup Your Mac: v${scriptVersion}" ;; -esac + "Recon" | "recon" ) + outputLineNumberInVerboseDebugMode + updateSetupYourMacDialog "Confirm Policy Execution: ${validation}" + if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then + updateSetupYourMacDialog "DEBUG MODE: Set 'debugMode' to false to update computer inventory with the following 'reconOptions': \"${reconOptions}\" …" + sleep "${debugModeSleepAmount}" + else + updateSetupYourMacDialog "Updating computer inventory with the following 'reconOptions': \"${reconOptions}\" …" + dialogUpdateSetupYourMac "listitem: index: $i, status: wait, statustext: Updating …, " + reconRaw=$( eval "${jamfBinary} recon ${reconOptions} -verbose | tee -a ${scriptLog}" ) + computerID=$( echo "${reconRaw}" | grep '' | xmllint --xpath xmllint --xpath '/computer_id/text()' - ) + fi + ;; + * ) -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Set JAMF binary, Dialog path and Command Files -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + outputLineNumberInVerboseDebugMode + updateSetupYourMacDialog "Confirm Policy Execution Catch-all: ${validation}" + if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then + sleep "${debugModeSleepAmount}" + else + run_jamf_trigger "${trigger}" + fi + ;; -jamfBinary="/usr/local/bin/jamf" -dialogBinary="/usr/local/bin/dialog" -welcomeJSONFile=$( mktemp -u /var/tmp/welcomeJSONFile.XXX ) -welcomeCommandFile=$( mktemp -u /var/tmp/dialogCommandFileWelcome.XXX ) -setupYourMacCommandFile=$( mktemp -u /var/tmp/dialogCommandFileSetupYourMac.XXX ) -failureCommandFile=$( mktemp -u /var/tmp/dialogCommandFileFailure.XXX ) + esac +} -#################################################################################################### -# -# Welcome dialog -# -#################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# "Welcome" dialog Title, Message and Icon +# Validate Policy Result # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -welcomeTitle="Happy $( date +'%A' ), ${loggedInUserFirstname}! \nWelcome to your new ${modelName}" - -welcomeMessage="Please enter the **required** information for your ${modelName}, select your preferred **Configuration** then click **Continue** to start applying settings to your new Mac. \n\nOnce completed, the **Wait** button will be enabled and you‘ll be able to review the results before restarting your ${modelName}." - -if [[ -n "${supportTeamName}" ]]; then - - welcomeMessage+="\n\nIf you need assistance, please contact the **${supportTeamName}**: \n" - - if [[ -n "${supportTeamPhone}" ]]; then - welcomeMessage+="- **Telephone**: ${supportTeamPhone}\n" - fi - - if [[ -n "${supportTeamEmail}" ]]; then - welcomeMessage+="- **Email**: ${supportTeamEmail}\n" - fi - - if [[ -n "${supportTeamWebsite}" ]]; then - welcomeMessage+="- **Web**: ${supportTeamHyperlink}\n" - fi - - if [[ -n "${supportKB}" ]]; then - welcomeMessage+="- **Knowledge Base Article:** ${supportTeamErrorKB}\n" - fi - -fi - -welcomeMessage+="\n\n---" - -if { [[ "${promptForConfiguration}" == "true" ]] && [[ "${welcomeDialog}" != "messageOnly" ]]; } then - welcomeMessage+=" \n\n#### Configurations \n- **${configurationOneName}:** ${configurationOneDescription} \n- **${configurationTwoName}:** ${configurationTwoDescription} \n- **${configurationThreeName}:** ${configurationThreeDescription}" -else - welcomeMessage=${welcomeMessage//", select your preferred **Configuration**"/} -fi - - -if [[ "${brandingBannerDisplayText}" == "true" ]]; then welcomeBannerText="Happy $( date +'%A' ), ${loggedInUserFirstname}! \nWelcome to your new ${modelName}"; -else welcomeBannerText=""; fi -welcomeCaption="Please review the above video, then click Continue." -welcomeVideoID="vimeoid=909473114" - - -# Check brandingBanner and cache if necessary -case ${brandingBanner} in - - *"https"* ) - welcomeBannerImage="${brandingBanner}" - bannerImage="${brandingBanner}" - if curl -L --output /dev/null --silent --head --fail "$welcomeBannerImage" || [ -f "$welcomeBannerImage" ]; then - updateScriptLog "WELCOME DIALOG: brandingBanner is available, using it" - else - updateScriptLog "WELCOME DIALOG: brandingBanner is not available, using a default image" - welcomeBannerImage="https://img.freepik.com/free-vector/green-abstract-geometric-wallpaper_52683-29623.jpg" # Image by pikisuperstar on Freepik - bannerImage="https://img.freepik.com/free-vector/green-abstract-geometric-wallpaper_52683-29623.jpg" # Image by pikisuperstar on Freepik - fi - - welcomeBannerImageFileName=$( echo ${welcomeBannerImage} | awk -F '/' '{print $NF}' ) - updateScriptLog "WELCOME DIALOG: Auto-caching hosted '$welcomeBannerImageFileName' …" - curl -L --location --silent "$welcomeBannerImage" -o "/var/tmp/${welcomeBannerImageFileName}" - welcomeBannerImage="/var/tmp/${welcomeBannerImageFileName}" - bannerImage="/var/tmp/${welcomeBannerImageFileName}" - ;; - - */* ) - updateScriptLog "WELCOME DIALOG: brandingBanner is local file, using it" - welcomeBannerImage="${brandingBanner}" - bannerImage="${brandingBanner}" - ;; - - "None" | "none" | "" ) - updateScriptLog "WELCOME DIALOG: brandingBanner set to \"None\", or empty" - welcomeBannerImage="${brandingBanner}" - bannerImage="${brandingBanner}" - ;; +function validatePolicyResult() { - * ) - updateScriptLog "WELCOME DIALOG: brandingBanner set to \"None\"" - ;; + outputLineNumberInVerboseDebugMode -esac + trigger="${1}" + validation="${2}" + updateSetupYourMacDialog "Validate Policy Result: '${trigger}' '${validation}'" + case ${validation} in + ### + # Absolute Path + # Simulates pre-v1.6.0 behavior, for example: "/Applications/Microsoft Teams classic.app/Contents/Info.plist" + ### -# Welcome icon set to either light or dark, based on user's Apperance setting (thanks, @mm2270!) -appleInterfaceStyle=$( /usr/bin/defaults read /Users/"${loggedInUser}"/Library/Preferences/.GlobalPreferences.plist AppleInterfaceStyle 2>&1 ) -if [[ "${appleInterfaceStyle}" == "Dark" ]]; then - if [[ -n "$brandingIconDark" ]]; then welcomeIcon="$brandingIconDark"; - else welcomeIcon="https://cdn-icons-png.flaticon.com/512/740/740878.png"; fi -else - if [[ -n "$brandingIconLight" ]]; then welcomeIcon="$brandingIconLight"; - else welcomeIcon="https://cdn-icons-png.flaticon.com/512/979/979585.png"; fi -fi + */* ) + updateSetupYourMacDialog "Validate Policy Result: Testing for \"$validation\" …" + if [[ "${previouslyInstalled}" == "true" ]]; then + dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Previously Installed" + elif [[ -e "${validation}" ]]; then + dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Installed" + else + dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" + jamfProPolicyTriggerFailure="failed" + exitCode="1" + jamfProPolicyNameFailures+="• $listitem \n" + fi + ;; -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# "Welcome" Video Settings and Features -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + ### + # Local + # Validation within this script, for example: "rosetta" or "filevault" + ### -welcomeVideo="--title \"$welcomeTitle\" \ ---videocaption \"$welcomeCaption\" \ ---video \"$welcomeVideoID\" \ ---infotext \"$scriptVersion\" \ ---button1text \"Continue …\" \ ---autoplay \ ---moveable \ ---ontop \ ---width '800' \ ---height '600' \ ---commandfile \"$welcomeCommandFile\" " + "Local" ) + case ${trigger} in + rosetta ) + updateSetupYourMacDialog "Locally Validate Policy Result: Rosetta 2 … " # Thanks, @smithjw! + dialogUpdateSetupYourMac "listitem: index: $i, status: wait, statustext: Checking …" + arch=$( /usr/bin/arch ) + if [[ "${arch}" == "arm64" ]]; then + # Mac with Apple silicon; check for Rosetta + rosettaTest=$( arch -x86_64 /usr/bin/true 2> /dev/null ; echo $? ) + if [[ "${rosettaTest}" -eq 0 ]]; then + # Installed + updateSetupYourMacDialog "Locally Validate Policy Result: Rosetta 2 is installed" + dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Running" + else + # Not Installed + updateSetupYourMacDialog "Locally Validate Policy Result: Rosetta 2 is NOT installed" + dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" + jamfProPolicyTriggerFailure="failed" + exitCode="1" + jamfProPolicyNameFailures+="• $listitem \n" + fi + else + # Ineligible + updateSetupYourMacDialog "Locally Validate Policy Result: Rosetta 2 is not applicable" + dialogUpdateSetupYourMac "listitem: index: $i, status: error, statustext: Ineligible" + fi + ;; + filevault ) + updateSetupYourMacDialog "Locally Validate Policy Result: Validate FileVault … " + dialogUpdateSetupYourMac "listitem: index: $i, status: wait, statustext: Checking …" + updateSetupYourMacDialog "Validate Policy Result: Pausing for 5 seconds for FileVault … " + sleep 5 # Arbitrary value; tuning needed + fileVaultCheck=$( fdesetup isactive ) + if [[ -f /Library/Preferences/com.apple.fdesetup.plist ]] || [[ "$fileVaultCheck" == "true" ]]; then + fileVaultStatus=$( fdesetup status -extended -verbose 2>&1 ) + case ${fileVaultStatus} in + *"FileVault is On."* ) + updateSetupYourMacDialog "Locally Validate Policy Result: FileVault: FileVault is On." + dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Enabled" + ;; + *"Deferred enablement appears to be active for user"* ) + updateSetupYourMacDialog "Locally Validate Policy Result: FileVault: Enabled" + dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Enabled (next login)" + ;; + * ) + dialogUpdateSetupYourMac "listitem: index: $i, status: error, statustext: Unknown" + jamfProPolicyTriggerFailure="failed" + exitCode="1" + jamfProPolicyNameFailures+="• $listitem \n" + ;; + esac + else + updateSetupYourMacDialog "Locally Validate Policy Result: '/Library/Preferences/com.apple.fdesetup.plist' NOT Found" + dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" + jamfProPolicyTriggerFailure="failed" + exitCode="1" + jamfProPolicyNameFailures+="• $listitem \n" + fi + ;; + sophosEndpointServices ) + updateSetupYourMacDialog "Locally Validate Policy Result: Sophos Endpoint RTS Status … " + dialogUpdateSetupYourMac "listitem: index: $i, status: wait, statustext: Checking …" + if [[ -d /Applications/Sophos/Sophos\ Endpoint.app ]]; then + if [[ -f /Library/Preferences/com.sophos.sav.plist ]]; then + sophosOnAccessRunning=$( /usr/bin/defaults read /Library/Preferences/com.sophos.sav.plist OnAccessRunning ) + case ${sophosOnAccessRunning} in + "0" ) + updateSetupYourMacDialog "Locally Validate Policy Result: Sophos Endpoint RTS Status: Disabled" + dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" + jamfProPolicyTriggerFailure="failed" + exitCode="1" + jamfProPolicyNameFailures+="• $listitem \n" + ;; + "1" ) + updateSetupYourMacDialog "Locally Validate Policy Result: Sophos Endpoint RTS Status: Enabled" + dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Running" + ;; + * ) + updateSetupYourMacDialog "Locally Validate Policy Result: Sophos Endpoint RTS Status: Unknown" + dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Unknown" + jamfProPolicyTriggerFailure="failed" + exitCode="1" + jamfProPolicyNameFailures+="• $listitem \n" + ;; + esac + else + updateSetupYourMacDialog "Locally Validate Policy Result: Sophos Endpoint Not Found" + dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" + jamfProPolicyTriggerFailure="failed" + exitCode="1" + jamfProPolicyNameFailures+="• $listitem \n" + fi + else + dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" + jamfProPolicyTriggerFailure="failed" + exitCode="1" + jamfProPolicyNameFailures+="• $listitem \n" + fi + ;; + globalProtect ) + updateSetupYourMacDialog "Locally Validate Policy Result: Palo Alto Networks GlobalProtect Status … " + dialogUpdateSetupYourMac "listitem: index: $i, status: wait, statustext: Checking …" + if [[ -d /Applications/GlobalProtect.app ]]; then + updateSetupYourMacDialog "Locally Validate Policy Result: Pausing for 10 seconds to allow Palo Alto Networks GlobalProtect Services … " + sleep 10 # Arbitrary value; tuning needed + if [[ -f /Library/Preferences/com.paloaltonetworks.GlobalProtect.settings.plist ]]; then + globalProtectStatus=$( /usr/libexec/PlistBuddy -c "print :Palo\ Alto\ Networks:GlobalProtect:PanGPS:disable-globalprotect" /Library/Preferences/com.paloaltonetworks.GlobalProtect.settings.plist ) + case "${globalProtectStatus}" in + "0" ) + updateSetupYourMacDialog "Locally Validate Policy Result: Palo Alto Networks GlobalProtect Status: Enabled" + dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Running" + ;; + "1" ) + updateSetupYourMacDialog "Locally Validate Policy Result: Palo Alto Networks GlobalProtect Status: Disabled" + dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" + jamfProPolicyTriggerFailure="failed" + exitCode="1" + jamfProPolicyNameFailures+="• $listitem \n" + ;; + * ) + updateSetupYourMacDialog "Locally Validate Policy Result: Palo Alto Networks GlobalProtect Status: Unknown" + dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Unknown" + jamfProPolicyTriggerFailure="failed" + exitCode="1" + jamfProPolicyNameFailures+="• $listitem \n" + ;; + esac + else + updateSetupYourMacDialog "Locally Validate Policy Result: Palo Alto Networks GlobalProtect Not Found" + dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" + jamfProPolicyTriggerFailure="failed" + exitCode="1" + jamfProPolicyNameFailures+="• $listitem \n" + fi + else + dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" + jamfProPolicyTriggerFailure="failed" + exitCode="1" + jamfProPolicyNameFailures+="• $listitem \n" + fi + ;; + * ) + updateSetupYourMacDialog "Locally Validate Policy Result: Local Validation “${validation}” Missing" + dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Missing Local “${validation}” Validation" + jamfProPolicyTriggerFailure="failed" + exitCode="1" + jamfProPolicyNameFailures+="• $listitem \n" + ;; + esac + ;; -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# "Welcome" JSON Conditionals (thanks, @rougegoat!) -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + ### + # Remote + # Validation via a Jamf Pro policy which has a single-script payload, for example: "symvGlobalProtect" + # See: https://vimeo.com/782561166 + ### -# Text Fields -if [ "$prefillUsername" == "true" ]; then usernamePrefil=',"value" : "'${loggedInUser}'"'; fi -if [ "$prefillRealname" == "true" ]; then realnamePrefil=',"value" : "'${loggedInUserFullname}'"'; fi -if [ "$promptForUsername" == "true" ]; then usernameJSON='{ "title" : "User Name","required" : false,"prompt" : "User Name"'${usernamePrefil}'},'; fi -if [ "$promptForRealName" == "true" ]; then realNameJSON='{ "title" : "Full Name","required" : false,"prompt" : "Full Name"'${realnamePrefil}'},'; fi -if [ "$promptForEmail" == "true" ]; then - emailJSON='{ "title" : "E-mail", - "required" : true, - "prompt" : "E-mail Address", - "regex" : "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", - "regexerror" : "Please enter a valid email address." - },' -fi -if [ "$promptForComputerName" == "true" ]; then compNameJSON='{ "title" : "Computer Name","required" : false,"prompt" : "Computer Name" },'; fi -if [ "$promptForAssetTag" == "true" ]; then - assetTagJSON='{ "title" : "Asset Tag", - "required" : true, - "prompt" : "Please enter the (at least) seven-digit Asset Tag", - "regex" : "^(AP|IP|CD)?[0-9]{7,}$", - "regexerror" : "Please enter (at least) seven digits for the Asset Tag, optionally preceded by either AP, IP or CD." - },' -fi -if [ "$promptForRoom" == "true" ]; then roomJSON='{ "title" : "Room","required" : false,"prompt" : "Optional" },'; fi -if [[ "$promptForPosition" == "true" && -z "$positionListRaw" ]]; then positionJSON='{ "title" : "Position","required" : false,"prompt" : "Position" },'; fi + "Remote" ) + if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then + updateSetupYourMacDialog "DEBUG MODE: Remotely Confirm Policy Execution: Skipping 'run_jamf_trigger ${trigger}'" + dialogUpdateSetupYourMac "listitem: index: $i, status: error, statustext: Debug Mode Enabled" + sleep 0.5 + else + updateSetupYourMacDialog "Remotely Validate '${trigger}' '${validation}'" + dialogUpdateSetupYourMac "listitem: index: $i, status: wait, statustext: Checking …" + result=$( "${jamfBinary}" policy -event "${trigger}" | grep "Script result:" ) + if [[ "${result}" == *"Running"* ]]; then + dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Running" + elif [[ "${result}" == *"Installed"* || "${result}" == *"Success"* ]]; then + dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Installed" + else + dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" + jamfProPolicyTriggerFailure="failed" + exitCode="1" + jamfProPolicyNameFailures+="• $listitem \n" + fi + fi + ;; -textFieldJSON="${usernameJSON}${realNameJSON}${emailJSON}${compNameJSON}${assetTagJSON}${positionJSON}${roomJSON}" -textFieldJSON=$( echo ${textFieldJSON} | sed 's/,$//' ) -# Dropdowns -if [ "$promptForBuilding" == "true" ]; then - if [ -n "$buildingsListRaw" ]; then - buildingJSON='{ - "title" : "Building", - "default" : "", - "required" : true, - "values" : [ - '${buildingsList}' - ] - },' - fi -fi -if [ "$promptForDepartment" == "true" ]; then - if [ -n "$departmentListRaw" ]; then - departmentJSON='{ - "title" : "Department", - "default" : "", - "required" : true, - "values" : [ - '${departmentList}' - ] - },' - fi -fi + ### + # None: For triggers which don't require validation + # (Always evaluates as: 'success' and 'Installed') + ### -if [ "$promptForPosition" == "true" ]; then - if [ -n "${positionListRaw}" ]; then - positionSelectJSON='{ - "title" : "Position", - "default" : "", - "required" : true, - "values" : [ - '${positionList}' - ] - },' - fi -fi + "None" | "none") -if [ "$promptForConfiguration" == "true" ] && [ -z "${presetConfiguration}" ]; then - configurationJSON='{ - "title" : "Configuration", - "style" : "radio", - "default" : "'"${configurationOneName}"'", - "values" : [ - "'"${configurationOneName}"'", - "'"${configurationTwoName}"'", - "'"${configurationThreeName}"'" - ] - }' -fi + outputLineNumberInVerboseDebugMode + updateSetupYourMacDialog "Confirm Policy Execution: ${validation}" + dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Installed" + ;; -selectItemsJSON="${buildingJSON}${departmentJSON}${positionSelectJSON}${configurationJSON}" -selectItemsJSON=$( echo $selectItemsJSON | sed 's/,$//' ) + + + ### + # Recon: For reporting computer inventory update + # (Always evaluates as: 'success' and 'Updated') + ### + + "Recon" | "recon" ) + + outputLineNumberInVerboseDebugMode + updateSetupYourMacDialog "Confirm Policy Execution: ${validation}" + dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Updated" + ;; + + + + ### + # Catch-all + ### + + * ) + + outputLineNumberInVerboseDebugMode + updateSetupYourMacDialog "Validate Policy Results Catch-all: ${validation}" + dialogUpdateSetupYourMac "listitem: index: $i, status: error, statustext: Error" + ;; + + esac + +} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# "Welcome" JSON for Capturing User Input (thanks, @bartreardon!) +# Kill a specified process (thanks, @grahampugh!) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -welcomeJSON=' -{ - "commandfile" : "'"${welcomeCommandFile}"'", - "bannerimage" : "'"${welcomeBannerImage}"'", - "bannertext" : "'"${welcomeBannerText}"'", - "title" : "'"${welcomeTitle}"'", - "message" : "'"${welcomeMessage}"'", - "icon" : "'"${welcomeIcon}"'", - "infobox" : "Analyzing …", - "iconsize" : "198.0", - "button1text" : "Continue", - "button2text" : "Quit", - "infotext" : "'"${scriptVersion}"'", - "blurscreen" : "true", - "ontop" : "true", - "titlefont" : "shadow=true, size=36, colour=#FFFDF4", - "messagefont" : "size=14", - "textfield" : [ - '${textFieldJSON}' - ], - "selectitems" : [ - '${selectItemsJSON}' - ], - "height" : "800" +function killProcess() { + process="$1" + if process_pid=$( pgrep -a "${process}" 2>/dev/null ) ; then + info "Attempting to terminate the '$process' process …" + info "(Termination message indicates success.)" + kill "$process_pid" 2> /dev/null + if pgrep -a "$process" >/dev/null ; then + error "'$process' could not be terminated." + fi + else + info "The '$process' process isn't running." + fi } -' - -#################################################################################################### -# -# Setup Your Mac dialog -# -#################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# "Setup Your Mac" dialog Title, Message, Icon and Overlay Icon +# Completion Action (i.e., Wait, Sleep, Logout, Restart or Shutdown) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -title="Setting up ${loggedInUserFirstname}‘s ${modelName}" -message="Please wait while the following apps are installed …" +function completionAction() { -if [[ "${brandingBannerDisplayText}" == "true" ]] ; then - bannerText="Setting up ${loggedInUserFirstname}‘s ${modelName}"; -else - bannerText="" -fi + outputLineNumberInVerboseDebugMode -if [ -n "$supportTeamName" ]; then - helpmessage+="If you need assistance, please contact: \n\n**${supportTeamName}** \n" -fi + if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then -if [ -n "$supportTeamPhone" ]; then - helpmessage+="- **Telephone:** ${supportTeamPhone} \n" -fi + # If Debug Mode is enabled, ignore specified `completionActionOption`, display simple dialog box and exit + runAsUser osascript -e 'display dialog "Setup Your Mac is operating in Debug Mode.\r\r• completionActionOption == '"'${completionActionOption}'"'\r\r" with title "Setup Your Mac: Debug Mode" buttons {"Close"} with icon note' + exitCode="0" -if [ -n "$supportTeamEmail" ]; then - helpmessage+="- **Email:** ${supportTeamEmail} \n" -fi + else -if [ -n "$supportTeamWebsite" ]; then - helpmessage+="- **Web**: ${supportTeamHyperlink} \n" -fi + shopt -s nocasematch -if [ -n "$supportKB" ]; then - helpmessage+="- **Knowledge Base Article:** ${supportTeamErrorKB} \n" -fi + case ${completionActionOption} in -helpmessage+="\n**Computer Information:** \n" -helpmessage+="- **Operating System:** ${macOSproductVersion} (${macOSbuildVersion}) \n" -helpmessage+="- **Serial Number:** ${serialNumber} \n" -helpmessage+="- **Dialog:** ${dialogVersion} \n" -helpmessage+="- **Started:** ${timestamp}" + "Shut Down" ) + completionActionOut "Shut Down sans user interaction" + killProcess "Self Service" + # runAsUser osascript -e 'tell app "System Events" to shut down' + # sleep 5 && runAsUser osascript -e 'tell app "System Events" to shut down' & + sleep 5 && shutdown -h now & + ;; -infobox="Analyzing input …" # Customize at "Update Setup Your Mac's infobox" + "Shut Down Attended" ) + completionActionOut "Shut Down, requiring user-interaction" + killProcess "Self Service" + wait + # runAsUser osascript -e 'tell app "System Events" to shut down' + # sleep 5 && runAsUser osascript -e 'tell app "System Events" to shut down' & + sleep 5 && shutdown -h now & + ;; + "Shut Down Confirm" ) + completionActionOut "Shut down, only after macOS time-out or user confirmation" + runAsUser osascript -e 'tell app "loginwindow" to «event aevtrsdn»' + ;; -# Create `overlayicon` from Self Service's custom icon (thanks, @meschwartz!) -xxd -p -s 260 "$(defaults read /Library/Preferences/com.jamfsoftware.jamf self_service_app_path)"/Icon$'\r'/..namedfork/rsrc | xxd -r -p > /var/tmp/overlayicon.icns -overlayicon="/var/tmp/overlayicon.icns" + "Restart" ) + completionActionOut "Restart sans user interaction" + killProcess "Self Service" + # runAsUser osascript -e 'tell app "System Events" to restart' + # sleep 5 && runAsUser osascript -e 'tell app "System Events" to restart' & + sleep 5 && shutdown -r now & + ;; -# Uncomment to use generic, Self Service icon as overlayicon -# overlayicon="https://ics.services.jamfcloud.com/icon/hash_aa63d5813d6ed4846b623ed82acdd1562779bf3716f2d432a8ee533bba8950ee" + "Restart Attended" ) + completionActionOut "Restart, requiring user-interaction" + killProcess "Self Service" + wait + # runAsUser osascript -e 'tell app "System Events" to restart' + # sleep 5 && runAsUser osascript -e 'tell app "System Events" to restart' & + sleep 5 && shutdown -r now & + ;; -# Set initial icon based on whether the Mac is a desktop or laptop -if system_profiler SPPowerDataType | grep -q "Battery Power"; then - icon="SF=laptopcomputer.and.arrow.down,weight=semibold,colour1=#ef9d51,colour2=#ef7951" -else - icon="SF=desktopcomputer.and.arrow.down,weight=semibold,colour1=#ef9d51,colour2=#ef7951" -fi + "Restart Confirm" ) + completionActionOut "Restart, only after macOS time-out or user confirmation" + runAsUser osascript -e 'tell app "loginwindow" to «event aevtrrst»' + ;; + "Log Out" ) + completionActionOut "Log out sans user interaction" + killProcess "Self Service" + # sleep 5 && runAsUser osascript -e 'tell app "loginwindow" to «event aevtrlgo»' + # sleep 5 && runAsUser osascript -e 'tell app "loginwindow" to «event aevtrlgo»' & + sleep 5 && launchctl bootout user/"${loggedInUserID}" + ;; + "Log Out Attended" ) + completionActionOut "Log out sans user interaction" + killProcess "Self Service" + wait + # sleep 5 && runAsUser osascript -e 'tell app "loginwindow" to «event aevtrlgo»' + # sleep 5 && runAsUser osascript -e 'tell app "loginwindow" to «event aevtrlgo»' & + sleep 5 && launchctl bootout user/"${loggedInUserID}" + ;; -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# "Setup Your Mac" dialog Settings and Features -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + "Log Out Confirm" ) + completionActionOut "Log out, only after macOS time-out or user confirmation" + sleep 5 && runAsUser osascript -e 'tell app "System Events" to log out' + ;; -dialogSetupYourMacCMD="$dialogBinary \ ---bannerimage \"$bannerImage\" \ ---bannertext \"$bannerText\" \ ---title \"$title\" \ ---message \"$message\" \ ---helpmessage \"$helpmessage\" \ ---icon \"$icon\" \ ---infobox \"${infobox}\" \ ---progress \ ---progresstext \"Initializing configuration …\" \ ---button1text \"Wait\" \ ---button1disabled \ ---infotext \"$scriptVersion\" \ ---titlefont 'shadow=true, size=36, colour=#FFFDF4' \ ---messagefont 'size=14' \ ---height '800' \ ---position 'centre' \ ---blurscreen \ ---ontop \ ---overlayicon \"$overlayicon\" \ ---quitkey k \ ---commandfile \"$setupYourMacCommandFile\" " + "Sleep"* ) + sleepDuration=$( awk '{print $NF}' <<< "${1}" ) + completionActionOut "Sleeping for ${sleepDuration} seconds …" + sleep "${sleepDuration}" + killProcess "Dialog" + info "Goodnight!" + ;; + "Wait" ) + completionActionOut "Waiting for user interaction …" + wait + ;; + "Quit" ) + completionActionOut "Quitting script" + exitCode="0" + ;; -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# [SYM-Helper] "Setup Your Mac" policies to execute (Thanks, Obi-@smithjw!) -# -# For each configuration step, specify: -# - listitem: The text to be displayed in the list -# - icon: The hash of the icon to be displayed on the left -# - See: https://vimeo.com/772998915 -# - progresstext: The text to be displayed below the progress bar -# - trigger: The Jamf Pro Policy Custom Event Name -# - validation: [ {absolute path} | Local | Remote | None | Recon ] -# See: https://snelson.us/2023/01/setup-your-mac-validation/ -# - {absolute path} (simulates pre-v1.6.0 behavior, for example: "/Applications/Microsoft Teams classic.app/Contents/Info.plist") -# - Local (for validation within this script, for example: "filevault") -# - Remote (for validation via a single-script Jamf Pro policy, for example: "symvGlobalProtect") -# - None (for triggers which don't require validation; always evaluates as successful) -# - Recon (to update the computer's inventory with your Jamf Pro server) -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# Thanks, @wakco: If you would prefer to get your policyJSON externally replace it with: -# - policyJSON="$(cat /path/to/file.json)" # For getting from a file, replacing /path/to/file.json with the path to your file, or -# - policyJSON="$(curl -sL https://server.name/jsonquery)" # For a URL, replacing https://server.name/jsonquery with the URL of your file. -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# Thanks, @astrugatch: I added this line to global variables: -# jsonURL=${10} # URL Hosting JSON for policy_array -# -# And this line replaces the entirety of the policy_array (~ line 503): -# policy_array=("$(curl -sL $jsonURL)") -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + * ) + completionActionOut "Using the default of 'wait'" + wait + ;; + esac + shopt -u nocasematch -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Select `policyJSON` based on Configuration selected in "Welcome" dialog (thanks, @drtaru!) -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + fi -function policyJSONConfiguration() { + # Remove custom welcomeBannerImageFileName + if [[ -e "/var/tmp/${welcomeBannerImageFileName}" ]]; then + completionActionOut "Removing /var/tmp/${welcomeBannerImageFileName} …" + rm "/var/tmp/${welcomeBannerImageFileName}" + fi - outputLineNumberInVerboseDebugMode + # Remove overlayicon + if [[ -e ${overlayicon} ]]; then + completionActionOut "Removing ${overlayicon} …" + rm "${overlayicon}" + fi - updateScriptLog "WELCOME DIALOG: PolicyJSON Configuration: $symConfiguration" + exit "${exitCode}" - case ${symConfiguration} in +} - "${configurationOneName}" ) - overlayoverride="" - policyJSON=' - { - "steps": [ - { - "listitem": "Rosetta", - "subtitle": "Enables a Mac with Apple silicon to use apps built for an Intel processor", - "icon": "https://ics.services.jamfcloud.com/icon/hash_8bac19160fabb0c8e7bac97b37b51d2ac8f38b7100b6357642d9505645d37b52", - "progresstext": "Rosetta enables a Mac with Apple silicon to use apps built for a Mac with an Intel processor.", - "trigger_list": [ - { - "trigger": "rosettaInstall", - "validation": "None" - }, - { - "trigger": "rosetta", - "validation": "Local" - } - ] - }, - { - "listitem": "FileVault Disk Encryption", - "subtitle": "FileVault provides full-disk encryption", - "icon": "https://ics.services.jamfcloud.com/icon/hash_f9ba35bd55488783456d64ec73372f029560531ca10dfa0e8154a46d7732b913", - "progresstext": "FileVault is built-in to macOS and provides full-disk encryption to help prevent unauthorized access to your Mac.", - "trigger_list": [ - { - "trigger": "filevault", - "validation": "Local" - } - ] - }, - { - "listitem": "Sophos Endpoint", - "subtitle": "Catches malware without relying on signatures", - "icon": "https://ics.services.jamfcloud.com/icon/hash_c70f1acf8c96b99568fec83e165d2a534d111b0510fb561a283d32aa5b01c60c", - "progresstext": "You’ll enjoy next-gen protection with Sophos Endpoint which doesn’t rely on signatures to catch malware.", - "trigger_list": [ - { - "trigger": "sophosEndpoint", - "validation": "/Applications/Sophos/Sophos Endpoint.app/Contents/Info.plist" - } - ] - }, - { - "listitem": "Sophos Endpoint Services (Remote)", - "subtitle": "Ensures Sophos Endpoint services are running", - "icon": "https://ics.services.jamfcloud.com/icon/hash_0f68be689684a00a3a054d71a31e43e2362f96c16efa5a560fb61bc1bf41901c", - "progresstext": "Remotely validating Sophos Endpoint services …", - "trigger_list": [ - { - "trigger": "symvSophosEndpointRTS", - "validation": "Remote" - } - ] - }, - { - "listitem": "Palo Alto GlobalProtect", - "subtitle": "Virtual Private Network (VPN) connection to Church headquarters", - "icon": "https://ics.services.jamfcloud.com/icon/hash_acbf39d8904ad1a772cf71c45d93e373626d379a24f8b1283b88134880acb8ef", - "progresstext": "Use Palo Alto GlobalProtect to establish a Virtual Private Network (VPN) connection to Church headquarters.", - "trigger_list": [ - { - "trigger": "globalProtect", - "validation": "/Applications/GlobalProtect.app/Contents/Info.plist" - } - ] - }, - { - "listitem": "Palo Alto GlobalProtect Services (Remote)", - "subtitle": "Ensures GlobalProtect services are running", - "icon": "https://ics.services.jamfcloud.com/icon/hash_709e8bdf0019e8faf9df85ec0a68545bfdb8bfa1227ac9bed9bba40a1fa8ff42", - "progresstext": "Remotely validating Palo Alto GlobalProtect services …", - "trigger_list": [ - { - "trigger": "symvGlobalProtect", - "validation": "Remote" - } - ] - }, - { - "listitem": "Final Configuration", - "subtitle": "Configures remaining Church settings", - "icon": "https://ics.services.jamfcloud.com/icon/hash_4723e3e341a7e11e6881e418cf91b157fcc081bdb8948697750e5da3562df728", - "progresstext": "Finalizing Configuration …", - "trigger_list": [ - { - "trigger": "finalConfiguration", - "validation": "None" - }, - { - "trigger": "reconAtReboot", - "validation": "None" - } - ] - }, - { - "listitem": "Computer Inventory", - "subtitle": "The listing of your Mac’s apps and settings", - "icon": "https://ics.services.jamfcloud.com/icon/hash_ff2147a6c09f5ef73d1c4406d00346811a9c64c0b6b7f36eb52fcb44943d26f9", - "progresstext": "A listing of your Mac’s apps and settings — its inventory — is sent automatically to the Jamf Pro server daily.", - "trigger_list": [ - { - "trigger": "recon", - "validation": "recon" - } - ] - } - ] - } - ' - ;; - "${configurationTwoName}" ) +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Welcome dialog 'infobox' animation (thanks, @bartreadon!) +# To convert emojis, see: https://r12a.github.io/app-conversion/ +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - overlayoverride="" - policyJSON=' - { - "steps": [ - { - "listitem": "Rosetta", - "subtitle": "Enables a Mac with Apple silicon to use apps built for an Intel processor", - "icon": "https://ics.services.jamfcloud.com/icon/hash_8bac19160fabb0c8e7bac97b37b51d2ac8f38b7100b6357642d9505645d37b52", - "progresstext": "Rosetta enables a Mac with Apple silicon to use apps built for a Mac with an Intel processor.", - "trigger_list": [ - { - "trigger": "rosettaInstall", - "validation": "None" - }, - { - "trigger": "rosetta", - "validation": "Local" - } - ] - }, - { - "listitem": "FileVault Disk Encryption", - "subtitle": "FileVault provides full-disk encryption", - "icon": "https://ics.services.jamfcloud.com/icon/hash_f9ba35bd55488783456d64ec73372f029560531ca10dfa0e8154a46d7732b913", - "progresstext": "FileVault is built-in to macOS and provides full-disk encryption to help prevent unauthorized access to your Mac.", - "trigger_list": [ - { - "trigger": "filevault", - "validation": "Local" - } - ] - }, - { - "listitem": "Sophos Endpoint", - "subtitle": "Catches malware without relying on signatures", - "icon": "https://ics.services.jamfcloud.com/icon/hash_c70f1acf8c96b99568fec83e165d2a534d111b0510fb561a283d32aa5b01c60c", - "progresstext": "You’ll enjoy next-gen protection with Sophos Endpoint which doesn’t rely on signatures to catch malware.", - "trigger_list": [ - { - "trigger": "sophosEndpoint", - "validation": "/Applications/Sophos/Sophos Endpoint.app/Contents/Info.plist" - } - ] - }, - { - "listitem": "Sophos Endpoint Services (Local)", - "subtitle": "Ensures Sophos Endpoint services are running", - "icon": "https://ics.services.jamfcloud.com/icon/hash_0f68be689684a00a3a054d71a31e43e2362f96c16efa5a560fb61bc1bf41901c", - "progresstext": "Locally validating Sophos Endpoint services …", - "trigger_list": [ - { - "trigger": "sophosEndpointServices", - "validation": "Local" - } - ] - }, - { - "listitem": "Palo Alto GlobalProtect", - "subtitle": "Virtual Private Network (VPN) connection to Church headquarters", - "icon": "https://ics.services.jamfcloud.com/icon/hash_acbf39d8904ad1a772cf71c45d93e373626d379a24f8b1283b88134880acb8ef", - "progresstext": "Use Palo Alto GlobalProtect to establish a Virtual Private Network (VPN) connection to Church headquarters.", - "trigger_list": [ - { - "trigger": "globalProtect", - "validation": "/Applications/GlobalProtect.app/Contents/Info.plist" - } - ] - }, - { - "listitem": "Palo Alto GlobalProtect Services (Local)", - "subtitle": "Ensures GlobalProtect services are running", - "icon": "https://ics.services.jamfcloud.com/icon/hash_709e8bdf0019e8faf9df85ec0a68545bfdb8bfa1227ac9bed9bba40a1fa8ff42", - "progresstext": "Locally validating Palo Alto GlobalProtect services …", - "trigger_list": [ - { - "trigger": "globalProtect", - "validation": "Local" - } - ] - }, - { - "listitem": "Microsoft 365", - "subtitle": "Microsoft Office is now Microsoft 365", - "icon": "https://ics.services.jamfcloud.com/icon/hash_1801d1fdd81e19ce5eb0e567371377e7995bff32947adb7a94c5feea760edcb5", - "progresstext": "Office is now Microsoft 365. Create, share, and collaborate with your favorite apps — all in one place — with Microsoft 365.", - "trigger_list": [ - { - "trigger": "microsoftOffice365", - "validation": "/Applications/Microsoft Outlook.app/Contents/Info.plist" - }, - { - "trigger": "symvMicrosoftOffice365", - "validation": "Remote" - } - ] - }, - { - "listitem": "Microsoft Teams", - "subtitle": "The hub for teamwork in Microsoft 365", - "icon": "https://ics.services.jamfcloud.com/icon/hash_dcb65709dba6cffa90a5eeaa54cb548d5ecc3b051f39feadd39e02744f37c19e", - "progresstext": "Microsoft Teams is a hub for teamwork in Microsoft 365. Keep all your team’s chats, meetings and files together in one place.", - "trigger_list": [ - { - "trigger": "microsoftTeams", - "validation": "/Applications/Microsoft Teams classic.app/Contents/Info.plist" - } - ] - }, - { - "listitem": "Final Configuration", - "subtitle": "Configures remaining Church settings", - "icon": "https://ics.services.jamfcloud.com/icon/hash_4723e3e341a7e11e6881e418cf91b157fcc081bdb8948697750e5da3562df728", - "progresstext": "Finalizing Configuration …", - "trigger_list": [ - { - "trigger": "finalConfiguration", - "validation": "None" - }, - { - "trigger": "reconAtReboot", - "validation": "None" - } - ] - }, - { - "listitem": "Computer Inventory", - "subtitle": "The listing of your Mac’s apps and settings", - "icon": "https://ics.services.jamfcloud.com/icon/hash_ff2147a6c09f5ef73d1c4406d00346811a9c64c0b6b7f36eb52fcb44943d26f9", - "progresstext": "A listing of your Mac’s apps and settings — its inventory — is sent automatically to the Jamf Pro server daily.", - "trigger_list": [ - { - "trigger": "recon", - "validation": "recon" - } - ] - } - ] - } - ' +function welcomeDialogInfoboxAnimation() { + callingPID=$1 + # clock_emojis=("🕐" "🕑" "🕒" "🕓" "🕔" "🕕" "🕖" "🕗" "🕘" "🕙" "🕚" "🕛") + clock_emojis=("🕐" "🕑" "🕒" "🕓" "🕔" "🕕" "🕖" "🕗" "🕘" "🕙" "🕚" "🕛") + while true; do + for emoji in "${clock_emojis[@]}"; do + if kill -0 "$callingPID" 2>/dev/null; then + dialogUpdateWelcome "infobox: Testing Connection $emoji" + else + break + fi + sleep 0.6 + done + done +} + + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Setup Your Mac dialog 'infobox' animation (thanks, @bartreadon!) +# To convert emojis, see: https://r12a.github.io/app-conversion/ +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +function setupYourMacDialogInfoboxAnimation() { + callingPID=$1 + # clock_emojis=("🕐" "🕑" "🕒" "🕓" "🕔" "🕕" "🕖" "🕗" "🕘" "🕙" "🕚" "🕛") + clock_emojis=("🕐" "🕑" "🕒" "🕓" "🕔" "🕕" "🕖" "🕗" "🕘" "🕙" "🕚" "🕛") + while true; do + for emoji in "${clock_emojis[@]}"; do + if kill -0 "$callingPID" 2>/dev/null; then + dialogUpdateSetupYourMac "infobox: Testing Connection $emoji" + else + break + fi + sleep 0.6 + done + done +} + + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Check Network Quality for Configurations (thanks, @bartreadon!) +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +function checkNetworkQualityConfigurations() { + + myPID="$$" + welcomeDialog "Display Welcome dialog 'infobox' animation …" + welcomeDialogInfoboxAnimation "$myPID" & + welcomeDialogInfoboxAnimationPID="$!" + + networkQuality -s -v -c > /var/tmp/networkQualityTest + kill ${welcomeDialogInfoboxAnimationPID} + outputLineNumberInVerboseDebugMode + + welcomeDialog "Completed networkQualityTest …" + networkQualityTest=$( < /var/tmp/networkQualityTest ) + rm /var/tmp/networkQualityTest + + case "${osVersion}" in + + 11* ) + dlThroughput="N/A; macOS ${osVersion}" + dlResponsiveness="N/A; macOS ${osVersion}" + dlStartDate="N/A; macOS ${osVersion}" + dlEndDate="N/A; macOS ${osVersion}" ;; - "${configurationThreeName}" ) + 12* | 13* | 14*) + dlThroughput=$( get_json_value "$networkQualityTest" "dl_throughput") + dlResponsiveness=$( get_json_value "$networkQualityTest" "dl_responsiveness" ) + dlStartDate=$( get_json_value "$networkQualityTest" "start_date" ) + dlEndDate=$( get_json_value "$networkQualityTest" "end_date" ) + ;; - overlayoverride="" - policyJSON=' - { - "steps": [ - { - "listitem": "Rosetta", - "subtitle": "Enables a Mac with Apple silicon to use apps built for an Intel processor", - "icon": "https://ics.services.jamfcloud.com/icon/hash_8bac19160fabb0c8e7bac97b37b51d2ac8f38b7100b6357642d9505645d37b52", - "progresstext": "Rosetta enables a Mac with Apple silicon to use apps built for a Mac with an Intel processor.", - "trigger_list": [ - { - "trigger": "rosettaInstall", - "validation": "None" - }, - { - "trigger": "rosetta", - "validation": "Local" - } - ] - }, - { - "listitem": "FileVault Disk Encryption", - "subtitle": "FileVault provides full-disk encryption", - "icon": "https://ics.services.jamfcloud.com/icon/hash_f9ba35bd55488783456d64ec73372f029560531ca10dfa0e8154a46d7732b913", - "progresstext": "FileVault is built-in to macOS and provides full-disk encryption to help prevent unauthorized access to your Mac.", - "trigger_list": [ - { - "trigger": "filevault", - "validation": "Local" - } - ] - }, - { - "listitem": "Sophos Endpoint", - "subtitle": "Catches malware without relying on signatures", - "icon": "https://ics.services.jamfcloud.com/icon/hash_c70f1acf8c96b99568fec83e165d2a534d111b0510fb561a283d32aa5b01c60c", - "progresstext": "You’ll enjoy next-gen protection with Sophos Endpoint which doesn’t rely on signatures to catch malware.", - "trigger_list": [ - { - "trigger": "sophosEndpoint", - "validation": "/Applications/Sophos/Sophos Endpoint.app/Contents/Info.plist" - } - ] - }, - { - "listitem": "Sophos Endpoint Services (Local)", - "subtitle": "Ensures Sophos Endpoint services are running", - "icon": "https://ics.services.jamfcloud.com/icon/hash_0f68be689684a00a3a054d71a31e43e2362f96c16efa5a560fb61bc1bf41901c", - "progresstext": "Locally validating Sophos Endpoint services …", - "trigger_list": [ - { - "trigger": "sophosEndpointServices", - "validation": "Local" - } - ] - }, - { - "listitem": "Sophos Endpoint Services (Remote)", - "subtitle": "Ensures Sophos Endpoint services are running", - "icon": "https://ics.services.jamfcloud.com/icon/hash_0f68be689684a00a3a054d71a31e43e2362f96c16efa5a560fb61bc1bf41901c", - "progresstext": "Remotely validating Sophos Endpoint services …", - "trigger_list": [ - { - "trigger": "symvSophosEndpointRTS", - "validation": "Remote" - } - ] - }, - { - "listitem": "Palo Alto GlobalProtect", - "subtitle": "Virtual Private Network (VPN) connection to Church headquarters", - "icon": "https://ics.services.jamfcloud.com/icon/hash_acbf39d8904ad1a772cf71c45d93e373626d379a24f8b1283b88134880acb8ef", - "progresstext": "Use Palo Alto GlobalProtect to establish a Virtual Private Network (VPN) connection to Church headquarters.", - "trigger_list": [ - { - "trigger": "globalProtect", - "validation": "/Applications/GlobalProtect.app/Contents/Info.plist" - } - ] - }, - { - "listitem": "Palo Alto GlobalProtect Services (Local)", - "subtitle": "Ensures GlobalProtect services are running", - "icon": "https://ics.services.jamfcloud.com/icon/hash_709e8bdf0019e8faf9df85ec0a68545bfdb8bfa1227ac9bed9bba40a1fa8ff42", - "progresstext": "Locally validating Palo Alto GlobalProtect services …", - "trigger_list": [ - { - "trigger": "globalProtect", - "validation": "Local" - } - ] - }, - { - "listitem": "Palo Alto GlobalProtect Services (Remote)", - "subtitle": "Ensures GlobalProtect services are running", - "icon": "https://ics.services.jamfcloud.com/icon/hash_709e8bdf0019e8faf9df85ec0a68545bfdb8bfa1227ac9bed9bba40a1fa8ff42", - "progresstext": "Remotely validating Palo Alto GlobalProtect services …", - "trigger_list": [ - { - "trigger": "symvGlobalProtect", - "validation": "Remote" - } - ] - }, - { - "listitem": "Microsoft 365", - "subtitle": "Microsoft Office is now Microsoft 365", - "icon": "https://ics.services.jamfcloud.com/icon/hash_1801d1fdd81e19ce5eb0e567371377e7995bff32947adb7a94c5feea760edcb5", - "progresstext": "Office is now Microsoft 365. Create, share, and collaborate with your favorite apps — all in one place — with Microsoft 365.", - "trigger_list": [ - { - "trigger": "microsoftOffice365", - "validation": "/Applications/Microsoft Outlook.app/Contents/Info.plist" - }, - { - "trigger": "symvMicrosoftOffice365", - "validation": "Remote" - } - ] - }, - { - "listitem": "Microsoft Teams", - "subtitle": "The hub for teamwork in Microsoft 365", - "icon": "https://ics.services.jamfcloud.com/icon/hash_dcb65709dba6cffa90a5eeaa54cb548d5ecc3b051f39feadd39e02744f37c19e", - "progresstext": "Microsoft Teams is a hub for teamwork in Microsoft 365. Keep all your team’s chats, meetings and files together in one place.", - "trigger_list": [ - { - "trigger": "microsoftTeams", - "validation": "/Applications/Microsoft Teams classic.app/Contents/Info.plist" - } - ] - }, - { - "listitem": "Adobe Acrobat Reader", - "subtitle": "Full-featured PDF reader", - "icon": "https://ics.services.jamfcloud.com/icon/hash_988b669ca27eab93a9bcd53bb7e2873fb98be4eaa95ae8974c14d611bea1d95f", - "progresstext": "Views, prints, and comments on PDF documents, and connects to Adobe Document Cloud.", - "trigger_list": [ - { - "trigger": "adobeAcrobatReader", - "validation": "/Applications/Adobe Acrobat Reader.app/Contents/Info.plist" - } - ] - }, - { - "listitem": "Google Chrome", - "subtitle": "Third-party Web browser", - "icon": "https://ics.services.jamfcloud.com/icon/hash_12d3d198f40ab2ac237cff3b5cb05b09f7f26966d6dffba780e4d4e5325cc701", - "progresstext": "Google Chrome is a browser that combines a minimal design with sophisticated technology to make the Web faster.", - "trigger_list": [ - { - "trigger": "googleChrome", - "validation": "/Applications/Google Chrome.app/Contents/Info.plist" - } - ] - }, - { - "listitem": "Final Configuration", - "subtitle": "Configures remaining Church settings", - "icon": "https://ics.services.jamfcloud.com/icon/hash_4723e3e341a7e11e6881e418cf91b157fcc081bdb8948697750e5da3562df728", - "progresstext": "Finalizing Configuration …", - "trigger_list": [ - { - "trigger": "finalConfiguration", - "validation": "None" - }, - { - "trigger": "reconAtReboot", - "validation": "None" - } - ] - }, - { - "listitem": "Computer Inventory", - "subtitle": "The listing of your Mac’s apps and settings", - "icon": "https://ics.services.jamfcloud.com/icon/hash_ff2147a6c09f5ef73d1c4406d00346811a9c64c0b6b7f36eb52fcb44943d26f9", - "progresstext": "A listing of your Mac’s apps and settings — its inventory — is sent automatically to the Jamf Pro server daily.", - "trigger_list": [ - { - "trigger": "recon", - "validation": "recon" - } - ] - } - ] - } - ' + esac + + mbps=$( echo "scale=2; ( $dlThroughput / 1000000 )" | bc ) + welcomeDialog "$mbps (Mbps)" + + configurationOneEstimatedSeconds=$( echo "scale=2; ((((( $configurationOneSize / $mbps ) * 60 ) * 60 ) * $correctionCoefficient ) + $configurationOneInstallBuffer)" | bc | sed 's/\.[0-9]*//' ) + welcomeDialog "Configuration One Estimated Seconds: $configurationOneEstimatedSeconds" + welcomeDialog "Configuration One Estimate: $(printf '%dh:%dm:%ds\n' $((configurationOneEstimatedSeconds/3600)) $((configurationOneEstimatedSeconds%3600/60)) $((configurationOneEstimatedSeconds%60)))" + + configurationTwoEstimatedSeconds=$( echo "scale=2; ((((( $configurationTwoSize / $mbps ) * 60 ) * 60 ) * $correctionCoefficient ) + $configurationTwoInstallBuffer)" | bc | sed 's/\.[0-9]*//' ) + welcomeDialog "Configuration Two Estimated Seconds: $configurationTwoEstimatedSeconds" + welcomeDialog "Configuration Two Estimate: $(printf '%dh:%dm:%ds\n' $((configurationTwoEstimatedSeconds/3600)) $((configurationTwoEstimatedSeconds%3600/60)) $((configurationTwoEstimatedSeconds%60)))" + + configurationThreeEstimatedSeconds=$( echo "scale=2; ((((( $configurationThreeSize / $mbps ) * 60 ) * 60 ) * $correctionCoefficient ) + $configurationThreeInstallBuffer)" | bc | sed 's/\.[0-9]*//' ) + welcomeDialog "Configuration Three Estimated Seconds: $configurationThreeEstimatedSeconds" + welcomeDialog "Configuration Three Estimate: $(printf '%dh:%dm:%ds\n' $((configurationThreeEstimatedSeconds/3600)) $((configurationThreeEstimatedSeconds%3600/60)) $((configurationThreeEstimatedSeconds%60)))" + + welcomeDialog "Network Quality Test: Started: $dlStartDate, Ended: $dlEndDate; Download: $mbps Mbps, Responsiveness: $dlResponsiveness" + dialogUpdateWelcome "infobox: **Connection:** \n- Download: \n$mbps Mbps \n\n**Estimates:** \n- ${configurationOneName}: \n$(printf '%dh:%dm:%ds\n' $((configurationOneEstimatedSeconds/3600)) $((configurationOneEstimatedSeconds%3600/60)) $((configurationOneEstimatedSeconds%60))) \n\n- ${configurationTwoName}: \n$(printf '%dh:%dm:%ds\n' $((configurationTwoEstimatedSeconds/3600)) $((configurationTwoEstimatedSeconds%3600/60)) $((configurationTwoEstimatedSeconds%60))) \n\n- ${configurationThreeName}: \n$(printf '%dh:%dm:%ds\n' $((configurationThreeEstimatedSeconds/3600)) $((configurationThreeEstimatedSeconds%3600/60)) $((configurationThreeEstimatedSeconds%60)))" + + # If option to lock the continue button is set to true, enable the continue button now to let the user progress + if [[ "${lockContinueBeforeEstimations}" == "true" ]]; then + welcomeDialog "Enabling Continue Button" + dialogUpdateWelcome "button1: enable" + fi +} + + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Check Network Quality for Catch-all Configuration (thanks, @bartreadon!) +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +function checkNetworkQualityCatchAllConfiguration() { + + myPID="$$" + updateSetupYourMacDialog "Display Welcome dialog 'infobox' animation …" + setupYourMacDialogInfoboxAnimation "$myPID" & + setupYourMacDialogInfoboxAnimationPID="$!" + + networkQuality -s -v -c > /var/tmp/networkQualityTest + kill ${setupYourMacDialogInfoboxAnimationPID} + outputLineNumberInVerboseDebugMode + + updateSetupYourMacDialog "Completed networkQualityTest …" + networkQualityTest=$( < /var/tmp/networkQualityTest ) + rm /var/tmp/networkQualityTest + + case "${osVersion}" in + + 11* ) + dlThroughput="N/A; macOS ${osVersion}" + dlResponsiveness="N/A; macOS ${osVersion}" + dlStartDate="N/A; macOS ${osVersion}" + dlEndDate="N/A; macOS ${osVersion}" ;; - * ) # Catch-all (i.e., used when `welcomeDialog` is set to `video`, `messageOnly` or `false`) - - overlayoverride="" - policyJSON=' - { - "steps": [ - { - "listitem": "Rosetta", - "subtitle": "Enables a Mac with Apple silicon to use apps built for an Intel processor", - "icon": "https://ics.services.jamfcloud.com/icon/hash_8bac19160fabb0c8e7bac97b37b51d2ac8f38b7100b6357642d9505645d37b52", - "progresstext": "Rosetta enables a Mac with Apple silicon to use apps built for a Mac with an Intel processor.", - "trigger_list": [ - { - "trigger": "rosettaInstall", - "validation": "None" - }, - { - "trigger": "rosetta", - "validation": "Local" - } - ] - }, - { - "listitem": "FileVault Disk Encryption", - "subtitle": "FileVault provides full-disk encryption", - "icon": "https://ics.services.jamfcloud.com/icon/hash_f9ba35bd55488783456d64ec73372f029560531ca10dfa0e8154a46d7732b913", - "progresstext": "FileVault is built-in to macOS and provides full-disk encryption to help prevent unauthorized access to your Mac.", - "trigger_list": [ - { - "trigger": "filevault", - "validation": "Local" - } - ] - }, - { - "listitem": "Sophos Endpoint", - "subtitle": "Catches malware without relying on signatures", - "icon": "https://ics.services.jamfcloud.com/icon/hash_c70f1acf8c96b99568fec83e165d2a534d111b0510fb561a283d32aa5b01c60c", - "progresstext": "You’ll enjoy next-gen protection with Sophos Endpoint which doesn’t rely on signatures to catch malware.", - "trigger_list": [ - { - "trigger": "sophosEndpoint", - "validation": "/Applications/Sophos/Sophos Endpoint.app/Contents/Info.plist" - }, - { - "trigger": "symvSophosEndpointRTS", - "validation": "Remote" - } - ] - }, - { - "listitem": "Palo Alto GlobalProtect", - "subtitle": "Virtual Private Network (VPN) connection to Church headquarters", - "icon": "https://ics.services.jamfcloud.com/icon/hash_acbf39d8904ad1a772cf71c45d93e373626d379a24f8b1283b88134880acb8ef", - "progresstext": "Use Palo Alto GlobalProtect to establish a Virtual Private Network (VPN) connection to Church headquarters.", - "trigger_list": [ - { - "trigger": "globalProtect", - "validation": "/Applications/GlobalProtect.app/Contents/Info.plist" - }, - { - "trigger": "symvGlobalProtect", - "validation": "Remote" - } - ] - }, - { - "listitem": "Final Configuration", - "subtitle": "Configures remaining Church settings", - "icon": "https://ics.services.jamfcloud.com/icon/hash_4723e3e341a7e11e6881e418cf91b157fcc081bdb8948697750e5da3562df728", - "progresstext": "Finalizing Configuration …", - "trigger_list": [ - { - "trigger": "finalConfiguration", - "validation": "None" - }, - { - "trigger": "reconAtReboot", - "validation": "None" - } - ] - }, - { - "listitem": "Computer Inventory", - "subtitle": "The listing of your Mac’s apps and settings", - "icon": "https://ics.services.jamfcloud.com/icon/hash_ff2147a6c09f5ef73d1c4406d00346811a9c64c0b6b7f36eb52fcb44943d26f9", - "progresstext": "A listing of your Mac’s apps and settings — its inventory — is sent automatically to the Jamf Pro server daily.", - "trigger_list": [ - { - "trigger": "recon", - "validation": "recon" - } - ] - } - ] - } - ' + 12* | 13* | 14*) + dlThroughput=$( get_json_value "$networkQualityTest" "dl_throughput") + dlResponsiveness=$( get_json_value "$networkQualityTest" "dl_responsiveness" ) + dlStartDate=$( get_json_value "$networkQualityTest" "start_date" ) + dlEndDate=$( get_json_value "$networkQualityTest" "end_date" ) ;; esac + mbps=$( echo "scale=2; ( $dlThroughput / 1000000 )" | bc ) + updateSetupYourMacDialog "$mbps (Mbps)" + + configurationCatchAllEstimatedSeconds=$( echo "scale=2; ((((( $configurationCatchAllSize / $mbps ) * 60 ) * 60 ) * $correctionCoefficient ) + $configurationCatchAllInstallBuffer)" | bc | sed 's/\.[0-9]*//' ) + updateSetupYourMacDialog "Catch-all Configuration Estimated Seconds: $configurationCatchAllEstimatedSeconds" + updateSetupYourMacDialog "Catch-all Configuration Estimate: $(printf '%dh:%dm:%ds\n' $((configurationCatchAllEstimatedSeconds/3600)) $((configurationCatchAllEstimatedSeconds%3600/60)) $((configurationCatchAllEstimatedSeconds%60)))" + + updateSetupYourMacDialog "Network Quality Test: Started: $dlStartDate, Ended: $dlEndDate; Download: $mbps Mbps, Responsiveness: $dlResponsiveness" + dialogUpdateSetupYourMac "infobox: **Connection:** \n- Download: \n$mbps Mbps \n\n**Estimates:** \n- $(printf '%dh:%dm:%ds\n' $((configurationCatchAllEstimatedSeconds/3600)) $((configurationCatchAllEstimatedSeconds%3600/60)) $((configurationCatchAllEstimatedSeconds%60)))" + if [[ "${lockContinueBeforeEstimations}" == "true" ]]; then + welcomeDialog "Enabling Continue Button" + dialogUpdateWelcome "button1: enable" + fi +} + + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Webhook Message (Microsoft Teams or Slack) (thanks, @robjschroeder! and @iDrewbs!) +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +function webHookMessage() { + + outputLineNumberInVerboseDebugMode + + jamfProURL=$(/usr/bin/defaults read /Library/Preferences/com.jamfsoftware.jamf.plist jss_url) + + # # Jamf Pro URL for on-prem, multi-node, clustered environments + # case ${jamfProURL} in + # *"beta"* ) jamfProURL="https://jamfpro-beta.internal.company.com/" ;; + # * ) jamfProURL="https://jamfpro-prod.internal.company.com/" ;; + # esac + + jamfProComputerURL="${jamfProURL}computers.html?id=${computerID}&o=r" + + # If there aren't any failures, use "None" for the value of `jamfProPolicyNameFailures` + if [[ -z "${jamfProPolicyNameFailures}" ]]; then + jamfProPolicyNameFailures="None" + fi + + if [[ $webhookURL == *"slack"* ]]; then + + info "Generating Slack Message …" + + webHookdata=$(cat <&1 + + webhookResult="$?" + info "Slack Webhook Result: ${webhookResult}" + + else + + info "Generating Microsoft Teams Message …" + + # URL to an image to add to your notification + activityImage="https://creazilla-store.fra1.digitaloceanspaces.com/cliparts/78010/old-mac-computer-clipart-md.png" + + webHookdata=$(cat <> "$welcomeCommandFile" -} +while pgrep -q -x "Setup Assistant"; do + preFlight "Setup Assistant is still running; pausing for 2 seconds" + sleep 2 +done + +preFlight "Setup Assistant is no longer running; proceeding …" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Update the "Setup Your Mac" dialog +# Pre-flight Check: Confirm Dock is running / user is at Desktop # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function dialogUpdateSetupYourMac() { - updateScriptLog "SETUP YOUR MAC DIALOG: $1" - echo "$1" >> "$setupYourMacCommandFile" -} +until pgrep -q -x "Finder" && pgrep -q -x "Dock"; do + preFlight "Finder & Dock are NOT running; pausing for 1 second" + sleep 1 +done + +preFlight "Finder & Dock are running; proceeding …" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Update the "Failure" dialog +# Pre-flight Check: Validate Logged-in System Accounts # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function dialogUpdateFailure(){ - updateScriptLog "FAILURE DIALOG: $1" - echo "$1" >> "$failureCommandFile" -} +preFlight "Check for Logged-in System Accounts …" +currentLoggedInUser + +counter="1" + +until { [[ "${loggedInUser}" != "_mbsetupuser" ]] || [[ "${counter}" -gt "180" ]]; } && { [[ "${loggedInUser}" != "loginwindow" ]] || [[ "${counter}" -gt "30" ]]; } ; do + + preFlight "Logged-in User Counter: ${counter}" + currentLoggedInUser + sleep 2 + ((counter++)) + +done + +loggedInUserFullname=$( id -F "${loggedInUser}" ) +loggedInUserFirstname=$( echo "$loggedInUserFullname" | sed -E 's/^.*, // ; s/([^ ]*).*/\1/' | sed 's/\(.\{25\}\).*/\1…/' | awk '{print ( $0 == toupper($0) ? toupper(substr($0,1,1))substr(tolower($0),2) : toupper(substr($0,1,1))substr($0,2) )}' ) +loggedInUserID=$( id -u "${loggedInUser}" ) +preFlight "Current Logged-in User First Name: ${loggedInUserFirstname}" +preFlight "Current Logged-in User ID: ${loggedInUserID}" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Finalise User Experience +# Pre-flight Check: Validate Operating System Version and Build # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function finalise(){ +if [[ "${requiredMinimumBuild}" == "disabled" ]]; then - outputLineNumberInVerboseDebugMode + preFlight "'requiredMinimumBuild' has been set to ${requiredMinimumBuild}; skipping OS validation." + preFlight "macOS ${osVersion} (${osBuild}) installed" - if [[ "${configurationDownloadEstimation}" == "true" ]]; then +else - outputLineNumberInVerboseDebugMode - calculateFreeDiskSpace "FINALISE USER EXPERIENCE" + # Since swiftDialog requires at least macOS 12 Monterey, first confirm the major OS version + if [[ "${osMajorVersion}" -ge 12 ]] ; then - fi + preFlight "macOS ${osMajorVersion} installed; checking build version ..." - if [[ "${jamfProPolicyTriggerFailure}" == "failed" ]]; then + # Confirm the Mac is running `requiredMinimumBuild` (or later) + if [[ "${osBuild}" > "${requiredMinimumBuild}" ]]; then + + preFlight "macOS ${osVersion} (${osBuild}) installed; proceeding ..." + + # When the current `osBuild` is older than `requiredMinimumBuild`; exit with error + else + preFlight "The installed operating system, macOS ${osVersion} (${osBuild}), needs to be updated to Build ${requiredMinimumBuild}; exiting with error." + osascript -e 'display dialog "Please advise your Support Representative of the following error:\r\rExpected macOS Build '${requiredMinimumBuild}' (or newer), but found macOS '${osVersion}' ('${osBuild}').\r\r" with title "Setup Your Mac: Detected Outdated Operating System" buttons {"Open Software Update"} with icon caution' + preFlight "Executing /usr/bin/open '${outdatedOsAction}' …" + su - "${loggedInUser}" -c "/usr/bin/open \"${outdatedOsAction}\"" + exit 1 - outputLineNumberInVerboseDebugMode - updateScriptLog "Failed policies detected …" - if [[ -n "${webhookURL}" ]]; then - updateScriptLog "Display Failure dialog: Sending webhook message" - webhookStatus="Failures detected" - webHookMessage fi - if [[ "${failureDialog}" == "true" ]]; then + # The Mac is running an operating system older than macOS 12 Monterey; exit with error + else + + preFlight "swiftDialog requires at least macOS 12 Monterey and this Mac is running ${osVersion} (${osBuild}), exiting with error." + osascript -e 'display dialog "Please advise your Support Representative of the following error:\r\rExpected macOS Build '${requiredMinimumBuild}' (or newer), but found macOS '${osVersion}' ('${osBuild}').\r\r" with title "Setup Your Mac: Detected Outdated Operating System" buttons {"Open Software Update"} with icon caution' + preFlight "Executing /usr/bin/open '${outdatedOsAction}' …" + su - "${loggedInUser}" -c "/usr/bin/open \"${outdatedOsAction}\"" + exit 1 - outputLineNumberInVerboseDebugMode - updateScriptLog "Display Failure dialog: ${failureDialog}" + fi - killProcess "caffeinate" - if [[ "${brandingBannerDisplayText}" == "true" ]] ; then dialogUpdateSetupYourMac "title: Sorry ${loggedInUserFirstname}, something went sideways"; fi - dialogUpdateSetupYourMac "icon: SF=xmark.circle.fill,weight=bold,colour1=#BB1717,colour2=#F31F1F" - dialogUpdateSetupYourMac "progresstext: Failures detected. Please click Continue for troubleshooting information." - dialogUpdateSetupYourMac "button1text: Continue …" - dialogUpdateSetupYourMac "button1: enable" - dialogUpdateSetupYourMac "progress: reset" - - # Wait for user-acknowledgment due to detected failure - wait +fi - dialogUpdateSetupYourMac "quit:" - eval "${dialogFailureCMD}" & sleep 0.3 - updateScriptLog "\n\n# # #\n# FAILURE DIALOG\n# # #\n" - updateScriptLog "Jamf Pro Policy Name Failures:" - updateScriptLog "${jamfProPolicyNameFailures}" - failureMessage="A failure has been detected, ${loggedInUserFirstname}. \n\nPlease complete the following steps:\n1. Reboot and login to your ${modelName} \n2. Login to Self Service \n3. Re-run any failed policy listed below \n\nThe following failed: \n${jamfProPolicyNameFailures}" - - if [[ -n "${supportTeamName}" ]]; then +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Pre-flight Check: Ensure computer does not go to sleep during SYM (thanks, @grahampugh!) +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - supportContactMessage+="If you need assistance, please contact the **${supportTeamName}**: \n" +symPID="$$" +preFlight "Caffeinating this script (PID: $symPID)" +caffeinate -dimsu -w $symPID & - if [[ -n "${supportTeamPhone}" ]]; then - supportContactMessage+="- **Telephone:** ${supportTeamPhone}\n" - fi - if [[ -n "${supportTeamEmail}" ]]; then - supportContactMessage+="- **Email:** $supportTeamEmail\n" - fi - if [[ -n "${supportTeamWebsite}" ]]; then - supportContactMessage+="- **Web**: ${supportTeamHyperlink}\n" - fi +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Pre-flight Check: Toggle `jamf` binary check-in (thanks, @robjschroeder!) +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - if [[ -n "${supportKB}" ]]; then - supportContactMessage+="- **Knowledge Base Article:** $supportTeamErrorKB\n" - fi - - fi +function toggleJamfLaunchDaemon() { + + jamflaunchDaemon="/Library/LaunchDaemons/com.jamfsoftware.task.1.plist" - failureMessage+="\n\n${supportContactMessage}" + if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then - dialogUpdateFailure "message: ${failureMessage}" + if [[ $(/bin/launchctl list | grep com.jamfsoftware.task.E) ]]; then + preFlight "DEBUG MODE: Normally, 'jamf' binary check-in would be temporarily disabled" + else + quitOut "DEBUG MODE: Normally, 'jamf' binary check-in would be re-enabled" + fi - dialogUpdateFailure "icon: SF=xmark.circle.fill,weight=bold,colour1=#BB1717,colour2=#F31F1F" - dialogUpdateFailure "button1text: ${button1textCompletionActionOption}" + else - # Wait for user-acknowledgment due to detected failure - wait + while [[ ! -f "${jamflaunchDaemon}" ]] ; do + preFlight "Waiting for installation of ${jamflaunchDaemon}" + sleep 0.1 + done - dialogUpdateFailure "quit:" - quitScript "1" + if [[ $(/bin/launchctl list | grep com.jamfsoftware.task.E) ]]; then + + preFlight "Temporarily disable 'jamf' binary check-in" + /bin/launchctl bootout system "${jamflaunchDaemon}" else - outputLineNumberInVerboseDebugMode - updateScriptLog "Display Failure dialog: ${failureDialog}" + quitOut "Re-enabling 'jamf' binary check-in" + quitOut "'jamf' binary check-in daemon not loaded, attempting to bootstrap and start" + result="0" - killProcess "caffeinate" - if [[ "${brandingBannerDisplayText}" == "true" ]] ; then dialogUpdateSetupYourMac "title: Sorry ${loggedInUserFirstname}, something went sideways"; fi - dialogUpdateSetupYourMac "icon: SF=xmark.circle.fill,weight=bold,colour1=#BB1717,colour2=#F31F1F" - dialogUpdateSetupYourMac "progresstext: Failures detected." - dialogUpdateSetupYourMac "button1text: ${button1textCompletionActionOption}" - dialogUpdateSetupYourMac "button1: enable" - dialogUpdateSetupYourMac "progress: reset" - dialogUpdateSetupYourMac "progresstext: Errors detected; please ${progressTextCompletionAction// and } your ${modelName}, ${loggedInUserFirstname}." + until [ $result -eq 3 ]; do - quitScript "1" + /bin/launchctl bootstrap system "${jamflaunchDaemon}" && /bin/launchctl start "${jamflaunchDaemon}" + result="$?" - fi + if [ $result = 3 ]; then + quitOut "Staring 'jamf' binary check-in daemon" + else + quitOut "Failed to start 'jamf' binary check-in daemon" + fi - else + done - outputLineNumberInVerboseDebugMode - updateScriptLog "All policies executed successfully" - if [[ -n "${webhookURL}" ]]; then - webhookStatus="Successful" - updateScriptLog "Sending success webhook message" - webHookMessage fi - if [[ "${brandingBannerDisplayText}" == "true" ]] ; then dialogUpdateSetupYourMac "title: ${loggedInUserFirstname}‘s ${modelName} is ready!"; fi - dialogUpdateSetupYourMac "icon: SF=checkmark.circle.fill,weight=bold,colour1=#00ff44,colour2=#075c1e" - dialogUpdateSetupYourMac "progresstext: Complete! Please ${progressTextCompletionAction}enjoy your new ${modelName}, ${loggedInUserFirstname}!" - dialogUpdateSetupYourMac "progress: complete" - dialogUpdateSetupYourMac "button1text: ${button1textCompletionActionOption}" - dialogUpdateSetupYourMac "button1: enable" - - quitScript "0" - fi } +toggleJamfLaunchDaemon + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Parse JSON via osascript and JavaScript +# Pre-flight Check: Validate / install swiftDialog (Thanks big bunches, @acodega!) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function get_json_value() { - # set -x - JSON="$1" osascript -l 'JavaScript' \ - -e 'const env = $.NSProcessInfo.processInfo.environment.objectForKey("JSON").js' \ - -e "JSON.parse(env).$2" - # set +x -} +function dialogInstall() { + # Get the URL of the latest PKG From the Dialog GitHub repo + dialogURL=$(curl -L --silent --fail "https://api.github.com/repos/swiftDialog/swiftDialog/releases/latest" | awk -F '"' "/browser_download_url/ && /pkg\"/ { print \$4; exit }") + # Expected Team ID of the downloaded PKG + expectedDialogTeamID="PWA5E9TQ59" -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Parse JSON via osascript and JavaScript for the Welcome dialog (thanks, @bartreardon!) -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + preFlight "Installing swiftDialog..." -function get_json_value_welcomeDialog() { - # set -x - for var in "${@:2}"; do jsonkey="${jsonkey}['${var}']"; done - JSON="$1" osascript -l 'JavaScript' \ - -e 'const env = $.NSProcessInfo.processInfo.environment.objectForKey("JSON").js' \ - -e "JSON.parse(env)$jsonkey" - # set +x -} + # Create temporary working directory + workDirectory=$( /usr/bin/basename "$0" ) + tempDirectory=$( /usr/bin/mktemp -d "/private/tmp/$workDirectory.XXXXXX" ) + # Download the installer package + /usr/bin/curl --location --silent "$dialogURL" -o "$tempDirectory/Dialog.pkg" + # Verify the download + teamID=$(/usr/sbin/spctl -a -vv -t install "$tempDirectory/Dialog.pkg" 2>&1 | awk '/origin=/ {print $NF }' | tr -d '()') -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Execute Jamf Pro Policy Custom Events (thanks, @smithjw) -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # Install the package if Team ID validates + if [[ "$expectedDialogTeamID" == "$teamID" ]]; then -function run_jamf_trigger() { + /usr/sbin/installer -pkg "$tempDirectory/Dialog.pkg" -target / + sleep 2 + dialogVersion=$( /usr/local/bin/dialog --version ) + preFlight "swiftDialog version ${dialogVersion} installed; proceeding..." - outputLineNumberInVerboseDebugMode + else - trigger="$1" + # Display a so-called "simple" dialog if Team ID fails to validate + osascript -e 'display dialog "Please advise your Support Representative of the following error:\r\r• Dialog Team ID verification failed\r\r" with title "Setup Your Mac: Error" buttons {"Close"} with icon caution' + completionActionOption="Quit" + exitCode="1" + quitScript - if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then + fi + + # Remove the temporary working directory when done + /bin/rm -Rf "$tempDirectory" + +} - updateScriptLog "SETUP YOUR MAC DIALOG: DEBUG MODE: TRIGGER: $jamfBinary policy -event $trigger ${suppressRecon}" - sleep "${debugModeSleepAmount}" + + +function dialogCheck() { + + # Output Line Number in `verbose` Debug Mode + if [[ "${debugMode}" == "verbose" ]]; then preFlight "# # # SETUP YOUR MAC VERBOSE DEBUG MODE: Line No. ${LINENO} # # #" ; fi + + # Check for Dialog and install if not found + if [ ! -e "/Library/Application Support/Dialog/Dialog.app" ]; then + + preFlight "swiftDialog not found. Installing..." + dialogInstall else - updateScriptLog "SETUP YOUR MAC DIALOG: RUNNING: $jamfBinary policy -event $trigger" - eval "${jamfBinary} policy -event ${trigger} ${suppressRecon}" # Add comment for policy testing - # eval "${jamfBinary} policy -event ${trigger} ${suppressRecon} -verbose | tee -a ${scriptLog}" # Remove comment for policy testing + dialogVersion=$(/usr/local/bin/dialog --version) + if [[ "${dialogVersion}" < "${swiftDialogMinimumRequiredVersion}" ]]; then + + preFlight "swiftDialog version ${dialogVersion} found but swiftDialog ${swiftDialogMinimumRequiredVersion} or newer is required; updating..." + dialogInstall + + else + preFlight "swiftDialog version ${dialogVersion} found; proceeding..." + + fi + fi } +dialogCheck + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Confirm Policy Execution +# Pre-flight Check: Validate `supportTeam` variables are populated # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function confirmPolicyExecution() { +if [[ -z $supportTeamName ]]; then + preFlight "'supportTeamName' must be populated to proceed; exiting" + exit 1 +fi - outputLineNumberInVerboseDebugMode +if [[ -z $supportTeamPhone && -z $supportTeamEmail && -z $supportKB ]]; then + preFlight "At least ONE 'supportTeam' variable must be populated to proceed; exiting" + exit 1 +fi - trigger="${1}" - validation="${2}" - updateScriptLog "SETUP YOUR MAC DIALOG: Confirm Policy Execution: '${trigger}' '${validation}'" - if [ "${suppressReconOnPolicy}" == "true" ]; then suppressRecon="-forceNoRecon"; fi - case ${validation} in - */* ) # If the validation variable contains a forward slash (i.e., "/"), presume it's a path and check if that path exists on disk +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Pre-flight Check: Complete +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - outputLineNumberInVerboseDebugMode - if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then - updateScriptLog "SETUP YOUR MAC DIALOG: Confirm Policy Execution: DEBUG MODE: Skipping 'run_jamf_trigger ${trigger}'" - sleep "${debugModeSleepAmount}" - elif [[ -e "${validation}" ]]; then - updateScriptLog "SETUP YOUR MAC DIALOG: Confirm Policy Execution: ${validation} exists; skipping 'run_jamf_trigger ${trigger}'" - previouslyInstalled="true" - else - updateScriptLog "SETUP YOUR MAC DIALOG: Confirm Policy Execution: ${validation} does NOT exist; executing 'run_jamf_trigger ${trigger}'" - previouslyInstalled="false" - run_jamf_trigger "${trigger}" - fi - ;; +preFlight "Complete" - "None" | "none" ) - outputLineNumberInVerboseDebugMode - updateScriptLog "SETUP YOUR MAC DIALOG: Confirm Policy Execution: ${validation}" - if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then - sleep "${debugModeSleepAmount}" - else - run_jamf_trigger "${trigger}" - fi - ;; - "Recon" | "recon" ) +#################################################################################################### +# +# Dialog Variables +# +#################################################################################################### - outputLineNumberInVerboseDebugMode - updateScriptLog "SETUP YOUR MAC DIALOG: Confirm Policy Execution: ${validation}" - if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then - updateScriptLog "SETUP YOUR MAC DIALOG: DEBUG MODE: Set 'debugMode' to false to update computer inventory with the following 'reconOptions': \"${reconOptions}\" …" - sleep "${debugModeSleepAmount}" - else - updateScriptLog "SETUP YOUR MAC DIALOG: Updating computer inventory with the following 'reconOptions': \"${reconOptions}\" …" - dialogUpdateSetupYourMac "listitem: index: $i, status: wait, statustext: Updating …, " - reconRaw=$( eval "${jamfBinary} recon ${reconOptions} -verbose | tee -a ${scriptLog}" ) - computerID=$( echo "${reconRaw}" | grep '' | xmllint --xpath xmllint --xpath '/computer_id/text()' - ) - fi - ;; +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# infobox-related variables +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - * ) +macOSproductVersion="$( sw_vers -productVersion )" +macOSbuildVersion="$( sw_vers -buildVersion )" +serialNumber=$( ioreg -rd1 -c IOPlatformExpertDevice | awk -F'"' '/IOPlatformSerialNumber/{print $4}' ) +timestamp="$( date '+%Y-%m-%d-%H%M%S' )" +dialogVersion=$( /usr/local/bin/dialog --version ) - outputLineNumberInVerboseDebugMode - updateScriptLog "SETUP YOUR MAC DIALOG: Confirm Policy Execution Catch-all: ${validation}" - if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then - sleep "${debugModeSleepAmount}" - else - run_jamf_trigger "${trigger}" - fi - ;; - esac -} +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Reflect Debug Mode in `infotext` (i.e., bottom, left-hand corner of each dialog) +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +case ${debugMode} in + "true" ) scriptVersion="DEBUG MODE | Dialog: v${dialogVersion} • Setup Your Mac: v${scriptVersion}" ;; + "verbose" ) scriptVersion="VERBOSE DEBUG MODE | Dialog: v${dialogVersion} • Setup Your Mac: v${scriptVersion}" ;; +esac # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Validate Policy Result +# Set JAMF binary, Dialog path and Command Files # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function validatePolicyResult() { - - outputLineNumberInVerboseDebugMode - - trigger="${1}" - validation="${2}" - updateScriptLog "SETUP YOUR MAC DIALOG: Validate Policy Result: '${trigger}' '${validation}'" +jamfBinary="/usr/local/bin/jamf" +dialogBinary="/usr/local/bin/dialog" +welcomeJSONFile=$( mktemp -u /var/tmp/welcomeJSONFile.XXX ) +welcomeCommandFile=$( mktemp -u /var/tmp/dialogCommandFileWelcome.XXX ) +setupYourMacCommandFile=$( mktemp -u /var/tmp/dialogCommandFileSetupYourMac.XXX ) +failureCommandFile=$( mktemp -u /var/tmp/dialogCommandFileFailure.XXX ) - case ${validation} in - ### - # Absolute Path - # Simulates pre-v1.6.0 behavior, for example: "/Applications/Microsoft Teams classic.app/Contents/Info.plist" - ### - */* ) - updateScriptLog "SETUP YOUR MAC DIALOG: Validate Policy Result: Testing for \"$validation\" …" - if [[ "${previouslyInstalled}" == "true" ]]; then - dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Previously Installed" - elif [[ -e "${validation}" ]]; then - dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Installed" - else - dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" - jamfProPolicyTriggerFailure="failed" - exitCode="1" - jamfProPolicyNameFailures+="• $listitem \n" - fi - ;; +#################################################################################################### +# +# Welcome dialog +# +#################################################################################################### +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# "Welcome" dialog Title, Message and Icon +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +welcomeTitle="Happy $( date +'%A' ), ${loggedInUserFirstname}! \nWelcome to your new ${modelName}" - ### - # Local - # Validation within this script, for example: "rosetta" or "filevault" - ### +welcomeMessage="Please enter the **required** information for your ${modelName}, select your preferred **Configuration** then click **Continue** to start applying settings to your new Mac. \n\nOnce completed, the **Wait** button will be enabled and you‘ll be able to review the results before restarting your ${modelName}." - "Local" ) - case ${trigger} in - rosetta ) - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: Rosetta 2 … " # Thanks, @smithjw! - dialogUpdateSetupYourMac "listitem: index: $i, status: wait, statustext: Checking …" - arch=$( /usr/bin/arch ) - if [[ "${arch}" == "arm64" ]]; then - # Mac with Apple silicon; check for Rosetta - rosettaTest=$( arch -x86_64 /usr/bin/true 2> /dev/null ; echo $? ) - if [[ "${rosettaTest}" -eq 0 ]]; then - # Installed - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: Rosetta 2 is installed" - dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Running" - else - # Not Installed - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: Rosetta 2 is NOT installed" - dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" - jamfProPolicyTriggerFailure="failed" - exitCode="1" - jamfProPolicyNameFailures+="• $listitem \n" - fi - else - # Ineligible - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: Rosetta 2 is not applicable" - dialogUpdateSetupYourMac "listitem: index: $i, status: error, statustext: Ineligible" - fi - ;; - filevault ) - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: Validate FileVault … " - dialogUpdateSetupYourMac "listitem: index: $i, status: wait, statustext: Checking …" - updateScriptLog "SETUP YOUR MAC DIALOG: Validate Policy Result: Pausing for 5 seconds for FileVault … " - sleep 5 # Arbitrary value; tuning needed - fileVaultCheck=$( fdesetup isactive ) - if [[ -f /Library/Preferences/com.apple.fdesetup.plist ]] || [[ "$fileVaultCheck" == "true" ]]; then - fileVaultStatus=$( fdesetup status -extended -verbose 2>&1 ) - case ${fileVaultStatus} in - *"FileVault is On."* ) - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: FileVault: FileVault is On." - dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Enabled" - ;; - *"Deferred enablement appears to be active for user"* ) - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: FileVault: Enabled" - dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Enabled (next login)" - ;; - * ) - dialogUpdateSetupYourMac "listitem: index: $i, status: error, statustext: Unknown" - jamfProPolicyTriggerFailure="failed" - exitCode="1" - jamfProPolicyNameFailures+="• $listitem \n" - ;; - esac - else - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: '/Library/Preferences/com.apple.fdesetup.plist' NOT Found" - dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" - jamfProPolicyTriggerFailure="failed" - exitCode="1" - jamfProPolicyNameFailures+="• $listitem \n" - fi - ;; - sophosEndpointServices ) - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: Sophos Endpoint RTS Status … " - dialogUpdateSetupYourMac "listitem: index: $i, status: wait, statustext: Checking …" - if [[ -d /Applications/Sophos/Sophos\ Endpoint.app ]]; then - if [[ -f /Library/Preferences/com.sophos.sav.plist ]]; then - sophosOnAccessRunning=$( /usr/bin/defaults read /Library/Preferences/com.sophos.sav.plist OnAccessRunning ) - case ${sophosOnAccessRunning} in - "0" ) - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: Sophos Endpoint RTS Status: Disabled" - dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" - jamfProPolicyTriggerFailure="failed" - exitCode="1" - jamfProPolicyNameFailures+="• $listitem \n" - ;; - "1" ) - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: Sophos Endpoint RTS Status: Enabled" - dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Running" - ;; - * ) - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: Sophos Endpoint RTS Status: Unknown" - dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Unknown" - jamfProPolicyTriggerFailure="failed" - exitCode="1" - jamfProPolicyNameFailures+="• $listitem \n" - ;; - esac - else - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: Sophos Endpoint Not Found" - dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" - jamfProPolicyTriggerFailure="failed" - exitCode="1" - jamfProPolicyNameFailures+="• $listitem \n" - fi - else - dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" - jamfProPolicyTriggerFailure="failed" - exitCode="1" - jamfProPolicyNameFailures+="• $listitem \n" - fi - ;; - globalProtect ) - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: Palo Alto Networks GlobalProtect Status … " - dialogUpdateSetupYourMac "listitem: index: $i, status: wait, statustext: Checking …" - if [[ -d /Applications/GlobalProtect.app ]]; then - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: Pausing for 10 seconds to allow Palo Alto Networks GlobalProtect Services … " - sleep 10 # Arbitrary value; tuning needed - if [[ -f /Library/Preferences/com.paloaltonetworks.GlobalProtect.settings.plist ]]; then - globalProtectStatus=$( /usr/libexec/PlistBuddy -c "print :Palo\ Alto\ Networks:GlobalProtect:PanGPS:disable-globalprotect" /Library/Preferences/com.paloaltonetworks.GlobalProtect.settings.plist ) - case "${globalProtectStatus}" in - "0" ) - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: Palo Alto Networks GlobalProtect Status: Enabled" - dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Running" - ;; - "1" ) - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: Palo Alto Networks GlobalProtect Status: Disabled" - dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" - jamfProPolicyTriggerFailure="failed" - exitCode="1" - jamfProPolicyNameFailures+="• $listitem \n" - ;; - * ) - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: Palo Alto Networks GlobalProtect Status: Unknown" - dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Unknown" - jamfProPolicyTriggerFailure="failed" - exitCode="1" - jamfProPolicyNameFailures+="• $listitem \n" - ;; - esac - else - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: Palo Alto Networks GlobalProtect Not Found" - dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" - jamfProPolicyTriggerFailure="failed" - exitCode="1" - jamfProPolicyNameFailures+="• $listitem \n" - fi - else - dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" - jamfProPolicyTriggerFailure="failed" - exitCode="1" - jamfProPolicyNameFailures+="• $listitem \n" - fi - ;; - * ) - updateScriptLog "SETUP YOUR MAC DIALOG: Locally Validate Policy Result: Local Validation “${validation}” Missing" - dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Missing Local “${validation}” Validation" - jamfProPolicyTriggerFailure="failed" - exitCode="1" - jamfProPolicyNameFailures+="• $listitem \n" - ;; - esac - ;; +if [[ -n "${supportTeamName}" ]]; then + welcomeMessage+="\n\nIf you need assistance, please contact the **${supportTeamName}**: \n" + if [[ -n "${supportTeamPhone}" ]]; then + welcomeMessage+="- **Telephone**: ${supportTeamPhone}\n" + fi - ### - # Remote - # Validation via a Jamf Pro policy which has a single-script payload, for example: "symvGlobalProtect" - # See: https://vimeo.com/782561166 - ### + if [[ -n "${supportTeamEmail}" ]]; then + welcomeMessage+="- **Email**: ${supportTeamEmail}\n" + fi - "Remote" ) - if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then - updateScriptLog "SETUP YOUR MAC DIALOG: DEBUG MODE: Remotely Confirm Policy Execution: Skipping 'run_jamf_trigger ${trigger}'" - dialogUpdateSetupYourMac "listitem: index: $i, status: error, statustext: Debug Mode Enabled" - sleep 0.5 - else - updateScriptLog "SETUP YOUR MAC DIALOG: Remotely Validate '${trigger}' '${validation}'" - dialogUpdateSetupYourMac "listitem: index: $i, status: wait, statustext: Checking …" - result=$( "${jamfBinary}" policy -event "${trigger}" | grep "Script result:" ) - if [[ "${result}" == *"Running"* ]]; then - dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Running" - elif [[ "${result}" == *"Installed"* || "${result}" == *"Success"* ]]; then - dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Installed" - else - dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" - jamfProPolicyTriggerFailure="failed" - exitCode="1" - jamfProPolicyNameFailures+="• $listitem \n" - fi - fi - ;; + if [[ -n "${supportTeamWebsite}" ]]; then + welcomeMessage+="- **Web**: ${supportTeamHyperlink}\n" + fi + if [[ -n "${supportKB}" ]]; then + welcomeMessage+="- **Knowledge Base Article:** ${supportTeamErrorKB}\n" + fi +fi - ### - # None: For triggers which don't require validation - # (Always evaluates as: 'success' and 'Installed') - ### +welcomeMessage+="\n\n---" - "None" | "none") +if { [[ "${promptForConfiguration}" == "true" ]] && [[ "${welcomeDialog}" != "messageOnly" ]]; } then + welcomeMessage+=" \n\n#### Configurations \n- **${configurationOneName}:** ${configurationOneDescription} \n- **${configurationTwoName}:** ${configurationTwoDescription} \n- **${configurationThreeName}:** ${configurationThreeDescription}" +else + welcomeMessage=${welcomeMessage//", select your preferred **Configuration**"/} +fi - outputLineNumberInVerboseDebugMode - updateScriptLog "SETUP YOUR MAC DIALOG: Confirm Policy Execution: ${validation}" - dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Installed" - ;; +if [[ "${brandingBannerDisplayText}" == "true" ]]; then welcomeBannerText="Happy $( date +'%A' ), ${loggedInUserFirstname}! \nWelcome to your new ${modelName}"; +else welcomeBannerText=""; fi +welcomeCaption="Please review the above video, then click Continue." +welcomeVideoID="vimeoid=909473114" - ### - # Recon: For reporting computer inventory update - # (Always evaluates as: 'success' and 'Updated') - ### +# Check brandingBanner and cache if necessary +case ${brandingBanner} in - "Recon" | "recon" ) + *"https"* ) + welcomeBannerImage="${brandingBanner}" + bannerImage="${brandingBanner}" + if curl -L --output /dev/null --silent --head --fail "$welcomeBannerImage" || [ -f "$welcomeBannerImage" ]; then + welcomeDialog "brandingBanner is available, using it" + else + welcomeDialog "brandingBanner is not available, using a default image" + welcomeBannerImage="https://img.freepik.com/free-vector/green-abstract-geometric-wallpaper_52683-29623.jpg" # Image by pikisuperstar on Freepik + bannerImage="https://img.freepik.com/free-vector/green-abstract-geometric-wallpaper_52683-29623.jpg" # Image by pikisuperstar on Freepik + fi - outputLineNumberInVerboseDebugMode - updateScriptLog "SETUP YOUR MAC DIALOG: Confirm Policy Execution: ${validation}" - dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Updated" - ;; + welcomeBannerImageFileName=$( echo ${welcomeBannerImage} | awk -F '/' '{print $NF}' ) + welcomeDialog "Auto-caching hosted '$welcomeBannerImageFileName' …" + curl -L --location --silent "$welcomeBannerImage" -o "/var/tmp/${welcomeBannerImageFileName}" + welcomeBannerImage="/var/tmp/${welcomeBannerImageFileName}" + bannerImage="/var/tmp/${welcomeBannerImageFileName}" + ;; + */* ) + welcomeDialog "brandingBanner is local file, using it" + welcomeBannerImage="${brandingBanner}" + bannerImage="${brandingBanner}" + ;; + "None" | "none" | "" ) + welcomeDialog "brandingBanner set to \"None\", or empty" + welcomeBannerImage="${brandingBanner}" + bannerImage="${brandingBanner}" + ;; - ### - # Catch-all - ### + * ) + welcomeDialog "brandingBanner set to \"None\"" + ;; - * ) +esac - outputLineNumberInVerboseDebugMode - updateScriptLog "SETUP YOUR MAC DIALOG: Validate Policy Results Catch-all: ${validation}" - dialogUpdateSetupYourMac "listitem: index: $i, status: error, statustext: Error" - ;; - esac -} +# Welcome icon set to either light or dark, based on user's Apperance setting (thanks, @mm2270!) +appleInterfaceStyle=$( /usr/bin/defaults read /Users/"${loggedInUser}"/Library/Preferences/.GlobalPreferences.plist AppleInterfaceStyle 2>&1 ) +if [[ "${appleInterfaceStyle}" == "Dark" ]]; then + if [[ -n "$brandingIconDark" ]]; then welcomeIcon="$brandingIconDark"; + else welcomeIcon="https://cdn-icons-png.flaticon.com/512/740/740878.png"; fi +else + if [[ -n "$brandingIconLight" ]]; then welcomeIcon="$brandingIconLight"; + else welcomeIcon="https://cdn-icons-png.flaticon.com/512/979/979585.png"; fi +fi # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Kill a specified process (thanks, @grahampugh!) +# "Welcome" Video Settings and Features # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function killProcess() { - process="$1" - if process_pid=$( pgrep -a "${process}" 2>/dev/null ) ; then - updateScriptLog "Attempting to terminate the '$process' process …" - updateScriptLog "(Termination message indicates success.)" - kill "$process_pid" 2> /dev/null - if pgrep -a "$process" >/dev/null ; then - updateScriptLog "ERROR: '$process' could not be terminated." - fi - else - updateScriptLog "The '$process' process isn't running." - fi -} +welcomeVideo="--title \"$welcomeTitle\" \ +--videocaption \"$welcomeCaption\" \ +--video \"$welcomeVideoID\" \ +--infotext \"$scriptVersion\" \ +--button1text \"Continue …\" \ +--autoplay \ +--moveable \ +--ontop \ +--width '800' \ +--height '600' \ +--commandfile \"$welcomeCommandFile\" " # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Completion Action (i.e., Wait, Sleep, Logout, Restart or Shutdown) +# "Welcome" JSON Conditionals (thanks, @rougegoat!) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function completionAction() { +# Text Fields +if [ "$prefillUsername" == "true" ]; then usernamePrefil=',"value" : "'${loggedInUser}'"'; fi +if [ "$prefillRealname" == "true" ]; then realnamePrefil=',"value" : "'${loggedInUserFullname}'"'; fi +if [ "$promptForUsername" == "true" ]; then usernameJSON='{ "title" : "User Name","required" : false,"prompt" : "User Name"'${usernamePrefil}'},'; fi +if [ "$promptForRealName" == "true" ]; then realNameJSON='{ "title" : "Full Name","required" : false,"prompt" : "Full Name"'${realnamePrefil}'},'; fi +if [ "$promptForEmail" == "true" ]; then + emailJSON='{ "title" : "E-mail", + "required" : true, + "prompt" : "E-mail Address", + "regex" : "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", + "regexerror" : "Please enter a valid email address." + },' +fi +if [ "$promptForComputerName" == "true" ]; then compNameJSON='{ "title" : "Computer Name","required" : false,"prompt" : "Computer Name" },'; fi +if [ "$promptForAssetTag" == "true" ]; then + assetTagJSON='{ "title" : "Asset Tag", + "required" : true, + "prompt" : "Please enter the (at least) seven-digit Asset Tag", + "regex" : "^(AP|IP|CD)?[0-9]{7,}$", + "regexerror" : "Please enter (at least) seven digits for the Asset Tag, optionally preceded by either AP, IP or CD." + },' +fi +if [ "$promptForRoom" == "true" ]; then roomJSON='{ "title" : "Room","required" : false,"prompt" : "Optional" },'; fi +if [[ "$promptForPosition" == "true" && -z "$positionListRaw" ]]; then positionJSON='{ "title" : "Position","required" : false,"prompt" : "Position" },'; fi - outputLineNumberInVerboseDebugMode +textFieldJSON="${usernameJSON}${realNameJSON}${emailJSON}${compNameJSON}${assetTagJSON}${positionJSON}${roomJSON}" +textFieldJSON=$( echo ${textFieldJSON} | sed 's/,$//' ) - if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then +# Dropdowns +if [ "$promptForBuilding" == "true" ]; then + if [ -n "$buildingsListRaw" ]; then + buildingJSON='{ + "title" : "Building", + "default" : "", + "required" : true, + "values" : [ + '${buildingsList}' + ] + },' + fi +fi - # If Debug Mode is enabled, ignore specified `completionActionOption`, display simple dialog box and exit - runAsUser osascript -e 'display dialog "Setup Your Mac is operating in Debug Mode.\r\r• completionActionOption == '"'${completionActionOption}'"'\r\r" with title "Setup Your Mac: Debug Mode" buttons {"Close"} with icon note' - exitCode="0" +if [ "$promptForDepartment" == "true" ]; then + if [ -n "$departmentListRaw" ]; then + departmentJSON='{ + "title" : "Department", + "default" : "", + "required" : true, + "values" : [ + '${departmentList}' + ] + },' + fi +fi - else +if [ "$promptForPosition" == "true" ]; then + if [ -n "${positionListRaw}" ]; then + positionSelectJSON='{ + "title" : "Position", + "default" : "", + "required" : true, + "values" : [ + '${positionList}' + ] + },' + fi +fi - shopt -s nocasematch +if [ "$promptForConfiguration" == "true" ] && [ -z "${presetConfiguration}" ]; then + configurationJSON='{ + "title" : "Configuration", + "style" : "radio", + "default" : "'"${configurationOneName}"'", + "values" : [ + "'"${configurationOneName}"'", + "'"${configurationTwoName}"'", + "'"${configurationThreeName}"'" + ] + }' +fi - case ${completionActionOption} in +selectItemsJSON="${buildingJSON}${departmentJSON}${positionSelectJSON}${configurationJSON}" +selectItemsJSON=$( echo $selectItemsJSON | sed 's/,$//' ) - "Shut Down" ) - updateScriptLog "COMPLETION ACTION: Shut Down sans user interaction" - killProcess "Self Service" - # runAsUser osascript -e 'tell app "System Events" to shut down' - # sleep 5 && runAsUser osascript -e 'tell app "System Events" to shut down' & - sleep 5 && shutdown -h now & - ;; - "Shut Down Attended" ) - updateScriptLog "COMPLETION ACTION: Shut Down, requiring user-interaction" - killProcess "Self Service" - wait - # runAsUser osascript -e 'tell app "System Events" to shut down' - # sleep 5 && runAsUser osascript -e 'tell app "System Events" to shut down' & - sleep 5 && shutdown -h now & - ;; - "Shut Down Confirm" ) - updateScriptLog "COMPLETION ACTION: Shut down, only after macOS time-out or user confirmation" - runAsUser osascript -e 'tell app "loginwindow" to «event aevtrsdn»' - ;; +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# "Welcome" JSON for Capturing User Input (thanks, @bartreardon!) +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - "Restart" ) - updateScriptLog "COMPLETION ACTION: Restart sans user interaction" - killProcess "Self Service" - # runAsUser osascript -e 'tell app "System Events" to restart' - # sleep 5 && runAsUser osascript -e 'tell app "System Events" to restart' & - sleep 5 && shutdown -r now & - ;; +welcomeJSON=' +{ + "commandfile" : "'"${welcomeCommandFile}"'", + "bannerimage" : "'"${welcomeBannerImage}"'", + "bannertext" : "'"${welcomeBannerText}"'", + "title" : "'"${welcomeTitle}"'", + "message" : "'"${welcomeMessage}"'", + "icon" : "'"${welcomeIcon}"'", + "infobox" : "Analyzing …", + "iconsize" : "198.0", + "button1text" : "Continue", + "button2text" : "Quit", + "infotext" : "'"${scriptVersion}"'", + "blurscreen" : "true", + "ontop" : "true", + "titlefont" : "shadow=true, size=36, colour=#FFFDF4", + "messagefont" : "size=14", + "textfield" : [ + '${textFieldJSON}' + ], + "selectitems" : [ + '${selectItemsJSON}' + ], + "height" : "800" +} +' - "Restart Attended" ) - updateScriptLog "COMPLETION ACTION: Restart, requiring user-interaction" - killProcess "Self Service" - wait - # runAsUser osascript -e 'tell app "System Events" to restart' - # sleep 5 && runAsUser osascript -e 'tell app "System Events" to restart' & - sleep 5 && shutdown -r now & - ;; - "Restart Confirm" ) - updateScriptLog "COMPLETION ACTION: Restart, only after macOS time-out or user confirmation" - runAsUser osascript -e 'tell app "loginwindow" to «event aevtrrst»' - ;; - "Log Out" ) - updateScriptLog "COMPLETION ACTION: Log out sans user interaction" - killProcess "Self Service" - # sleep 5 && runAsUser osascript -e 'tell app "loginwindow" to «event aevtrlgo»' - # sleep 5 && runAsUser osascript -e 'tell app "loginwindow" to «event aevtrlgo»' & - sleep 5 && launchctl bootout user/"${loggedInUserID}" - ;; +#################################################################################################### +# +# Setup Your Mac dialog +# +#################################################################################################### - "Log Out Attended" ) - updateScriptLog "COMPLETION ACTION: Log out sans user interaction" - killProcess "Self Service" - wait - # sleep 5 && runAsUser osascript -e 'tell app "loginwindow" to «event aevtrlgo»' - # sleep 5 && runAsUser osascript -e 'tell app "loginwindow" to «event aevtrlgo»' & - sleep 5 && launchctl bootout user/"${loggedInUserID}" - ;; +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# "Setup Your Mac" dialog Title, Message, Icon and Overlay Icon +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - "Log Out Confirm" ) - updateScriptLog "COMPLETION ACTION: Log out, only after macOS time-out or user confirmation" - sleep 5 && runAsUser osascript -e 'tell app "System Events" to log out' - ;; +title="Setting up ${loggedInUserFirstname}‘s ${modelName}" +message="Please wait while the following apps are installed …" - "Sleep"* ) - sleepDuration=$( awk '{print $NF}' <<< "${1}" ) - updateScriptLog "COMPLETION ACTION: Sleeping for ${sleepDuration} seconds …" - sleep "${sleepDuration}" - killProcess "Dialog" - updateScriptLog "Goodnight!" - ;; +if [[ "${brandingBannerDisplayText}" == "true" ]] ; then + bannerText="Setting up ${loggedInUserFirstname}‘s ${modelName}"; +else + bannerText="" +fi - "Wait" ) - updateScriptLog "COMPLETION ACTION: Waiting for user interaction …" - wait - ;; +if [ -n "$supportTeamName" ]; then + helpmessage+="If you need assistance, please contact: \n\n**${supportTeamName}** \n" +fi - "Quit" ) - updateScriptLog "COMPLETION ACTION: Quitting script" - exitCode="0" - ;; +if [ -n "$supportTeamPhone" ]; then + helpmessage+="- **Telephone:** ${supportTeamPhone} \n" +fi - * ) - updateScriptLog "COMPLETION ACTION: Using the default of 'wait'" - wait - ;; +if [ -n "$supportTeamEmail" ]; then + helpmessage+="- **Email:** ${supportTeamEmail} \n" +fi - esac +if [ -n "$supportTeamWebsite" ]; then + helpmessage+="- **Web**: ${supportTeamHyperlink} \n" +fi - shopt -u nocasematch +if [ -n "$supportKB" ]; then + helpmessage+="- **Knowledge Base Article:** ${supportTeamErrorKB} \n" +fi - fi +helpmessage+="\n**Computer Information:** \n" +helpmessage+="- **Operating System:** ${macOSproductVersion} (${macOSbuildVersion}) \n" +helpmessage+="- **Serial Number:** ${serialNumber} \n" +helpmessage+="- **Dialog:** ${dialogVersion} \n" +helpmessage+="- **Started:** ${timestamp}" - # Remove custom welcomeBannerImageFileName - if [[ -e "/var/tmp/${welcomeBannerImageFileName}" ]]; then - updateScriptLog "COMPLETION ACTION: Removing /var/tmp/${welcomeBannerImageFileName} …" - rm "/var/tmp/${welcomeBannerImageFileName}" - fi +infobox="Analyzing input …" # Customize at "Update Setup Your Mac's infobox" - # Remove overlayicon - if [[ -e ${overlayicon} ]]; then - updateScriptLog "COMPLETION ACTION: Removing ${overlayicon} …" - rm "${overlayicon}" - fi - exit "${exitCode}" +# Create `overlayicon` from Self Service's custom icon (thanks, @meschwartz!) +xxd -p -s 260 "$(defaults read /Library/Preferences/com.jamfsoftware.jamf self_service_app_path)"/Icon$'\r'/..namedfork/rsrc | xxd -r -p > /var/tmp/overlayicon.icns +overlayicon="/var/tmp/overlayicon.icns" -} +# Uncomment to use generic, Self Service icon as overlayicon +# overlayicon="https://ics.services.jamfcloud.com/icon/hash_aa63d5813d6ed4846b623ed82acdd1562779bf3716f2d432a8ee533bba8950ee" + +# Set initial icon based on whether the Mac is a desktop or laptop +if system_profiler SPPowerDataType | grep -q "Battery Power"; then + icon="SF=laptopcomputer.and.arrow.down,weight=semibold,colour1=#ef9d51,colour2=#ef7951" +else + icon="SF=desktopcomputer.and.arrow.down,weight=semibold,colour1=#ef9d51,colour2=#ef7951" +fi # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Welcome dialog 'infobox' animation (thanks, @bartreadon!) -# To convert emojis, see: https://r12a.github.io/app-conversion/ +# "Setup Your Mac" dialog Settings and Features # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function welcomeDialogInfoboxAnimation() { - callingPID=$1 - # clock_emojis=("🕐" "🕑" "🕒" "🕓" "🕔" "🕕" "🕖" "🕗" "🕘" "🕙" "🕚" "🕛") - clock_emojis=("🕐" "🕑" "🕒" "🕓" "🕔" "🕕" "🕖" "🕗" "🕘" "🕙" "🕚" "🕛") - while true; do - for emoji in "${clock_emojis[@]}"; do - if kill -0 "$callingPID" 2>/dev/null; then - dialogUpdateWelcome "infobox: Testing Connection $emoji" - else - break - fi - sleep 0.6 - done - done -} +dialogSetupYourMacCMD="$dialogBinary \ +--bannerimage \"$bannerImage\" \ +--bannertext \"$bannerText\" \ +--title \"$title\" \ +--message \"$message\" \ +--helpmessage \"$helpmessage\" \ +--icon \"$icon\" \ +--infobox \"${infobox}\" \ +--progress \ +--progresstext \"Initializing configuration …\" \ +--button1text \"Wait\" \ +--button1disabled \ +--infotext \"$scriptVersion\" \ +--titlefont 'shadow=true, size=36, colour=#FFFDF4' \ +--messagefont 'size=14' \ +--height '800' \ +--position 'centre' \ +--blurscreen \ +--ontop \ +--overlayicon \"$overlayicon\" \ +--quitkey k \ +--commandfile \"$setupYourMacCommandFile\" " # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Setup Your Mac dialog 'infobox' animation (thanks, @bartreadon!) -# To convert emojis, see: https://r12a.github.io/app-conversion/ +# +# [SYM-Helper] "Setup Your Mac" policies to execute (Thanks, Obi-@smithjw!) +# +# For each configuration step, specify: +# - listitem: The text to be displayed in the list +# - icon: The hash of the icon to be displayed on the left +# - See: https://vimeo.com/772998915 +# - progresstext: The text to be displayed below the progress bar +# - trigger: The Jamf Pro Policy Custom Event Name +# - validation: [ {absolute path} | Local | Remote | None | Recon ] +# See: https://snelson.us/2023/01/setup-your-mac-validation/ +# - {absolute path} (simulates pre-v1.6.0 behavior, for example: "/Applications/Microsoft Teams classic.app/Contents/Info.plist") +# - Local (for validation within this script, for example: "filevault") +# - Remote (for validation via a single-script Jamf Pro policy, for example: "symvGlobalProtect") +# - None (for triggers which don't require validation; always evaluates as successful) +# - Recon (to update the computer's inventory with your Jamf Pro server) +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# Thanks, @wakco: If you would prefer to get your policyJSON externally replace it with: +# - policyJSON="$(cat /path/to/file.json)" # For getting from a file, replacing /path/to/file.json with the path to your file, or +# - policyJSON="$(curl -sL https://server.name/jsonquery)" # For a URL, replacing https://server.name/jsonquery with the URL of your file. +# +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# +# Thanks, @astrugatch: I added this line to global variables: +# jsonURL=${10} # URL Hosting JSON for policy_array +# +# And this line replaces the entirety of the policy_array (~ line 503): +# policy_array=("$(curl -sL $jsonURL)") +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -function setupYourMacDialogInfoboxAnimation() { - callingPID=$1 - # clock_emojis=("🕐" "🕑" "🕒" "🕓" "🕔" "🕕" "🕖" "🕗" "🕘" "🕙" "🕚" "🕛") - clock_emojis=("🕐" "🕑" "🕒" "🕓" "🕔" "🕕" "🕖" "🕗" "🕘" "🕙" "🕚" "🕛") - while true; do - for emoji in "${clock_emojis[@]}"; do - if kill -0 "$callingPID" 2>/dev/null; then - dialogUpdateSetupYourMac "infobox: Testing Connection $emoji" - else - break - fi - sleep 0.6 - done - done -} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Check Network Quality for Configurations (thanks, @bartreadon!) +# Select `policyJSON` based on Configuration selected in "Welcome" dialog (thanks, @drtaru!) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function checkNetworkQualityConfigurations() { - - myPID="$$" - updateScriptLog "WELCOME DIALOG: Display Welcome dialog 'infobox' animation …" - welcomeDialogInfoboxAnimation "$myPID" & - welcomeDialogInfoboxAnimationPID="$!" +function policyJSONConfiguration() { - networkQuality -s -v -c > /var/tmp/networkQualityTest - kill ${welcomeDialogInfoboxAnimationPID} outputLineNumberInVerboseDebugMode - updateScriptLog "WELCOME DIALOG: Completed networkQualityTest …" - networkQualityTest=$( < /var/tmp/networkQualityTest ) - rm /var/tmp/networkQualityTest + welcomeDialog "PolicyJSON Configuration: $symConfiguration" - case "${osVersion}" in + case ${symConfiguration} in + + "${configurationOneName}" ) + + overlayoverride="" + policyJSON=' + { + "steps": [ + { + "listitem": "Rosetta", + "subtitle": "Enables a Mac with Apple silicon to use apps built for an Intel processor", + "icon": "https://ics.services.jamfcloud.com/icon/hash_8bac19160fabb0c8e7bac97b37b51d2ac8f38b7100b6357642d9505645d37b52", + "progresstext": "Rosetta enables a Mac with Apple silicon to use apps built for a Mac with an Intel processor.", + "trigger_list": [ + { + "trigger": "rosettaInstall", + "validation": "None" + }, + { + "trigger": "rosetta", + "validation": "Local" + } + ] + }, + { + "listitem": "FileVault Disk Encryption", + "subtitle": "FileVault provides full-disk encryption", + "icon": "https://ics.services.jamfcloud.com/icon/hash_f9ba35bd55488783456d64ec73372f029560531ca10dfa0e8154a46d7732b913", + "progresstext": "FileVault is built-in to macOS and provides full-disk encryption to help prevent unauthorized access to your Mac.", + "trigger_list": [ + { + "trigger": "filevault", + "validation": "Local" + } + ] + }, + { + "listitem": "Sophos Endpoint", + "subtitle": "Catches malware without relying on signatures", + "icon": "https://ics.services.jamfcloud.com/icon/hash_c70f1acf8c96b99568fec83e165d2a534d111b0510fb561a283d32aa5b01c60c", + "progresstext": "You’ll enjoy next-gen protection with Sophos Endpoint which doesn’t rely on signatures to catch malware.", + "trigger_list": [ + { + "trigger": "sophosEndpoint", + "validation": "/Applications/Sophos/Sophos Endpoint.app/Contents/Info.plist" + } + ] + }, + { + "listitem": "Sophos Endpoint Services (Remote)", + "subtitle": "Ensures Sophos Endpoint services are running", + "icon": "https://ics.services.jamfcloud.com/icon/hash_0f68be689684a00a3a054d71a31e43e2362f96c16efa5a560fb61bc1bf41901c", + "progresstext": "Remotely validating Sophos Endpoint services …", + "trigger_list": [ + { + "trigger": "symvSophosEndpointRTS", + "validation": "Remote" + } + ] + }, + { + "listitem": "Palo Alto GlobalProtect", + "subtitle": "Virtual Private Network (VPN) connection to Church headquarters", + "icon": "https://ics.services.jamfcloud.com/icon/hash_acbf39d8904ad1a772cf71c45d93e373626d379a24f8b1283b88134880acb8ef", + "progresstext": "Use Palo Alto GlobalProtect to establish a Virtual Private Network (VPN) connection to Church headquarters.", + "trigger_list": [ + { + "trigger": "globalProtect", + "validation": "/Applications/GlobalProtect.app/Contents/Info.plist" + } + ] + }, + { + "listitem": "Palo Alto GlobalProtect Services (Remote)", + "subtitle": "Ensures GlobalProtect services are running", + "icon": "https://ics.services.jamfcloud.com/icon/hash_709e8bdf0019e8faf9df85ec0a68545bfdb8bfa1227ac9bed9bba40a1fa8ff42", + "progresstext": "Remotely validating Palo Alto GlobalProtect services …", + "trigger_list": [ + { + "trigger": "symvGlobalProtect", + "validation": "Remote" + } + ] + }, + { + "listitem": "Final Configuration", + "subtitle": "Configures remaining Church settings", + "icon": "https://ics.services.jamfcloud.com/icon/hash_4723e3e341a7e11e6881e418cf91b157fcc081bdb8948697750e5da3562df728", + "progresstext": "Finalizing Configuration …", + "trigger_list": [ + { + "trigger": "finalConfiguration", + "validation": "None" + }, + { + "trigger": "reconAtReboot", + "validation": "None" + } + ] + }, + { + "listitem": "Computer Inventory", + "subtitle": "The listing of your Mac’s apps and settings", + "icon": "https://ics.services.jamfcloud.com/icon/hash_ff2147a6c09f5ef73d1c4406d00346811a9c64c0b6b7f36eb52fcb44943d26f9", + "progresstext": "A listing of your Mac’s apps and settings — its inventory — is sent automatically to the Jamf Pro server daily.", + "trigger_list": [ + { + "trigger": "recon", + "validation": "recon" + } + ] + } + ] + } + ' + ;; + + "${configurationTwoName}" ) - 11* ) - dlThroughput="N/A; macOS ${osVersion}" - dlResponsiveness="N/A; macOS ${osVersion}" - dlStartDate="N/A; macOS ${osVersion}" - dlEndDate="N/A; macOS ${osVersion}" + overlayoverride="" + policyJSON=' + { + "steps": [ + { + "listitem": "Rosetta", + "subtitle": "Enables a Mac with Apple silicon to use apps built for an Intel processor", + "icon": "https://ics.services.jamfcloud.com/icon/hash_8bac19160fabb0c8e7bac97b37b51d2ac8f38b7100b6357642d9505645d37b52", + "progresstext": "Rosetta enables a Mac with Apple silicon to use apps built for a Mac with an Intel processor.", + "trigger_list": [ + { + "trigger": "rosettaInstall", + "validation": "None" + }, + { + "trigger": "rosetta", + "validation": "Local" + } + ] + }, + { + "listitem": "FileVault Disk Encryption", + "subtitle": "FileVault provides full-disk encryption", + "icon": "https://ics.services.jamfcloud.com/icon/hash_f9ba35bd55488783456d64ec73372f029560531ca10dfa0e8154a46d7732b913", + "progresstext": "FileVault is built-in to macOS and provides full-disk encryption to help prevent unauthorized access to your Mac.", + "trigger_list": [ + { + "trigger": "filevault", + "validation": "Local" + } + ] + }, + { + "listitem": "Sophos Endpoint", + "subtitle": "Catches malware without relying on signatures", + "icon": "https://ics.services.jamfcloud.com/icon/hash_c70f1acf8c96b99568fec83e165d2a534d111b0510fb561a283d32aa5b01c60c", + "progresstext": "You’ll enjoy next-gen protection with Sophos Endpoint which doesn’t rely on signatures to catch malware.", + "trigger_list": [ + { + "trigger": "sophosEndpoint", + "validation": "/Applications/Sophos/Sophos Endpoint.app/Contents/Info.plist" + } + ] + }, + { + "listitem": "Sophos Endpoint Services (Local)", + "subtitle": "Ensures Sophos Endpoint services are running", + "icon": "https://ics.services.jamfcloud.com/icon/hash_0f68be689684a00a3a054d71a31e43e2362f96c16efa5a560fb61bc1bf41901c", + "progresstext": "Locally validating Sophos Endpoint services …", + "trigger_list": [ + { + "trigger": "sophosEndpointServices", + "validation": "Local" + } + ] + }, + { + "listitem": "Palo Alto GlobalProtect", + "subtitle": "Virtual Private Network (VPN) connection to Church headquarters", + "icon": "https://ics.services.jamfcloud.com/icon/hash_acbf39d8904ad1a772cf71c45d93e373626d379a24f8b1283b88134880acb8ef", + "progresstext": "Use Palo Alto GlobalProtect to establish a Virtual Private Network (VPN) connection to Church headquarters.", + "trigger_list": [ + { + "trigger": "globalProtect", + "validation": "/Applications/GlobalProtect.app/Contents/Info.plist" + } + ] + }, + { + "listitem": "Palo Alto GlobalProtect Services (Local)", + "subtitle": "Ensures GlobalProtect services are running", + "icon": "https://ics.services.jamfcloud.com/icon/hash_709e8bdf0019e8faf9df85ec0a68545bfdb8bfa1227ac9bed9bba40a1fa8ff42", + "progresstext": "Locally validating Palo Alto GlobalProtect services …", + "trigger_list": [ + { + "trigger": "globalProtect", + "validation": "Local" + } + ] + }, + { + "listitem": "Microsoft 365", + "subtitle": "Microsoft Office is now Microsoft 365", + "icon": "https://ics.services.jamfcloud.com/icon/hash_1801d1fdd81e19ce5eb0e567371377e7995bff32947adb7a94c5feea760edcb5", + "progresstext": "Office is now Microsoft 365. Create, share, and collaborate with your favorite apps — all in one place — with Microsoft 365.", + "trigger_list": [ + { + "trigger": "microsoftOffice365", + "validation": "/Applications/Microsoft Outlook.app/Contents/Info.plist" + }, + { + "trigger": "symvMicrosoftOffice365", + "validation": "Remote" + } + ] + }, + { + "listitem": "Microsoft Teams", + "subtitle": "The hub for teamwork in Microsoft 365", + "icon": "https://ics.services.jamfcloud.com/icon/hash_dcb65709dba6cffa90a5eeaa54cb548d5ecc3b051f39feadd39e02744f37c19e", + "progresstext": "Microsoft Teams is a hub for teamwork in Microsoft 365. Keep all your team’s chats, meetings and files together in one place.", + "trigger_list": [ + { + "trigger": "microsoftTeams", + "validation": "/Applications/Microsoft Teams classic.app/Contents/Info.plist" + } + ] + }, + { + "listitem": "Final Configuration", + "subtitle": "Configures remaining Church settings", + "icon": "https://ics.services.jamfcloud.com/icon/hash_4723e3e341a7e11e6881e418cf91b157fcc081bdb8948697750e5da3562df728", + "progresstext": "Finalizing Configuration …", + "trigger_list": [ + { + "trigger": "finalConfiguration", + "validation": "None" + }, + { + "trigger": "reconAtReboot", + "validation": "None" + } + ] + }, + { + "listitem": "Computer Inventory", + "subtitle": "The listing of your Mac’s apps and settings", + "icon": "https://ics.services.jamfcloud.com/icon/hash_ff2147a6c09f5ef73d1c4406d00346811a9c64c0b6b7f36eb52fcb44943d26f9", + "progresstext": "A listing of your Mac’s apps and settings — its inventory — is sent automatically to the Jamf Pro server daily.", + "trigger_list": [ + { + "trigger": "recon", + "validation": "recon" + } + ] + } + ] + } + ' ;; - 12* | 13* | 14*) - dlThroughput=$( get_json_value "$networkQualityTest" "dl_throughput") - dlResponsiveness=$( get_json_value "$networkQualityTest" "dl_responsiveness" ) - dlStartDate=$( get_json_value "$networkQualityTest" "start_date" ) - dlEndDate=$( get_json_value "$networkQualityTest" "end_date" ) + "${configurationThreeName}" ) + + overlayoverride="" + policyJSON=' + { + "steps": [ + { + "listitem": "Rosetta", + "subtitle": "Enables a Mac with Apple silicon to use apps built for an Intel processor", + "icon": "https://ics.services.jamfcloud.com/icon/hash_8bac19160fabb0c8e7bac97b37b51d2ac8f38b7100b6357642d9505645d37b52", + "progresstext": "Rosetta enables a Mac with Apple silicon to use apps built for a Mac with an Intel processor.", + "trigger_list": [ + { + "trigger": "rosettaInstall", + "validation": "None" + }, + { + "trigger": "rosetta", + "validation": "Local" + } + ] + }, + { + "listitem": "FileVault Disk Encryption", + "subtitle": "FileVault provides full-disk encryption", + "icon": "https://ics.services.jamfcloud.com/icon/hash_f9ba35bd55488783456d64ec73372f029560531ca10dfa0e8154a46d7732b913", + "progresstext": "FileVault is built-in to macOS and provides full-disk encryption to help prevent unauthorized access to your Mac.", + "trigger_list": [ + { + "trigger": "filevault", + "validation": "Local" + } + ] + }, + { + "listitem": "Sophos Endpoint", + "subtitle": "Catches malware without relying on signatures", + "icon": "https://ics.services.jamfcloud.com/icon/hash_c70f1acf8c96b99568fec83e165d2a534d111b0510fb561a283d32aa5b01c60c", + "progresstext": "You’ll enjoy next-gen protection with Sophos Endpoint which doesn’t rely on signatures to catch malware.", + "trigger_list": [ + { + "trigger": "sophosEndpoint", + "validation": "/Applications/Sophos/Sophos Endpoint.app/Contents/Info.plist" + } + ] + }, + { + "listitem": "Sophos Endpoint Services (Local)", + "subtitle": "Ensures Sophos Endpoint services are running", + "icon": "https://ics.services.jamfcloud.com/icon/hash_0f68be689684a00a3a054d71a31e43e2362f96c16efa5a560fb61bc1bf41901c", + "progresstext": "Locally validating Sophos Endpoint services …", + "trigger_list": [ + { + "trigger": "sophosEndpointServices", + "validation": "Local" + } + ] + }, + { + "listitem": "Sophos Endpoint Services (Remote)", + "subtitle": "Ensures Sophos Endpoint services are running", + "icon": "https://ics.services.jamfcloud.com/icon/hash_0f68be689684a00a3a054d71a31e43e2362f96c16efa5a560fb61bc1bf41901c", + "progresstext": "Remotely validating Sophos Endpoint services …", + "trigger_list": [ + { + "trigger": "symvSophosEndpointRTS", + "validation": "Remote" + } + ] + }, + { + "listitem": "Palo Alto GlobalProtect", + "subtitle": "Virtual Private Network (VPN) connection to Church headquarters", + "icon": "https://ics.services.jamfcloud.com/icon/hash_acbf39d8904ad1a772cf71c45d93e373626d379a24f8b1283b88134880acb8ef", + "progresstext": "Use Palo Alto GlobalProtect to establish a Virtual Private Network (VPN) connection to Church headquarters.", + "trigger_list": [ + { + "trigger": "globalProtect", + "validation": "/Applications/GlobalProtect.app/Contents/Info.plist" + } + ] + }, + { + "listitem": "Palo Alto GlobalProtect Services (Local)", + "subtitle": "Ensures GlobalProtect services are running", + "icon": "https://ics.services.jamfcloud.com/icon/hash_709e8bdf0019e8faf9df85ec0a68545bfdb8bfa1227ac9bed9bba40a1fa8ff42", + "progresstext": "Locally validating Palo Alto GlobalProtect services …", + "trigger_list": [ + { + "trigger": "globalProtect", + "validation": "Local" + } + ] + }, + { + "listitem": "Palo Alto GlobalProtect Services (Remote)", + "subtitle": "Ensures GlobalProtect services are running", + "icon": "https://ics.services.jamfcloud.com/icon/hash_709e8bdf0019e8faf9df85ec0a68545bfdb8bfa1227ac9bed9bba40a1fa8ff42", + "progresstext": "Remotely validating Palo Alto GlobalProtect services …", + "trigger_list": [ + { + "trigger": "symvGlobalProtect", + "validation": "Remote" + } + ] + }, + { + "listitem": "Microsoft 365", + "subtitle": "Microsoft Office is now Microsoft 365", + "icon": "https://ics.services.jamfcloud.com/icon/hash_1801d1fdd81e19ce5eb0e567371377e7995bff32947adb7a94c5feea760edcb5", + "progresstext": "Office is now Microsoft 365. Create, share, and collaborate with your favorite apps — all in one place — with Microsoft 365.", + "trigger_list": [ + { + "trigger": "microsoftOffice365", + "validation": "/Applications/Microsoft Outlook.app/Contents/Info.plist" + }, + { + "trigger": "symvMicrosoftOffice365", + "validation": "Remote" + } + ] + }, + { + "listitem": "Microsoft Teams", + "subtitle": "The hub for teamwork in Microsoft 365", + "icon": "https://ics.services.jamfcloud.com/icon/hash_dcb65709dba6cffa90a5eeaa54cb548d5ecc3b051f39feadd39e02744f37c19e", + "progresstext": "Microsoft Teams is a hub for teamwork in Microsoft 365. Keep all your team’s chats, meetings and files together in one place.", + "trigger_list": [ + { + "trigger": "microsoftTeams", + "validation": "/Applications/Microsoft Teams classic.app/Contents/Info.plist" + } + ] + }, + { + "listitem": "Adobe Acrobat Reader", + "subtitle": "Full-featured PDF reader", + "icon": "https://ics.services.jamfcloud.com/icon/hash_988b669ca27eab93a9bcd53bb7e2873fb98be4eaa95ae8974c14d611bea1d95f", + "progresstext": "Views, prints, and comments on PDF documents, and connects to Adobe Document Cloud.", + "trigger_list": [ + { + "trigger": "adobeAcrobatReader", + "validation": "/Applications/Adobe Acrobat Reader.app/Contents/Info.plist" + } + ] + }, + { + "listitem": "Google Chrome", + "subtitle": "Third-party Web browser", + "icon": "https://ics.services.jamfcloud.com/icon/hash_12d3d198f40ab2ac237cff3b5cb05b09f7f26966d6dffba780e4d4e5325cc701", + "progresstext": "Google Chrome is a browser that combines a minimal design with sophisticated technology to make the Web faster.", + "trigger_list": [ + { + "trigger": "googleChrome", + "validation": "/Applications/Google Chrome.app/Contents/Info.plist" + } + ] + }, + { + "listitem": "Final Configuration", + "subtitle": "Configures remaining Church settings", + "icon": "https://ics.services.jamfcloud.com/icon/hash_4723e3e341a7e11e6881e418cf91b157fcc081bdb8948697750e5da3562df728", + "progresstext": "Finalizing Configuration …", + "trigger_list": [ + { + "trigger": "finalConfiguration", + "validation": "None" + }, + { + "trigger": "reconAtReboot", + "validation": "None" + } + ] + }, + { + "listitem": "Computer Inventory", + "subtitle": "The listing of your Mac’s apps and settings", + "icon": "https://ics.services.jamfcloud.com/icon/hash_ff2147a6c09f5ef73d1c4406d00346811a9c64c0b6b7f36eb52fcb44943d26f9", + "progresstext": "A listing of your Mac’s apps and settings — its inventory — is sent automatically to the Jamf Pro server daily.", + "trigger_list": [ + { + "trigger": "recon", + "validation": "recon" + } + ] + } + ] + } + ' ;; - esac - - mbps=$( echo "scale=2; ( $dlThroughput / 1000000 )" | bc ) - updateScriptLog "WELCOME DIALOG: $mbps (Mbps)" - - configurationOneEstimatedSeconds=$( echo "scale=2; ((((( $configurationOneSize / $mbps ) * 60 ) * 60 ) * $correctionCoefficient ) + $configurationOneInstallBuffer)" | bc | sed 's/\.[0-9]*//' ) - updateScriptLog "WELCOME DIALOG: Configuration One Estimated Seconds: $configurationOneEstimatedSeconds" - updateScriptLog "WELCOME DIALOG: Configuration One Estimate: $(printf '%dh:%dm:%ds\n' $((configurationOneEstimatedSeconds/3600)) $((configurationOneEstimatedSeconds%3600/60)) $((configurationOneEstimatedSeconds%60)))" - - configurationTwoEstimatedSeconds=$( echo "scale=2; ((((( $configurationTwoSize / $mbps ) * 60 ) * 60 ) * $correctionCoefficient ) + $configurationTwoInstallBuffer)" | bc | sed 's/\.[0-9]*//' ) - updateScriptLog "WELCOME DIALOG: Configuration Two Estimated Seconds: $configurationTwoEstimatedSeconds" - updateScriptLog "WELCOME DIALOG: Configuration Two Estimate: $(printf '%dh:%dm:%ds\n' $((configurationTwoEstimatedSeconds/3600)) $((configurationTwoEstimatedSeconds%3600/60)) $((configurationTwoEstimatedSeconds%60)))" - - configurationThreeEstimatedSeconds=$( echo "scale=2; ((((( $configurationThreeSize / $mbps ) * 60 ) * 60 ) * $correctionCoefficient ) + $configurationThreeInstallBuffer)" | bc | sed 's/\.[0-9]*//' ) - updateScriptLog "WELCOME DIALOG: Configuration Three Estimated Seconds: $configurationThreeEstimatedSeconds" - updateScriptLog "WELCOME DIALOG: Configuration Three Estimate: $(printf '%dh:%dm:%ds\n' $((configurationThreeEstimatedSeconds/3600)) $((configurationThreeEstimatedSeconds%3600/60)) $((configurationThreeEstimatedSeconds%60)))" - - updateScriptLog "WELCOME DIALOG: Network Quality Test: Started: $dlStartDate, Ended: $dlEndDate; Download: $mbps Mbps, Responsiveness: $dlResponsiveness" - dialogUpdateWelcome "infobox: **Connection:** \n- Download: \n$mbps Mbps \n\n**Estimates:** \n- ${configurationOneName}: \n$(printf '%dh:%dm:%ds\n' $((configurationOneEstimatedSeconds/3600)) $((configurationOneEstimatedSeconds%3600/60)) $((configurationOneEstimatedSeconds%60))) \n\n- ${configurationTwoName}: \n$(printf '%dh:%dm:%ds\n' $((configurationTwoEstimatedSeconds/3600)) $((configurationTwoEstimatedSeconds%3600/60)) $((configurationTwoEstimatedSeconds%60))) \n\n- ${configurationThreeName}: \n$(printf '%dh:%dm:%ds\n' $((configurationThreeEstimatedSeconds/3600)) $((configurationThreeEstimatedSeconds%3600/60)) $((configurationThreeEstimatedSeconds%60)))" - - # If option to lock the continue button is set to true, enable the continue button now to let the user progress - if [[ "${lockContinueBeforeEstimations}" == "true" ]]; then - updateScriptLog "WELCOME DIALOG: Enabling Continue Button" - dialogUpdateWelcome "button1: enable" - fi -} - - - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Check Network Quality for Catch-all Configuration (thanks, @bartreadon!) -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -function checkNetworkQualityCatchAllConfiguration() { - - myPID="$$" - updateScriptLog "SETUP YOUR MAC DIALOG: Display Welcome dialog 'infobox' animation …" - setupYourMacDialogInfoboxAnimation "$myPID" & - setupYourMacDialogInfoboxAnimationPID="$!" - - networkQuality -s -v -c > /var/tmp/networkQualityTest - kill ${setupYourMacDialogInfoboxAnimationPID} - outputLineNumberInVerboseDebugMode - - updateScriptLog "SETUP YOUR MAC DIALOG: Completed networkQualityTest …" - networkQualityTest=$( < /var/tmp/networkQualityTest ) - rm /var/tmp/networkQualityTest - - case "${osVersion}" in - - 11* ) - dlThroughput="N/A; macOS ${osVersion}" - dlResponsiveness="N/A; macOS ${osVersion}" - dlStartDate="N/A; macOS ${osVersion}" - dlEndDate="N/A; macOS ${osVersion}" - ;; + * ) # Catch-all (i.e., used when `welcomeDialog` is set to `video`, `messageOnly` or `false`) - 12* | 13* | 14*) - dlThroughput=$( get_json_value "$networkQualityTest" "dl_throughput") - dlResponsiveness=$( get_json_value "$networkQualityTest" "dl_responsiveness" ) - dlStartDate=$( get_json_value "$networkQualityTest" "start_date" ) - dlEndDate=$( get_json_value "$networkQualityTest" "end_date" ) + overlayoverride="" + policyJSON=' + { + "steps": [ + { + "listitem": "Rosetta", + "subtitle": "Enables a Mac with Apple silicon to use apps built for an Intel processor", + "icon": "https://ics.services.jamfcloud.com/icon/hash_8bac19160fabb0c8e7bac97b37b51d2ac8f38b7100b6357642d9505645d37b52", + "progresstext": "Rosetta enables a Mac with Apple silicon to use apps built for a Mac with an Intel processor.", + "trigger_list": [ + { + "trigger": "rosettaInstall", + "validation": "None" + }, + { + "trigger": "rosetta", + "validation": "Local" + } + ] + }, + { + "listitem": "FileVault Disk Encryption", + "subtitle": "FileVault provides full-disk encryption", + "icon": "https://ics.services.jamfcloud.com/icon/hash_f9ba35bd55488783456d64ec73372f029560531ca10dfa0e8154a46d7732b913", + "progresstext": "FileVault is built-in to macOS and provides full-disk encryption to help prevent unauthorized access to your Mac.", + "trigger_list": [ + { + "trigger": "filevault", + "validation": "Local" + } + ] + }, + { + "listitem": "Sophos Endpoint", + "subtitle": "Catches malware without relying on signatures", + "icon": "https://ics.services.jamfcloud.com/icon/hash_c70f1acf8c96b99568fec83e165d2a534d111b0510fb561a283d32aa5b01c60c", + "progresstext": "You’ll enjoy next-gen protection with Sophos Endpoint which doesn’t rely on signatures to catch malware.", + "trigger_list": [ + { + "trigger": "sophosEndpoint", + "validation": "/Applications/Sophos/Sophos Endpoint.app/Contents/Info.plist" + }, + { + "trigger": "symvSophosEndpointRTS", + "validation": "Remote" + } + ] + }, + { + "listitem": "Palo Alto GlobalProtect", + "subtitle": "Virtual Private Network (VPN) connection to Church headquarters", + "icon": "https://ics.services.jamfcloud.com/icon/hash_acbf39d8904ad1a772cf71c45d93e373626d379a24f8b1283b88134880acb8ef", + "progresstext": "Use Palo Alto GlobalProtect to establish a Virtual Private Network (VPN) connection to Church headquarters.", + "trigger_list": [ + { + "trigger": "globalProtect", + "validation": "/Applications/GlobalProtect.app/Contents/Info.plist" + }, + { + "trigger": "symvGlobalProtect", + "validation": "Remote" + } + ] + }, + { + "listitem": "Final Configuration", + "subtitle": "Configures remaining Church settings", + "icon": "https://ics.services.jamfcloud.com/icon/hash_4723e3e341a7e11e6881e418cf91b157fcc081bdb8948697750e5da3562df728", + "progresstext": "Finalizing Configuration …", + "trigger_list": [ + { + "trigger": "finalConfiguration", + "validation": "None" + }, + { + "trigger": "reconAtReboot", + "validation": "None" + } + ] + }, + { + "listitem": "Computer Inventory", + "subtitle": "The listing of your Mac’s apps and settings", + "icon": "https://ics.services.jamfcloud.com/icon/hash_ff2147a6c09f5ef73d1c4406d00346811a9c64c0b6b7f36eb52fcb44943d26f9", + "progresstext": "A listing of your Mac’s apps and settings — its inventory — is sent automatically to the Jamf Pro server daily.", + "trigger_list": [ + { + "trigger": "recon", + "validation": "recon" + } + ] + } + ] + } + ' ;; esac - mbps=$( echo "scale=2; ( $dlThroughput / 1000000 )" | bc ) - updateScriptLog "SETUP YOUR MAC DIALOG: $mbps (Mbps)" - - configurationCatchAllEstimatedSeconds=$( echo "scale=2; ((((( $configurationCatchAllSize / $mbps ) * 60 ) * 60 ) * $correctionCoefficient ) + $configurationCatchAllInstallBuffer)" | bc | sed 's/\.[0-9]*//' ) - updateScriptLog "SETUP YOUR MAC DIALOG: Catch-all Configuration Estimated Seconds: $configurationCatchAllEstimatedSeconds" - updateScriptLog "SETUP YOUR MAC DIALOG: Catch-all Configuration Estimate: $(printf '%dh:%dm:%ds\n' $((configurationCatchAllEstimatedSeconds/3600)) $((configurationCatchAllEstimatedSeconds%3600/60)) $((configurationCatchAllEstimatedSeconds%60)))" - - updateScriptLog "SETUP YOUR MAC DIALOG: Network Quality Test: Started: $dlStartDate, Ended: $dlEndDate; Download: $mbps Mbps, Responsiveness: $dlResponsiveness" - dialogUpdateSetupYourMac "infobox: **Connection:** \n- Download: \n$mbps Mbps \n\n**Estimates:** \n- $(printf '%dh:%dm:%ds\n' $((configurationCatchAllEstimatedSeconds/3600)) $((configurationCatchAllEstimatedSeconds%3600/60)) $((configurationCatchAllEstimatedSeconds%60)))" - if [[ "${lockContinueBeforeEstimations}" == "true" ]]; then - updateScriptLog "WELCOME DIALOG: Enabling Continue Button" - dialogUpdateWelcome "button1: enable" - fi } -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Webhook Message (Microsoft Teams or Slack) (thanks, @robjschroeder! and @iDrewbs!) -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -function webHookMessage() { - - outputLineNumberInVerboseDebugMode - - jamfProURL=$(/usr/bin/defaults read /Library/Preferences/com.jamfsoftware.jamf.plist jss_url) - - # # Jamf Pro URL for on-prem, multi-node, clustered environments - # case ${jamfProURL} in - # *"beta"* ) jamfProURL="https://jamfpro-beta.internal.company.com/" ;; - # * ) jamfProURL="https://jamfpro-prod.internal.company.com/" ;; - # esac +#################################################################################################### +# +# Failure dialog +# +#################################################################################################### - jamfProComputerURL="${jamfProURL}computers.html?id=${computerID}&o=r" +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# "Failure" dialog Title, Message and Icon +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # If there aren't any failures, use "None" for the value of `jamfProPolicyNameFailures` - if [[ -z "${jamfProPolicyNameFailures}" ]]; then - jamfProPolicyNameFailures="None" - fi +failureTitle="Failure Detected" +failureMessage="Placeholder message; update in the 'finalise' function" +failureIcon="SF=xmark.circle.fill,weight=bold,colour1=#BB1717,colour2=#F31F1F" - if [[ $webhookURL == *"slack"* ]]; then - - updateScriptLog "Generating Slack Message …" - - webHookdata=$(cat <&1 - - webhookResult="$?" - updateScriptLog "Slack Webhook Result: ${webhookResult}" - - else - - updateScriptLog "Generating Microsoft Teams Message …" - # URL to an image to add to your notification - activityImage="https://creazilla-store.fra1.digitaloceanspaces.com/cliparts/78010/old-mac-computer-clipart-md.png" +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# "Failure" dialog Settings and Features +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - webHookdata=$(cat < "$welcomeJSONFile" # If option to lock the continue button is set to true, open welcome dialog with button 1 disabled if [[ "${lockContinueBeforeEstimations}" == "true" ]]; then - updateScriptLog "WELCOME DIALOG: Display 'Welcome' dialog with disabled Continue Button …" + welcomeDialog "Display 'Welcome' dialog with disabled Continue Button …" welcomeResults=$( eval "${dialogBinary} --jsonfile ${welcomeJSONFile} --json --button1disabled" ) else - updateScriptLog "WELCOME DIALOG: Display 'Welcome' dialog …" + welcomeDialog "Display 'Welcome' dialog …" welcomeResults=$( eval "${dialogBinary} --jsonfile ${welcomeJSONFile} --json" ) fi @@ -2890,7 +2932,7 @@ elif [[ "${welcomeDialog}" == "userInput" ]]; then else # Display Welcome dialog, sans estimation of Configuration download times - updateScriptLog "WELCOME DIALOG: Skipping estimation of Configuration download times" + welcomeDialog "Skipping estimation of Configuration download times" # Write Welcome JSON to disk welcomeJSON=${welcomeJSON//Analyzing …/} @@ -2909,7 +2951,7 @@ elif [[ "${welcomeDialog}" == "userInput" ]]; then case "${welcomeReturnCode}" in 0) # Process exit code 0 scenario here - updateScriptLog "WELCOME DIALOG: ${loggedInUser} entered information and clicked Continue" + welcomeDialog "${loggedInUser} entered information and clicked Continue" ### # Extract the various values from the welcomeResults JSON @@ -2938,16 +2980,16 @@ elif [[ "${welcomeDialog}" == "userInput" ]]; then # Output the various values from the welcomeResults JSON to the log file ### - updateScriptLog "WELCOME DIALOG: • Computer Name: $computerName" - updateScriptLog "WELCOME DIALOG: • User Name: $userName" - updateScriptLog "WELCOME DIALOG: • Real Name: $realName" - updateScriptLog "WELCOME DIALOG: • E-mail: $email" - updateScriptLog "WELCOME DIALOG: • Asset Tag: $assetTag" - updateScriptLog "WELCOME DIALOG: • Configuration: $symConfiguration" - updateScriptLog "WELCOME DIALOG: • Department: $department" - updateScriptLog "WELCOME DIALOG: • Building: $building" - updateScriptLog "WELCOME DIALOG: • Room: $room" - updateScriptLog "WELCOME DIALOG: • Position: $position" + welcomeDialog "• Computer Name: $computerName" + welcomeDialog "• User Name: $userName" + welcomeDialog "• Real Name: $realName" + welcomeDialog "• E-mail: $email" + welcomeDialog "• Asset Tag: $assetTag" + welcomeDialog "• Configuration: $symConfiguration" + welcomeDialog "• Department: $department" + welcomeDialog "• Building: $building" + welcomeDialog "• Room: $room" + welcomeDialog "• Position: $position" ### @@ -2966,7 +3008,7 @@ elif [[ "${welcomeDialog}" == "userInput" ]]; then if [[ -n "${computerName}" ]]; then # UNTESTED, UNSUPPORTED "YOYO" EXAMPLE - updateScriptLog "WELCOME DIALOG: Set Computer Name …" + welcomeDialog "Set Computer Name …" currentComputerName=$( scutil --get ComputerName ) currentLocalHostName=$( scutil --get LocalHostName ) @@ -2978,8 +3020,8 @@ elif [[ "${welcomeDialog}" == "userInput" ]]; then if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then - updateScriptLog "WELCOME DIALOG: DEBUG MODE: Would have renamed computer from: \"${currentComputerName}\" to \"${computerName}\" " - updateScriptLog "WELCOME DIALOG: DEBUG MODE: Would have renamed LocalHostName from: \"${currentLocalHostName}\" to \"${newLocalHostName}\" " + welcomeDialog "DEBUG MODE: Would have renamed computer from: \"${currentComputerName}\" to \"${computerName}\" " + welcomeDialog "DEBUG MODE: Would have renamed LocalHostName from: \"${currentLocalHostName}\" to \"${newLocalHostName}\" " else @@ -2992,16 +3034,16 @@ elif [[ "${welcomeDialog}" == "userInput" ]]; then # Delay required to reflect change … # … side-effect is a delay in the "Setup Your Mac" dialog appearing sleep 5 - updateScriptLog "WELCOME DIALOG: Renamed computer from: \"${currentComputerName}\" to \"$( scutil --get ComputerName )\" " - updateScriptLog "WELCOME DIALOG: Renamed LocalHostName from: \"${currentLocalHostName}\" to \"$( scutil --get LocalHostName )\" " + welcomeDialog "Renamed computer from: \"${currentComputerName}\" to \"$( scutil --get ComputerName )\" " + welcomeDialog "Renamed LocalHostName from: \"${currentLocalHostName}\" to \"$( scutil --get LocalHostName )\" " fi else - updateScriptLog "WELCOME DIALOG: ${loggedInUser} did NOT specify a new computer name" - updateScriptLog "WELCOME DIALOG: • Current Computer Name: \"$( scutil --get ComputerName )\" " - updateScriptLog "WELCOME DIALOG: • Current Local Host Name: \"$( scutil --get LocalHostName )\" " + welcomeDialog "${loggedInUser} did NOT specify a new computer name" + welcomeDialog "• Current Computer Name: \"$( scutil --get ComputerName )\" " + welcomeDialog "• Current Local Host Name: \"$( scutil --get LocalHostName )\" " fi @@ -3044,7 +3086,7 @@ elif [[ "${welcomeDialog}" == "userInput" ]]; then if [[ -n "${position}" ]]; then reconOptions+="-position \"${position}\" "; fi # Output `recon` options to log - updateScriptLog "WELCOME DIALOG: reconOptions: ${reconOptions}" + welcomeDialog "reconOptions: ${reconOptions}" ### # Display "Setup Your Mac" dialog (and capture Process ID) @@ -3053,10 +3095,10 @@ elif [[ "${welcomeDialog}" == "userInput" ]]; then eval "${dialogSetupYourMacCMD[*]}" & sleep 0.3 until pgrep -q -x "Dialog"; do outputLineNumberInVerboseDebugMode - updateScriptLog "WELCOME DIALOG: Waiting to display 'Setup Your Mac' dialog; pausing" + welcomeDialog "Waiting to display 'Setup Your Mac' dialog; pausing" sleep 0.5 done - updateScriptLog "WELCOME DIALOG: 'Setup Your Mac' dialog displayed; ensure it's the front-most app" + welcomeDialog "'Setup Your Mac' dialog displayed; ensure it's the front-most app" runAsUser osascript -e 'tell application "Dialog" to activate' if [[ -n "${overlayoverride}" ]]; then dialogUpdateSetupYourMac "overlayicon: ${overlayoverride}" @@ -3064,24 +3106,24 @@ elif [[ "${welcomeDialog}" == "userInput" ]]; then ;; 2) # Process exit code 2 scenario here - updateScriptLog "WELCOME DIALOG: ${loggedInUser} clicked Quit at Welcome dialog" + welcomeDialog "${loggedInUser} clicked Quit at Welcome dialog" completionActionOption="Quit" quitScript "1" ;; 3) # Process exit code 3 scenario here - updateScriptLog "WELCOME DIALOG: ${loggedInUser} clicked infobutton" + welcomeDialog "${loggedInUser} clicked infobutton" osascript -e "set Volume 3" afplay /System/Library/Sounds/Glass.aiff ;; 4) # Process exit code 4 scenario here - updateScriptLog "WELCOME DIALOG: ${loggedInUser} allowed timer to expire" + welcomeDialog "${loggedInUser} allowed timer to expire" quitScript "1" ;; *) # Catch all processing - updateScriptLog "WELCOME DIALOG: Something else happened; Exit code: ${welcomeReturnCode}" + welcomeDialog "Something else happened; Exit code: ${welcomeReturnCode}" quitScript "1" ;; @@ -3099,7 +3141,7 @@ else else symConfiguration="Catch-all ('Welcome' dialog disabled)" fi - updateScriptLog "WELCOME DIALOG: Using ${symConfiguration} Configuration …" + welcomeDialog "Using ${symConfiguration} Configuration …" policyJSONConfiguration @@ -3111,10 +3153,10 @@ else eval "${dialogSetupYourMacCMD[*]}" & sleep 0.3 until pgrep -q -x "Dialog"; do outputLineNumberInVerboseDebugMode - updateScriptLog "WELCOME DIALOG: Waiting to display 'Setup Your Mac' dialog; pausing" + welcomeDialog "Waiting to display 'Setup Your Mac' dialog; pausing" sleep 0.5 done - updateScriptLog "WELCOME DIALOG: 'Setup Your Mac' dialog displayed; ensure it's the front-most app" + welcomeDialog "'Setup Your Mac' dialog displayed; ensure it's the front-most app" runAsUser osascript -e 'tell application "Dialog" to activate' if [[ -n "${overlayoverride}" ]]; then dialogUpdateSetupYourMac "overlayicon: ${overlayoverride}" @@ -3150,8 +3192,8 @@ outputLineNumberInVerboseDebugMode totalProgressSteps=$(get_json_value "${policyJSON}" "steps.length") progressIncrementValue=$(( 100 / totalProgressSteps )) -updateScriptLog "SETUP YOUR MAC DIALOG: Total Number of Steps: ${totalProgressSteps}" -updateScriptLog "SETUP YOUR MAC DIALOG: Progress Increment Value: ${progressIncrementValue}" +updateSetupYourMacDialog "Total Number of Steps: ${totalProgressSteps}" +updateSetupYourMacDialog "Progress Increment Value: ${progressIncrementValue}" @@ -3178,7 +3220,7 @@ dialogUpdateSetupYourMac "list: show" outputLineNumberInVerboseDebugMode -updateScriptLog "SETUP YOUR MAC DIALOG: Initial progress bar" +updateSetupYourMacDialog "Initial progress bar" dialogUpdateSetupYourMac "progress: 1" @@ -3207,7 +3249,7 @@ if [[ "${symConfiguration}" == *"Catch-all"* ]] || [[ -z "${symConfiguration}" ] checkNetworkQualityCatchAllConfiguration & - updateScriptLog "SETUP YOUR MAC DIALOG: **Connection:** \n- Download: \n$mbps Mbps \n\n**Estimate:** \n- $(printf '%dh:%dm:%ds\n' $((configurationCatchAllEstimatedSeconds/3600)) $((configurationCatchAllEstimatedSeconds%3600/60)) $((configurationCatchAllEstimatedSeconds%60)))" + updateSetupYourMacDialog "**Connection:** \n- Download: \n$mbps Mbps \n\n**Estimate:** \n- $(printf '%dh:%dm:%ds\n' $((configurationCatchAllEstimatedSeconds/3600)) $((configurationCatchAllEstimatedSeconds%3600/60)) $((configurationCatchAllEstimatedSeconds%60)))" infoboxConfiguration="**Connection:** \n- Download: \n$mbps Mbps \n\n**Estimate:** \n- $(printf '%dh:%dm:%ds\n' $((configurationCatchAllEstimatedSeconds/3600)) $((configurationCatchAllEstimatedSeconds%3600/60)) $((configurationCatchAllEstimatedSeconds%60)))" @@ -3237,9 +3279,9 @@ if [[ -n ${room} ]]; then infobox+="**Room:** \n$room \n\n" ; fi if [[ -n ${position} ]]; then infobox+="**Position:** \n$position \n\n" ; fi if { [[ "${promptForConfiguration}" != "true" ]] && [[ "${configurationDownloadEstimation}" == "true" ]]; } || { [[ "${welcomeDialog}" == "false" ]] || [[ "${welcomeDialog}" == "messageOnly" ]]; } then - updateScriptLog "SETUP YOUR MAC DIALOG: Purposely NOT updating 'infobox'" + updateSetupYourMacDialog "Purposely NOT updating 'infobox'" else - updateScriptLog "SETUP YOUR MAC DIALOG: Updating 'infobox'" + updateSetupYourMacDialog "Updating 'infobox'" dialogUpdateSetupYourMac "infobox: ${infobox}" fi @@ -3279,7 +3321,7 @@ if [[ "${symConfiguration}" != *"Catch-all"* ]]; then fi - updateScriptLog "Update 'helpmessage' with Configuration: ${infoboxConfiguration} …" + updateSetupYourMacDialog "Update 'helpmessage' with Configuration: ${infoboxConfiguration} …" helpmessage+="\n**Configuration:**\n- $infoboxConfiguration\n" helpmessage+="\n**Computer Information:** \n" @@ -3315,7 +3357,7 @@ for (( i=0; i listitem: ${listitem}\n# # #\n" + updateSetupYourMacDialog "\n\n# # #\n# policyJSON > listitem: ${listitem}\n# # #\n" dialogUpdateSetupYourMac "listitem: index: $i, status: wait, statustext: Installing …, " fi if [[ -n "$icon" ]]; then dialogUpdateSetupYourMac "icon: ${icon}"; fi @@ -3329,7 +3371,7 @@ for (( i=0; i