diff --git a/CHANGELOG.md b/CHANGELOG.md index 697c7b4..f13df4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,20 @@ # CHANGELOG -## 1.14.3, 29-May-2024 +## 1.15.0 +### 11-Jun-2024 +- Added logging functions [Pull Request No. 137](https://github.com/setup-your-mac/Setup-Your-Mac/pull/137); thanks, @robjschroeder! +- Modified Microsoft Teams Message `activitySubtitle` +- Activated main "Setup Your Mac" dialog with each `listitem` +- Added swiftDialog `2.5.0`'s `--verbose`, `--debug` and `--resizable` flags to debugModes +- Failure Message: Increased `sleep` value from `0.3` to `0.7` (thanks, for the report, @arnoldtaw; thanks for the code suggestion, @jcmbowman) +- Miscellaneous formatting and clean-up +- Added Support Team fields (thanks, @HowardGMac!) +- Set `swiftDialogMinimumRequiredVersion` to `2.5.0.4768` - Improved exit code processing for 'Welcome' dialog +- Added pre-flight check for AC power (thanks for the suggestion, @arnoldtaw; thanks for the code, Obi-Josh!) +- Added Variables for Prefill Email and Computer Name (thanks, @AndrewMBarnett!) +- Improved Remote Validation error-checking +- Updated Dynamic Download Estimates for macOS 15 Sequoia ## 1.14.2 ### 15-Feb-2024 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 5fbe458..1bd336b 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -16,6 +16,13 @@ ### Humbled & Amazed The response to Setup Your Mac has been both humbling and amazing; my personal thanks to MacAdmins worldwide and the ever-growing number of contributors: +- lukecharters for eleventh-hour testing feedback +- AndrewMBarnett for [Pull Request No. 145](https://github.com/setup-your-mac/Setup-Your-Mac/pull/145) +- arnoldtaw for [Issue No. 132](https://github.com/setup-your-mac/Setup-Your-Mac/issues/132) +- HowardGMac for [Pull Request No. 148](https://github.com/setup-your-mac/Setup-Your-Mac/pull/148) +- jcmbowman for suggesting a fix for arnoldtaw's [Issue No. 149](https://github.com/setup-your-mac/Setup-Your-Mac/issues/149) +- robjschroeder for [Pull Request No. 137](https://github.com/setup-your-mac/Setup-Your-Mac/pull/137) +- Mazi for helping to identify a logging typo in "Log Out Attended" completionActionOption - drtaru for [Pull Request No. 140](https://github.com/setup-your-mac/Setup-Your-Mac/pull/140) - seaneldridge7 for [Issue No. 139](https://github.com/setup-your-mac/Setup-Your-Mac/issues/139) - bartreardon for helping to resolve an issue when icons were not displaying as expected diff --git a/README.md b/README.md index ff1b276..fc2b6da 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ [Setup Your Mac (1.14.0)](https://snelson.us/sym) -# Setup Your Mac (1.14.3) with SYM-Helper (1.1.2) via swiftDialog (2.5.0) +# Setup Your Mac (1.15.0) with SYM-Helper (1.2.0) via swiftDialog (2.5.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.2), Setup Your Mac (1.14.3) includes a break-fix for swiftDialog (2.5.0) +> Optimized to leverage SYM-Helper (1.2.0), Setup Your Mac (1.15.0) leverages new features of swiftDialog (2.5.0) -[Setup Your Mac (1.14.1)](https://snelson.us/sym) +[Setup Your Mac (1.15.0)](https://snelson.us/sym) ## Introduction @@ -16,7 +16,7 @@ Apple's Automated Device Enrollment helps streamline Mobile Device Management (M **Setup Your Mac** aims to simplify initial device configuration by leveraging `swiftDialog` and Jamf Pro Policy Custom Events to allow end-users to self-complete Mac setup _post-enrollment_. -[Continue reading …](https://snelson.us/symh) +[Continue reading …](https://snelson.us/sym) ### Script - [Setup-Your-Mac-via-Dialog.bash](Setup-Your-Mac-via-Dialog.bash) @@ -30,11 +30,11 @@ A special thanks to the ever-growing list of [contributors](CONTRIBUTORS.md); le > A stand-alone macOS app to help MacAdmins more easily deploy [Setup Your Mac](https://snelson.us/sym) -![SYM-Helper Hero](images/SYM-Helper-Hero.png) +![SYM-Helper Hero](images/SYM-Helper-1.2.0-Hero.png) **NOTE:** When editing [Setup-Your-Mac-via-Dialog.bash](Setup-Your-Mac-via-Dialog.bash), “[SYM-Helper]” indicates variables which can be configured with SYM-Helper. -[Documentation](https://snelson.us/sym-helper) | [Releases](https://github.com/setup-your-mac/SYM-Helper/releases) +[Documentation](https://snelson.us/sym) | [Releases](https://github.com/setup-your-mac/SYM-Helper/releases) --- diff --git a/Setup-Your-Mac-via-Dialog.bash b/Setup-Your-Mac-via-Dialog.bash index 0c72515..901f3d0 100755 --- a/Setup-Your-Mac-via-Dialog.bash +++ b/Setup-Your-Mac-via-Dialog.bash @@ -1,5 +1,5 @@ #!/bin/bash -# shellcheck disable=SC2001,SC1111,SC1112,SC2143,SC2145,SC2086,SC2089,SC2090 +# shellcheck disable=SC2001,SC1111,SC1112,SC2143,SC2145,SC2086,SC2089,SC2090,SC2269 #################################################################################################### # @@ -10,28 +10,20 @@ # # 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.14.1, 11-Feb-2024 -# - Addressed an issue where icons were not displaying (thanks, @bartreardon!) -# -# Version 1.14.2, 15-Feb-2024 -# - Addresses [Issue 139](https://github.com/setup-your-mac/Setup-Your-Mac/issues/139) `brandingBannerDisplayText=false` not working (thanks for the report, @seaneldridge7!) -# Thanks for the fix, @drtaru! [Pull Request No. 140](https://github.com/setup-your-mac/Setup-Your-Mac/pull/140) -# -# Version 1.14.3, 29-May-2024 +# Version 1.15.0, 11-Jun-2024 +# - Added logging functions +# - Modified Microsoft Teams Message `activitySubtitle` +# - Activated main "Setup Your Mac" dialog with each `listitem` +# - Added swiftDialog `2.5.0`'s `--verbose`, `--debug` and `--resizable` flags to debugModes +# - Failure Message: Increased `sleep` value from `0.3` to `0.7` (thanks, for the report, @arnoldtaw; thanks for the code suggestion, @jcmbowman) +# - Miscellaneous formatting and clean-up +# - Added Support Team fields (thanks, @HowardGMac!) +# - Set `swiftDialogMinimumRequiredVersion` to `2.5.0.4768` # - Improved exit code processing for 'Welcome' dialog +# - Added pre-flight check for AC power (thanks for the suggestion, @arnoldtaw; thanks for the code, Obi-Josh!) +# - Added Variables for Prefill Email and Computer Name (thanks, @AndrewMBarnett!) +# - Improved Remote Validation error-checking +# - Updated Dynamic Download Estimates for macOS 15 Sequoia # #################################################################################################### @@ -47,17 +39,17 @@ # Script Version and Jamf Pro Script Parameters # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -scriptVersion="1.14.3" +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 ] welcomeDialog="${6:-"userInput"}" # Parameter 6: Welcome dialog [ userInput (default) | video | messageOnly | false ] completionActionOption="${7:-"Restart Attended"}" # Parameter 7: Completion Action [ wait | sleep (with seconds) | Shut Down | Shut Down Attended | Shut Down Confirm | Restart | Restart Attended (default) | Restart Confirm | Log Out | Log Out Attended | Log Out Confirm ] -requiredMinimumBuild="${8:-"disabled"}" # Parameter 8: Required Minimum Build [ disabled (default) | 22E ] (i.e., Your organization's required minimum build of macOS to allow users to proceed; use "22E" for macOS 13.3) +requiredMinimumBuild="${8:-"disabled"}" # Parameter 8: Required Minimum Build [ disabled (default) | 23F ] (i.e., Your organization's required minimum build of macOS to allow users to proceed; use "23F" for macOS 14.5) outdatedOsAction="${9:-"/System/Library/CoreServices/Software Update.app"}" # Parameter 9: Outdated OS Action [ /System/Library/CoreServices/Software Update.app (default) | jamfselfservice://content?entity=policy&id=117&action=view ] (i.e., Jamf Pro Self Service policy ID for operating system ugprades) webhookURL="${10:-""}" # Parameter 10: Microsoft Teams or Slack Webhook URL [ Leave blank to disable (default) | https://microsoftTeams.webhook.com/URL | https://hooks.slack.com/services/URL ] Can be used to send a success or failure message to Microsoft Teams or Slack via Webhook. (Function will automatically detect if Webhook URL is for Slack or Teams; can be modified to include other communication tools that support functionality.) presetConfiguration="${11:-""}" # Parameter 11: Specify a Configuration (i.e., `policyJSON`; NOTE: If set, `promptForConfiguration` will be automatically suppressed and the preselected configuration will be used instead) -swiftDialogMinimumRequiredVersion="2.4.0.4750" # This will be set and updated as dependancies on newer features change. +swiftDialogMinimumRequiredVersion="2.5.0.4768" # This will be set and updated as dependancies on newer features change. @@ -65,8 +57,10 @@ swiftDialogMinimumRequiredVersion="2.4.0.4750" # Various Feature Variables # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -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 ] +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 ] @@ -80,7 +74,9 @@ prefillUsername="true" # prefills the currently logged in user's userna promptForRealName="true" prefillRealname="true" # prefills the currently logged in user's fullname promptForEmail="true" +prefillEmail="true" # prefills the currently logged in user's email. You need to add to email ending variable promptForComputerName="true" +prefillComputerName="true" # prefills the currently logged in user's current computer name promptForAssetTag="true" promptForRoom="true" promptForBuilding="true" @@ -109,6 +105,9 @@ departmentList=$( echo "${departmentListRaw}" | tr ',' '\n' | sort -f | uniq | s # An unsorted, comma-separated list of departments (with possible duplication). If empty and promptForPosition is "true" a user-input box will be shown instead of a dropdown positionListRaw="Developer,Management,Sales,Marketing" +# Email ending variable +emailEnding="@company.com" + # A sorted, unique, JSON-compatible list of positions positionList=$( echo "${positionListRaw}" | tr ',' '\n' | sort -f | uniq | sed -e 's/^/\"/' -e 's/$/\",/' -e '$ s/.$//' ) @@ -122,10 +121,13 @@ brandingIconDark="https://cdn-icons-png.flaticon.com/512/740/740878.png" supportTeamName="Support Team Name" supportTeamPhone="+1 (801) 555-1212" supportTeamEmail="support@domain.com" +supportTeamChat="chat.support.domain.com" +supportTeamChatHyperlink="[${supportTeamChat}](https://${supportTeamChat})" supportTeamWebsite="support.domain.com" supportTeamHyperlink="[${supportTeamWebsite}](https://${supportTeamWebsite})" supportKB="KB8675309" supportTeamErrorKB="[${supportKB}](https://servicenow.company.com/support?id=kb_article_view&sysparm_article=${supportKB}#Failures)" +supportTeamHours="Monday through Friday, 8 a.m. to 5 p.m." # Disable the "Continue" button in the User Input "Welcome" dialog until Dynamic Download Estimates have complete [ true | false ] (thanks, @Eltord!) lockContinueBeforeEstimations="false" @@ -176,1323 +178,1236 @@ configurationThreeInstallBuffer="0" # Buffer time added to estimates to #################################################################################################### # -# 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 logComment() { + updateScriptLog " ${1}" +} + +function welcomeDialog() { + updateScriptLog "[WELCOME DIALOG] ${1}" +} + +function error() { + updateScriptLog "[ERROR] ${1}" +} + +function fatal() { + updateScriptLog "[FATAL ERROR] ${1}" + exit 1 +} + +function info() { + updateScriptLog "[INFO] ${1}" +} + +function updateSetupYourMacDialog() { + updateScriptLog "[SETUP YOUR MAC DIALOG] ${1}" +} + +function updateFailureDialog() { + updateScriptLog "[FAILURE DIALOG] ${1}" +} + +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: Client-side Script Logging Function +# Output Line Number in `verbose` Debug Mode (thanks, @bartreardon!) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function updateScriptLog() { - echo -e "$( date +%Y-%m-%d\ %H:%M:%S ) - ${1}" | tee -a "${scriptLog}" +function outputLineNumberInVerboseDebugMode() { + if [[ "${debugMode}" == "verbose" ]]; then updateScriptLog "# # # SETUP YOUR MAC VERBOSE DEBUG MODE: Line No. ${BASH_LINENO[0]} # # #" ; fi } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Current Logged-in User Function +# Run command as logged-in user (thanks, @scriptingosx!) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function currentLoggedInUser() { - loggedInUser=$( echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ { print $3 }' ) - updateScriptLog "PRE-FLIGHT CHECK: Current Logged-in User: ${loggedInUser}" +function runAsUser() { + + info "Run \"$@\" as \"$loggedInUserID\" … " + launchctl asuser "$loggedInUserID" sudo -u "$loggedInUser" "$@" + } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Logging Preamble +# Calculate Free Disk Space +# Disk Usage with swiftDialog (https://snelson.us/2022/11/disk-usage-with-swiftdialog-0-0-2/) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -updateScriptLog "\n\n###\n# Setup Your Mac (${scriptVersion})\n# https://snelson.us/sym\n###\n" -updateScriptLog "PRE-FLIGHT CHECK: Initiating …" +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=$("Disk Space: ${diskSpace}") + +} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Confirm script is running under bash +# Update the "Welcome" dialog # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -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 dialogUpdateWelcome(){ + echo "$1" >> "$welcomeCommandFile" +} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Confirm script is running as root +# Update the "Setup Your Mac" dialog # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -if [[ $(id -u) -ne 0 ]]; then - updateScriptLog "PRE-FLIGHT CHECK: This script must be run as root; exiting." - exit 1 -fi +function dialogUpdateSetupYourMac() { + updateSetupYourMacDialog "$1" + echo "$1" >> "$setupYourMacCommandFile" +} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Validate Setup Assistant has completed +# Update the "Failure" dialog # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -while pgrep -q -x "Setup Assistant"; do - updateScriptLog "PRE-FLIGHT CHECK: Setup Assistant is still running; pausing for 2 seconds" - sleep 2 -done - -updateScriptLog "PRE-FLIGHT CHECK: Setup Assistant is no longer running; proceeding …" +function dialogUpdateFailure(){ + updateFailureDialog "$1" + echo "$1" >> "$failureCommandFile" +} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Confirm Dock is running / user is at Desktop +# Finalise User Experience # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -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 +function finalise(){ -updateScriptLog "PRE-FLIGHT CHECK: Finder & Dock are running; proceeding …" + outputLineNumberInVerboseDebugMode + if [[ "${configurationDownloadEstimation}" == "true" ]]; then + outputLineNumberInVerboseDebugMode + calculateFreeDiskSpace + finaliseUserExperience "${diskMessage}" -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Validate Logged-in System Accounts -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + fi -updateScriptLog "PRE-FLIGHT CHECK: Check for Logged-in System Accounts …" -currentLoggedInUser + if [[ "${jamfProPolicyTriggerFailure}" == "failed" ]]; then -counter="1" + outputLineNumberInVerboseDebugMode + updateFailureDialog "Failed policies detected …" + if [[ -n "${webhookURL}" ]]; then + updateFailureDialog "Display Failure dialog: Sending webhook message" + webhookStatus="Failures detected" + webHookMessage + fi -until { [[ "${loggedInUser}" != "_mbsetupuser" ]] || [[ "${counter}" -gt "180" ]]; } && { [[ "${loggedInUser}" != "loginwindow" ]] || [[ "${counter}" -gt "30" ]]; } ; do + if [[ "${failureDialog}" == "true" ]]; then - updateScriptLog "PRE-FLIGHT CHECK: Logged-in User Counter: ${counter}" - currentLoggedInUser - sleep 2 - ((counter++)) + outputLineNumberInVerboseDebugMode + updateFailureDialog "Display Failure dialog: ${failureDialog}" -done + 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 -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}" + dialogUpdateSetupYourMac "quit:" + eval "${dialogFailureCMD}" & sleep 0.7 + + updateFailureDialog "\n\n# # #\n# FAILURE DIALOG\n# # #\n" + updateFailureDialog "Jamf Pro Policy Name Failures:" + updateFailureDialog "${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 + supportContactMessage+="If you need assistance, please contact the **${supportTeamName}**: \n" -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Validate Operating System Version and Build -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + if [[ -n "${supportTeamPhone}" ]]; then + supportContactMessage+="- **Telephone:** ${supportTeamPhone}\n" + fi -if [[ "${requiredMinimumBuild}" == "disabled" ]]; then + if [[ -n "${supportTeamEmail}" ]]; then + supportContactMessage+="- **Email:** ${supportTeamEmail}\n" + fi + + if [[ -n "${supportTeamChat}" ]]; then + supportContactMessage+="- **Online Chat:** ${supportTeamChatHyperlink}\n" + fi + + if [[ -n "${supportTeamWebsite}" ]]; then + supportContactMessage+="- **Web**: ${supportTeamHyperlink}\n" + fi - updateScriptLog "PRE-FLIGHT CHECK: 'requiredMinimumBuild' has been set to ${requiredMinimumBuild}; skipping OS validation." - updateScriptLog "PRE-FLIGHT CHECK: macOS ${osVersion} (${osBuild}) installed" + if [[ -n "${supportKB}" ]]; then + supportContactMessage+="- **Knowledge Base Article:** ${supportTeamErrorKB}\n" + fi + + if [[ -n "${supportTeamHours}" ]]; then + supportContactMessage+="- **Support Hours:** ${supportTeamHours}\n" + fi + + fi -else + failureMessage+="\n\n${supportContactMessage}" - # Since swiftDialog requires at least macOS 12 Monterey, first confirm the major OS version - if [[ "${osMajorVersion}" -ge 12 ]] ; then + dialogUpdateFailure "message: ${failureMessage}" - updateScriptLog "PRE-FLIGHT CHECK: macOS ${osMajorVersion} installed; checking build version ..." + dialogUpdateFailure "icon: SF=xmark.circle.fill,weight=bold,colour1=#BB1717,colour2=#F31F1F" + dialogUpdateFailure "button1text: ${button1textCompletionActionOption}" - # Confirm the Mac is running `requiredMinimumBuild` (or later) - if [[ "${osBuild}" > "${requiredMinimumBuild}" ]]; then + # Wait for user-acknowledgment due to detected failure + wait - updateScriptLog "PRE-FLIGHT CHECK: macOS ${osVersion} (${osBuild}) installed; proceeding ..." + dialogUpdateFailure "quit:" + quitScript "1" - # 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 + + outputLineNumberInVerboseDebugMode + dialogUpdateFailure "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." + dialogUpdateSetupYourMac "button1text: ${button1textCompletionActionOption}" + dialogUpdateSetupYourMac "button1: enable" + dialogUpdateSetupYourMac "progress: reset" + dialogUpdateSetupYourMac "progresstext: Errors detected; please ${progressTextCompletionAction// and } your ${modelName}, ${loggedInUserFirstname}." + + quitScript "1" 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 + 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 -fi +} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Ensure computer does not go to sleep during SYM (thanks, @grahampugh!) +# Parse JSON via osascript and JavaScript # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -symPID="$$" -updateScriptLog "PRE-FLIGHT CHECK: Caffeinating this script (PID: $symPID)" -caffeinate -dimsu -w $symPID & +function get_json_value() { + JSON="$1" osascript -l 'JavaScript' \ + -e 'const env = $.NSProcessInfo.processInfo.environment.objectForKey("JSON").js' \ + -e "JSON.parse(env).$2" +} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Toggle `jamf` binary check-in (thanks, @robjschroeder!) +# Parse JSON via osascript and JavaScript for the Welcome dialog (thanks, @bartreardon!) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function toggleJamfLaunchDaemon() { - - jamflaunchDaemon="/Library/LaunchDaemons/com.jamfsoftware.task.1.plist" - - if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then - - 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 - - else +function get_json_value_welcomeDialog() { + 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" +} - while [[ ! -f "${jamflaunchDaemon}" ]] ; do - updateScriptLog "PRE-FLIGHT CHECK: Waiting for installation of ${jamflaunchDaemon}" - sleep 0.1 - done - if [[ $(/bin/launchctl list | grep com.jamfsoftware.task.E) ]]; then - updateScriptLog "PRE-FLIGHT CHECK: Temporarily disable 'jamf' binary check-in" - /bin/launchctl bootout system "${jamflaunchDaemon}" +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Execute Jamf Pro Policy Custom Events (thanks, @smithjw) +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - else +function run_jamf_trigger() { - 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 - until [ $result -eq 3 ]; do + trigger="$1" - /bin/launchctl bootstrap system "${jamflaunchDaemon}" && /bin/launchctl start "${jamflaunchDaemon}" - result="$?" + if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then - 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 + updateSetupYourMacDialog "DEBUG MODE: TRIGGER: $jamfBinary policy -event $trigger ${suppressRecon}" + sleep "${debugModeSleepAmount}" - done + else - fi + 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 } -toggleJamfLaunchDaemon - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Validate / install swiftDialog (Thanks big bunches, @acodega!) +# Confirm Policy Execution # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function dialogInstall() { +function confirmPolicyExecution() { - # 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 }") + outputLineNumberInVerboseDebugMode - # Expected Team ID of the downloaded PKG - expectedDialogTeamID="PWA5E9TQ59" + trigger="${1}" + validation="${2}" + updateSetupYourMacDialog "Confirm Policy Execution: '${trigger}' '${validation}'" + if [ "${suppressReconOnPolicy}" == "true" ]; then suppressRecon="-forceNoRecon"; fi - updateScriptLog "PRE-FLIGHT CHECK: Installing swiftDialog..." + case ${validation} in - # Create temporary working directory - workDirectory=$( /usr/bin/basename "$0" ) - tempDirectory=$( /usr/bin/mktemp -d "/private/tmp/$workDirectory.XXXXXX" ) + */* ) # If the validation variable contains a forward slash (i.e., "/"), presume it's a path and check if that path exists on disk - # Download the installer package - /usr/bin/curl --location --silent "$dialogURL" -o "$tempDirectory/Dialog.pkg" + 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 + ;; - # Verify the download - teamID=$(/usr/sbin/spctl -a -vv -t install "$tempDirectory/Dialog.pkg" 2>&1 | awk '/origin=/ {print $NF }' | tr -d '()') + "None" | "none" ) - # Install the package if Team ID validates - if [[ "$expectedDialogTeamID" == "$teamID" ]]; then + outputLineNumberInVerboseDebugMode + updateSetupYourMacDialog "Confirm Policy Execution: ${validation}" + if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then + sleep "${debugModeSleepAmount}" + else + run_jamf_trigger "${trigger}" + fi + ;; - /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..." + "Recon" | "recon" ) - else + 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 + ;; - # 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 + outputLineNumberInVerboseDebugMode + updateSetupYourMacDialog "Confirm Policy Execution Catch-all: ${validation}" + if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then + sleep "${debugModeSleepAmount}" + else + run_jamf_trigger "${trigger}" + fi + ;; - # Remove the temporary working directory when done - /bin/rm -Rf "$tempDirectory" + esac } -function dialogCheck() { - - # 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 +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Validate Policy Result +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # Check for Dialog and install if not found - if [ ! -e "/Library/Application Support/Dialog/Dialog.app" ]; then +function validatePolicyResult() { - updateScriptLog "PRE-FLIGHT CHECK: swiftDialog not found. Installing..." - dialogInstall + outputLineNumberInVerboseDebugMode - else + trigger="${1}" + validation="${2}" + updateSetupYourMacDialog "Validate Policy Result: '${trigger}' '${validation}'" - 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 + case ${validation} in - updateScriptLog "PRE-FLIGHT CHECK: swiftDialog version ${dialogVersion} found; proceeding..." + ### + # Absolute Path + # Simulates pre-v1.6.0 behavior, for example: "/Applications/Microsoft Teams classic.app/Contents/Info.plist" + ### - 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 + ;; -} -dialogCheck + ### + # Local + # Validation within this script, for example: "rosetta" or "filevault" + ### + "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 + ;; -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# 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 + ### + # Remote + # Validation via a Jamf Pro policy which has a single-script payload, for example: "symvGlobalProtect" + # See: https://vimeo.com/782561166 + ### + "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}" == *"Failed"* ]]; then + dialogUpdateSetupYourMac "listitem: index: $i, status: fail, statustext: Failed" + jamfProPolicyTriggerFailure="failed" + exitCode="1" + jamfProPolicyNameFailures+="• $listitem \n" + elif [[ "${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: Unknown" + jamfProPolicyTriggerFailure="failed" + exitCode="1" + jamfProPolicyNameFailures+="• $listitem \n" + fi + fi + ;; -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Pre-flight Check: Complete -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -updateScriptLog "PRE-FLIGHT CHECK: Complete" + ### + # None: For triggers which don't require validation + # (Always evaluates as: 'success' and 'Installed') + ### + "None" | "none") + outputLineNumberInVerboseDebugMode + updateSetupYourMacDialog "Confirm Policy Execution: ${validation}" + dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Installed" + ;; -#################################################################################################### -# -# Dialog Variables -# -#################################################################################################### -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# 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 ) + ### + # 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" + ;; -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# 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 + + ### + # Catch-all + ### + + * ) + + outputLineNumberInVerboseDebugMode + updateSetupYourMacDialog "Validate Policy Results Catch-all: ${validation}" + dialogUpdateSetupYourMac "listitem: index: $i, status: error, statustext: Error" + ;; + + esac + +} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Set JAMF binary, Dialog path and Command Files +# Kill a specified process (thanks, @grahampugh!) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -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 ) - +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 +} -#################################################################################################### -# -# Welcome dialog -# -#################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# "Welcome" dialog Title, Message and Icon +# Completion Action (i.e., Wait, Sleep, Logout, Restart or Shutdown) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -welcomeTitle="Happy $( date +'%A' ), ${loggedInUserFirstname}! \nWelcome to your new ${modelName}" +function completionAction() { -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}." + outputLineNumberInVerboseDebugMode -if [[ -n "${supportTeamName}" ]]; then + if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then - welcomeMessage+="\n\nIf you need assistance, please contact the **${supportTeamName}**: \n" + # 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 "${supportTeamPhone}" ]]; then - welcomeMessage+="- **Telephone**: ${supportTeamPhone}\n" - fi + else - if [[ -n "${supportTeamEmail}" ]]; then - welcomeMessage+="- **Email**: ${supportTeamEmail}\n" - fi + shopt -s nocasematch - if [[ -n "${supportTeamWebsite}" ]]; then - welcomeMessage+="- **Web**: ${supportTeamHyperlink}\n" - fi + case ${completionActionOption} in - if [[ -n "${supportKB}" ]]; then - welcomeMessage+="- **Knowledge Base Article:** ${supportTeamErrorKB}\n" - fi + "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 & + ;; -fi + "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 & + ;; -welcomeMessage+="\n\n---" + "Shut Down Confirm" ) + completionActionOut "Shut down, only after macOS time-out or user confirmation" + runAsUser osascript -e 'tell app "loginwindow" to «event aevtrsdn»' + ;; -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 + "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 & + ;; + "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 & + ;; -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" + "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}" + ;; -# Check brandingBanner and cache if necessary -case ${brandingBanner} in + "Log Out Attended" ) + completionActionOut "Log out, requiring 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}" + ;; - *"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 + "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' + ;; - 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}" - ;; + "Sleep"* ) + sleepDuration=$( awk '{print $NF}' <<< "${1}" ) + completionActionOut "Sleeping for ${sleepDuration} seconds …" + sleep "${sleepDuration}" + killProcess "Dialog" + info "Goodnight!" + ;; - */* ) - updateScriptLog "WELCOME DIALOG: brandingBanner is local file, using it" - welcomeBannerImage="${brandingBanner}" - bannerImage="${brandingBanner}" - ;; + "Wait" ) + completionActionOut "Waiting for user interaction …" + wait + ;; - "None" | "none" | "" ) - updateScriptLog "WELCOME DIALOG: brandingBanner set to \"None\", or empty" - welcomeBannerImage="${brandingBanner}" - bannerImage="${brandingBanner}" - ;; + "Quit" ) + completionActionOut "Quitting script" + exitCode="0" + ;; - * ) - updateScriptLog "WELCOME DIALOG: brandingBanner set to \"None\"" - ;; + * ) + completionActionOut "Using the default of 'wait'" + wait + ;; -esac + esac + shopt -u nocasematch + fi -# 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 + # Remove custom welcomeBannerImageFileName + if [[ -e "/var/tmp/${welcomeBannerImageFileName}" ]]; then + completionActionOut "Removing /var/tmp/${welcomeBannerImageFileName} …" + rm "/var/tmp/${welcomeBannerImageFileName}" + fi + # Remove overlayicon + if [[ -e ${overlayicon} ]]; then + completionActionOut "Removing ${overlayicon} …" + rm "${overlayicon}" + fi + exit "${exitCode}" -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# "Welcome" Video Settings and Features -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - -welcomeVideo="--title \"$welcomeTitle\" \ ---videocaption \"$welcomeCaption\" \ ---video \"$welcomeVideoID\" \ ---infotext \"$scriptVersion\" \ ---button1text \"Continue …\" \ ---autoplay \ ---moveable \ ---ontop \ ---width '800' \ ---height '600' \ ---commandfile \"$welcomeCommandFile\" " +} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# "Welcome" JSON Conditionals (thanks, @rougegoat!) +# Welcome dialog 'infobox' animation (thanks, @bartreadon!) +# To convert emojis, see: https://r12a.github.io/app-conversion/ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# 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 - -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 - -if [ "$promptForPosition" == "true" ]; then - if [ -n "${positionListRaw}" ]; then - positionSelectJSON='{ - "title" : "Position", - "default" : "", - "required" : true, - "values" : [ - '${positionList}' - ] - },' - fi -fi - -if [ "$promptForConfiguration" == "true" ] && [ -z "${presetConfiguration}" ]; then - configurationJSON='{ - "title" : "Configuration", - "style" : "radio", - "default" : "'"${configurationOneName}"'", - "values" : [ - "'"${configurationOneName}"'", - "'"${configurationTwoName}"'", - "'"${configurationThreeName}"'" - ] - }' -fi - -selectItemsJSON="${buildingJSON}${departmentJSON}${positionSelectJSON}${configurationJSON}" -selectItemsJSON=$( echo $selectItemsJSON | sed 's/,$//' ) +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 +} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# "Welcome" JSON for Capturing User Input (thanks, @bartreardon!) +# Setup Your Mac dialog 'infobox' animation (thanks, @bartreadon!) +# To convert emojis, see: https://r12a.github.io/app-conversion/ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -welcomeJSON=' -{ - "commandfile" : "'"${welcomeCommandFile}"'", - "bannerimage" : "'"${welcomeBannerImage}"'", - "bannertext" : "'"${welcomeBannerText}"'", - "title" : "'"${welcomeTitle}"'", - "message" : "'"${welcomeMessage}"'", - "icon" : "'"${welcomeIcon}"'", - "infobox" : "Analyzing …", - "iconsize" : "198", - "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 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 } -' -#################################################################################################### -# -# Setup Your Mac dialog -# -#################################################################################################### - # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# "Setup Your Mac" dialog Title, Message, Icon and Overlay Icon +# Check Network Quality for Configurations (thanks, @bartreadon!) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -title="Setting up ${loggedInUserFirstname}‘s ${modelName}" -message="Please wait while the following apps are installed …" +function checkNetworkQualityConfigurations() { + + myPID="$$" + welcomeDialog "Display Welcome dialog 'infobox' animation …" + welcomeDialogInfoboxAnimation "$myPID" & + welcomeDialogInfoboxAnimationPID="$!" -if [[ "${brandingBannerDisplayText}" == "true" ]] ; then - bannerText="Setting up ${loggedInUserFirstname}‘s ${modelName}"; -else - bannerText=" " -fi + networkQuality -s -v -c > /var/tmp/networkQualityTest + kill ${welcomeDialogInfoboxAnimationPID} + outputLineNumberInVerboseDebugMode -if [ -n "$supportTeamName" ]; then - helpmessage+="If you need assistance, please contact: \n\n**${supportTeamName}** \n" -fi + welcomeDialog "Completed networkQualityTest …" + networkQualityTest=$( < /var/tmp/networkQualityTest ) + rm /var/tmp/networkQualityTest -if [ -n "$supportTeamPhone" ]; then - helpmessage+="- **Telephone:** ${supportTeamPhone} \n" -fi + case "${osVersion}" in -if [ -n "$supportTeamEmail" ]; then - helpmessage+="- **Email:** ${supportTeamEmail} \n" -fi + 11* ) + dlThroughput="N/A; macOS ${osVersion}" + dlResponsiveness="N/A; macOS ${osVersion}" + dlStartDate="N/A; macOS ${osVersion}" + dlEndDate="N/A; macOS ${osVersion}" + ;; -if [ -n "$supportTeamWebsite" ]; then - helpmessage+="- **Web**: ${supportTeamHyperlink} \n" -fi + 12* | 13* | 14* | 15* ) + 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" ) + ;; -if [ -n "$supportKB" ]; then - helpmessage+="- **Knowledge Base Article:** ${supportTeamErrorKB} \n" -fi + esac -helpmessage+="\n**Computer Information:** \n" -helpmessage+="- **Operating System:** ${macOSproductVersion} (${macOSbuildVersion}) \n" -helpmessage+="- **Serial Number:** ${serialNumber} \n" -helpmessage+="- **Dialog:** ${dialogVersion} \n" -helpmessage+="- **Started:** ${timestamp}" + mbps=$( echo "scale=2; ( $dlThroughput / 1000000 )" | bc ) + welcomeDialog "$mbps (Mbps)" -infobox="Analyzing input …" # Customize at "Update Setup Your Mac's infobox" + 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)))" -# 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" + 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)))" -# Uncomment to use generic, Self Service icon as overlayicon -# overlayicon="https://ics.services.jamfcloud.com/icon/hash_aa63d5813d6ed4846b623ed82acdd1562779bf3716f2d432a8ee533bba8950ee" + 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)))" -# 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 + # 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 +} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# "Setup Your Mac" dialog Settings and Features +# Check Network Quality for Catch-all Configuration (thanks, @bartreadon!) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -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\" " +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 -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# [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)") -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + case "${osVersion}" in + + 11* ) + dlThroughput="N/A; macOS ${osVersion}" + dlResponsiveness="N/A; macOS ${osVersion}" + dlStartDate="N/A; macOS ${osVersion}" + dlEndDate="N/A; macOS ${osVersion}" + ;; + + 12* | 13* | 14* | 15* ) + 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 +} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Select `policyJSON` based on Configuration selected in "Welcome" dialog (thanks, @drtaru!) +# Webhook Message (Microsoft Teams or Slack) (thanks, @robjschroeder! and @iDrewbs!) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function policyJSONConfiguration() { +function webHookMessage() { outputLineNumberInVerboseDebugMode - updateScriptLog "WELCOME DIALOG: PolicyJSON Configuration: $symConfiguration" + jamfProURL=$(/usr/bin/defaults read /Library/Preferences/com.jamfsoftware.jamf.plist jss_url) - case ${symConfiguration} in + # Jamf Pro URL for on-prem, multi-node, clustered environments + # case ${jamfProURL} in + # *"dev"* ) jamfProURL="https://jamfpro-dev.internal.company.com/" ;; + # *"beta"* ) jamfProURL="https://jamfpro-beta.internal.company.com/" ;; + # * ) jamfProURL="https://jamfpro-prod.internal.company.com/" ;; + # esac - "${configurationOneName}" ) + jamfProComputerURL="${jamfProURL}computers.html?id=${computerID}&o=r" - 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" - } - ] + # 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 …" - 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" - } - ] - } - ] - } - ' - ;; + # URL to an image to add to your notification + activityImage="https://creazilla-store.fra1.digitaloceanspaces.com/cliparts/78010/old-mac-computer-clipart-md.png" - "${configurationThreeName}" ) + webHookdata=$(cat < "${requiredMinimumBuild}" ]]; then -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Update the "Welcome" dialog -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + preFlight "macOS ${osVersion} (${osBuild}) installed; proceeding ..." -function dialogUpdateWelcome(){ - # updateScriptLog "WELCOME DIALOG: $1" - echo "$1" >> "$welcomeCommandFile" -} + # 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 + + fi + # 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 -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Update the "Setup Your Mac" dialog -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + fi -function dialogUpdateSetupYourMac() { - updateScriptLog "SETUP YOUR MAC DIALOG: $1" - echo "$1" >> "$setupYourMacCommandFile" -} +fi # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Update the "Failure" dialog +# Pre-flight Check: Ensure computer does not go to sleep during SYM (thanks, @grahampugh!) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function dialogUpdateFailure(){ - updateScriptLog "FAILURE DIALOG: $1" - echo "$1" >> "$failureCommandFile" -} +symPID="$$" +preFlight "Caffeinating this script (PID: $symPID)" +caffeinate -dimsu -w $symPID & # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Finalise User Experience +# Pre-flight Check: Ensure computer is connected to AC power (thanks, Josh!) +# https://github.com/kc9wwh/macOSUpgrade/blob/master/macOSUpgrade.sh # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function finalise(){ +function acPowerCheck() { + + preFlight "Ensure computer is connected to AC power" + + # Amount of time (in seconds) to allow a user to connect to AC power before exiting + # If 0, then the user will not have the opportunity to connect to AC power + acPowerWaitTimer="300" + humanReadablePowerWaitTimer=$(printf '%dh:%dm:%ds\n' $((acPowerWaitTimer/3600)) $((acPowerWaitTimer%3600/60)) $((acPowerWaitTimer%60))) + + function waitForPower() { + + preFlight "Waiting for AC power …" + + while [[ "$acPowerWaitTimer" -gt "0" ]]; do + if pmset -g ps | grep "AC Power" > /dev/null ; then + preFlight "AC power detected; proceeding …" + killProcess "osascript" + return + fi + sleep 1 + ((acPowerWaitTimer--)) + done + killProcess "osascript" + preFlight "No AC power detected, exiting" + osascript -e 'display dialog "Setup Your Mac requires AC power to be connected before proceeding and waited for '${humanReadablePowerWaitTimer}'.\r\rPlease connect AC power and try again.\r\r" with title "Setup Your Mac: No AC power detected" buttons {"OK"} with icon caution' + exit 1 + + } + + + + # Check if computer is on AC power + # If not — and the `acPowerWaitTimer` is greater than 1 — allow user to connect to power for the specified time period + + if pmset -g ps | grep "AC Power" > /dev/null ; then + + preFlight "AC power detected; proceeding …" + + else + + if [[ "$acPowerWaitTimer" -gt 0 ]]; then + + osascript -e 'display dialog "Setup Your Mac requires AC power to be connected before proceeding.\r\rPlease connect your computer to power using an AC power adapter.\r\rThis process will wait for '${humanReadablePowerWaitTimer}' for AC power to be connected.\r\r" with title "Setup Your Mac: No AC power detected" buttons {"OK"} with icon caution' & + waitForPower + + else + + preFlight "No AC power detected, exiting" + osascript -e 'display dialog "Setup Your Mac requires AC power to be connected before proceeding and waited for '${humanReadablePowerWaitTimer}'.\r\rPlease connect AC power and try again.\r\r" with title "Setup Your Mac: No AC power detected" buttons {"OK"} with icon caution' + exit 1 + + fi + + fi + +} - outputLineNumberInVerboseDebugMode +acPowerCheck # Comment-out to disable - if [[ "${configurationDownloadEstimation}" == "true" ]]; then - outputLineNumberInVerboseDebugMode - calculateFreeDiskSpace "FINALISE USER EXPERIENCE" - fi +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Pre-flight Check: Toggle `jamf` binary check-in (thanks, @robjschroeder!) +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - if [[ "${jamfProPolicyTriggerFailure}" == "failed" ]]; then +function toggleJamfLaunchDaemon() { + + jamflaunchDaemon="/Library/LaunchDaemons/com.jamfsoftware.task.1.plist" - outputLineNumberInVerboseDebugMode - updateScriptLog "Failed policies detected …" - if [[ -n "${webhookURL}" ]]; then - updateScriptLog "Display Failure dialog: Sending webhook message" - webhookStatus="Failures detected" - webHookMessage + if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then + + 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 - if [[ "${failureDialog}" == "true" ]]; then + else - outputLineNumberInVerboseDebugMode - updateScriptLog "Display Failure dialog: ${failureDialog}" + while [[ ! -f "${jamflaunchDaemon}" ]] ; do + preFlight "Waiting for installation of ${jamflaunchDaemon}" + sleep 0.1 + done - 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 + if [[ $(/bin/launchctl list | grep com.jamfsoftware.task.E) ]]; then - dialogUpdateSetupYourMac "quit:" - eval "${dialogFailureCMD}" & sleep 0.3 + preFlight "Temporarily disable 'jamf' binary check-in" + /bin/launchctl bootout system "${jamflaunchDaemon}" - updateScriptLog "\n\n# # #\n# FAILURE DIALOG\n# # #\n" - updateScriptLog "Jamf Pro Policy Name Failures:" - updateScriptLog "${jamfProPolicyNameFailures}" + else - 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 + quitOut "Re-enabling 'jamf' binary check-in" + quitOut "'jamf' binary check-in daemon not loaded, attempting to bootstrap and start" + result="0" - supportContactMessage+="If you need assistance, please contact the **${supportTeamName}**: \n" + until [ $result -eq 3 ]; do - if [[ -n "${supportTeamPhone}" ]]; then - supportContactMessage+="- **Telephone:** ${supportTeamPhone}\n" - fi + /bin/launchctl bootstrap system "${jamflaunchDaemon}" && /bin/launchctl start "${jamflaunchDaemon}" + result="$?" - if [[ -n "${supportTeamEmail}" ]]; then - supportContactMessage+="- **Email:** $supportTeamEmail\n" + if [ $result = 3 ]; then + quitOut "Staring 'jamf' binary check-in daemon" + else + quitOut "Failed to start 'jamf' binary check-in daemon" fi - if [[ -n "${supportTeamWebsite}" ]]; then - supportContactMessage+="- **Web**: ${supportTeamHyperlink}\n" - fi + done - if [[ -n "${supportKB}" ]]; then - supportContactMessage+="- **Knowledge Base Article:** $supportTeamErrorKB\n" - fi - - fi + fi - failureMessage+="\n\n${supportContactMessage}" + fi - dialogUpdateFailure "message: ${failureMessage}" +} - dialogUpdateFailure "icon: SF=xmark.circle.fill,weight=bold,colour1=#BB1717,colour2=#F31F1F" - dialogUpdateFailure "button1text: ${button1textCompletionActionOption}" +toggleJamfLaunchDaemon - # Wait for user-acknowledgment due to detected failure - wait - dialogUpdateFailure "quit:" - quitScript "1" - else +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Pre-flight Check: Validate / install swiftDialog (Thanks big bunches, @acodega!) +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - outputLineNumberInVerboseDebugMode - updateScriptLog "Display Failure dialog: ${failureDialog}" +function dialogInstall() { - 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}." + # 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 }") - quitScript "1" + # Expected Team ID of the downloaded PKG + expectedDialogTeamID="PWA5E9TQ59" - fi + preFlight "Installing swiftDialog..." - else + # Create temporary working directory + workDirectory=$( /usr/bin/basename "$0" ) + tempDirectory=$( /usr/bin/mktemp -d "/private/tmp/$workDirectory.XXXXXX" ) - outputLineNumberInVerboseDebugMode - updateScriptLog "All policies executed successfully" - if [[ -n "${webhookURL}" ]]; then - webhookStatus="Successful" - updateScriptLog "Sending success webhook message" - webHookMessage - fi + # Download the installer package + /usr/bin/curl --location --silent "$dialogURL" -o "$tempDirectory/Dialog.pkg" - 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" + # Verify the download + teamID=$(/usr/sbin/spctl -a -vv -t install "$tempDirectory/Dialog.pkg" 2>&1 | awk '/origin=/ {print $NF }' | tr -d '()') - quitScript "0" + # Install the package if Team ID validates + if [[ "$expectedDialogTeamID" == "$teamID" ]]; then - fi + /usr/sbin/installer -pkg "$tempDirectory/Dialog.pkg" -target / + sleep 2 + dialogVersion=$( /usr/local/bin/dialog --version ) + preFlight "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 -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Parse JSON via osascript and JavaScript -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # Remove the temporary working directory when done + /bin/rm -Rf "$tempDirectory" -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 } -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Parse JSON via osascript and JavaScript for the Welcome dialog (thanks, @bartreardon!) -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +function dialogCheck() { -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 -} + # 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 -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Execute Jamf Pro Policy Custom Events (thanks, @smithjw) -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + else -function run_jamf_trigger() { + 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 - outputLineNumberInVerboseDebugMode + preFlight "swiftDialog version ${dialogVersion} found; proceeding..." - trigger="$1" + fi + + fi - if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then +} - updateScriptLog "SETUP YOUR MAC DIALOG: DEBUG MODE: TRIGGER: $jamfBinary policy -event $trigger ${suppressRecon}" - sleep "${debugModeSleepAmount}" +dialogCheck - 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 - fi +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Pre-flight Check: Validate `supportTeam` variables are populated +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -} +if [[ -z $supportTeamName ]]; then + preFlight "'supportTeamName' must be populated to proceed; exiting" + exit 1 +fi + +if [[ -z $supportTeamPhone && -z $supportTeamEmail && -z $supportTeamChat && -z $supportKB ]]; then + preFlight "At least ONE 'supportTeam' variable must be populated to proceed; exiting" + exit 1 +fi # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Confirm Policy Execution +# Pre-flight Check: Complete # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function confirmPolicyExecution() { +preFlight "Complete" - outputLineNumberInVerboseDebugMode - trigger="${1}" - validation="${2}" - updateScriptLog "SETUP YOUR MAC DIALOG: Confirm Policy Execution: '${trigger}' '${validation}'" - if [ "${suppressReconOnPolicy}" == "true" ]; then suppressRecon="-forceNoRecon"; fi - case ${validation} in +#################################################################################################### +# +# Dialog Variables +# +#################################################################################################### - */* ) # If the validation variable contains a forward slash (i.e., "/"), presume it's a path and check if that path exists on disk +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# infobox-related variables +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - 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 - ;; +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 ) - "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" ) +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Reflect Debug Mode in `infotext` (i.e., bottom, left-hand corner of each dialog) +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - 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 - ;; +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 - * ) - 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 +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Dialog binary (and enable swiftDialog's `--verbose` mode with SYM's debugMode) +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -} +dialogBinary="/usr/local/bin/dialog" +case ${debugMode} in + "true" ) dialogBinary="${dialogBinary} --verbose" ;; + "verbose" ) dialogBinary="${dialogBinary} --verbose --resizable --debug red" ;; +esac # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Validate Policy Result +# Set JAMF binary, Dialog Command Files # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -function validatePolicyResult() { - - outputLineNumberInVerboseDebugMode - - trigger="${1}" - validation="${2}" - updateScriptLog "SETUP YOUR MAC DIALOG: Validate Policy Result: '${trigger}' '${validation}'" - - case ${validation} in +jamfBinary="/usr/local/bin/jamf" +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 ) - ### - # 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 +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - ### - # Local - # Validation within this script, for example: "rosetta" or "filevault" - ### +welcomeTitle="Happy $( date +'%A' ), ${loggedInUserFirstname}! \nWelcome to your new ${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 - ;; +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" - ### - # Remote - # Validation via a Jamf Pro policy which has a single-script payload, for example: "symvGlobalProtect" - # See: https://vimeo.com/782561166 - ### + if [[ -n "${supportTeamPhone}" ]]; then + welcomeMessage+="- **Telephone**: ${supportTeamPhone}\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 "${supportTeamEmail}" ]]; then + welcomeMessage+="- **Email**: ${supportTeamEmail}\n" + fi + + if [[ -n "${supportTeamChat}" ]]; then + welcomeMessage+="- **Online Chat:** ${supportTeamChatHyperlink}\n" + fi + if [[ -n "${supportTeamWebsite}" ]]; then + welcomeMessage+="- **Web**: ${supportTeamHyperlink}\n" + fi + if [[ -n "${supportKB}" ]]; then + welcomeMessage+="- **Knowledge Base Article:** ${supportTeamErrorKB}\n" + fi - ### - # None: For triggers which don't require validation - # (Always evaluates as: 'success' and 'Installed') - ### + if [[ -n "${supportTeamHours}" ]]; then + welcomeMessage+="- **Support Hours:** ${supportTeamHours}\n" + fi + +fi - "None" | "none") +welcomeMessage+="\n\n---" - outputLineNumberInVerboseDebugMode - updateScriptLog "SETUP YOUR MAC DIALOG: Confirm Policy Execution: ${validation}" - dialogUpdateSetupYourMac "listitem: index: $i, status: success, statustext: Installed" - ;; +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" - ### - # 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 [ "$prefillEmail" == "true" ]; then emailPrefill=',"value" : "'${loggedInUser}${emailEnding}'"'; fi - outputLineNumberInVerboseDebugMode +if [ "$promptForEmail" == "true" ]; then + emailJSON='{ "title" : "E-mail", + "required" : true, + "prompt" : "E-mail Address"'${emailPrefill}', + "regex" : "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", + "regexerror" : "Please enter a valid email address." + },' +fi +if [ "$prefillComputerName" == "true" ]; then computerNamePrefill=',"value" : "'${serialNumber}'"'; fi +if [ "$promptForComputerName" == "true" ]; then compNameJSON='{ "title" : "Computer Name","required" : false,"prompt" : "Computer Name"'${computerNamePrefill}'},'; 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 - if [[ "${debugMode}" == "true" ]] || [[ "${debugMode}" == "verbose" ]] ; then +textFieldJSON="${usernameJSON}${realNameJSON}${emailJSON}${compNameJSON}${assetTagJSON}${positionJSON}${roomJSON}" +textFieldJSON=$( echo ${textFieldJSON} | sed 's/,$//' ) - # 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" +# Dropdowns +if [ "$promptForBuilding" == "true" ]; then + if [ -n "$buildingsListRaw" ]; then + buildingJSON='{ + "title" : "Building", + "default" : "", + "required" : true, + "values" : [ + '${buildingsList}' + ] + },' + fi +fi - else +if [ "$promptForDepartment" == "true" ]; then + if [ -n "$departmentListRaw" ]; then + departmentJSON='{ + "title" : "Department", + "default" : "", + "required" : true, + "values" : [ + '${departmentList}' + ] + },' + fi +fi - shopt -s nocasematch +if [ "$promptForPosition" == "true" ]; then + if [ -n "${positionListRaw}" ]; then + positionSelectJSON='{ + "title" : "Position", + "default" : "", + "required" : true, + "values" : [ + '${positionList}' + ] + },' + fi +fi - case ${completionActionOption} in +if [ "$promptForConfiguration" == "true" ] && [ -z "${presetConfiguration}" ]; then + configurationJSON='{ + "title" : "Configuration", + "style" : "radio", + "default" : "'"${configurationOneName}"'", + "values" : [ + "'"${configurationOneName}"'", + "'"${configurationTwoName}"'", + "'"${configurationThreeName}"'" + ] + }' +fi - "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 & - ;; +selectItemsJSON="${buildingJSON}${departmentJSON}${positionSelectJSON}${configurationJSON}" +selectItemsJSON=$( echo $selectItemsJSON | sed 's/,$//' ) - "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»' - ;; - "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 & - ;; +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# "Welcome" JSON for Capturing User Input (thanks, @bartreardon!) +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - "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 & - ;; +welcomeJSON=' +{ + "commandfile" : "'"${welcomeCommandFile}"'", + "bannerimage" : "'"${welcomeBannerImage}"'", + "bannertext" : "'"${welcomeBannerText}"'", + "title" : "'"${welcomeTitle}"'", + "message" : "'"${welcomeMessage}"'", + "icon" : "'"${welcomeIcon}"'", + "infobox" : "Analyzing …", + "iconsize" : "198", + "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 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}" - ;; - "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 +# +#################################################################################################### - "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' - ;; +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# "Setup Your Mac" dialog Title, Message, Icon and Overlay Icon +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - "Sleep"* ) - sleepDuration=$( awk '{print $NF}' <<< "${1}" ) - updateScriptLog "COMPLETION ACTION: Sleeping for ${sleepDuration} seconds …" - sleep "${sleepDuration}" - killProcess "Dialog" - updateScriptLog "Goodnight!" - ;; +title="Setting up ${loggedInUserFirstname}‘s ${modelName}" +message="Please wait while the following apps are installed …" - "Wait" ) - updateScriptLog "COMPLETION ACTION: Waiting for user interaction …" - wait - ;; +if [[ "${brandingBannerDisplayText}" == "true" ]] ; then + bannerText="Setting up ${loggedInUserFirstname}‘s ${modelName}"; +else + bannerText=" " +fi - "Quit" ) - updateScriptLog "COMPLETION ACTION: Quitting script" - exitCode="0" - ;; +if [ -n "$supportTeamName" ]; then + helpmessage+="If you need assistance, please contact: \n\n**${supportTeamName}** \n" +fi - * ) - updateScriptLog "COMPLETION ACTION: Using the default of 'wait'" - wait - ;; +if [ -n "${supportTeamPhone}" ]; then + helpmessage+="- **Telephone:** ${supportTeamPhone} \n" +fi + +if [ -n "${supportTeamEmail}" ]; then + helpmessage+="- **Email:** ${supportTeamEmail} \n" +fi + +if [ -n "${supportTeamChat}" ]; then + helpmessage+="- **Online Chat:** ${supportTeamChatHyperlink} \n" +fi - esac +if [ -n "${supportTeamWebsite}" ]; then + helpmessage+="- **Web**: ${supportTeamHyperlink} \n" +fi + +if [ -n "${supportKB}" ]; then + helpmessage+="- **Knowledge Base Article:** ${supportTeamErrorKB} \n" +fi + +if [ -n "${supportTeamHours}" ]; then + helpmessage+="- **Support Hours:** ${supportTeamHours} \n" +fi - shopt -u nocasematch +helpmessage+="\n**Computer Information:** \n" +helpmessage+="- **Operating System:** ${macOSproductVersion} (${macOSbuildVersion}) \n" +helpmessage+="- **Serial Number:** ${serialNumber} \n" +helpmessage+="- **Dialog:** ${dialogVersion} \n" +helpmessage+="- **Started:** ${timestamp}" - fi +infobox="Analyzing input …" # Customize at "Update Setup Your Mac's infobox" - # Remove custom welcomeBannerImageFileName - if [[ -e "/var/tmp/${welcomeBannerImageFileName}" ]]; then - updateScriptLog "COMPLETION ACTION: Removing /var/tmp/${welcomeBannerImageFileName} …" - rm "/var/tmp/${welcomeBannerImageFileName}" - fi - # Remove overlayicon - if [[ -e ${overlayicon} ]]; then - updateScriptLog "COMPLETION ACTION: Removing ${overlayicon} …" - rm "${overlayicon}" - fi +# 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" - exit "${exitCode}" +# 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 ${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" + } + ] + } + ] + } + ' + ;; - case "${osVersion}" in + "${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 } +#################################################################################################### +# +# Failure dialog +# +#################################################################################################### + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# Webhook Message (Microsoft Teams or Slack) (thanks, @robjschroeder! and @iDrewbs!) +# "Failure" dialog Title, Message and Icon # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -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" +failureTitle="Failure Detected" +failureMessage="Placeholder message; update in the 'finalise' function" +failureIcon="SF=xmark.circle.fill,weight=bold,colour1=#BB1717,colour2=#F31F1F" - # If there aren't any failures, use "None" for the value of `jamfProPolicyNameFailures` - if [[ -z "${jamfProPolicyNameFailures}" ]]; then - jamfProPolicyNameFailures="None" - fi - if [[ $webhookURL == *"slack"* ]]; then - - updateScriptLog "Generating Slack Message …" - - webHookdata=$(cat <&1 - - webhookResult="$?" - updateScriptLog "Slack Webhook Result: ${webhookResult}" - - else - - updateScriptLog "Generating Microsoft Teams Message …" +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# "Failure" dialog Settings and Features +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - # URL to an image to add to your notification - activityImage="https://creazilla-store.fra1.digitaloceanspaces.com/cliparts/78010/old-mac-computer-clipart-md.png" +dialogFailureCMD="$dialogBinary \ +--moveable \ +--title \"$failureTitle\" \ +--message \"$failureMessage\" \ +--icon \"$failureIcon\" \ +--iconsize 125 \ +--width 625 \ +--height 45% \ +--position topright \ +--button1text \"Close\" \ +--infotext \"$scriptVersion\" \ +--titlefont 'size=22' \ +--messagefont 'size=14' \ +--overlayicon \"$overlayicon\" \ +--commandfile \"$failureCommandFile\" " - 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 …" + outputLineNumberInVerboseDebugMode + welcomeDialog "Display 'Welcome' dialog with disabled Continue Button …" welcomeResults=$( eval "${dialogBinary} --jsonfile ${welcomeJSONFile} --json --button1disabled" ) - welcomeResultsExitCode=$? - + welcomeResultsExitCode=$? + else - updateScriptLog "WELCOME DIALOG: Display 'Welcome' dialog …" + outputLineNumberInVerboseDebugMode + welcomeDialog "Display 'Welcome' dialog …" welcomeResults=$( eval "${dialogBinary} --jsonfile ${welcomeJSONFile} --json" ) - welcomeResultsExitCode=$? + welcomeResultsExitCode=$? fi else # Display Welcome dialog, sans estimation of Configuration download times - updateScriptLog "WELCOME DIALOG: Skipping estimation of Configuration download times" + outputLineNumberInVerboseDebugMode + welcomeDialog "Skipping estimation of Configuration download times" # Write Welcome JSON to disk welcomeJSON=${welcomeJSON//Analyzing …/} echo "$welcomeJSON" > "$welcomeJSONFile" welcomeResults=$( eval "${dialogBinary} --jsonfile ${welcomeJSONFile} --json" ) - welcomeResultsExitCode=$? + welcomeResultsExitCode=$? fi # Evaluate User Input outputLineNumberInVerboseDebugMode - updateScriptLog "welcomeResultsExitCode: ${welcomeResultsExitCode}" + logComment "welcomeResultsExitCode: ${welcomeResultsExitCode}" case "${welcomeResultsExitCode}" 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 @@ -2951,16 +3113,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" ### @@ -2979,7 +3141,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 ) @@ -2991,8 +3153,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 @@ -3005,16 +3167,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 @@ -3057,7 +3219,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) @@ -3066,10 +3228,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}" @@ -3077,24 +3239,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: ${welcomeResultsExitCode}" + welcomeDialog "Something else happened; Exit code: ${welcomeResultsExitCode}" quitScript "1" ;; @@ -3112,7 +3274,7 @@ else else symConfiguration="Catch-all ('Welcome' dialog disabled)" fi - updateScriptLog "WELCOME DIALOG: Using ${symConfiguration} Configuration …" + welcomeDialog "Using ${symConfiguration} Configuration …" policyJSONConfiguration @@ -3124,10 +3286,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}" @@ -3163,8 +3325,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}" @@ -3191,7 +3353,7 @@ dialogUpdateSetupYourMac "list: show" outputLineNumberInVerboseDebugMode -updateScriptLog "SETUP YOUR MAC DIALOG: Initial progress bar" +updateSetupYourMacDialog "Initial progress bar" dialogUpdateSetupYourMac "progress: 1" @@ -3220,7 +3382,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)))" @@ -3250,9 +3412,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 @@ -3281,6 +3443,10 @@ if [[ "${symConfiguration}" != *"Catch-all"* ]]; then if [[ -n "${supportTeamEmail}" ]]; then helpmessage+="- **Email:** ${supportTeamEmail} \n" fi + + if [[ -n "${supportTeamChat}" ]]; then + helpmessage+="- **Online Chat:** ${supportTeamChatHyperlink} \n" + fi if [[ -n "${supportTeamWebsite}" ]]; then helpmessage+="- **Web**: ${supportTeamHyperlink} \n" @@ -3289,10 +3455,14 @@ if [[ "${symConfiguration}" != *"Catch-all"* ]]; then if [[ -n "${supportKB}" ]]; then helpmessage+="- **Knowledge Base Article:** ${supportTeamErrorKB} \n" fi + + if [[ -n "${supportTeamHours}" ]]; then + helpmessage+="- **Support Hours:** ${supportTeamHours} \n" + fi fi - updateScriptLog "Update 'helpmessage' with Configuration: ${infoboxConfiguration} …" + updateSetupYourMacDialog "Update 'helpmessage' with Configuration: ${infoboxConfiguration} …" helpmessage+="\n**Configuration:**\n- $infoboxConfiguration\n" helpmessage+="\n**Computer Information:** \n" @@ -3328,7 +3498,8 @@ for (( i=0; i listitem: ${listitem}\n# # #\n" + updateSetupYourMacDialog "\n\n# # #\n# policyJSON > listitem: ${listitem}\n# # #\n" + dialogUpdateSetupYourMac "activate:" dialogUpdateSetupYourMac "listitem: index: $i, status: wait, statustext: Installing …, " fi if [[ -n "$icon" ]]; then dialogUpdateSetupYourMac "icon: ${icon}"; fi @@ -3342,7 +3513,7 @@ for (( i=0; i${RESULT}" \ No newline at end of file diff --git a/Validations/CrowdStrike Falcon Status.bash b/Validations/CrowdStrike Falcon Status.bash index 373afb0..12fb022 100644 --- a/Validations/CrowdStrike Falcon Status.bash +++ b/Validations/CrowdStrike Falcon Status.bash @@ -8,8 +8,8 @@ ######################################################################################################################################## export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin/ -scriptVersion="0.0.3" -RESULT="Not Installed" +scriptVersion="0.0.5" +RESULT="Failed: Not Installed" lastConnectedVariance="7" # The number of days before reporting device has not connected to the CrowdStrike Cloud. ### @@ -35,6 +35,21 @@ report_result() { } +### +# Pre-flight: Check the Locale; this will affect the output of falconctl stats +### + +lib_locale=$( /usr/bin/defaults read "/Library/Preferences/.GlobalPreferences.plist" AppleLocale ) +root_locale=$( /usr/bin/defaults read "/var/root/Library/Preferences/.GlobalPreferences.plist" AppleLocale ) + +if [[ "${lib_locale}" != "en_US" ]]; then + /usr/bin/defaults write "/Library/Preferences/.GlobalPreferences.plist" AppleLocale "en_US" +fi + +if [[ "${root_locale}" != "en_US" ]]; then + /usr/bin/defaults write "/var/root/Library/Preferences/.GlobalPreferences.plist" AppleLocale "en_US" +fi + ### # Program ### diff --git a/images/SYM-Helper-1.2.0-Hero.png b/images/SYM-Helper-1.2.0-Hero.png new file mode 100644 index 0000000..4b4d34f Binary files /dev/null and b/images/SYM-Helper-1.2.0-Hero.png differ diff --git a/images/SYM-Helper_1.2.0_SYM-1.15.0_Hero.png b/images/SYM-Helper_1.2.0_SYM-1.15.0_Hero.png new file mode 100644 index 0000000..8b03e4b Binary files /dev/null and b/images/SYM-Helper_1.2.0_SYM-1.15.0_Hero.png differ