Menu Close

CrowdStrike Falcon Diagnose (0.0.3) with swiftDialog

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.

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 …

… 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
  1. Add the CrowdStrike Falcon diagnose with Progress script to your Jamf Pro server
  2. Specify the following for Options > Parameter Labels
    • Parameter 4: Script Log Location
    • Parameter 5: Estimated Total Seconds
  3. 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
  1. 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
  2. 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
  1. Adjust Scope to your liking; we use our custom CrowdStrike Falcon Smart Computer Group
  2. Use the following for Self Service
    • Self Service Display Name: CrowdStrike Falcon Diagnose
    • Button Name Before Initiation: Diagnose
    • Button Name After Initiation: Diagnose
    • Description:
**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.
  1. 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)
Posted in CrowdStrike Falcon, Jamf Pro, macOS, Scripts, swiftDialog, Tips & Tricks

Related Posts