Provide your users more detailed feedback on CrowdStrike Falcon’s built-in
falconctl diagnose
command
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.
falconctl diagnose
Quick-and-dirty 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.
swiftDialog
Diagnose with 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 Progress
script 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 Progress
script, 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
find
command (thanks, @Samantha Demi and @Pico)