Provide your users more detailed feedback on CrowdStrike Falcon’s built-in
falconctl diagnosecommand
Background
Testing software from potential vendors frequently involves providing vendor support with client-side logs.
How easily — and timely — you can provide the requested logs will certainly influence the success of your pilot.
We leveraged swiftDialog to provide our users more detailed feedback on CrowdStrike Falcon’s built-in falconctl diagnose command.
Quick-and-dirty falconctl diagnose
When a CrowdStrike Support representative asks for client-side logs, they’ll most likely ask you to run the following Terminal command on the affected Mac:
sudo /Applications/Falcon.app/Contents/Resources/falconctl diagnose
Unless your users are both local administrators and are comfortable with the macOS Terminal, you’ll most likely want to create a Jamf Pro policy, which is made available in Self Service.
One quick-and-dirty approach is to create a new policy in Jamf Pro, then add the Files and Processes payload and enter the supplied command in the Execute Command field:

Your users can then watch Self Service’s built-in spinner for the next five minutes …

… and you can try to keep track of which Mac generated which file:

Realizing we had cracked this nut before …
- User-friendly sysdiagnose
- Inventory Update Progress with swiftDialog
- Disk Usage (0.0.3) with swiftDialog
- User-friendly Adobe Acrobat Add-in Removal
… we proceeded to create a more user-friendly solution.
Diagnose with swiftDialog
To provide a more user-friendly experience, we once again turned to swiftDialog and provided users interactive feedback on the multi-minute falconctl diagnose command.



The Mac’s Serial Number is prepended to the generated filename, which is then moved to /Users/Shared.

Configuration
Complete the following steps to help users easily generate diagnostic logs for CrowdStrike Falcon.
A. Add the CrowdStrike Falcon diagnose with Progress script to your Jamf Pro server
- Add the
CrowdStrike Falcon diagnose with Progressscript to your Jamf Pro server - Specify the following for Options > Parameter Labels
- Parameter 4:
Script Log Location - Parameter 5:
Estimated Total Seconds
- Parameter 4:
- Click Save

Latest version available on GitHub.
#!/bin/bash
####################################################################################################
#
# CrowdStrike Falcon diagnose with Progress
#
# Purpose: Provide more detailed feedback on CrowdStrike Falcon's built-in diagnose command
#
####################################################################################################
#
# HISTORY
#
# Version 0.0.1, 06-Feb-2023, Dan K. Snelson (@dan-snelson)
# Original version
#
# Version 0.0.2, 13-Mar-2023, Dan K. Snelson (@dan-snelson)
# Prepend Serial Number on output file
#
# Version 0.0.3, 14-Mar-2023, Dan K. Snelson (@dan-snelson)
# Modified `find` command (thanks, @Samantha Demi and @Pico)
#
####################################################################################################
####################################################################################################
#
# Variables
#
####################################################################################################
scriptVersion="0.0.3"
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
falconBinary="/Applications/Falcon.app/Contents/Resources/falconctl"
osVersion=$( /usr/bin/sw_vers -productVersion )
osMajorVersion=$( echo "${osVersion}" | /usr/bin/awk -F '.' '{print $1}' )
dialogBinary="/usr/local/bin/dialog"
dialogCommandLog=$( mktemp /var/tmp/dialogCommandLog.XXX )
serialNumber=$( system_profiler SPHardwareDataType | grep "Serial Number" | awk -F ": " '{ print $2 }' )
scriptLog="${4:-"/var/tmp/com.company.log"}" # Parameter 4: Full path to your company's client-side log
estimatedTotalSeconds="${5:-"333"}" # Parameter 5: Estimated number of seconds to complete diagnose
####################################################################################################
#
# Pre-flight Checks
#
####################################################################################################
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pre-flight Check: Client-side Logging
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
if [[ ! -f "${scriptLog}" ]]; then
touch "${scriptLog}"
fi
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pre-flight Check: Client-side Script Logging Function
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function updateScriptLog() {
echo -e "$( date +%Y-%m-%d\ %H:%M:%S ) - ${1}" | tee -a "${scriptLog}"
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pre-flight Check: Logging Preamble
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
updateScriptLog "\n\n###\n# CrowdStrike Falcon diagnose with Progress (${scriptVersion})\n###\n"
updateScriptLog "PRE-FLIGHT CHECK: Initiating …"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pre-flight Check: Confirm script is running as root
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
if [[ $(id -u) -ne 0 ]]; then
updateScriptLog "PRE-FLIGHT CHECK: This script must be run as root; exiting."
exit 1
fi
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pre-flight Check: Validate Operating System
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
if [[ "${osMajorVersion}" -ge 11 ]] ; then
updateScriptLog "PRE-FLIGHT CHECK: macOS ${osMajorVersion} installed; proceeding …"
else
updateScriptLog "PRE-FLIGHT CHECK: macOS ${osMajorVersion} installed; exiting."
exit 1
fi
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pre-flight Check: Confirm CrowdStrike Falcon is installed
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
if [[ -f "${falconBinary}" ]]; then
updateScriptLog "PRE-FLIGHT CHECK: CrowdStrike Falcon installed; proceeding …"
else
updateScriptLog "PRE-FLIGHT CHECK: CrowdStrike Falcon NOT found; exiting."
exit 1
fi
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pre-flight Check: Confirm Apple's sysdiagnose directory is empty / Delete any previous diagnose files
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
if [[ -d "/private/var/db/sysdiagnose/" ]]; then
updateScriptLog "PRE-FLIGHT CHECK: sysdiagnose directory found; deleting …"
rm -Rf /private/var/db/sysdiagnose/
rm /private/tmp/falconctl_diagnose_*
rm /Users/Shared/"${serialNumber}_"*
fi
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pre-flight Check: Check for / install swiftDialog (Thanks big bunches, @acodega!)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function dialogCheck() {
# Get the URL of the latest PKG From the Dialog GitHub repo
dialogURL=$(curl --silent --fail "https://api.github.com/repos/bartreardon/swiftDialog/releases/latest" | awk -F '"' "/browser_download_url/ && /pkg\"/ { print \$4; exit }")
# Expected Team ID of the downloaded PKG
expectedDialogTeamID="PWA5E9TQ59"
# Check for Dialog and install if not found
if [ ! -e "/Library/Application Support/Dialog/Dialog.app" ]; then
updateScriptLog "PRE-FLIGHT CHECK: Dialog not found. Installing..."
# Create temporary working directory
workDirectory=$( /usr/bin/basename "$0" )
tempDirectory=$( /usr/bin/mktemp -d "/private/tmp/$workDirectory.XXXXXX" )
# Download the installer package
/usr/bin/curl --location --silent "$dialogURL" -o "$tempDirectory/Dialog.pkg"
# Verify the download
teamID=$(/usr/sbin/spctl -a -vv -t install "$tempDirectory/Dialog.pkg" 2>&1 | awk '/origin=/ {print $NF }' | tr -d '()')
# Install the package if Team ID validates
if [[ "$expectedDialogTeamID" == "$teamID" ]]; then
/usr/sbin/installer -pkg "$tempDirectory/Dialog.pkg" -target /
sleep 2
dialogVersion=$( /usr/local/bin/dialog --version )
updateScriptLog "PRE-FLIGHT CHECK: swiftDialog version ${dialogVersion} installed; proceeding..."
else
# Display a so-called "simple" dialog if Team ID fails to validate
osascript -e 'display dialog "Please advise your Support Representative of the following error:\r\r• Dialog Team ID verification failed\r\r" with title "Setup Your Mac: Error" buttons {"Close"} with icon caution'
quitScript "1"
fi
# Remove the temporary working directory when done
/bin/rm -Rf "$tempDirectory"
else
updateScriptLog "PRE-FLIGHT CHECK: swiftDialog version $( /usr/local/bin/dialog --version) found; proceeding..."
fi
}
if [[ ! -e "/Library/Application Support/Dialog/Dialog.app" ]]; then
dialogCheck
else
updateScriptLog "PRE-FLIGHT CHECK: swiftDialog version $(dialog --version) found; proceeding..."
fi
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Pre-flight Checks Complete
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
updateScriptLog "PRE-FLIGHT CHECK: Complete"
####################################################################################################
#
# Functions
#
####################################################################################################
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Update Dialog
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function updateDialog() {
echo "${1}" >> "${dialogCommandLog}"
sleep 0.4
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Quit Script (thanks, @bartreadon!)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
function quitScript() {
updateScriptLog "QUIT SCRIPT: Exiting …"
updateDialog "quit:"
# Remove dialogCommandLog
if [[ -e ${dialogCommandLog} ]]; then
updateScriptLog "QUIT SCRIPT: Removing ${dialogCommandLog} …"
rm "${dialogCommandLog}"
fi
# Remove any default dialog file
if [[ -e /var/tmp/dialog.log ]]; then
updateScriptLog "QUIT SCRIPT: Removing default dialog file …"
rm /var/tmp/dialog.log
fi
exit "${1}"
}
####################################################################################################
#
# General Dialog Variables
#
####################################################################################################
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Dialog Title, Message and Icon
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
title="CrowdStrike Falcon Diagnose (${scriptVersion})"
message="Please wait while a diagnosis is performed …"
icon="https://ics.services.jamfcloud.com/icon/hash_37bf84a34fb6d957fab0718cbf9dfea0a54562db2cd9ecfe8e16cdbe5a24197c"
# overlay=$( defaults read /Library/Preferences/com.jamfsoftware.jamf.plist self_service_app_path )
progressText="Initializing …"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Dialog Settings and Features
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
dialogFalconctlDiagnose="$dialogBinary \
--title \"$title\" \
--message \"$message\" \
--icon \"$icon\" \
--mini \
--position bottomright \
--moveable \
--progress \
--progresstext \"$progressText\" \
--quitkey K \
--commandfile \"$dialogCommandLog\" "
# --overlayicon \"$overlay\" \
####################################################################################################
#
# Program
#
####################################################################################################
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# falconctl diagnose progress
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
updateScriptLog "Create Progress Dialog …"
eval "$dialogFalconctlDiagnose" & sleep 0.5
updateScriptLog "Starting falconctl diagnose …"
SECONDS="0"
eval "$falconBinary diagnose" &
sleep 2
while [[ -n $(pgrep "sysdiagnose_helper") ]]; do
progressPercentage=$( echo "scale=2 ; ( $SECONDS / $estimatedTotalSeconds ) * 100" | bc | sed 's/\.00//g' )
updateDialog "progress: ${progressPercentage}"
updateDialog "progresstext: ${progressPercentage}%"
done
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Update results in diagnose progress
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
updateScriptLog "QUIT SCRIPT: Diagnose Sucessful"
updateDialog "icon: SF=checkmark.circle.fill,weight=bold,colour1=#00ff44,colour2=#075c1e"
updateDialog "message: CrowdStrike Falcon Diagnose Complete"
updateDialog "progress: 100"
updateDialog "progresstext: Elapsed Time: $(printf '%dh:%dm:%ds\n' $((SECONDS/3600)) $((SECONDS%3600/60)) $((SECONDS%60)))"
updateScriptLog "Elapsed Time: $(printf '%dh:%dm:%ds\n' $((SECONDS/3600)) $((SECONDS%3600/60)) $((SECONDS%60)))"
sleep 5
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Wait for 'falconctl_diagnose_' directory to be removed
# See: https://macadmins.slack.com/archives/C07MGJ2SD/p1678735637841009
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
updateScriptLog "Wait for 'falconctl_diagnose_' directory to be removed from /private/tmp/ …"
updateDialog "icon: SF=deskclock.fill,weight=bold,colour1=#0066ff,colour2=#003380"
updateDialog "message: Waiting for file output …"
updateDialog "progresstext: Please wait …"
updateDialog "progress: reset"
until [[ -z "$( find /private/tmp -name "falconctl_diagnose_*" -type d )" ]]; do
updateScriptLog "Pausing for one second before re-checking …"
updateDialog "progress: increment 6"
sleep 1
done
updateDialog "progress: 100"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Prepend output with Serial Number
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
originalFilename=$( find /private/tmp -name "falconctl_diagnose_*" -type f -print0 | xargs basename )
updateScriptLog "Original Filename: ${originalFilename}"
updateScriptLog "Move ${originalFilename} to /User/Shared/ …"
mv -v "/private/tmp/${originalFilename}" "/Users/Shared/${serialNumber}_${originalFilename}"
updateScriptLog "Reveal /User/Shared/${serialNumber}_${originalFilename}"
open -R "/Users/Shared/${serialNumber}_${originalFilename}"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Exit
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
quitScript "0"
B. Create a Jamf Pro Policy to execute CrowdStrike Falcon diagnose
- Create a new Jamf Pro Policy, using the following as a guide for Options > General:
- Set Display Name to
CrowdStrike Falcon Diagnose
- Set Execution Frequency to
Ongoing
- Set Display Name to
- Select the Scripts payload and add the
CrowdStrike Falcon diagnose with Progressscript, specifying the following Parameter Values- Script Log Location:
/var/log/com.company.log
- Expected Duration (in seconds):
391
- Script Log Location:

- Adjust Scope to your liking; we use our custom CrowdStrike Falcon Smart Computer Group
- Use the following for Self Service
- Self Service Display Name:
CrowdStrike Falcon Diagnose - Button Name Before Initiation:
Diagnose - Button Name After Initiation:
Diagnose
- Description:
- Self Service Display Name:
**For use as directed by Support representatives** Executes CrowdStrike Falcon's built-in `diagnose` and outputs the results to the `/Users/Shared/` directory. (Please forward the output to your support representative.) See [KB0131413](https://servicenow.company.com/support?id=kb_article_view&sysparm_article=KB0131413) for additional details.
- Click Save
Testing
Expected Duration
Ask a few of your opt-in Beta Tester power-users to execute the policy, then review the policy logs, searching for the phrase Elapsed Time immediately followed by the duration:
2023-03-13 19:57:14 - Elapsed Time: 0h:4m:43s
In early testing, when the Expected Duration value was too low, we observed some entertaining percentages:

Updates
0.0.3 (14-Mar-2023)
- Modified
findcommand (thanks, @Samantha Demi and @Pico)