Last active
July 17, 2026 19:37
-
-
Save angelorobo/313e10a4e32503fb55d8118010f2fa3d to your computer and use it in GitHub Desktop.
Huntress post-install for Swif V2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/zsh | |
| # | |
| # Huntress post-install script for Swif.ai | |
| # | |
| # Before deploying: | |
| # 1. Replace ACCOUNT_KEY with the 32-character Huntress Account Key. | |
| # 2. Replace ORGANIZATION_KEY with the exact Huntress Organization Key. | |
| # 3. Deploy HuntressSystemExtensionProfile.mobileconfig before this app. | |
| # | |
| # This script must run as root. | |
| # | |
| set -u | |
| readonly ACCOUNT_KEY="REPLACE_WITH_32_CHARACTER_ACCOUNT_KEY" | |
| readonly ORGANIZATION_KEY="REPLACE_WITH_EXACT_ORGANIZATION_KEY" | |
| readonly TAGS="" | |
| readonly DEPLOYMENT_SCRIPT_URL="https://raw.githubusercontent.com/huntresslabs/deployment-scripts/refs/heads/main/Bash/mac/InstallHuntress-macOS-bash.sh" | |
| readonly HUNTRESS_PROFILE_IDENTIFIER="com.huntress.app" | |
| readonly HUNTRESS_APP="/Applications/Huntress.app" | |
| readonly HUNTRESS_BINARY="${HUNTRESS_APP}/Contents/MacOS/Huntress" | |
| readonly LOG_FILE="/var/log/HuntressSwifInstaller.log" | |
| TEMP_SCRIPT="" | |
| REINSTALL_REQUIRED=false | |
| log() | |
| { | |
| local message="$*" | |
| echo "$(date '+%Y-%m-%d %H:%M:%S') -- ${message}" | | |
| /usr/bin/tee -a "$LOG_FILE" | |
| } | |
| cleanup() | |
| { | |
| local exit_code=$? | |
| trap - EXIT | |
| if [[ -n "$TEMP_SCRIPT" && -f "$TEMP_SCRIPT" ]]; then | |
| /bin/rm -f "$TEMP_SCRIPT" | |
| fi | |
| exit "$exit_code" | |
| } | |
| trap cleanup EXIT | |
| is_huntress_healthy() | |
| { | |
| if [[ ! -x "$HUNTRESS_BINARY" ]]; then | |
| return 1 | |
| fi | |
| local status_output | |
| status_output="$("$HUNTRESS_BINARY" status 2>&1)" || return 1 | |
| echo "$status_output" | /usr/bin/grep -Eiq \ | |
| 'Full Disk Access for Agent:[[:space:]]*true' || return 1 | |
| echo "$status_output" | /usr/bin/grep -Eiq \ | |
| 'Extension Status:[[:space:]]*installed' || return 1 | |
| echo "$status_output" | /usr/bin/grep -Eiq \ | |
| 'Full Disk Access for Extension:[[:space:]]*true' || return 1 | |
| echo "$status_output" | /usr/bin/grep -Eiq \ | |
| 'EDR status:[[:space:]]*enabled' || return 1 | |
| echo "$status_output" | /usr/bin/grep -Eiq \ | |
| 'Preauthorization Status:[[:space:]]*granted' || return 1 | |
| return 0 | |
| } | |
| log "Starting Huntress installation through Swif.ai." | |
| # | |
| # Confirm root execution. | |
| # | |
| if [[ "$EUID" -ne 0 ]]; then | |
| log "ERROR: This script must run as root." | |
| exit 1 | |
| fi | |
| # | |
| # Protect the log file because it may contain deployment details. | |
| # | |
| /usr/bin/touch "$LOG_FILE" | |
| /bin/chmod 600 "$LOG_FILE" | |
| # | |
| # Validate the Huntress Account Key format. | |
| # | |
| if [[ ! "$ACCOUNT_KEY" =~ ^[[:xdigit:]]{32}$ ]]; then | |
| log "ERROR: ACCOUNT_KEY must be a 32-character hexadecimal Huntress Account Key." | |
| exit 1 | |
| fi | |
| # | |
| # Confirm the Organization Key was configured. | |
| # | |
| if [[ -z "$ORGANIZATION_KEY" || | |
| "$ORGANIZATION_KEY" == "REPLACE_WITH_EXACT_ORGANIZATION_KEY" ]]; then | |
| log "ERROR: ORGANIZATION_KEY has not been configured." | |
| exit 1 | |
| fi | |
| # | |
| # Verify the Huntress configuration profile. | |
| # | |
| # Use 'profiles list' and check the top-level PayloadIdentifier. | |
| # The previous nested-payload check could produce a false negative because | |
| # macOS does not always display nested payload details in 'profiles show'. | |
| # | |
| profile_output="$(/usr/bin/profiles list -type configuration 2>&1 || true)" | |
| if echo "$profile_output" | | |
| /usr/bin/grep -Fq "$HUNTRESS_PROFILE_IDENTIFIER"; then | |
| log "Huntress MDM permission profile is installed." | |
| else | |
| log "ERROR: Huntress configuration profile ${HUNTRESS_PROFILE_IDENTIFIER} is not installed." | |
| log "Deploy HuntressSystemExtensionProfile.mobileconfig before installing Huntress." | |
| log "Current configuration-profile output follows:" | |
| echo "$profile_output" >> "$LOG_FILE" | |
| exit 2 | |
| fi | |
| # | |
| # Treat an existing, fully configured installation as successful. | |
| # | |
| # If Huntress exists but EDR or preauthorization is incomplete, run the | |
| # official deployment script with its reinstall option. | |
| # | |
| if [[ -d "$HUNTRESS_APP" ]]; then | |
| if is_huntress_healthy; then | |
| log "Huntress is already installed, provisioned, and running." | |
| exit 0 | |
| fi | |
| log "An incomplete or unhealthy Huntress installation was found." | |
| log "The deployment script will run with the reinstall option." | |
| REINSTALL_REQUIRED=true | |
| fi | |
| # | |
| # Download the current official Huntress deployment script. | |
| # | |
| TEMP_SCRIPT="$(/usr/bin/mktemp /private/tmp/HuntressDeployment.XXXXXX)" | |
| if [[ -z "$TEMP_SCRIPT" || ! -f "$TEMP_SCRIPT" ]]; then | |
| log "ERROR: Could not create the temporary deployment script." | |
| exit 1 | |
| fi | |
| /usr/bin/curl \ | |
| --fail \ | |
| --location \ | |
| --silent \ | |
| --show-error \ | |
| --retry 3 \ | |
| --retry-delay 5 \ | |
| --connect-timeout 30 \ | |
| "$DEPLOYMENT_SCRIPT_URL" \ | |
| --output "$TEMP_SCRIPT" | |
| curl_exit=$? | |
| if [[ "$curl_exit" -ne 0 ]]; then | |
| log "ERROR: Failed to download the official Huntress deployment script." | |
| log "curl returned exit code ${curl_exit}." | |
| exit "$curl_exit" | |
| fi | |
| if [[ ! -s "$TEMP_SCRIPT" ]]; then | |
| log "ERROR: The downloaded Huntress deployment script is empty." | |
| exit 1 | |
| fi | |
| /bin/chmod 700 "$TEMP_SCRIPT" | |
| # | |
| # Identify Swif.ai as the deployment tool in Huntress support logs. | |
| # | |
| /usr/bin/sed -i '' \ | |
| 's/^rmm="Unspecified RMM"$/rmm="Swif.ai"/' \ | |
| "$TEMP_SCRIPT" | |
| # | |
| # Build the official Huntress deployment arguments. | |
| # | |
| install_arguments=( | |
| -a "$ACCOUNT_KEY" | |
| -o "$ORGANIZATION_KEY" | |
| -i | |
| ) | |
| if [[ -n "$TAGS" ]]; then | |
| install_arguments+=( | |
| -t "$TAGS" | |
| ) | |
| fi | |
| if [[ "$REINSTALL_REQUIRED" == true ]]; then | |
| install_arguments+=( | |
| -r | |
| ) | |
| fi | |
| log "Running the official Huntress deployment script." | |
| # | |
| # Do not use 'set -x' or print the arguments because they contain the | |
| # Huntress Account Key. | |
| # | |
| /bin/zsh "$TEMP_SCRIPT" "${install_arguments[@]}" >> "$LOG_FILE" 2>&1 | |
| install_exit=$? | |
| if [[ "$install_exit" -ne 0 ]]; then | |
| log "ERROR: Huntress deployment failed with exit code ${install_exit}." | |
| log "Review ${LOG_FILE} and /Users/Shared/HuntressInstaller.log." | |
| exit "$install_exit" | |
| fi | |
| log "Huntress deployment script completed successfully." | |
| # | |
| # Allow the system extension and EDR services time to initialize. | |
| # | |
| attempt=1 | |
| maximum_attempts=6 | |
| while [[ "$attempt" -le "$maximum_attempts" ]]; do | |
| if is_huntress_healthy; then | |
| log "Huntress installation and EDR readiness verification succeeded." | |
| exit 0 | |
| fi | |
| log "Huntress readiness check ${attempt}/${maximum_attempts} is not complete." | |
| if [[ "$attempt" -lt "$maximum_attempts" ]]; then | |
| /bin/sleep 5 | |
| fi | |
| attempt=$((attempt + 1)) | |
| done | |
| # | |
| # The Huntress installer succeeded, but activation can be asynchronous. | |
| # Return success here so Munki does not immediately reinstall the package. | |
| # The Swif installcheck_script should independently verify EDR readiness. | |
| # | |
| log "WARNING: The Huntress installer completed, but EDR readiness is still initializing." | |
| if [[ -x "$HUNTRESS_BINARY" ]]; then | |
| log "Current Huntress status:" | |
| "$HUNTRESS_BINARY" status >> "$LOG_FILE" 2>&1 || true | |
| fi | |
| log "Recheck later with: sudo ${HUNTRESS_BINARY} status" | |
| exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment