This is part two of Dan’s blog, and it belongs with this ‘Part 1’ article.
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Check Available Software Updates
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function checkAvailableSoftwareUpdates() {
notice "Check Available Software Updates …"
dialogUpdate "icon: SF=arrow.trianglehead.2.clockwise,${organizationColorScheme}"
dialogUpdate "listitem: index: ${1}, status: wait, statustext: Checking …"
dialogUpdate "progress: increment"
dialogUpdate "progresstext: Determining Available Software Updates status …"
sleep "${anticipationDuration}"
mdmClientAvailableOSUpdates=$( /usr/libexec/mdmclient AvailableOSUpdates | head -n 5 )
if [[ "${mdmClientAvailableOSUpdates}" == *"OS Update Item"* ]]; then
notice "MDM Client Available OS Updates"
info "${mdmClientAvailableOSUpdates}"
fi
recommendedUpdates=$( /usr/libexec/PlistBuddy -c "Print :RecommendedUpdates:0" /Library/Preferences/com.apple.SoftwareUpdate.plist 2>/dev/null )
if [[ -n "${recommendedUpdates}" ]]; then
SUListRaw=$( softwareupdate --list --include-config-data 2>&1 )
case "${SUListRaw}" in
*"Can’t connect"* )
availableSoftwareUpdates="Can’t connect to the Software Update server"
dialogUpdate "listitem: index: ${1}, status: fail, statustext: ${availableSoftwareUpdates}"
errorOut "Available Software Updates: ${availableSoftwareUpdates}"
overallCompliance+="Failed: ${1}; "
;;
*"The operation couldn’t be completed."* )
availableSoftwareUpdates="The operation couldn’t be completed."
dialogUpdate "listitem: index: ${1}, status: fail, statustext: ${availableSoftwareUpdates}"
errorOut "Available Software Updates: ${availableSoftwareUpdates}"
overallCompliance+="Failed: ${1}; "
;;
*"Deferred: YES"* )
availableSoftwareUpdates="Deferred software available."
dialogUpdate "listitem: index: ${1}, status: error, statustext: ${availableSoftwareUpdates}"
warning "Available Software Updates: ${availableSoftwareUpdates}"
;;
*"No new software available."* )
availableSoftwareUpdates="No new software available."
dialogUpdate "listitem: index: ${1}, status: success, statustext: ${availableSoftwareUpdates}"
info "Available Software Updates: ${availableSoftwareUpdates}"
;;
* )
SUList=$( echo "${SUListRaw}" | grep "*" | sed "s/\* Label: //g" | sed "s/,*$//g" )
availableSoftwareUpdates="${SUList}"
dialogUpdate "listitem: index: ${1}, status: error, statustext: ${availableSoftwareUpdates}"
warning "Available Software Updates: ${availableSoftwareUpdates}"
;;
esac
else
availableSoftwareUpdates="None"
dialogUpdate "listitem: index: ${1}, status: success, statustext: ${availableSoftwareUpdates}"
info "Available Software Updates: ${availableSoftwareUpdates}"
fi
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Check System Integrity Protection
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function checkSIP() {
notice "Check System Integrity Protection …"
dialogUpdate "icon: SF=checkmark.shield.fill,${organizationColorScheme}"
dialogUpdate "listitem: index: ${1}, status: wait, statustext: Checking …"
dialogUpdate "progress: increment"
dialogUpdate "progresstext: Determining System Integrity Protection status …"
sleep "${anticipationDuration}"
sipCheck=$( csrutil status )
case ${sipCheck} in
*"enabled"* )
dialogUpdate "listitem: index: ${1}, status: success, statustext: Enabled"
info "System Integrity Protection: Enabled"
;;
* )
dialogUpdate "listitem: index: ${1}, status: fail, statustext: Failed"
errorOut "System Integrity Protection: Failed"
overallCompliance+="Failed: ${1}; "
;;
esac
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Check Firewall
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function checkFirewall() {
notice "Check Firewall …"
dialogUpdate "icon: SF=firewall.fill,${organizationColorScheme}"
dialogUpdate "listitem: index: ${1}, status: wait, statustext: Checking …"
dialogUpdate "progress: increment"
dialogUpdate "progresstext: Determining Firewall status …"
sleep "${anticipationDuration}"
firewallCheck=$( /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate )
case ${firewallCheck} in
*"enabled"* )
dialogUpdate "listitem: index: ${1}, status: success, statustext: Enabled"
info "Firewall: Enabled"
;;
* )
dialogUpdate "listitem: index: ${1}, status: fail, statustext: Failed"
errorOut "Firewall: Failed"
overallCompliance+="Failed: ${1}; "
;;
esac
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Check Uptime
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function checkUptime() {
notice "Check Uptime …"
dialogUpdate "icon: SF=stopwatch,${organizationColorScheme}"
dialogUpdate "listitem: index: ${1}, status: wait, statustext: Checking …"
dialogUpdate "progress: increment"
dialogUpdate "progresstext: Calculating time since last reboot …"
sleep "${anticipationDuration}"
timestamp="$( date '+%Y-%m-%d-%H%M%S' )"
lastBootTime=$( sysctl kern.boottime | awk -F'[ |,]' '{print $5}' )
currentTime=$( date +"%s" )
upTimeRaw=$((currentTime-lastBootTime))
upTimeMin=$((upTimeRaw/60))
upTimeHours=$((upTimeMin/60))
uptimeDays=$( uptime | awk '{ print $4 }' | sed 's/,//g' )
uptimeNumber=$( uptime | awk '{ print $3 }' | sed 's/,//g' )
if [[ "${uptimeDays}" = "day"* ]]; then
if [[ "${uptimeNumber}" -gt 1 ]]; then
uptimeHumanReadable="${uptimeNumber} days"
else
uptimeHumanReadable="${uptimeNumber} day"
fi
elif [[ "${uptimeDays}" == "mins"* ]]; then
uptimeHumanReadable="${uptimeNumber} mins"
else
uptimeHumanReadable="${uptimeNumber} (HH:MM)"
fi
if [[ "${upTimeMin}" -gt "${allowedUptimeMinutes}" ]]; then
case ${excessiveUptimeAlertStyle} in
"warning" )
dialogUpdate "listitem: index: ${1}, status: error, statustext: ${uptimeHumanReadable}"
warning "Uptime: ${uptimeHumanReadable}"
;;
"error" | * )
dialogUpdate "listitem: index: ${1}, status: fail, statustext: ${uptimeHumanReadable}"
errorOut "Uptime: ${uptimeHumanReadable}"
overallCompliance+="Failed: ${1}; "
;;
esac
else
dialogUpdate "listitem: index: ${1}, status: success, statustext: ${uptimeHumanReadable}"
info "Uptime: ${uptimeHumanReadable}"
fi
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Check Free Disk Space
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function checkFreeDiskSpace() {
notice "Checking Free Disk Space …"
dialogUpdate "icon: SF=externaldrive.fill.badge.checkmark,${organizationColorScheme}"
dialogUpdate "listitem: index: ${1}, status: wait, statustext: Checking …"
dialogUpdate "progress: increment"
dialogUpdate "progresstext: Determining free disk space …"
sleep "${anticipationDuration}"
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}"
if [[ "${freePercentage}" < "${allowedFreeDiskPercentage}" ]]; then
dialogUpdate "listitem: index: ${1}, status: fail, statustext: ${diskSpace}"
errorOut "Disk Space: ${diskSpace}"
overallCompliance+="Failed: ${1}; "
else
dialogUpdate "listitem: index: ${1}, status: success, statustext: ${diskSpace}"
info "Disk Space: ${diskSpace}"
fi
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Check the status of the Jamf Pro MDM Profile
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function checkJamfProMdmProfile() {
notice "Check the status of the Jamf Pro MDM Profile …"
dialogUpdate "icon: SF=gear.badge,${organizationColorScheme}"
dialogUpdate "listitem: index: ${1}, status: wait, statustext: Checking …"
dialogUpdate "progress: increment"
dialogUpdate "progresstext: Determining MDM Profile status …"
sleep "${anticipationDuration}"
mdmProfileTest=$( profiles list -all | grep "00000000-0000-0000-A000-4A414D460003" )
if [[ -n "${mdmProfileTest}" ]]; then
dialogUpdate "listitem: index: ${1}, status: success, statustext: Installed"
info "Jamf Pro MDM Profile: Installed"
else
dialogUpdate "listitem: index: ${1}, status: fail, statustext: NOT Installed"
errorOut "Jamf Pro MDM Profile: NOT Installed"
overallCompliance+="Failed: ${1}; "
fi
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Check Apple Push Notification service (thanks, @isaacatmann!)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function checkAPNs() {
notice "Check Apple Push Notification service …"
dialogUpdate "icon: SF=wave.3.up.circle.fill,${organizationColorScheme}"
dialogUpdate "listitem: index: ${1}, status: wait, statustext: Checking …"
dialogUpdate "progress: increment"
dialogUpdate "progresstext: Determining Apple Push Notification service status …"
sleep "${anticipationDuration}"
apnsCheck=$( command log show --last 24h --predicate 'subsystem == "com.apple.ManagedClient" && (eventMessage CONTAINS[c] "Received HTTP response (200) [Acknowledged" || eventMessage CONTAINS[c] "Received HTTP response (200) [NotNow")' | tail -1 | cut -d '.' -f 1 )
if [[ "${apnsCheck}" == *"Timestamp"* ]] || [[ -z "${apnsCheck}" ]]; then
dialogUpdate "listitem: index: ${1}, status: fail, statustext: Failed"
errorOut "Apple Push Notification service: ${apnsCheck}"
overallCompliance+="Failed: ${1}; "
else
apnsStatusEpoch=$( date -j -f "%Y-%m-%d %H:%M:%S" "${apnsCheck}" +"%s" )
apnsStatus=$( date -r "${apnsStatusEpoch}" "+%A %-l:%M %p" )
dialogUpdate "listitem: index: ${1}, status: success, statustext: ${apnsStatus}"
info "Apple Push Notification service: ${apnsCheck}"
fi
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Check the expiration date of the JSS Built-in Certificate Authority (thanks, @isaacatmann!)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function checkJssCertificateExpiration() {
notice "Check the expiration date of the JSS Built-in Certificate Authority …"
dialogUpdate "icon: SF=mail.and.text.magnifyingglass,${organizationColorScheme}"
dialogUpdate "listitem: index: ${1}, status: wait, statustext: Checking …"
dialogUpdate "progress: increment"
dialogUpdate "progresstext: Determining MDM Certificate expiration date …"
sleep "${anticipationDuration}"
identities=( $( security find-identity -v /Library/Keychains/System.keychain | awk '{print $3}' | tr -d '"' | head -n 1 ) )
now_seconds=$( date +%s )
if [[ "${identities}" != "identities" ]]; then
for i in $identities; do
if [[ $(security find-certificate -c "$i" | grep issu | tr -d '"') == *"JSS BUILT-IN CERTIFICATE AUTHORITY"* ]] || [[ $(security find-certificate -c "$i" | grep issu | tr -d '"') == *"JSS Built-in Certificate Authority"* ]]; then
expiry=$(security find-certificate -c "$i" -p | openssl x509 -noout -enddate | cut -f2 -d"=")
expirationDateFormatted=$( date -j -f "%b %d %H:%M:%S %Y GMT" "${expiry}" "+%d-%b-%Y" )
date_seconds=$(date -j -f "%b %d %T %Y %Z" "$expiry" +%s)
if (( date_seconds > now_seconds )); then
dialogUpdate "listitem: index: ${1}, status: success, statustext: ${expirationDateFormatted}"
info "JSS Built-in Certificate Authority Expiration: ${expirationDateFormatted}"
else
dialogUpdate "listitem: index: ${1}, status: fail, statustext: ${expirationDateFormatted}"
errorOut "JSS Built-in Certificate Authority Expiration: ${expirationDateFormatted}"
overallCompliance+="Failed: ${1}; "
fi
fi
done
else
expirationDateFormatted="NOT Installed"
dialogUpdate "listitem: index: ${1}, status: fail, statustext: ${expirationDateFormatted}"
errorOut "JSS Built-in Certificate Authority Expiration: ${expirationDateFormatted}"
overallCompliance+="Failed: ${1}; "
fi
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Check Last Jamf Pro Check-In (thanks, @jordywitteman!)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function checkJamfProCheckIn() {
notice "Checking last Jamf Pro check-in …"
dialogUpdate "icon: SF=dot.radiowaves.left.and.right,${organizationColorScheme}"
dialogUpdate "listitem: index: ${1}, status: wait, statustext: Checking …"
dialogUpdate "progress: increment"
dialogUpdate "progresstext: Determining last Jamf Pro check-in …"
sleep "${anticipationDuration}"
# Enable 24 hour clock format (12 hour clock enabled by default)
twenty_four_hour_format="false"
# Number of seconds since action last occurred (86400 = 1 day)
check_in_time_old=86400 # 1 day
check_in_time_aging=28800 # 8 hours
last_check_in_time=$(grep "Checking for policies triggered by \"recurring check-in\"" "/private/var/log/jamf.log" | tail -n 1 | awk '{ print $2,$3,$4 }')
# Convert last Jamf Pro check-in time to epoch
last_check_in_time_epoch=$(date -j -f "%b %d %T" "${last_check_in_time}" +"%s")
time_since_check_in_epoch=$(($currentTimeEpoch-$last_check_in_time_epoch))
# Convert last Jamf Pro epoch to something easier to read
if [[ "${twenty_four_hour_format}" == "true" ]]; then
# Outputs 24 hour clock format
last_check_in_time_human_reable=$(date -r "${last_check_in_time_epoch}" "+%A %H:%M")
else
# Outputs 12 hour clock format
last_check_in_time_human_reable=$(date -r "${last_check_in_time_epoch}" "+%A %-l:%M %p")
fi
# Set status indicator for last check-in
if [ ${time_since_check_in_epoch} -ge ${check_in_time_old} ]; then
# check_in_status_indicator="🔴"
dialogUpdate "listitem: index: ${1}, status: fail, statustext: ${last_check_in_time_human_reable}"
errorOut "Last Jamf Pro Check-in: ${last_check_in_time_human_reable}"
overallCompliance+="Failed: ${1}; "
elif [ ${time_since_check_in_epoch} -ge ${check_in_time_aging} ]; then
# check_in_status_indicator="🟠"
dialogUpdate "listitem: index: ${1}, status: error, statustext: ${last_check_in_time_human_reable}"
warning "Last Jamf Pro Check-in: ${last_check_in_time_human_reable}"
overallCompliance+="Error ${1}"
elif [ ${time_since_check_in_epoch} -lt ${check_in_time_aging} ]; then
# check_in_status_indicator="🟢"
dialogUpdate "listitem: index: ${1}, status: success, statustext: ${last_check_in_time_human_reable}"
info "Last Jamf Pro Check-in: ${last_check_in_time_human_reable}"
fi
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Check Last Jamf Pro Inventory Update (thanks, @jordywitteman!)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function checkJamfProInventory() {
notice "Checking last Jamf Pro inventory update …"
dialogUpdate "icon: SF=checklist,${organizationColorScheme}"
dialogUpdate "listitem: index: ${1}, status: wait, statustext: Checking …"
dialogUpdate "progress: increment"
dialogUpdate "progresstext: Determining last Jamf Pro inventory update …"
sleep "${anticipationDuration}"
# Enable 24 hour clock format (12 hour clock enabled by default)
twenty_four_hour_format="false"
# Number of seconds since action last occurred (86400 = 1 day)
inventory_time_old=604800 # 1 week
inventory_time_aging=259200 # 3 days
# Get last Jamf Pro inventory time from jamf.log
last_inventory_time=$(grep "Removing existing launchd task /Library/LaunchDaemons/com.jamfsoftware.task.bgrecon.plist..." "/private/var/log/jamf.log" | tail -n 1 | awk '{ print $2,$3,$4 }')
# Convert last Jamf Pro inventory time to epoch
last_inventory_time_epoch=$(date -j -f "%b %d %T" "${last_inventory_time}" +"%s")
time_since_inventory_epoch=$(($currentTimeEpoch-$last_inventory_time_epoch))
# Convert last Jamf Pro epoch to something easier to read
if [[ "${twenty_four_hour_format}" == "true" ]]; then
# Outputs 24 hour clock format
last_inventory_time_human_reable=$(date -r "${last_inventory_time_epoch}" "+%A %H:%M")
else
# Outputs 12 hour clock format
last_inventory_time_human_reable=$(date -r "${last_inventory_time_epoch}" "+%A %-l:%M %p")
fi
#set status indicator for last inventory
if [ ${time_since_inventory_epoch} -ge ${inventory_time_old} ]; then
# inventory_status_indicator="🔴"
dialogUpdate "listitem: index: ${1}, status: fail, statustext: ${last_inventory_time_human_reable}"
errorOut "Last Jamf Pro Inventory Update: ${last_inventory_time_human_reable}"
overallCompliance+="Failed: ${1}; "
elif [ ${time_since_inventory_epoch} -ge ${inventory_time_aging} ]; then
# inventory_status_indicator="🟠"
dialogUpdate "listitem: index: ${1}, status: error, statustext: ${last_inventory_time_human_reable}"
warning "Last Jamf Pro Inventory Update: ${last_inventory_time_human_reable}"
overallCompliance+="Error ${1}"
elif [ ${time_since_inventory_epoch} -lt ${inventory_time_aging} ]; then
# inventory_status_indicator="🟢"
dialogUpdate "listitem: index: ${1}, status: success, statustext: ${last_inventory_time_human_reable}"
info "Last Jamf Pro Inventory Update: ${last_inventory_time_human_reable}"
fi
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Check FileVault
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function checkFileVault() {
notice "Checking FileVault status …"
dialogUpdate "icon: SF=lock.laptopcomputer,${organizationColorScheme}"
dialogUpdate "listitem: index: ${1}, status: wait, statustext: Checking …"
dialogUpdate "progress: increment"
dialogUpdate "progresstext: Determining FileVault disk encryption status …"
sleep "${anticipationDuration}"
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."* )
dialogUpdate "listitem: index: ${1}, status: success, statustext: Enabled"
info "FileVault: Enabled"
;;
*"Deferred enablement appears to be active for user"* )
dialogUpdate "listitem: index: ${1}, status: success, statustext: Enabled (next login)"
warning "FileVault: Enabled (next login)"
;;
* )
dialogUpdate "listitem: index: ${1}, status: error, statustext: Failed"
errorOut "FileVault: Failed"
overallCompliance+="Failed: ${1}; "
;;
esac
else
dialogUpdate "listitem: index: ${1}, status: error, statustext: Failed"
errorOut "FileVault: Failed"
overallCompliance+="Failed: ${1}; "
fi
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Check Setup Your Mac Validation (where Parameter 2 represents the Jamf Pro Policy Custom Trigger)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function checkSetupYourMacValidation() {
trigger="${2}"
appPath="${3}"
notice "Setup Your Mac Validation: ${appPath} …"
dialogUpdate "icon: ${appPath}"
dialogUpdate "listitem: index: ${1}, status: wait, statustext: Checking …"
dialogUpdate "progress: increment"
dialogUpdate "progresstext: Determining status of ${appPath} …"
# sleep "${anticipationDuration}"
symValidation=$( /usr/local/bin/jamf policy -event $trigger | grep "Script result:" )
case ${symValidation} in
*"Failed"* )
dialogUpdate "listitem: index: ${1}, status: fail, statustext: Failed"
errorOut "${appPath} Failed"
overallCompliance+="Failed: ${1}; "
;;
*"Running"* )
dialogUpdate "listitem: index: ${1}, status: success, statustext: Running"
info "${appPath} Running"
;;
*"Error"* | * )
dialogUpdate "listitem: index: ${1}, status: error, statustext: Error"
errorOut "${appPath} Error"
overallCompliance+="Error: ${1}; "
;;
esac
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Check Network Quality
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function checkNetworkQuality() {
notice "Checking Network Quality …"
dialogUpdate "icon: SF=gauge.with.dots.needle.67percent,${organizationColorScheme}"
dialogUpdate "listitem: index: ${1}, status: wait, statustext: Checking …"
dialogUpdate "progress: increment"
dialogUpdate "progresstext: Determining Network Quality …"
# sleep "${anticipationDuration}"
networkQualityTestFile="/var/tmp/networkQualityTest"
if [[ -e "${networkQualityTestFile}" ]]; then
networkQualityTestFileCreationEpoch=$( stat -f "%m" "${networkQualityTestFile}" )
networkQualityTestMaximumEpoch=$( date -v-"${networkQualityTestMaximumAge}" +%s )
if [[ "${networkQualityTestFileCreationEpoch}" -gt "${networkQualityTestMaximumEpoch}" ]]; then
info "Using cached Network Quality Test"
testStatus="(cached)"
else
unset testStatus
info "Removing cached result …"
rm "${networkQualityTestFile}"
info "Starting Network Quality Test …"
networkQuality -s -v -c > "${networkQualityTestFile}"
info "Completed Network Quality Test"
fi
else
info "Starting Network Quality Test …"
networkQuality -s -v -c > "${networkQualityTestFile}"
info "Completed Network Quality Test"
fi
networkQualityTest=$( < "${networkQualityTestFile}" )
case "${osVersion}" in
11* )
dlThroughput="N/A; macOS ${osVersion}"
dlResponsiveness="N/A; macOS ${osVersion}"
;;
* )
dlThroughput=$( get_json_value "$networkQualityTest" "dl_throughput" )
dlResponsiveness=$( get_json_value "$networkQualityTest" "dl_responsiveness" )
;;
esac
mbps=$( echo "scale=2; ( $dlThroughput / 1000000 )" | bc )
dialogUpdate "listitem: index: ${1}, status: success, statustext: ${mbps} Mbps ${testStatus}"
info "Download: ${mbps} Mbps, Responsiveness: ${dlResponsiveness}; "
dialogUpdate "icon: ${icon}"
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# "Check" Update Computer Inventory
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function checkUpdateComputerInventory() {
notice "Updating Computer Inventory …"
dialogUpdate "icon: SF=pencil.and.list.clipboard,${organizationColorScheme}"
dialogUpdate "listitem: index: ${1}, status: wait, statustext: Updating …"
dialogUpdate "progress: increment"
dialogUpdate "progresstext: Updating Computer Inventory …"
if [[ "${operationMode}" == "production" ]]; then
jamf recon # -verbose
else
sleep "${anticipationDuration}"
fi
dialogUpdate "listitem: index: ${1}, status: success, statustext: Updated"
}
####################################################################################################
#
# Program
#
####################################################################################################
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Create Dialog
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
eval ${dialogBinary} --jsonfile ${dialogJSONFile} --json &
dialogUpdate "progresstext: Initializing …"
# Band-Aid for macOS 15 `withAnimation` SwiftUI bug
dialogUpdate "list: hide"
dialogUpdate "list: show"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Compliance Checks (where "n" represents the listitem order)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
if [[ "${operationMode}" == "production" ]]; then
# Production Mode
checkOS "0"
checkAvailableSoftwareUpdates "1"
checkSIP "2"
checkFirewall "3"
checkFileVault "4"
checkUptime "5"
checkFreeDiskSpace "6"
checkJamfProMdmProfile "7"
checkJssCertificateExpiration "8"
checkAPNs "9"
checkJamfProCheckIn "10"
checkJamfProInventory "11"
checkSetupYourMacValidation "12" "symvBeyondTrustPMfM" "/Applications/PrivilegeManagement.app"
checkSetupYourMacValidation "13" "symvCiscoUmbrella" "/Applications/Cisco/Cisco Secure Client.app"
checkSetupYourMacValidation "14" "symvCrowdStrikeFalcon" "/Applications/Falcon.app"
checkSetupYourMacValidation "15" "symvGlobalProtect" "/Applications/GlobalProtect.app"
checkNetworkQuality "16"
checkUpdateComputerInventory "17"
dialogUpdate "icon: ${icon}"
dialogUpdate "progresstext: Final Analysis …"
sleep "${anticipationDuration}"
else
# Non-production Mode
dialogUpdate "title: ${humanReadableScriptName} (${scriptVersion}) [Operation Mode: ${operationMode}]"
listitemLength=$(get_json_value "${dialogJSON}" "listitem.length")
for (( i=0; i<listitemLength; i++ )); do
notice "[Operation Mode: ${operationMode}] Check ${i} …"
dialogUpdate "icon: SF=${i}.square,${organizationColorScheme}"
dialogUpdate "listitem: index: ${i}, status: wait, statustext: Checking …"
dialogUpdate "progress: increment"
dialogUpdate "progresstext: [Operation Mode: ${operationMode}] • Item No. ${i} …"
# sleep "${anticipationDuration}"
###
#
# START EXAMPLE
#
# Check-specific
#
# code
#
# goes
#
# here
#
# END EXAMPLE
#
###
dialogUpdate "listitem: index: ${i}, status: success, statustext: ${operationMode}"
done
dialogUpdate "icon: ${icon}"
dialogUpdate "progresstext: Final Analysis …"
sleep "${anticipationDuration}"
fi
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Quit Script
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
quitScript